diff --git a/binary/src/main/java/net/daporkchop/lib/binary/buffer/PorkBuf.java b/binary/src/main/java/net/daporkchop/lib/binary/buffer/PorkBuf.java new file mode 100644 index 000000000..883b2490c --- /dev/null +++ b/binary/src/main/java/net/daporkchop/lib/binary/buffer/PorkBuf.java @@ -0,0 +1,687 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.binary.buffer; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.common.misc.refcount.RefCounted; +import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; + +import java.nio.ByteBuffer; + +/** + * A buffer interface heavily inspired by Netty's {@link io.netty.buffer.ByteBuf}, but intended for use with 64-bit systems + * where Netty's restriction to {@code int} for indexing is a limiting factor. + *

+ * Additionally, in order to achieve maximum performance and maintain simplicity, all implementations are assumed to be backed by + * direct memory. + *

+ * Unless specifically stated by an implementation, instances of a {@link PorkBuf} are not expected to be thread-safe. + * + * @author DaPorkchop_ + */ +public abstract class PorkBuf implements RefCounted { + // + // + // Indexing methods + // + // + + /** + * @return this {@link PorkBuf}'s current reader index + */ + public abstract long readerIndex(); + + /** + * Sets this {@link PorkBuf}'s reader index. + * + * @param readerIndex the new reader index to set + * @throws IndexOutOfBoundsException if the given reader index is less than {@code 0}, or greater than {@link #writerIndex()} + */ + public abstract PorkBuf readerIndex(long readerIndex) throws IndexOutOfBoundsException; + + /** + * @return this {@link PorkBuf}'s current writer index + */ + public abstract long writerIndex(); + + /** + * Sets this {@link PorkBuf}'s writer index. + * + * @param writerIndex the new writer index to set + * @throws IndexOutOfBoundsException if the given writer index is less than {@link #readerIndex()}, or greater than {@link #capacity()} + */ + public abstract PorkBuf writerIndex(long writerIndex) throws IndexOutOfBoundsException; + + /** + * @return this {@link PorkBuf}'s current capacity. Will always be less than or equal to {@link #maxCapacity()}. + */ + public abstract long capacity(); + + /** + * @return this {@link PorkBuf}'s maximum capacity + */ + public abstract long maxCapacity(); + + /** + * Ensures that the given number of bytes are writable in this buffer. + *

+ * This will expand the buffer's {@link #capacity()} if needed. + * + * @param count the number of writable bytes to ensure + * @throws IndexOutOfBoundsException if {@link #writerIndex()} + {@code count} > {@link #maxCapacity()} + */ + public abstract PorkBuf ensureWritable(long count) throws IndexOutOfBoundsException; + + /** + * Ensures that the buffer's capacity is at least the given size. + *

+ * This will expand the buffer's {@link #capacity()} if needed. + * + * @param count the capacity to ensure + * @throws IndexOutOfBoundsException if {@code count} > {@link #maxCapacity()} + */ + public abstract PorkBuf ensureCapacity(long count) throws IndexOutOfBoundsException; + + /** + * Increases the buffer's {@link #readerIndex()} by the given number of bytes. + * + * @param count the number of bytes to skip + * @throws IndexOutOfBoundsException if {@link #readerIndex()} + {@code count} > {@link #writerIndex()} + */ + public abstract PorkBuf skip(long count) throws IndexOutOfBoundsException; + + /** + * @return the number of readable bytes in this {@link PorkBuf} + */ + public abstract long readableBytes(); + + /** + * @return the number of writable bytes in this {@link PorkBuf} + */ + public abstract long writableBytes(); + + // + // + // Indexed write methods + // + // + + /** + * Sets a {@code boolean} at the given index. + * + * @param index the index of the {@code boolean} to set + * @param val the value to set + */ + public abstract PorkBuf setBoolean(long index, boolean val); + + /** + * Sets a {@code byte} at the given index. + * + * @param index the index of the {@code byte} to set + * @param val the value to set + */ + public abstract PorkBuf setByte(long index, byte val); + + /** + * Sets a {@code byte} at the given index. + * + * @param index the index of the {@code byte} to set + * @param val the value to set + */ + public abstract PorkBuf setByte(long index, int val); + + /** + * Sets a big-endian {@code short} at the given index. + * + * @param index the index of the {@code short} to set + * @param val the value to set + */ + public abstract PorkBuf setShort(long index, short val); + + /** + * Sets a little-endian {@code short} at the given index. + * + * @param index the index of the {@code short} to set + * @param val the value to set + */ + public abstract PorkBuf setShortLE(long index, short val); + + /** + * Sets a {@code short} at the given index using the native byte order. + * + * @param index the index of the {@code short} to set + * @param val the value to set + */ + public abstract PorkBuf setShortN(long index, short val); + + /** + * Sets a big-endian {@code char} at the given index. + * + * @param index the index of the {@code char} to set + * @param val the value to set + */ + public abstract PorkBuf setChar(long index, char val); + + /** + * Sets a little-endian {@code char} at the given index. + * + * @param index the index of the {@code char} to set + * @param val the value to set + */ + public abstract PorkBuf setCharLE(long index, char val); + + /** + * Sets a {@code char} at the given index using the native byte order. + * + * @param index the index of the {@code char} to set + * @param val the value to set + */ + public abstract PorkBuf setCharN(long index, char val); + + /** + * Sets a big-endian {@code int} at the given index. + * + * @param index the index of the {@code int} to set + * @param val the value to set + */ + public abstract PorkBuf setInt(long index, int val); + + /** + * Sets a little-endian {@code int} at the given index. + * + * @param index the index of the {@code int} to set + * @param val the value to set + */ + public abstract PorkBuf setIntLE(long index, int val); + + /** + * Sets a {@code int} at the given index using the native byte order. + * + * @param index the index of the {@code int} to set + * @param val the value to set + */ + public abstract PorkBuf setIntN(long index, int val); + + /** + * Sets a big-endian {@code long} at the given index. + * + * @param index the index of the {@code long} to set + * @param val the value to set + */ + public abstract PorkBuf setLong(long index, long val); + + /** + * Sets a little-endian {@code long} at the given index. + * + * @param index the index of the {@code long} to set + * @param val the value to set + */ + public abstract PorkBuf setLongLE(long index, long val); + + /** + * Sets a {@code long} at the given index using the native byte order. + * + * @param index the index of the {@code long} to set + * @param val the value to set + */ + public abstract PorkBuf setLongN(long index, long val); + + /** + * Sets a big-endian {@code float} at the given index. + * + * @param index the index of the {@code float} to set + * @param val the value to set + */ + public abstract PorkBuf setFloat(long index, float val); + + /** + * Sets a little-endian {@code float} at the given index. + * + * @param index the index of the {@code float} to set + * @param val the value to set + */ + public abstract PorkBuf setFloatLE(long index, float val); + + /** + * Sets a {@code float} at the given index using the native byte order. + * + * @param index the index of the {@code float} to set + * @param val the value to set + */ + public abstract PorkBuf setFloatN(long index, float val); + + /** + * Sets a big-endian {@code double} at the given index. + * + * @param index the index of the {@code double} to set + * @param val the value to set + */ + public abstract PorkBuf setDouble(long index, double val); + + /** + * Sets a little-endian {@code double} at the given index. + * + * @param index the index of the {@code double} to set + * @param val the value to set + */ + public abstract PorkBuf setDoubleLE(long index, double val); + + /** + * Sets a {@code double} at the given index using the native byte order. + * + * @param index the index of the {@code double} to set + * @param val the value to set + */ + public abstract PorkBuf setDoubleN(long index, double val); + + /** + * Equivalent to {@code setBytes(index, arr, 0, arr.length);} + * + * @see #setBytes(long, byte[], int, int) + */ + public final PorkBuf setBytes(long index, @NonNull byte[] arr) { + return this.setBytes(index, arr, 0, arr.length); + } + + /** + * Sets the bytes at the given index by getting them from the given array. + * + * @param index the first index of the bytes to set + * @param arr the {@code byte[]} containing the new bytes + * @param start the first index in the array + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given array, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull byte[] arr, int start, int length) throws IndexOutOfBoundsException; + + /** + * Equivalent to {@code setBytes(index, buf, buf.readableBytes());} + * + * @see #setBytes(long, PorkBuf, long) + */ + public final PorkBuf setBytes(long index, @NonNull PorkBuf buf) throws IndexOutOfBoundsException { + return this.setBytes(index, buf, buf.readableBytes()); + } + + /** + * Sets the bytes at the given index by reading them from the given {@link PorkBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link PorkBuf} containing the new bytes + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code length} is out of bounds of the given {@link PorkBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull PorkBuf buf, long length) throws IndexOutOfBoundsException; + + /** + * Sets the bytes at the given index by getting them from the given {@link PorkBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link PorkBuf} containing the new bytes + * @param start the first index in the {@link PorkBuf} + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given {@link PorkBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull PorkBuf buf, long start, long length) throws IndexOutOfBoundsException; + + /** + * Equivalent to {@code setBytes(index, buf, buf.readableBytes());} + * + * @see #setBytes(long, ByteBuf, int) + */ + public final PorkBuf setBytes(long index, @NonNull ByteBuf buf) throws IndexOutOfBoundsException { + return this.setBytes(index, buf, buf.readableBytes()); + } + + /** + * Sets the bytes at the given index by reading them from the given {@link ByteBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuf} containing the new bytes + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code length} is out of bounds of the given {@link ByteBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuf buf, int length) throws IndexOutOfBoundsException; + + /** + * Sets the bytes at the given index by getting them from the given {@link ByteBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuf} containing the new bytes + * @param start the first index in the {@link ByteBuf} + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given {@link ByteBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuf buf, int start, int length) throws IndexOutOfBoundsException; + + /** + * Equivalent to {@code setBytes(index, buf, buf.readableBytes());} + * + * @see #setBytes(long, ByteBuffer, int) + */ + public PorkBuf setBytes(long index, @NonNull ByteBuffer buf) throws IndexOutOfBoundsException { + return this.setBytes(index, buf, buf.remaining()); + } + + /** + * Sets the bytes at the given index by reading them from the given {@link ByteBuffer}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuffer} containing the new bytes + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code length} is out of bounds of the given {@link ByteBuffer}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuffer buf, int length) throws IndexOutOfBoundsException; + + /** + * Sets the bytes at the given index by getting them from the given {@link ByteBuffer}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuffer} containing the new bytes + * @param start the first index in the {@link ByteBuffer} + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given {@link ByteBuffer}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuffer buf, int start, int length) throws IndexOutOfBoundsException; + + // + // + // Indexed read methods + // + // + + /** + * Gets a {@code boolean} at the given index. + * + * @param index the index of the {@code boolean} to get + */ + public abstract boolean getBoolean(long index); + + /** + * Gets a {@code byte} at the given index. + * + * @param index the index of the {@code byte} to get + */ + public abstract byte getByte(long index); + + /** + * Gets a big-endian {@code short} at the given index. + * + * @param index the index of the {@code short} to get + */ + public abstract short getShort(long index); + + /** + * Gets a little-endian {@code short} at the given index. + * + * @param index the index of the {@code short} to get + */ + public abstract short getShortLE(long index); + + /** + * Gets a {@code short} at the given index using the native byte order. + * + * @param index the index of the {@code short} to get + */ + public abstract short getShortN(long index); + + /** + * Gets a big-endian {@code char} at the given index. + * + * @param index the index of the {@code char} to get + */ + public abstract char getChar(long index); + + /** + * Gets a little-endian {@code char} at the given index. + * + * @param index the index of the {@code char} to get + */ + public abstract char getCharLE(long index); + + /** + * Gets a {@code char} at the given index using the native byte order. + * + * @param index the index of the {@code char} to get + */ + public abstract char getCharN(long index); + + /** + * Gets a big-endian {@code int} at the given index. + * + * @param index the index of the {@code int} to get + */ + public abstract int getInt(long index); + + /** + * Gets a little-endian {@code int} at the given index. + * + * @param index the index of the {@code int} to get + */ + public abstract int getIntLE(long index); + + /** + * Gets a {@code int} at the given index using the native byte order. + * + * @param index the index of the {@code int} to get + */ + public abstract int getIntN(long index); + + /** + * Gets a big-endian {@code long} at the given index. + * + * @param index the index of the {@code long} to get + */ + public abstract long getLong(long index); + + /** + * Gets a little-endian {@code long} at the given index. + * + * @param index the index of the {@code long} to get + */ + public abstract long getLongLE(long index); + + /** + * Gets a {@code long} at the given index using the native byte order. + * + * @param index the index of the {@code long} to get + */ + public abstract long getLongN(long index); + + /** + * Gets a big-endian {@code float} at the given index. + * + * @param index the index of the {@code float} to get + */ + public abstract float getFloat(long index); + + /** + * Gets a little-endian {@code float} at the given index. + * + * @param index the index of the {@code float} to get + */ + public abstract float getFloatLE(long index); + + /** + * Gets a {@code float} at the given index using the native byte order. + * + * @param index the index of the {@code float} to get + */ + public abstract float getFloatN(long index); + + /** + * Gets a big-endian {@code double} at the given index. + * + * @param index the index of the {@code double} to get + */ + public abstract double getDouble(long index); + + /** + * Gets a little-endian {@code double} at the given index. + * + * @param index the index of the {@code double} to get + */ + public abstract double getDoubleLE(long index); + + /** + * Gets a {@code double} at the given index using the native byte order. + * + * @param index the index of the {@code double} to get + */ + public abstract double getDoubleN(long index); + + /** + * Equivalent to {@code setBytes(index, arr, 0, arr.length);} + * + * @see #setBytes(long, byte[], int, int) + */ + public final PorkBuf getBytes(long index, @NonNull byte[] arr) { + return this.setBytes(index, arr, 0, arr.length); + } + + /** + * Sets the bytes at the given index by getting them from the given array. + * + * @param index the first index of the bytes to set + * @param arr the {@code byte[]} containing the new bytes + * @param start the first index in the array + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given array, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull byte[] arr, int start, int length) throws IndexOutOfBoundsException; + + /** + * Equivalent to {@code setBytes(index, buf, buf.readableBytes());} + * + * @see #setBytes(long, PorkBuf, long) + */ + public final PorkBuf setBytes(long index, @NonNull PorkBuf buf) throws IndexOutOfBoundsException { + return this.setBytes(index, buf, buf.readableBytes()); + } + + /** + * Sets the bytes at the given index by reading them from the given {@link PorkBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link PorkBuf} containing the new bytes + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code length} is out of bounds of the given {@link PorkBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull PorkBuf buf, long length) throws IndexOutOfBoundsException; + + /** + * Sets the bytes at the given index by getting them from the given {@link PorkBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link PorkBuf} containing the new bytes + * @param start the first index in the {@link PorkBuf} + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given {@link PorkBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull PorkBuf buf, long start, long length) throws IndexOutOfBoundsException; + + /** + * Equivalent to {@code setBytes(index, buf, buf.readableBytes());} + * + * @see #setBytes(long, ByteBuf, int) + */ + public final PorkBuf setBytes(long index, @NonNull ByteBuf buf) throws IndexOutOfBoundsException { + return this.setBytes(index, buf, buf.readableBytes()); + } + + /** + * Sets the bytes at the given index by reading them from the given {@link ByteBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuf} containing the new bytes + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code length} is out of bounds of the given {@link ByteBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuf buf, int length) throws IndexOutOfBoundsException; + + /** + * Sets the bytes at the given index by getting them from the given {@link ByteBuf}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuf} containing the new bytes + * @param start the first index in the {@link ByteBuf} + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given {@link ByteBuf}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuf buf, int start, int length) throws IndexOutOfBoundsException; + + /** + * Equivalent to {@code setBytes(index, buf, buf.readableBytes());} + * + * @see #setBytes(long, ByteBuffer, int) + */ + public PorkBuf setBytes(long index, @NonNull ByteBuffer buf) throws IndexOutOfBoundsException { + return this.setBytes(index, buf, buf.remaining()); + } + + /** + * Sets the bytes at the given index by reading them from the given {@link ByteBuffer}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuffer} containing the new bytes + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code length} is out of bounds of the given {@link ByteBuffer}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuffer buf, int length) throws IndexOutOfBoundsException; + + /** + * Sets the bytes at the given index by getting them from the given {@link ByteBuffer}. + * + * @param index the first index of the bytes to set + * @param buf the {@link ByteBuffer} containing the new bytes + * @param start the first index in the {@link ByteBuffer} + * @param length the number of bytes to set + * @throws IndexOutOfBoundsException if {@code start} and {@code length} are out of bounds of the given {@link ByteBuffer}, or {@code index} + {@code length} > {@link #capacity()} + */ + public abstract PorkBuf setBytes(long index, @NonNull ByteBuffer buf, int start, int length) throws IndexOutOfBoundsException; + + // + // + // General methods + // + // + + /** + * @return whether or not this {@link PorkBuf} is read-only + */ + public abstract boolean readOnly(); + + /** + * @return the base memory address of this {@link PorkBuf}'s internal buffer + */ + public abstract long memoryAddress(); + + // + // + // RefCounted methods + // + // + + @Override + public abstract int refCnt(); + + @Override + public abstract PorkBuf retain() throws AlreadyReleasedException; + + @Override + public abstract boolean release() throws AlreadyReleasedException; +} diff --git a/binary/src/main/java/net/daporkchop/lib/binary/buffer/impl/AbstractPorkBuf.java b/binary/src/main/java/net/daporkchop/lib/binary/buffer/impl/AbstractPorkBuf.java new file mode 100644 index 000000000..b472dcd93 --- /dev/null +++ b/binary/src/main/java/net/daporkchop/lib/binary/buffer/impl/AbstractPorkBuf.java @@ -0,0 +1,358 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.binary.buffer.impl; + +import io.netty.buffer.ByteBuf; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.daporkchop.lib.binary.buffer.PorkBuf; +import net.daporkchop.lib.common.misc.AlignedUnsafe; +import net.daporkchop.lib.common.util.PorkUtil; +import net.daporkchop.lib.unsafe.Endianess; +import net.daporkchop.lib.unsafe.PUnsafe; +import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; + +import java.nio.ByteBuffer; + +/** + * A base implementation of {@link PorkBuf} that implements the basic behaviors of reader and writer indices. + * + * @author DaPorkchop_ + */ +@Getter +@Accessors(fluent = true) +public abstract class AbstractPorkBuf extends PorkBuf { + protected static void validateIndices(long readerIndex, long writerIndex, long capacity) throws IndexOutOfBoundsException { + if (readerIndex < 0L || writerIndex < readerIndex || capacity < writerIndex) { + throw new IndexOutOfBoundsException(String.format("readerIndex: %d, writerIndex: %d, capacity: %d", readerIndex, writerIndex, capacity)); + } + } + + protected static void validateBounds(long index, long length, long capacity) throws IndexOutOfBoundsException { + if (index < 0L || length < 0L || capacity < index + length) { + throw new IndexOutOfBoundsException(String.format("index: %d, length: %d, capacity: %d", index, length, capacity)); + } + } + + private long readerIndex; + private long writerIndex; + + @Setter(AccessLevel.PROTECTED) + private long capacity; + @Setter(AccessLevel.PROTECTED) //TODO: use actual setter implementations here + private long maxCapacity; + + private long memoryAddress; + + @Override + public PorkBuf readerIndex(long readerIndex) throws IndexOutOfBoundsException { + validateIndices(readerIndex, this.writerIndex, this.capacity); + this.readerIndex = readerIndex; + return this; + } + + @Override + public PorkBuf writerIndex(long writerIndex) throws IndexOutOfBoundsException { + validateIndices(this.readerIndex, writerIndex, this.capacity); + this.writerIndex = writerIndex; + return this; + } + + @Override + public PorkBuf ensureWritable(long count) throws IndexOutOfBoundsException { + if (count < 0L) { + throw new IllegalArgumentException(String.valueOf(count)); + } + return this.ensureCapacity(this.writerIndex + count); + } + + @Override + public PorkBuf ensureCapacity(long count) throws IndexOutOfBoundsException { + if (count < 0L) { + throw new IllegalArgumentException(String.valueOf(count)); + } else if (count > this.capacity) { + if (count > this.maxCapacity) { + throw new IndexOutOfBoundsException(String.format("count: %d, maxCapacity: %d", count, this.maxCapacity)); + } + + //TODO: smarter + synchronized (this) { + long targetCapacity = this.capacity; + do { + targetCapacity = Math.min(this.maxCapacity, targetCapacity << 1L); + } while (targetCapacity < count); + + this.expand0(this.capacity, targetCapacity); + this.capacity = targetCapacity; + } + } + return this; + } + + @Override + public PorkBuf skip(long count) throws IndexOutOfBoundsException { + if (count < 0L) { + throw new IllegalArgumentException(String.valueOf(count)); + } + long readerIndex = this.readerIndex; + validateIndices(readerIndex + count, this.writerIndex, this.capacity); + this.readerIndex = readerIndex + count; + return this; + } + + @Override + public long readableBytes() { + return this.writerIndex - this.readerIndex; + } + + @Override + public long writableBytes() { + return this.capacity - this.writerIndex; + } + + /** + * Expands this {@link PorkBuf}'s capacity. + * + * @param from the current capacity + * @param capacity the capacity to expand to + */ + protected abstract void expand0(long from, long capacity); + + // + // + // Indexed write methods + // + // + + + @Override + public PorkBuf setBoolean(long index, boolean val) { + return this.setByte(index, val ? (byte) 1 : 0); + } + + @Override + public PorkBuf setByte(long index, byte val) { + validateBounds(index, 1L, this.capacity); + PUnsafe.putByte(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setByte(long index, int val) { + validateBounds(index, 1L, this.capacity); + PUnsafe.putByte(this.memoryAddress + index, (byte) val); + return this; + } + + @Override + public PorkBuf setShort(long index, short val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putShortBE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setShortLE(long index, short val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putShortLE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setShortN(long index, short val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putShortN(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setChar(long index, char val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putCharBE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setCharLE(long index, char val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putCharLE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setCharN(long index, char val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putCharN(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setInt(long index, int val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putIntBE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setIntLE(long index, int val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putIntLE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setIntN(long index, int val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putIntN(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setLong(long index, long val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putLongBE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setLongLE(long index, long val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putLongLE(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setLongN(long index, long val) { + validateBounds(index, 2L, this.capacity); + AlignedUnsafe.putLongN(this.memoryAddress + index, val); + return this; + } + + @Override + public PorkBuf setFloat(long index, float val) { + return this.setInt(index, Float.floatToRawIntBits(val)); + } + + @Override + public PorkBuf setFloatLE(long index, float val) { + return this.setIntLE(index, Float.floatToRawIntBits(val)); + } + + @Override + public PorkBuf setFloatN(long index, float val) { + return this.setIntN(index, Float.floatToRawIntBits(val)); + } + + @Override + public PorkBuf setDouble(long index, double val) { + return this.setLong(index, Double.doubleToRawLongBits(val)); + } + + @Override + public PorkBuf setDoubleLE(long index, double val) { + return this.setLongLE(index, Double.doubleToRawLongBits(val)); + } + + @Override + public PorkBuf setDoubleN(long index, double val) { + return this.setLongN(index, Double.doubleToRawLongBits(val)); + } + + @Override + public PorkBuf setBytes(long index, @NonNull byte[] arr, int start, int length) throws IndexOutOfBoundsException { + PorkUtil.assertInRangeLen(arr.length, start, length); + validateBounds(index, length, this.capacity); + PUnsafe.copyMemory(arr, PUnsafe.ARRAY_BYTE_BASE_OFFSET + start, null, this.memoryAddress + index, length); + return this; + } + + @Override + public PorkBuf setBytes(long index, @NonNull PorkBuf buf, long length) throws IndexOutOfBoundsException { + long readerIndex = buf.readerIndex(); + validateBounds(readerIndex, length, buf.capacity()); + validateBounds(index, length, this.capacity); + PUnsafe.copyMemory(buf.memoryAddress() + readerIndex, this.memoryAddress + index, length); + buf.readerIndex(readerIndex + length); + return this; + } + + @Override + public PorkBuf setBytes(long index, @NonNull PorkBuf buf, long start, long length) throws IndexOutOfBoundsException { + validateBounds(start, length, buf.capacity()); + validateBounds(index, length, this.capacity); + PUnsafe.copyMemory(buf.memoryAddress() + start, this.memoryAddress + index, length); + return this; + } + + @Override + public PorkBuf setBytes(long index, @NonNull ByteBuf buf, int length) throws IndexOutOfBoundsException { + int readerIndex = buf.readerIndex(); + validateBounds(readerIndex, length, buf.capacity()); + validateBounds(index, length, this.capacity); + if (buf.hasArray()) { + PUnsafe.copyMemory(buf.array(), PUnsafe.ARRAY_BYTE_BASE_OFFSET + buf.arrayOffset() + readerIndex, null, this.memoryAddress + index, length); + } else if (buf.hasMemoryAddress()) { + PUnsafe.copyMemory(buf.memoryAddress() + readerIndex, this.memoryAddress + index, length); + } else { + throw new IllegalArgumentException(); + } + buf.skipBytes(length); + return this; + } + + @Override + public PorkBuf setBytes(long index, @NonNull ByteBuf buf, int start, int length) throws IndexOutOfBoundsException { + validateBounds(start, length, buf.capacity()); + validateBounds(index, length, this.capacity); + if (buf.hasArray()) { + PUnsafe.copyMemory(buf.array(), PUnsafe.ARRAY_BYTE_BASE_OFFSET + buf.arrayOffset() + buf.readerIndex(), null, this.memoryAddress + index, length); + } else if (buf.hasMemoryAddress()) { + PUnsafe.copyMemory(buf.memoryAddress() + buf.readerIndex(), this.memoryAddress + index, length); + } else { + throw new IllegalArgumentException(); + } + return this; + } + + @Override + public PorkBuf setBytes(long index, @NonNull ByteBuffer buf, int length) throws IndexOutOfBoundsException { + int position = buf.position(); + validateBounds(position, length, buf.capacity()); + validateBounds(index, length, this.capacity); + if (buf.hasArray()) { + PUnsafe.copyMemory(buf.array(), PUnsafe.ARRAY_BYTE_BASE_OFFSET + buf.arrayOffset() + position, null, this.memoryAddress + index, length); + } else { + PUnsafe.copyMemory(PorkUtil.unwrap(buf) + position, this.memoryAddress + index, length); + } + buf.position(position + length); + return this; + } + + @Override + public PorkBuf setBytes(long index, @NonNull ByteBuffer buf, int start, int length) throws IndexOutOfBoundsException { + validateBounds(start, length, buf.capacity()); + validateBounds(index, length, this.capacity); + if (buf.hasArray()) { + PUnsafe.copyMemory(buf.array(), PUnsafe.ARRAY_BYTE_BASE_OFFSET + buf.arrayOffset() + buf.position(), null, this.memoryAddress + index, length); + } else { + PUnsafe.copyMemory(PorkUtil.unwrap(buf) + buf.position(), this.memoryAddress + index, length); + } + return this; + } +} diff --git a/binary/src/main/java/net/daporkchop/lib/binary/chars/DirectASCIISequence.java b/binary/src/main/java/net/daporkchop/lib/binary/chars/DirectASCIISequence.java index 7da8dc953..4f8b46f2b 100644 --- a/binary/src/main/java/net/daporkchop/lib/binary/chars/DirectASCIISequence.java +++ b/binary/src/main/java/net/daporkchop/lib/binary/chars/DirectASCIISequence.java @@ -15,7 +15,6 @@ package net.daporkchop.lib.binary.chars; -import io.netty.util.internal.PlatformDependent; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -24,7 +23,6 @@ import net.daporkchop.lib.unsafe.PUnsafe; import net.daporkchop.lib.unsafe.capability.Releasable; import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; -import sun.nio.ch.DirectBuffer; import java.nio.MappedByteBuffer; @@ -55,7 +53,7 @@ public CharSequence subSequence(int start, int end) { return start == 0 && end == this.length ? this : this.slice(start, end - start); } - protected CharSequence slice(int start, int len) { + protected CharSequence slice(int start, int len) { return new DirectASCIISequence(this.addr + start, len); } @@ -111,11 +109,11 @@ public static final class Mapped extends DirectASCIISequence implements Releasab private MappedByteBuffer buffer; public Mapped(@NonNull MappedByteBuffer buffer) { - this(buffer, PlatformDependent.directBufferAddress(buffer) + buffer.position(), buffer.remaining(), false); + this(buffer, PorkUtil.unwrap(buffer) + buffer.position(), buffer.remaining(), false); } public Mapped(@NonNull MappedByteBuffer buffer, boolean load) { - this(buffer, PlatformDependent.directBufferAddress(buffer) + buffer.position(), buffer.remaining(), load); + this(buffer, PorkUtil.unwrap(buffer) + buffer.position(), buffer.remaining(), load); } public Mapped(@NonNull MappedByteBuffer buffer, long address, int size) { @@ -127,7 +125,7 @@ public Mapped(@NonNull MappedByteBuffer buffer, long address, int size, boolean this.buffer = buffer; - if (load) { + if (load) { buffer.load(); } } @@ -139,7 +137,7 @@ protected CharSequence slice(int start, int len) { @Override public synchronized void release() throws AlreadyReleasedException { - if (this.buffer != null) { + if (this.buffer != null) { PorkUtil.release(this.buffer); this.buffer = null; } else { diff --git a/binary/src/main/java/net/daporkchop/lib/binary/netty/PUnpooled.java b/binary/src/main/java/net/daporkchop/lib/binary/netty/PUnpooled.java index 5af1b6b13..0a6cc2cd9 100644 --- a/binary/src/main/java/net/daporkchop/lib/binary/netty/PUnpooled.java +++ b/binary/src/main/java/net/daporkchop/lib/binary/netty/PUnpooled.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -22,6 +22,7 @@ import lombok.experimental.UtilityClass; import net.daporkchop.lib.binary.netty.buf.FreeingWrappedUnpooledUnsafeDirectByteBuf; import net.daporkchop.lib.binary.netty.buf.NotFreeingWrappedUnpooledUnsafeDirectByteBuf; +import net.daporkchop.lib.common.util.PorkUtil; import sun.nio.ch.DirectBuffer; import java.nio.ByteBuffer; @@ -57,7 +58,7 @@ public ByteBuf wrap(@NonNull ByteBuffer buffer, int size, boolean free) { if (buffer.isDirect()) { if (buffer.isReadOnly()) { //we have to do some hackery to make it be a read-only buffer - ByteBuffer notReadOnly = PlatformDependent.directBuffer(((DirectBuffer) buffer).address() + buffer.position(), size); + ByteBuffer notReadOnly = PlatformDependent.directBuffer(PorkUtil.unwrap(buffer) + buffer.position(), size); return free ? new FreeingWrappedUnpooledUnsafeDirectByteBuf(buffer, notReadOnly, size).asReadOnly() : new NotFreeingWrappedUnpooledUnsafeDirectByteBuf(notReadOnly, size).asReadOnly(); diff --git a/binary/src/main/java/net/daporkchop/lib/binary/oio/appendable/UTF16ByteBufAppendable.java b/binary/src/main/java/net/daporkchop/lib/binary/oio/appendable/UTF16ByteBufAppendable.java index c675d8b07..c6138d2c0 100644 --- a/binary/src/main/java/net/daporkchop/lib/binary/oio/appendable/UTF16ByteBufAppendable.java +++ b/binary/src/main/java/net/daporkchop/lib/binary/oio/appendable/UTF16ByteBufAppendable.java @@ -20,7 +20,7 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; -import net.daporkchop.lib.binary.Endianess; +import net.daporkchop.lib.unsafe.Endianess; import net.daporkchop.lib.common.system.PlatformInfo; import net.daporkchop.lib.common.util.PorkUtil; import net.daporkchop.lib.unsafe.PUnsafe; diff --git a/common/src/main/java/net/daporkchop/lib/common/misc/AlignedUnsafe.java b/common/src/main/java/net/daporkchop/lib/common/misc/AlignedUnsafe.java new file mode 100644 index 000000000..4309555ec --- /dev/null +++ b/common/src/main/java/net/daporkchop/lib/common/misc/AlignedUnsafe.java @@ -0,0 +1,273 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.common.misc; + +import io.netty.util.internal.PlatformDependent; +import lombok.experimental.UtilityClass; +import net.daporkchop.lib.common.system.PlatformInfo; +import net.daporkchop.lib.common.util.PorkUtil; +import net.daporkchop.lib.unsafe.Endianess; +import net.daporkchop.lib.unsafe.PUnsafe; + +/** + * {@link PUnsafe}, but with extra utilities for byte ordering and alignment-safety. + * + * @author DaPorkchop_ + */ +@UtilityClass +public class AlignedUnsafe { + /** + * Whether or not unaligned memory accesses are supported. + */ + public final boolean UNALIGNED = PorkUtil.classExistsWithName("io.netty.util.internal.PlatformDependent") + ? PlatformDependent.isUnaligned() + : PUnsafe.pork_getStaticField( + PorkUtil.classForName("java.nio.Bits", false, ClassLoader.getSystemClassLoader()), + PlatformInfo.JAVA_VERSION >= 11 ? "UNALIGNED" : "unaligned" + ).getBoolean(); + + public short getShortBE(long address) { + if (UNALIGNED || (address & 1L) == 0L) { + short s = PUnsafe.getShort(address); + return Endianess.BIG_ENDIAN_NATIVE ? s : Short.reverseBytes(s); + } else { + return (short) (PUnsafe.getByte(address) << 8 + | PUnsafe.getByte(address + 1L) & 0xFF); + } + } + + public short getShortLE(long address) { + if (UNALIGNED || (address & 1L) == 0L) { + short s = PUnsafe.getShort(address); + return Endianess.LITTLE_ENDIAN_NATIVE ? s : Short.reverseBytes(s); + } else { + return (short) (PUnsafe.getByte(address) & 0xFF + | PUnsafe.getByte(address + 1L) << 8); + } + } + + public short getShortN(long address) { + return Endianess.BIG_ENDIAN_NATIVE ? getShortBE(address) : getShortLE(address); + } + + public char getCharBE(long address) { + if (UNALIGNED || (address & 1L) == 0L) { + char s = PUnsafe.getChar(address); + return Endianess.BIG_ENDIAN_NATIVE ? s : Character.reverseBytes(s); + } else { + return (char) ((PUnsafe.getByte(address) & 0xFF) << 8 + | PUnsafe.getByte(address + 1L) & 0xFF); + } + } + + public char getCharLE(long address) { + if (UNALIGNED || (address & 1L) == 0L) { + char s = PUnsafe.getChar(address); + return Endianess.LITTLE_ENDIAN_NATIVE ? s : Character.reverseBytes(s); + } else { + return (char) (PUnsafe.getByte(address) & 0xFF + | (PUnsafe.getByte(address + 1L) & 0xFF) << 8); + } + } + + public char getCharN(long address) { + return Endianess.BIG_ENDIAN_NATIVE ? getCharBE(address) : getCharLE(address); + } + + public int getIntBE(long address) { + if (UNALIGNED || (address & 3L) == 0L) { + int s = PUnsafe.getInt(address); + return Endianess.BIG_ENDIAN_NATIVE ? s : Integer.reverseBytes(s); + } else { + return PUnsafe.getByte(address) << 24 + | (PUnsafe.getByte(address + 1L) & 0xFF) << 16 + | (PUnsafe.getByte(address + 2L) & 0xFF) << 8 + | PUnsafe.getByte(address + 3L) & 0xFF; + } + } + + public int getIntLE(long address) { + if (UNALIGNED || (address & 3L) == 0L) { + int s = PUnsafe.getInt(address); + return Endianess.LITTLE_ENDIAN_NATIVE ? s : Integer.reverseBytes(s); + } else { + return PUnsafe.getByte(address) & 0xFF + | (PUnsafe.getByte(address + 1L) & 0xFF) << 8 + | (PUnsafe.getByte(address + 2L) & 0xFF) << 16 + | PUnsafe.getByte(address + 3L) << 24; + } + } + + public int getIntN(long address) { + return Endianess.BIG_ENDIAN_NATIVE ? getIntBE(address) : getIntLE(address); + } + + public long getLongBE(long address) { + if (UNALIGNED || (address & 7L) == 0L) { + long s = PUnsafe.getLong(address); + return Endianess.BIG_ENDIAN_NATIVE ? s : Long.reverseBytes(s); + } else { + return ((long) PUnsafe.getByte(address)) << 56L + | (PUnsafe.getByte(address + 1L) & 0xFFL) << 48L + | (PUnsafe.getByte(address + 2L) & 0xFFL) << 40L + | (PUnsafe.getByte(address + 3L) & 0xFFL) << 32L + | (PUnsafe.getByte(address + 4L) & 0xFFL) << 24L + | (PUnsafe.getByte(address + 5L) & 0xFFL) << 16L + | (PUnsafe.getByte(address + 6L) & 0xFFL) << 8L + | PUnsafe.getByte(address + 7L) & 0xFFL; + } + } + + public long getLongLE(long address) { + if (UNALIGNED || (address & 7L) == 0L) { + long s = PUnsafe.getLong(address); + return Endianess.LITTLE_ENDIAN_NATIVE ? s : Long.reverseBytes(s); + } else { + return PUnsafe.getByte(address) & 0xFFL + | (PUnsafe.getByte(address + 1L) & 0xFFL) << 8L + | (PUnsafe.getByte(address + 2L) & 0xFFL) << 16L + | (PUnsafe.getByte(address + 3L) & 0xFFL) << 24L + | (PUnsafe.getByte(address + 4L) & 0xFFL) << 32L + | (PUnsafe.getByte(address + 5L) & 0xFFL) << 40L + | (PUnsafe.getByte(address + 6L) & 0xFFL) << 48L + | ((long) PUnsafe.getByte(address + 7L)) << 56L; + } + } + + public long getLongN(long address) { + return Endianess.BIG_ENDIAN_NATIVE ? getLongBE(address) : getLongLE(address); + } + + public void putShortBE(long address, short val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putShort(address, Endianess.BIG_ENDIAN_NATIVE ? val : Short.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) (val >>> 8)); + PUnsafe.putByte(address + 1L, (byte) val); + } + } + + public void putShortLE(long address, short val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putShort(address, Endianess.BIG_ENDIAN_NATIVE ? val : Short.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) val); + PUnsafe.putByte(address + 1L, (byte) (val >>> 8)); + } + } + + public void putShortN(long address, short val) { + if (Endianess.BIG_ENDIAN_NATIVE) { + putShortBE(address, val); + } else { + putShortLE(address, val); + } + } + + public void putCharBE(long address, char val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putChar(address, Endianess.BIG_ENDIAN_NATIVE ? val : Character.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) (val >>> 8)); + PUnsafe.putByte(address + 1L, (byte) val); + } + } + + public void putCharLE(long address, char val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putChar(address, Endianess.BIG_ENDIAN_NATIVE ? val : Character.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) val); + PUnsafe.putByte(address + 1L, (byte) (val >>> 8)); + } + } + + public void putCharN(long address, char val) { + if (Endianess.BIG_ENDIAN_NATIVE) { + putCharBE(address, val); + } else { + putCharLE(address, val); + } + } + + public void putIntBE(long address, int val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putInt(address, Endianess.BIG_ENDIAN_NATIVE ? val : Integer.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) (val >>> 24)); + PUnsafe.putByte(address + 1L, (byte) (val >>> 16)); + PUnsafe.putByte(address + 2L, (byte) (val >>> 8)); + PUnsafe.putByte(address + 3L, (byte) val); + } + } + + public void putIntLE(long address, int val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putInt(address, Endianess.BIG_ENDIAN_NATIVE ? val : Integer.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) val); + PUnsafe.putByte(address + 1L, (byte) (val >>> 8)); + PUnsafe.putByte(address + 2L, (byte) (val >>> 16)); + PUnsafe.putByte(address + 3L, (byte) (val >>> 24)); + } + } + + public void putIntN(long address, int val) { + if (Endianess.BIG_ENDIAN_NATIVE) { + putIntBE(address, val); + } else { + putIntLE(address, val); + } + } + + public void putLongBE(long address, long val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putLong(address, Endianess.BIG_ENDIAN_NATIVE ? val : Long.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) (val >>> 56L)); + PUnsafe.putByte(address + 1L, (byte) (val >>> 48L)); + PUnsafe.putByte(address + 2L, (byte) (val >>> 40L)); + PUnsafe.putByte(address + 3L, (byte) (val >>> 32L)); + PUnsafe.putByte(address + 4L, (byte) (val >>> 24L)); + PUnsafe.putByte(address + 5L, (byte) (val >>> 16L)); + PUnsafe.putByte(address + 6L, (byte) (val >>> 8L)); + PUnsafe.putByte(address + 7L, (byte) val); + } + } + + public void putLongLE(long address, long val) { + if (UNALIGNED || (address & 1L) == 0L) { + PUnsafe.putLong(address, Endianess.BIG_ENDIAN_NATIVE ? val : Long.reverseBytes(val)); + } else { + PUnsafe.putByte(address, (byte) val); + PUnsafe.putByte(address + 1L, (byte) (val >>> 8L)); + PUnsafe.putByte(address + 2L, (byte) (val >>> 16L)); + PUnsafe.putByte(address + 3L, (byte) (val >>> 24L)); + PUnsafe.putByte(address + 4L, (byte) (val >>> 32L)); + PUnsafe.putByte(address + 5L, (byte) (val >>> 40L)); + PUnsafe.putByte(address + 6L, (byte) (val >>> 48L)); + PUnsafe.putByte(address + 7L, (byte) (val >>> 56L)); + } + } + + public void putLongN(long address, long val) { + if (Endianess.BIG_ENDIAN_NATIVE) { + putLongBE(address, val); + } else { + putLongLE(address, val); + } + } +} diff --git a/common/src/main/java/net/daporkchop/lib/common/misc/refcount/AbstractRefCounted.java b/common/src/main/java/net/daporkchop/lib/common/misc/refcount/AbstractRefCounted.java index 8b1e2c0ca..b7c354914 100644 --- a/common/src/main/java/net/daporkchop/lib/common/misc/refcount/AbstractRefCounted.java +++ b/common/src/main/java/net/daporkchop/lib/common/misc/refcount/AbstractRefCounted.java @@ -34,13 +34,14 @@ public abstract class AbstractRefCounted implements RefCounted { private volatile int refCnt = 1; @Override - public void retain() throws AlreadyReleasedException { + public RefCounted retain() throws AlreadyReleasedException { int refCnt; do { if ((refCnt = PUnsafe.getIntVolatile(this, REFCNT_OFFSET)) == 0) { throw new AlreadyReleasedException(); } } while (!PUnsafe.compareAndSwapInt(this, REFCNT_OFFSET, refCnt, refCnt + 1)); + return this; } @Override diff --git a/common/src/main/java/net/daporkchop/lib/common/misc/refcount/RefCounted.java b/common/src/main/java/net/daporkchop/lib/common/misc/refcount/RefCounted.java index 2c5754ec8..8c1b60e16 100644 --- a/common/src/main/java/net/daporkchop/lib/common/misc/refcount/RefCounted.java +++ b/common/src/main/java/net/daporkchop/lib/common/misc/refcount/RefCounted.java @@ -38,7 +38,7 @@ public interface RefCounted extends AutoCloseable { * * @throws AlreadyReleasedException if this instance's reference count has already reached 0 */ - void retain() throws AlreadyReleasedException; + RefCounted retain() throws AlreadyReleasedException; /** * Releases this instance by decrementing the reference count. diff --git a/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java b/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java index 1bf9bb733..3ea3a9bdb 100644 --- a/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java +++ b/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java @@ -31,8 +31,10 @@ import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.lang.reflect.Method; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.CharBuffer; +import java.nio.MappedByteBuffer; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Map; @@ -57,6 +59,7 @@ public class PorkUtil { public final long STRING_VALUE_OFFSET = PUnsafe.pork_getOffset(String.class, "value"); public final long MATCHER_GROUPS_OFFSET = PUnsafe.pork_getOffset(Matcher.class, "groups"); public final long MATCHER_TEXT_OFFSET = PUnsafe.pork_getOffset(Matcher.class, "text"); + public final long BUFFER_ADDRESS_OFFSET = PUnsafe.pork_getOffset(Buffer.class, "address"); public final Class ABSTRACTSTRINGBUILDER_CLASS = classForName("java.lang.AbstractStringBuilder"); public final long ABSTRACTSTRINGBUILDER_VALUE_OFFSET = PUnsafe.pork_getOffset(ABSTRACTSTRINGBUILDER_CLASS, "value"); @@ -101,6 +104,21 @@ public class PorkUtil { } } + /** + * Unwraps a direct {@link ByteBuffer} to get its memory address. + * + * @param buffer the {@link ByteBuffer} to unwrap + * @return the buffer's memory address + * @throws IllegalArgumentException if the given {@link ByteBuffer} is not a direct buffer + */ + public long unwrap(@NonNull ByteBuffer buffer) throws IllegalArgumentException { + if (buffer instanceof MappedByteBuffer) { + return PUnsafe.getLong(buffer, BUFFER_ADDRESS_OFFSET); + } else { + throw new IllegalArgumentException(buffer.getClass().getCanonicalName()); + } + } + /** * Wraps a {@code char[]} into a {@link String} without copying the array. * @@ -219,6 +237,24 @@ public boolean classExistsWithName(@NonNull String name) { } } + @SuppressWarnings("unchecked") + public Class classForName(@NonNull String name, boolean initialize, @NonNull ClassLoader classLoader) { + try { + return (Class) Class.forName(name, initialize, classLoader); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public boolean classExistsWithName(@NonNull String name, boolean initialize, @NonNull ClassLoader classLoader) { + try { + Class.forName(name, initialize, classLoader); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + public Method getMethod(@NonNull Class clazz, @NonNull String name, @NonNull Class... params) { try { return clazz.getDeclaredMethod(name, params); @@ -327,4 +363,18 @@ public void assertInRange(int size, int start, int end) throws IndexOutOfBoundsE public void assertInRangeLen(int size, int start, int len) throws IndexOutOfBoundsException { assertInRange(size, start, start + len); } + + public void assertInRange(long size, long start, long end) throws IndexOutOfBoundsException { + if (start < 0L) { + throw new IndexOutOfBoundsException(String.format("start (%d) < 0", start)); + } else if (end > size) { + throw new IndexOutOfBoundsException(String.format("end (%d) > size (%d)", end, size)); + } else if (end < start) { + throw new IllegalArgumentException(String.format("end (%d) < start (%d)", end, start)); + } + } + + public void assertInRangeLen(long size, long start, long len) throws IndexOutOfBoundsException { + assertInRange(size, start, start + len); + } } diff --git a/encoding/src/main/java/net/daporkchop/lib/encoding/ToBytes.java b/encoding/src/main/java/net/daporkchop/lib/encoding/ToBytes.java index 1d860331f..7cb913055 100644 --- a/encoding/src/main/java/net/daporkchop/lib/encoding/ToBytes.java +++ b/encoding/src/main/java/net/daporkchop/lib/encoding/ToBytes.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -17,7 +17,7 @@ import lombok.NonNull; import lombok.experimental.UtilityClass; -import net.daporkchop.lib.binary.Endianess; +import net.daporkchop.lib.unsafe.Endianess; import net.daporkchop.lib.unsafe.PUnsafe; import java.util.UUID; diff --git a/natives/src/main/java/net/daporkchop/lib/natives/NativeCode.java b/natives/src/main/java/net/daporkchop/lib/natives/NativeCode.java index 5f96c8ca9..0ff66d583 100644 --- a/natives/src/main/java/net/daporkchop/lib/natives/NativeCode.java +++ b/natives/src/main/java/net/daporkchop/lib/natives/NativeCode.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -75,7 +75,7 @@ public static void loadNativeLibrary(@NonNull String name) { file.deleteOnExit(); try (InputStream is = NativeCode.class.getResourceAsStream(String.format("/%s/lib%s.%s", LIB_ARCH, name, LIB_EXT)); OutputStream os = new FileOutputStream(file)) { - byte[] arr = new byte[PUnsafe.pageSize()]; + byte[] arr = new byte[PUnsafe.PAGE_SIZE]; for (int b; (b = is.read(arr)) >= 0; os.write(arr, 0, b)); } System.load(file.getAbsolutePath()); diff --git a/binary/src/main/java/net/daporkchop/lib/binary/Endianess.java b/unsafe/src/main/java/net/daporkchop/lib/unsafe/Endianess.java similarity index 78% rename from binary/src/main/java/net/daporkchop/lib/binary/Endianess.java rename to unsafe/src/main/java/net/daporkchop/lib/unsafe/Endianess.java index 76cc7babe..2e93dedc4 100644 --- a/binary/src/main/java/net/daporkchop/lib/binary/Endianess.java +++ b/unsafe/src/main/java/net/daporkchop/lib/unsafe/Endianess.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -13,7 +13,7 @@ * */ -package net.daporkchop.lib.binary; +package net.daporkchop.lib.unsafe; import java.nio.ByteOrder; @@ -31,10 +31,20 @@ public enum Endianess { */ public static final Endianess NATIVE = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? BIG : LITTLE; + /** + * Whether or not the native byte order is big-endian. + */ + public static final boolean BIG_ENDIAN_NATIVE = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; + + /** + * Whether or not the native byte order is little-endian. + */ + public static final boolean LITTLE_ENDIAN_NATIVE = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN; + /** * @return whether or not this is the system's native endianess */ - public boolean isNative() { + public boolean isNative() { return this == NATIVE; } } diff --git a/unsafe/src/main/java/net/daporkchop/lib/unsafe/PUnsafe.java b/unsafe/src/main/java/net/daporkchop/lib/unsafe/PUnsafe.java index 30da57450..4470beed9 100644 --- a/unsafe/src/main/java/net/daporkchop/lib/unsafe/PUnsafe.java +++ b/unsafe/src/main/java/net/daporkchop/lib/unsafe/PUnsafe.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -356,14 +356,6 @@ public static int arrayIndexScale(Class clazz) { return UNSAFE.arrayIndexScale(clazz); } - public static int addressSize() { - return UNSAFE.addressSize(); - } - - public static int pageSize() { - return UNSAFE.pageSize(); - } - public static Class defineClass(String name, byte[] classBytes, int off, int len, ClassLoader srcLoader, ProtectionDomain domain) { return UNSAFE.defineClass(name, classBytes, off, len, srcLoader, domain); }