* Note: if base object is a byte array, this address ARRAY_BYTE_BASE_OFFSET, * since the byte array data starts AFTER the byte array object header. */ private final long address; /** * Size of the slice */ private final int size; /** * Bytes retained by the slice */ private final int retainedSize; /** * Reference is typically a ByteBuffer object, but can be any object this * slice must hold onto to assure that the underlying memory is not * freed by the garbage collector. */ private final Object reference; private int hash; /** * Creates an empty slice. */ Slice() { this.base = null; this.address = 0; this.size = 0; this.retainedSize = 0; this.reference = null; } /** * Creates a slice over the specified array. */ Slice(byte[] base) { checkNotNull(base, "base is null"); this.base = base; this.address = ARRAY_BYTE_BASE_OFFSET; this.size = base.length; this.retainedSize = base.length; this.reference = null; } /** * Creates a slice over the specified array range. */ Slice(byte[] base, int offset, int length) { checkNotNull(base, "base is null"); checkPositionIndexes(offset, offset + length, base.length); this.base = base; this.address = ARRAY_BYTE_BASE_OFFSET + offset; this.size = length; this.retainedSize = base.length; this.reference = null; } /** * Creates a slice over the specified array range. */ Slice(boolean[] base, int offset, int length) { checkNotNull(base, "base is null"); checkPositionIndexes(offset, offset + length, base.length); this.base = base; this.address = ARRAY_BOOLEAN_BASE_OFFSET + offset; this.size = length * ARRAY_BOOLEAN_INDEX_SCALE; this.retainedSize = base.length * ARRAY_BOOLEAN_INDEX_SCALE; this.reference = null; } /** * Creates a slice over the specified array range. */ Slice(short[] base, int offset, int length) { checkNotNull(base, "base is null"); checkPositionIndexes(offset, offset + length, base.length); this.base = base; this.address = ARRAY_SHORT_BASE_OFFSET + offset; this.size = length * ARRAY_SHORT_INDEX_SCALE; this.retainedSize = base.length * ARRAY_SHORT_INDEX_SCALE; this.reference = null; } /** * Creates a slice over the specified array range. */ Slice(int[] base, int offset, int length) { checkNotNull(base, "base is null"); checkPositionIndexes(offset, offset + length, base.length); this.base = base; this.address = ARRAY_INT_BASE_OFFSET + offset; this.size = length * ARRAY_INT_INDEX_SCALE; this.retainedSize = base.length * ARRAY_INT_INDEX_SCALE; this.reference = null; } /** * Creates a slice over the specified array range. */ Slice(long[] base, int offset, int length) { checkNotNull(base, "base is null"); checkPositionIndexes(offset, offset + length, base.length); this.base = base; this.address = ARRAY_LONG_BASE_OFFSET + offset; this.size = length * ARRAY_LONG_INDEX_SCALE; this.retainedSize = base.length * ARRAY_LONG_INDEX_SCALE; this.reference = null; } /** * Creates a slice over the specified array range. */ Slice(float[] base, int offset, int length) { checkNotNull(base, "base is null"); checkPositionIndexes(offset, offset + length, base.length); this.base = base; this.address = ARRAY_FLOAT_BASE_OFFSET + offset; this.size = length * ARRAY_FLOAT_INDEX_SCALE; this.retainedSize = base.length * ARRAY_FLOAT_INDEX_SCALE; this.reference = null; } /** * Creates a slice over the specified array range. */ Slice(double[] base, int offset, int length) { checkNotNull(base, "base is null"); checkPositionIndexes(offset, offset + length, base.length); this.base = base; this.address = ARRAY_DOUBLE_BASE_OFFSET + offset; this.size = length * ARRAY_DOUBLE_INDEX_SCALE; this.retainedSize = base.length * ARRAY_DOUBLE_INDEX_SCALE; this.reference = null; } /** * Creates a slice for directly accessing the base object. */ Slice(@Nullable Object base, long address, int size, int retainedSize, @Nullable Object reference) { if (address <= 0) { throw new IllegalArgumentException(format("Invalid address: %s", address)); } if (size <= 0) { throw new IllegalArgumentException(format("Invalid size: %s", size)); } checkArgument((address + size) >= size, "Address + size is greater than 64 bits"); this.reference = reference; this.base = base; this.address = address; this.size = size; this.retainedSize = retainedSize; } /** * Returns the base object of this Slice, or null. This is appropriate for use * with {@link Unsafe} if you wish to avoid all the safety belts e.g. bounds checks. */ public Object getBase() { return base; } /** * Return the address offset of this Slice. This is appropriate for use * with {@link Unsafe} if you wish to avoid all the safety belts e.g. bounds checks. */ public long getAddress() { return address; } /** * Length of this slice. */ public int length() { return size; } /** * Approximate number of bytes retained by this slice. */ public int getRetainedSize() { return retainedSize; } /** * Fill the slice with the specified value; */ public void fill(byte value) { int offset = 0; int length = size; long longValue = fillLong(value); while (length >= SIZE_OF_LONG) { unsafe.putLong(base, address + offset, longValue); offset += SIZE_OF_LONG; length -= SIZE_OF_LONG; } while (length > 0) { unsafe.putByte(base, address + offset, value); offset++; length--; } } /** * Fill the slice with zeros; */ public void clear() { clear(0, size); } public void clear(int offset, int length) { while (length >= SIZE_OF_LONG) { unsafe.putLong(base, address + offset, 0); offset += SIZE_OF_LONG; length -= SIZE_OF_LONG; } while (length > 0) { unsafe.putByte(base, address + offset, (byte) 0); offset++; length--; } } /** * Gets a byte at the specified absolute {@code index} in this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 1} is greater than {@code this.length()} */ public byte getByte(int index) { checkIndexLength(index, SIZE_OF_BYTE); return getByteUnchecked(index); } byte getByteUnchecked(int index) { return unsafe.getByte(base, address + index); } /** * Gets an unsigned byte at the specified absolute {@code index} in this * buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 1} is greater than {@code this.length()} */ public short getUnsignedByte(int index) { return (short) (getByte(index) & 0xFF); } /** * Gets a 16-bit short integer at the specified absolute {@code index} in * this slice. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 2} is greater than {@code this.length()} */ public short getShort(int index) { checkIndexLength(index, SIZE_OF_SHORT); return getShortUnchecked(index); } short getShortUnchecked(int index) { return unsafe.getShort(base, address + index); } /** * Gets a 32-bit integer at the specified absolute {@code index} in * this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 4} is greater than {@code this.length()} */ public int getInt(int index) { checkIndexLength(index, SIZE_OF_INT); return getIntUnchecked(index); } public int getIntUnchecked(int index) { return unsafe.getInt(base, address + index); } /** * Gets a 64-bit long integer at the specified absolute {@code index} in * this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 8} is greater than {@code this.length()} */ public long getLong(int index) { checkIndexLength(index, SIZE_OF_LONG); return getLongUnchecked(index); } long getLongUnchecked(int index) { return unsafe.getLong(base, address + index); } /** * Gets a 32-bit float at the specified absolute {@code index} in * this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 4} is greater than {@code this.length()} */ public float getFloat(int index) { checkIndexLength(index, SIZE_OF_FLOAT); return unsafe.getFloat(base, address + index); } /** * Gets a 64-bit double at the specified absolute {@code index} in * this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 8} is greater than {@code this.length()} */ public double getDouble(int index) { checkIndexLength(index, SIZE_OF_DOUBLE); return unsafe.getDouble(base, address + index); } /** * Transfers portion of data from this slice into the specified destination starting at * the specified absolute {@code index}. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, or * if {@code index + destination.length()} is greater than {@code this.length()} */ public void getBytes(int index, Slice destination) { getBytes(index, destination, 0, destination.length()); } /** * Transfers portion of data from this slice into the specified destination starting at * the specified absolute {@code index}. * * @param destinationIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, * if the specified {@code destinationIndex} is less than {@code 0}, * if {@code index + length} is greater than * {@code this.length()}, or * if {@code destinationIndex + length} is greater than * {@code destination.length()} */ public void getBytes(int index, Slice destination, int destinationIndex, int length) { destination.setBytes(destinationIndex, this, index, length); } /** * Transfers portion of data from this slice into the specified destination starting at * the specified absolute {@code index}. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, or * if {@code index + destination.length} is greater than {@code this.length()} */ public void getBytes(int index, byte[] destination) { getBytes(index, destination, 0, destination.length); } /** * Transfers portion of data from this slice into the specified destination starting at * the specified absolute {@code index}. * * @param destinationIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, * if the specified {@code destinationIndex} is less than {@code 0}, * if {@code index + length} is greater than * {@code this.length()}, or * if {@code destinationIndex + length} is greater than * {@code destination.length} */ public void getBytes(int index, byte[] destination, int destinationIndex, int length) { checkIndexLength(index, length); checkPositionIndexes(destinationIndex, destinationIndex + length, destination.length); copyMemory(base, address + index, destination, (long) ARRAY_BYTE_BASE_OFFSET + destinationIndex, length); } /** * Returns a copy of this buffer as a byte array. */ public byte[] getBytes() { return getBytes(0, length()); } /** * Returns a copy of this buffer as a byte array. * * @param index the absolute index to start at * @param length the number of bytes to return * @throws IndexOutOfBoundsException if the specified {@code index} is less then {@code 0}, * or if the specified {@code index + length} is greater than {@code this.length()} */ public byte[] getBytes(int index, int length) { byte[] bytes = new byte[length]; getBytes(index, bytes, 0, length); return bytes; } /** * Transfers a portion of data from this slice into the specified stream starting at the * specified absolute {@code index}. * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * if {@code index + length} is greater than * {@code this.length()} * @throws java.io.IOException if the specified stream threw an exception during I/O */ public void getBytes(int index, OutputStream out, int length) throws IOException { checkIndexLength(index, length); if (base instanceof byte[]) { out.write((byte[]) base, (int) ((address - ARRAY_BYTE_BASE_OFFSET) + index), length); return; } byte[] buffer = new byte[4096]; while (length > 0) { int size = min(buffer.length, length); getBytes(index, buffer, 0, size); out.write(buffer, 0, size); length -= size; index += size; } } /** * Sets the specified byte at the specified absolute {@code index} in this * buffer. The 24 high-order bits of the specified value are ignored. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 1} is greater than {@code this.length()} */ public void setByte(int index, int value) { checkIndexLength(index, SIZE_OF_BYTE); setByteUnchecked(index, value); } void setByteUnchecked(int index, int value) { unsafe.putByte(base, address + index, (byte) (value & 0xFF)); } /** * Sets the specified 16-bit short integer at the specified absolute * {@code index} in this buffer. The 16 high-order bits of the specified * value are ignored. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 2} is greater than {@code this.length()} */ public void setShort(int index, int value) { checkIndexLength(index, SIZE_OF_SHORT); setShortUnchecked(index, value); } void setShortUnchecked(int index, int value) { unsafe.putShort(base, address + index, (short) (value & 0xFFFF)); } /** * Sets the specified 32-bit integer at the specified absolute * {@code index} in this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 4} is greater than {@code this.length()} */ public void setInt(int index, int value) { checkIndexLength(index, SIZE_OF_INT); setIntUnchecked(index, value); } void setIntUnchecked(int index, int value) { unsafe.putInt(base, address + index, value); } /** * Sets the specified 64-bit long integer at the specified absolute * {@code index} in this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 8} is greater than {@code this.length()} */ public void setLong(int index, long value) { checkIndexLength(index, SIZE_OF_LONG); unsafe.putLong(base, address + index, value); } /** * Sets the specified 32-bit float at the specified absolute * {@code index} in this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 4} is greater than {@code this.length()} */ public void setFloat(int index, float value) { checkIndexLength(index, SIZE_OF_FLOAT); unsafe.putFloat(base, address + index, value); } /** * Sets the specified 64-bit double at the specified absolute * {@code index} in this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or * {@code index + 8} is greater than {@code this.length()} */ public void setDouble(int index, double value) { checkIndexLength(index, SIZE_OF_DOUBLE); unsafe.putDouble(base, address + index, value); } /** * Transfers data from the specified slice into this buffer starting at * the specified absolute {@code index}. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, or * if {@code index + source.length()} is greater than {@code this.length()} */ public void setBytes(int index, Slice source) { setBytes(index, source, 0, source.length()); } /** * Transfers data from the specified slice into this buffer starting at * the specified absolute {@code index}. * * @param sourceIndex the first index of the source * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, * if the specified {@code sourceIndex} is less than {@code 0}, * if {@code index + length} is greater than * {@code this.length()}, or * if {@code sourceIndex + length} is greater than * {@code source.length()} */ public void setBytes(int index, Slice source, int sourceIndex, int length) { checkIndexLength(index, length); checkPositionIndexes(sourceIndex, sourceIndex + length, source.length()); copyMemory(source.base, source.address + sourceIndex, base, address + index, length); } /** * Transfers data from the specified slice into this buffer starting at * the specified absolute {@code index}. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, or * if {@code index + source.length} is greater than {@code this.length()} */ public void setBytes(int index, byte[] source) { setBytes(index, source, 0, source.length); } /** * Transfers data from the specified array into this buffer starting at * the specified absolute {@code index}. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, * if the specified {@code sourceIndex} is less than {@code 0}, * if {@code index + length} is greater than * {@code this.length()}, or * if {@code sourceIndex + length} is greater than {@code source.length} */ public void setBytes(int index, byte[] source, int sourceIndex, int length) { checkPositionIndexes(sourceIndex, sourceIndex + length, source.length); copyMemory(source, (long) ARRAY_BYTE_BASE_OFFSET + sourceIndex, base, address + index, length); } /** * Transfers data from the specified input stream into this slice starting at * the specified absolute {@code index}. * * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0}, or * if {@code index + source.length} is greater than {@code this.length()} */ public void setBytes(int index, InputStream in, int length) throws IOException { checkIndexLength(index, length); byte[] bytes = new byte[4096]; while (length > 0) { int bytesRead = in.read(bytes, 0, min(bytes.length, length)); if (bytesRead < 0) { throw new IndexOutOfBoundsException("End of stream"); } copyMemory(bytes, ARRAY_BYTE_BASE_OFFSET, base, address + index, bytesRead); length -= bytesRead; index += bytesRead; } } /** * Returns a slice of this buffer's sub-region. Modifying the content of * the returned buffer or this buffer affects each other's content. */ public Slice slice(int index, int length) { if ((index == 0) && (length == length())) { return this; } checkIndexLength(index, length); if (length == 0) { return Slices.EMPTY_SLICE; } return new Slice(base, address + index, length, retainedSize, reference); } public int indexOfByte(int b) { b = b & 0xFF; for (int i = 0; i < size; i++) { if (getByteUnchecked(i) == b) { return i; } } return -1; } /** * Returns the index of the first occurrence of the pattern with this slice. * If the pattern is not found -1 is returned. If patten is empty, zero is * returned. */ public int indexOf(Slice slice) { return indexOf(slice, 0); } /** * Returns the index of the first occurrence of the pattern with this slice. * If the pattern is not found -1 is returned If patten is empty, the offset * is returned. */ public int indexOf(Slice pattern, int offset) { if (size == 0 || offset >= size) { return -1; } if (pattern.length() == 0) { return offset; } // Do we have enough characters if (pattern.length() < SIZE_OF_INT || size < SIZE_OF_LONG) { return indexOfBruteForce(pattern, offset); } // Using first four bytes for faster search. We are not using eight bytes for long // because we want more strings to get use of fast search. int head = pattern.getIntUnchecked(0); // Take the first byte of head for faster skipping int firstByteMask = head & 0xff; firstByteMask |= firstByteMask << 8; firstByteMask |= firstByteMask << 16; int lastValidIndex = size - pattern.length(); int index = offset; while (index <= lastValidIndex) { // Read four bytes in sequence int value = getIntUnchecked(index); // Compare all bytes of value with first byte of search data // see https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord int valueXor = value ^ firstByteMask; int hasZeroBytes = (valueXor - 0x01010101) & ~valueXor & 0x80808080; // If valueXor doesn't not have any zero byte then there is no match and we can advance if (hasZeroBytes == 0) { index += SIZE_OF_INT; continue; } // Try fast match of head and the rest if (value == head && equalsUnchecked(index, pattern, 0, pattern.length())) { return index; } index++; } return -1; } int indexOfBruteForce(Slice pattern, int offset) { if (size == 0 || offset >= size) { return -1; } if (pattern.length() == 0) { return offset; } byte firstByte = pattern.getByteUnchecked(0); int lastValidIndex = size - pattern.length(); int index = offset; while (true) { // seek to first byte match while (index < lastValidIndex && getByteUnchecked(index) != firstByte) { index++; } if (index > lastValidIndex) { break; } if (equalsUnchecked(index, pattern, 0, pattern.length())) { return index; } index++; } return -1; } /** * Compares the content of the specified buffer to the content of this * buffer. This comparison is performed byte by byte using an unsigned * comparison. */ @SuppressWarnings("ObjectEquality") @Override public int compareTo(Slice that) { if (this == that) { return 0; } return compareTo(0, size, that, 0, that.size); } /** * Compares a portion of this slice with a portion of the specified slice. Equality is * solely based on the contents of the slice. */ @SuppressWarnings("ObjectEquality") public int compareTo(int offset, int length, Slice that, int otherOffset, int otherLength) { if ((this == that) && (offset == otherOffset) && (length == otherLength)) { return 0; } checkIndexLength(offset, length); that.checkIndexLength(otherOffset, otherLength); int compareLength = min(length, otherLength); while (compareLength >= SIZE_OF_LONG) { long thisLong = getLongUnchecked(offset); thisLong = Long.reverseBytes(thisLong); long thatLong = that.getLongUnchecked(otherOffset); thatLong = Long.reverseBytes(thatLong); int v = compareUnsignedLongs(thisLong, thatLong); if (v != 0) { return v; } offset += SIZE_OF_LONG; otherOffset += SIZE_OF_LONG; compareLength -= SIZE_OF_LONG; } while (compareLength > 0) { byte thisByte = getByteUnchecked(offset); byte thatByte = that.getByteUnchecked(otherOffset); int v = compareUnsignedBytes(thisByte, thatByte); if (v != 0) { return v; } offset++; otherOffset++; compareLength--; } return Integer.compare(length, otherLength); } /** * Compares the specified object with this slice for equality. Equality is * solely based on the contents of the slice. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Slice)) { return false; } Slice that = (Slice) o; if (length() != that.length()) { return false; } int offset = 0; int length = size; while (length >= SIZE_OF_LONG) { long thisLong = getLongUnchecked(offset); long thatLong = that.getLongUnchecked(offset); if (thisLong != thatLong) { return false; } offset += SIZE_OF_LONG; length -= SIZE_OF_LONG; } while (length > 0) { byte thisByte = getByteUnchecked(offset); byte thatByte = that.getByteUnchecked(offset); if (thisByte != thatByte) { return false; } offset++; length--; } return true; } /** * Returns the hash code of this slice. The hash code is cached once calculated * and any future changes to the slice will not effect the hash code. */ @SuppressWarnings("NonFinalFieldReferencedInHashCode") @Override public int hashCode() { if (hash != 0) { return hash; } hash = hashCode(0, size); return hash; } /** * Returns the hash code of a portion of this slice. */ public int hashCode(int offset, int length) { return (int) XxHash64.hash(this, offset, length); } /** * Compares a portion of this slice with a portion of the specified slice. Equality is * solely based on the contents of the slice. */ @SuppressWarnings("ObjectEquality") public boolean equals(int offset, int length, Slice that, int otherOffset, int otherLength) { if (length != otherLength) { return false; } checkIndexLength(offset, length); that.checkIndexLength(otherOffset, otherLength); return equalsUnchecked(offset, that, otherOffset, length); } boolean equalsUnchecked(int offset, Slice that, int otherOffset, int length) { if ((this == that) && (offset == otherOffset)) { return true; } while (length >= SIZE_OF_LONG) { long thisLong = getLongUnchecked(offset); long thatLong = that.getLongUnchecked(otherOffset); if (thisLong != thatLong) { return false; } offset += SIZE_OF_LONG; otherOffset += SIZE_OF_LONG; length -= SIZE_OF_LONG; } while (length > 0) { byte thisByte = getByteUnchecked(offset); byte thatByte = that.getByteUnchecked(otherOffset); if (thisByte != thatByte) { return false; } offset++; otherOffset++; length--; } return true; } /** * Creates a slice input backed by this slice. Any changes to this slice * will be immediately visible to the slice input. */ public BasicSliceInput getInput() { return new BasicSliceInput(this); } /** * Creates a slice output backed by this slice. Any data written to the * slice output will be immediately visible in this slice. */ public SliceOutput getOutput() { return new BasicSliceOutput(this); } /** * Decodes the contents of this slice into a string with the specified * character set name. */ public String toString(Charset charset) { return toString(0, length(), charset); } /** * Decodes the contents of this slice into a string using the UTF-8 * character set. */ public String toStringUtf8() { return toString(UTF_8); } /** * Decodes the contents of this slice into a string using the US_ASCII * character set. The low order 7 bits if each byte are converted directly * into a code point for the string. */ public String toStringAscii() { return toStringAscii(0, size); } public String toStringAscii(int index, int length) { checkIndexLength(index, length); if (length == 0) { return ""; } if (base instanceof byte[]) { //noinspection deprecation return new String((byte[]) base, 0, (int) ((address - ARRAY_BYTE_BASE_OFFSET) + index), length); } char[] chars = new char[length]; for (int pos = index; pos < length; pos++) { chars[pos] = (char) (getByteUnchecked(pos) & 0x7F); } return new String(chars); } /** * Decodes the a portion of this slice into a string with the specified * character set name. */ public String toString(int index, int length, Charset charset) { if (length == 0) { return ""; } if (base instanceof byte[]) { return new String((byte[]) base, (int) ((address - ARRAY_BYTE_BASE_OFFSET) + index), length, charset); } // direct memory can only be converted to a string using a ByteBuffer return decodeString(toByteBuffer(index, length), charset); } public ByteBuffer toByteBuffer() { return toByteBuffer(0, size); } public ByteBuffer toByteBuffer(int index, int length) { checkIndexLength(index, length); if (base instanceof byte[]) { return ByteBuffer.wrap((byte[]) base, (int) ((address - ARRAY_BYTE_BASE_OFFSET) + index), length); } try { return (ByteBuffer) newByteBuffer.invokeExact(address + index, length, (Object) reference); } catch (Throwable throwable) { if (throwable instanceof Error) { throw (Error) throwable; } if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof InterruptedException) { Thread.currentThread().interrupt(); } throw new RuntimeException(throwable); } } /** * Decodes the a portion of this slice into a string with the specified * character set name. */ @Override public String toString() { StringBuilder builder = new StringBuilder("Slice{"); if (base != null) { builder.append("base=").append(identityToString(base)).append(", "); } builder.append("address=").append(address); builder.append(", length=").append(length()); builder.append('}'); return builder.toString(); } private static String identityToString(Object o) { if (o == null) { return null; } return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o)); } private static void copyMemory(Object src, long srcAddress, Object dest, long destAddress, int length) { // The Unsafe Javadoc specifies that the transfer size is 8 iff length % 8 == 0 // so ensure that we copy big chunks whenever possible, even at the expense of two separate copy operations int bytesToCopy = length - (length % 8); unsafe.copyMemory(src, srcAddress, dest, destAddress, bytesToCopy); unsafe.copyMemory(src, srcAddress + bytesToCopy, dest, destAddress + bytesToCopy, length - bytesToCopy); } private void checkIndexLength(int index, int length) { checkPositionIndexes(index, index + length, length()); } // // The following methods were forked from Guava primitives // private static long fillLong(byte value) { return (value & 0xFFL) << 56 | (value & 0xFFL) << 48 | (value & 0xFFL) << 40 | (value & 0xFFL) << 32 | (value & 0xFFL) << 24 | (value & 0xFFL) << 16 | (value & 0xFFL) << 8 | (value & 0xFFL); } private static int compareUnsignedBytes(byte thisByte, byte thatByte) { return unsignedByteToInt(thisByte) - unsignedByteToInt(thatByte); } private static int unsignedByteToInt(byte thisByte) { return thisByte & 0xFF; } private static int compareUnsignedLongs(long thisLong, long thatLong) { return Long.compare(flipUnsignedLong(thisLong), flipUnsignedLong(thatLong)); } private static long flipUnsignedLong(long thisLong) { return thisLong ^ Long.MIN_VALUE; } } slice-0.16/src/main/java/io/airlift/slice/SliceInput.java 0000664 0000000 0000000 00000024462 12614743173 0023275 0 ustar 00root root 0000000 0000000 /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.airlift.slice; import java.io.DataInput; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @SuppressWarnings("JavaDoc") // IDEA-81310 public abstract class SliceInput extends InputStream implements DataInput { /** * Returns the {@code position} of this buffer. */ public abstract long position(); /** * Sets the {@code position} of this buffer. * * @throws IndexOutOfBoundsException if the specified {@code position} is * less than {@code 0} or * greater than {@code this.writerIndex} */ public abstract void setPosition(long position); /** * Returns {@code true} * if and only if {@code available()} is greater * than {@code 0}. */ public abstract boolean isReadable(); /** * Returns the number of bytes that can be read without blocking. */ @SuppressWarnings("AbstractMethodOverridesConcreteMethod") @Override public abstract int available(); @Override public abstract int read(); /** * Returns true if the byte at the current {@code position} is not {@code 0} and increases * the {@code position} by {@code 1} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 1} */ @Override public abstract boolean readBoolean(); /** * Gets a byte at the current {@code position} and increases * the {@code position} by {@code 1} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 1} */ @Override public abstract byte readByte(); /** * Gets an unsigned byte at the current {@code position} and increases * the {@code position} by {@code 1} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 1} */ @Override public abstract int readUnsignedByte(); /** * Gets a 16-bit short integer at the current {@code position} * and increases the {@code position} by {@code 2} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 2} */ @Override public abstract short readShort(); /** * Gets an unsigned 16-bit short integer at the current {@code position} * and increases the {@code position} by {@code 2} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 2} */ @Override public abstract int readUnsignedShort(); /** * Gets a 32-bit integer at the current {@code position} * and increases the {@code position} by {@code 4} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 4} */ @Override public abstract int readInt(); /** * Gets an unsigned 32-bit integer at the current {@code position} * and increases the {@code position} by {@code 4} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 4} */ public final long readUnsignedInt() { return readInt() & 0xFFFFFFFFL; } /** * Gets a 64-bit long at the current {@code position} * and increases the {@code position} by {@code 8} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 8} */ @Override public abstract long readLong(); /** * Gets a 32-bit float at the current {@code position} * and increases the {@code position} by {@code 4} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 4} */ @Override public abstract float readFloat(); /** * Gets a 64-bit double at the current {@code position} * and increases the {@code position} by {@code 8} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.available()} is less than {@code 8} */ @Override public abstract double readDouble(); /** * Returns a new slice of this buffer's sub-region starting at the current * {@code position} and increases the {@code position} by the size * of the new slice (= {@code length}). * * @param length the size of the new slice * @return the newly created slice * @throws IndexOutOfBoundsException if {@code length} is greater than {@code this.available()} */ public abstract Slice readSlice(int length); @Override public final void readFully(byte[] destination) { readBytes(destination); } @Override public final int read(byte[] destination) { return read(destination, 0, destination.length); } @Override public abstract int read(byte[] destination, int destinationIndex, int length); /** * Transfers this buffer's data to the specified destination starting at * the current {@code position} and increases the {@code position} * by the number of the transferred bytes (= {@code dst.length}). * * @throws IndexOutOfBoundsException if {@code dst.length} is greater than {@code this.available()} */ public final void readBytes(byte[] destination) { readBytes(destination, 0, destination.length); } @Override public final void readFully(byte[] destination, int offset, int length) { readBytes(destination, offset, length); } /** * Transfers this buffer's data to the specified destination starting at * the current {@code position} and increases the {@code position} * by the number of the transferred bytes (= {@code length}). * * @param destinationIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code destinationIndex} is less than {@code 0}, * if {@code length} is greater than {@code this.available()}, or * if {@code destinationIndex + length} is greater than {@code destination.length} */ public abstract void readBytes(byte[] destination, int destinationIndex, int length); /** * Transfers this buffer's data to the specified destination starting at * the current {@code position} until the destination becomes * non-writable, and increases the {@code position} by the number of the * transferred bytes. This method is basically same with * {@link #readBytes(Slice, int, int)}, except that this method * increases the {@code writerIndex} of the destination by the number of * the transferred bytes while {@link #readBytes(Slice, int, int)} * does not. * * @throws IndexOutOfBoundsException if {@code destination.writableBytes} is greater than * {@code this.available()} */ public final void readBytes(Slice destination) { readBytes(destination, 0, destination.length()); } /** * Transfers this buffer's data to the specified destination starting at * the current {@code position} and increases the {@code position} * by the number of the transferred bytes (= {@code length}). This method * is basically same with {@link #readBytes(Slice, int, int)}, * except that this method increases the {@code writerIndex} of the * destination by the number of the transferred bytes (= {@code length}) * while {@link #readBytes(Slice, int, int)} does not. * * @throws IndexOutOfBoundsException if {@code length} is greater than {@code this.available()} or * if {@code length} is greater than {@code destination.writableBytes} */ public final void readBytes(Slice destination, int length) { readBytes(destination, 0, length); } /** * Transfers this buffer's data to the specified destination starting at * the current {@code position} and increases the {@code position} * by the number of the transferred bytes (= {@code length}). * * @param destinationIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code destinationIndex} is less than {@code 0}, * if {@code length} is greater than {@code this.available()}, or * if {@code destinationIndex + length} is greater than * {@code destination.capacity} */ public abstract void readBytes(Slice destination, int destinationIndex, int length); /** * Transfers this buffer's data to the specified stream starting at the * current {@code position}. * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if {@code length} is greater than {@code this.available()} * @throws java.io.IOException if the specified stream threw an exception during I/O */ public abstract void readBytes(OutputStream out, int length) throws IOException; @Override public abstract long skip(long length); @Override public abstract int skipBytes(int length); @Override public void close() { } // // Unsupported operations // @Override public final void mark(int readLimit) { throw new UnsupportedOperationException(); } @Override public final void reset() { throw new UnsupportedOperationException(); } @Override public final boolean markSupported() { throw new UnsupportedOperationException(); } @Override public final char readChar() { throw new UnsupportedOperationException(); } @Override public final String readLine() { throw new UnsupportedOperationException(); } @Override public final String readUTF() { throw new UnsupportedOperationException(); } } slice-0.16/src/main/java/io/airlift/slice/SliceOutput.java 0000664 0000000 0000000 00000024706 12614743173 0023477 0 ustar 00root root 0000000 0000000 /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.airlift.slice; import java.io.DataOutput; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; @SuppressWarnings("JavaDoc") // IDEA-81310 public abstract class SliceOutput extends OutputStream implements DataOutput { /** * Resets this stream to the initial position. */ public abstract void reset(); /** * Returns the {@code writerIndex} of this buffer. */ public abstract int size(); /** * Returns the number of writable bytes which is equal to * {@code (this.capacity - this.writerIndex)}. */ public abstract int writableBytes(); /** * Returns {@code true} * if and only if {@code (this.capacity - this.writerIndex)} is greater * than {@code 0}. */ public abstract boolean isWritable(); @Override public final void writeBoolean(boolean value) { writeByte(value ? 1 : 0); } @Override public final void write(int value) { writeByte(value); } /** * Sets the specified byte at the current {@code writerIndex} * and increases the {@code writerIndex} by {@code 1} in this buffer. * The 24 high-order bits of the specified value are ignored. * * @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 1} */ @Override public abstract void writeByte(int value); /** * Sets the specified 16-bit short integer at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 2} * in this buffer. The 16 high-order bits of the specified value are ignored. * * @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 2} */ @Override public abstract void writeShort(int value); /** * Sets the specified 32-bit integer at the current {@code writerIndex} * and increases the {@code writerIndex} by {@code 4} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 4} */ @Override public abstract void writeInt(int value); /** * Sets the specified 64-bit long integer at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 8} * in this buffer. * * @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 8} */ @Override public abstract void writeLong(long value); /** * Sets the specified 32-bit float at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 4} * in this buffer. * * @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 4} */ @Override public abstract void writeFloat(float v); /** * Sets the specified 64-bit double at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 8} * in this buffer. * * @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 8} */ @Override public abstract void writeDouble(double value); /** * Transfers the specified source buffer's data to this buffer starting at * the current {@code writerIndex} until the source buffer becomes * unreadable, and increases the {@code writerIndex} by the number of * the transferred bytes. This method is basically same with * {@link #writeBytes(Slice, int, int)}, except that this method * increases the {@code readerIndex} of the source buffer by the number of * the transferred bytes while {@link #writeBytes(Slice, int, int)} * does not. * * @throws IndexOutOfBoundsException if {@code source.readableBytes} is greater than {@code this.writableBytes} */ public abstract void writeBytes(Slice source); /** * Transfers the specified source buffer's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} * by the number of the transferred bytes (= {@code length}). * * @param sourceIndex the first index of the source * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code sourceIndex} is less than {@code 0}, * if {@code sourceIndex + length} is greater than {@code source.capacity}, or * if {@code length} is greater than {@code this.writableBytes} */ public abstract void writeBytes(Slice source, int sourceIndex, int length); @Override public final void write(byte[] source) throws IOException { writeBytes(source); } /** * Transfers the specified source array's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} * by the number of the transferred bytes (= {@code source.length}). * * @throws IndexOutOfBoundsException if {@code source.length} is greater than {@code this.writableBytes} */ public abstract void writeBytes(byte[] source); @Override public final void write(byte[] source, int sourceIndex, int length) { writeBytes(source, sourceIndex, length); } /** * Transfers the specified source array's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} * by the number of the transferred bytes (= {@code length}). * * @param sourceIndex the first index of the source * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code sourceIndex} is less than {@code 0}, * if {@code sourceIndex + length} is greater than {@code source.length}, or * if {@code length} is greater than {@code this.writableBytes} */ public abstract void writeBytes(byte[] source, int sourceIndex, int length); /** * Transfers the content of the specified stream to this buffer * starting at the current {@code writerIndex} and increases the * {@code writerIndex} by the number of the transferred bytes. * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if {@code length} is greater than {@code this.writableBytes} * @throws java.io.IOException if the specified stream threw an exception during I/O */ public abstract void writeBytes(InputStream in, int length) throws IOException; /** * Fills this buffer with NUL (0x00) starting at the current * {@code writerIndex} and increases the {@code writerIndex} by the * specified {@code length}. * * @param length the number of NULs to write to the buffer * @throws IndexOutOfBoundsException if {@code length} is greater than {@code this.writableBytes} */ public void writeZero(int length) { if (length == 0) { return; } if (length < 0) { throw new IllegalArgumentException( "length must be 0 or greater than 0."); } int nLong = length >>> 3; int nBytes = length & 7; for (int i = nLong; i > 0; i--) { writeLong(0); } if (nBytes == 4) { writeInt(0); } else if (nBytes < 4) { for (int i = nBytes; i > 0; i--) { writeByte((byte) 0); } } else { writeInt(0); for (int i = nBytes - 4; i > 0; i--) { writeByte((byte) 0); } } } /** * Returns a slice of this buffer's readable bytes. Modifying the content * of the returned buffer or this buffer affects each other's content * while they maintain separate indexes and marks. This method is * identical to {@code buf.slice(buf.readerIndex(), buf.readableBytes())}. * This method does not modify {@code readerIndex} or {@code writerIndex} of * this buffer. */ public abstract Slice slice(); /** * Returns the raw underlying slice of this output stream. The slice may * be larger than the size of this stream. */ public abstract Slice getUnderlyingSlice(); /** * Decodes this buffer's readable bytes into a string with the specified * character set name. This method is identical to * {@code buf.toString(buf.readerIndex(), buf.readableBytes(), charsetName)}. * This method does not modify {@code readerIndex} or {@code writerIndex} of * this buffer. */ public abstract String toString(Charset charset); public abstract SliceOutput appendLong(long value); public abstract SliceOutput appendDouble(double value); public abstract SliceOutput appendInt(int value); public abstract SliceOutput appendShort(int value); public abstract SliceOutput appendByte(int value); public abstract SliceOutput appendBytes(byte[] source, int sourceIndex, int length); public abstract SliceOutput appendBytes(byte[] source); public abstract SliceOutput appendBytes(Slice slice); // // Unsupported operations // /** * Unsupported operation * * @throws UnsupportedOperationException always */ @Override public void writeChar(int value) { throw new UnsupportedOperationException(); } /** * Unsupported operation * * @throws UnsupportedOperationException always */ @Override public void writeChars(String s) { throw new UnsupportedOperationException(); } /** * Unsupported operation * * @throws UnsupportedOperationException always */ @Override public void writeUTF(String s) { throw new UnsupportedOperationException(); } /** * Unsupported operation * * @throws UnsupportedOperationException always */ @Override public void writeBytes(String s) { throw new UnsupportedOperationException(); } } slice-0.16/src/main/java/io/airlift/slice/SliceStreamUtils.java 0000664 0000000 0000000 00000002314 12614743173 0024442 0 ustar 00root root 0000000 0000000 /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.airlift.slice; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; final class SliceStreamUtils { private SliceStreamUtils() { } public static void copyStreamFully(InputStream in, OutputStream out, int length) throws IOException { byte[] bytes = new byte[4096]; while (length > 0) { int newBytes = in.read(bytes, 0, Math.min(bytes.length, length)); if (newBytes < 0) { throw new EOFException(); } out.write(bytes, 0, newBytes); length -= newBytes; } } } slice-0.16/src/main/java/io/airlift/slice/SliceUtf8.java 0000664 0000000 0000000 00000102377 12614743173 0023026 0 ustar 00root root 0000000 0000000 /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.airlift.slice; import java.util.OptionalInt; import static io.airlift.slice.Preconditions.checkArgument; import static io.airlift.slice.Preconditions.checkPositionIndex; import static io.airlift.slice.Preconditions.checkPositionIndexes; import static java.lang.Character.MAX_CODE_POINT; import static java.lang.Character.MAX_SURROGATE; import static java.lang.Character.MIN_SURROGATE; import static java.lang.Integer.toHexString; /** * Utility methods for UTF-8 encoded slices. */ public final class SliceUtf8 { private SliceUtf8() {} private static final int REPLACEMENT_CODE_POINT = 0xFFFD; private static final int TOP_MASK32 = 0x8080_8080; private static final long TOP_MASK64 = 0x8080_8080_8080_8080L; private static final int[] LOWER_CODE_POINTS; private static final int[] UPPER_CODE_POINTS; private static final boolean[] WHITESPACE_CODE_POINTS; static { LOWER_CODE_POINTS = new int[MAX_CODE_POINT + 1]; UPPER_CODE_POINTS = new int[MAX_CODE_POINT + 1]; WHITESPACE_CODE_POINTS = new boolean[MAX_CODE_POINT + 1]; for (int codePoint = 0; codePoint <= MAX_CODE_POINT; codePoint++) { int type = Character.getType(codePoint); if (type != Character.SURROGATE) { LOWER_CODE_POINTS[codePoint] = Character.toLowerCase(codePoint); UPPER_CODE_POINTS[codePoint] = Character.toUpperCase(codePoint); WHITESPACE_CODE_POINTS[codePoint] = Character.isWhitespace(codePoint); } else { LOWER_CODE_POINTS[codePoint] = REPLACEMENT_CODE_POINT; UPPER_CODE_POINTS[codePoint] = REPLACEMENT_CODE_POINT; WHITESPACE_CODE_POINTS[codePoint] = false; } } } /** * Does the slice contain only 7-bit ASCII characters. */ public static boolean isAscii(Slice utf8) { int length = utf8.length(); int offset = 0; // Length rounded to 8 bytes int length8 = length & 0x7FFF_FFF8; for (; offset < length8; offset += 8) { if ((utf8.getLongUnchecked(offset) & TOP_MASK64) != 0) { return false; } } // Enough bytes left for 32 bits? if (offset + 4 < length) { if ((utf8.getIntUnchecked(offset) & TOP_MASK32) != 0) { return false; } offset += 4; } // Do the rest one by one for (; offset < length; offset++) { if ((utf8.getByteUnchecked(offset) & 0x80) != 0) { return false; } } return true; } /** * Counts the code points within UTF-8 encoded slice. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int countCodePoints(Slice utf8) { return countCodePoints(utf8, 0, utf8.length()); } /** * Counts the code points within UTF-8 encoded slice up to {@code length}. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int countCodePoints(Slice utf8, int offset, int length) { checkPositionIndexes(offset, offset + length, utf8.length()); // Quick exit if empty string if (length == 0) { return 0; } int continuationBytesCount = 0; // Length rounded to 8 bytes int length8 = length & 0x7FFF_FFF8; for (; offset < length8; offset += 8) { // Count bytes which are NOT the start of a code point continuationBytesCount += countContinuationBytes(utf8.getLongUnchecked(offset)); } // Enough bytes left for 32 bits? if (offset + 4 < length) { // Count bytes which are NOT the start of a code point continuationBytesCount += countContinuationBytes(utf8.getIntUnchecked(offset)); offset += 4; } // Do the rest one by one for (; offset < length; offset++) { // Count bytes which are NOT the start of a code point continuationBytesCount += countContinuationBytes(utf8.getByteUnchecked(offset)); } assert continuationBytesCount <= length; return length - continuationBytesCount; } /** * Gets the substring starting at {@code codePointStart} and extending for * {@code codePointLength} code points. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static Slice substring(Slice utf8, int codePointStart, int codePointLength) { checkArgument(codePointStart >= 0, "codePointStart is negative"); checkArgument(codePointLength >= 0, "codePointLength is negative"); int indexStart = offsetOfCodePoint(utf8, codePointStart); if (indexStart < 0) { throw new IllegalArgumentException("UTF-8 does not contain " + codePointStart + " code points"); } if (codePointLength == 0) { return Slices.EMPTY_SLICE; } int indexEnd = offsetOfCodePoint(utf8, indexStart, codePointLength - 1); if (indexEnd < 0) { throw new IllegalArgumentException("UTF-8 does not contain " + (codePointStart + codePointLength) + " code points"); } indexEnd += lengthOfCodePoint(utf8, indexEnd); if (indexEnd > utf8.length()) { throw new InvalidUtf8Exception("UTF-8 is not well formed"); } return utf8.slice(indexStart, indexEnd - indexStart); } /** * Reverses the slice code point by code point. *
* Note: Invalid UTF-8 sequences are copied directly to the output. */ public static Slice reverse(Slice utf8) { int length = utf8.length(); Slice reverse = Slices.allocate(length); int forwardPosition = 0; int reversePosition = length; while (forwardPosition < length) { int codePointLength = lengthOfCodePointSafe(utf8, forwardPosition); // backup the reverse pointer reversePosition -= codePointLength; if (reversePosition < 0) { // this should not happen throw new InvalidUtf8Exception("UTF-8 is not well formed"); } // copy the character copyUtf8SequenceUnsafe(utf8, forwardPosition, reverse, reversePosition, codePointLength); forwardPosition += codePointLength; } return reverse; } /** * Converts slice to upper case code point by code point. This method does * not perform perform locale-sensitive, context-sensitive, or one-to-many * mappings required for some languages. Specifically, this will return * incorrect results for Lithuanian, Turkish, and Azeri. *
* Note: Invalid UTF-8 sequences are copied directly to the output. */ public static Slice toUpperCase(Slice utf8) { return translateCodePoints(utf8, UPPER_CODE_POINTS); } /** * Converts slice to lower case code point by code point. This method does * not perform perform locale-sensitive, context-sensitive, or one-to-many * mappings required for some languages. Specifically, this will return * incorrect results for Lithuanian, Turkish, and Azeri. *
* Note: Invalid UTF-8 sequences are copied directly to the output. */ public static Slice toLowerCase(Slice utf8) { return translateCodePoints(utf8, LOWER_CODE_POINTS); } private static Slice translateCodePoints(Slice utf8, int[] codePointTranslationMap) { int length = utf8.length(); Slice newUtf8 = Slices.allocate(length); int position = 0; int upperPosition = 0; while (position < length) { int codePoint = tryGetCodePointAt(utf8, position); if (codePoint >= 0) { int upperCodePoint = codePointTranslationMap[codePoint]; // grow slice if necessary int nextUpperPosition = upperPosition + lengthOfCodePoint(upperCodePoint); if (nextUpperPosition > length) { newUtf8 = Slices.ensureSize(newUtf8, nextUpperPosition); } // write new byte setCodePointAt(upperCodePoint, newUtf8, upperPosition); position += lengthOfCodePoint(codePoint); upperPosition = nextUpperPosition; } else { int skipLength = -codePoint; // grow slice if necessary int nextUpperPosition = upperPosition + skipLength; if (nextUpperPosition > length) { newUtf8 = Slices.ensureSize(newUtf8, nextUpperPosition); } copyUtf8SequenceUnsafe(utf8, position, newUtf8, upperPosition, skipLength); position += skipLength; upperPosition = nextUpperPosition; } } return newUtf8.slice(0, upperPosition); } private static void copyUtf8SequenceUnsafe(Slice source, int sourcePosition, Slice destination, int destinationPosition, int length) { switch (length) { case 1: destination.setByteUnchecked(destinationPosition, source.getByteUnchecked(sourcePosition)); break; case 2: destination.setShortUnchecked(destinationPosition, source.getShortUnchecked(sourcePosition)); break; case 3: destination.setShortUnchecked(destinationPosition, source.getShortUnchecked(sourcePosition)); destination.setByteUnchecked(destinationPosition + 2, source.getByteUnchecked(sourcePosition + 2)); break; case 4: destination.setIntUnchecked(destinationPosition, source.getIntUnchecked(sourcePosition)); break; case 5: destination.setIntUnchecked(destinationPosition, source.getIntUnchecked(sourcePosition)); destination.setByteUnchecked(destinationPosition + 4, source.getByteUnchecked(sourcePosition + 4)); break; case 6: destination.setIntUnchecked(destinationPosition, source.getIntUnchecked(sourcePosition)); destination.setShortUnchecked(destinationPosition + 4, source.getShortUnchecked(sourcePosition + 4)); break; default: throw new IllegalStateException("Invalid code point length " + length); } } /** * Removes all white space characters from the left string of the string. *
* Note: Invalid UTF-8 sequences are not trimmed. */ public static Slice leftTrim(Slice utf8) { int length = utf8.length(); int position = firstNonWhitespacePosition(utf8); return utf8.slice(position, length - position); } private static int firstNonWhitespacePosition(Slice utf8) { int length = utf8.length(); int position = 0; while (position < length) { int codePoint = tryGetCodePointAt(utf8, position); if (codePoint < 0) { break; } if (!WHITESPACE_CODE_POINTS[codePoint]) { break; } position += lengthOfCodePoint(codePoint); } return position; } /** * Removes all white space characters from the right side of the string. *
* Note: Invalid UTF-8 sequences are not trimmed. */ public static Slice rightTrim(Slice utf8) { int position = lastNonWhitespacePosition(utf8, 0); return utf8.slice(0, position); } private static int lastNonWhitespacePosition(Slice utf8, int minPosition) { int length = utf8.length(); int position = length; while (minPosition < position) { // decode the code point before position if possible int codePoint; byte unsignedByte = utf8.getByte(position - 1); if (!isContinuationByte(unsignedByte)) { codePoint = unsignedByte & 0xFF; } else if (minPosition <= position -2 && !isContinuationByte(utf8.getByte(position - 2))) { codePoint = tryGetCodePointAt(utf8, position - 2); } else if (minPosition <= position -3 && !isContinuationByte(utf8.getByte(position - 3))) { codePoint = tryGetCodePointAt(utf8, position - 3); } else if (minPosition <= position -4 && !isContinuationByte(utf8.getByte(position - 4))) { codePoint = tryGetCodePointAt(utf8, position - 4); } else { break; } if (codePoint < 0 || !WHITESPACE_CODE_POINTS[codePoint]) { break; } position -= lengthOfCodePoint(codePoint); } return position; } /** * Removes all white space characters from the left and right side of the string. *
* Note: Invalid UTF-8 sequences are not trimmed. */ public static Slice trim(Slice utf8) { int start = firstNonWhitespacePosition(utf8); int end = lastNonWhitespacePosition(utf8, start); return utf8.slice(start, end - start); } public static Slice fixInvalidUtf8(Slice slice) { return fixInvalidUtf8(slice, OptionalInt.of(REPLACEMENT_CODE_POINT)); } public static Slice fixInvalidUtf8(Slice slice, OptionalInt replacementCodePoint) { if (isAscii(slice)) { return slice; } int replacementCodePointValue = -1; int replacementCodePointLength = 0; if (replacementCodePoint.isPresent()) { replacementCodePointValue = replacementCodePoint.getAsInt(); replacementCodePointLength = lengthOfCodePoint(replacementCodePointValue); } int length = slice.length(); Slice utf8 = Slices.allocate(length); int dataPosition = 0; int utf8Position = 0; while (dataPosition < length) { int codePoint = tryGetCodePointAt(slice, dataPosition); int codePointLength; if (codePoint >= 0) { codePointLength = lengthOfCodePoint(codePoint); dataPosition += codePointLength; } else { // negative number carries the number of invalid bytes dataPosition += (-codePoint); if (replacementCodePointValue < 0) { continue; } codePoint = replacementCodePointValue; codePointLength = replacementCodePointLength; } utf8 = Slices.ensureSize(utf8, utf8Position + codePointLength); utf8Position += setCodePointAt(codePoint, utf8, utf8Position); } return utf8.slice(0, utf8Position); } /** * Tries to get the UTF-8 encoded code point at the {@code position}. A positive * return value means the UTF-8 sequence at the position is valid, and the result * is the code point. A negative return value means the UTF-8 sequence at the * position is invalid, and the length of the invalid sequence is the absolute * value of the result. * @return the code point or negative the number of bytes in the invalid UTF-8 sequence. */ public static int tryGetCodePointAt(Slice utf8, int position) { // // Process first byte byte firstByte = utf8.getByte(position); int length = lengthOfCodePointFromStartByteSafe(firstByte); if (length < 0) { return length; } if (length == 1) { // normal ASCII // 0xxx_xxxx return firstByte; } // // Process second byte if (position + 1 >= utf8.length()) { return -1; } byte secondByte = utf8.getByteUnchecked(position + 1); if (!isContinuationByte(secondByte)) { return -1; } if (length == 2) { // 110x_xxxx 10xx_xxxx return ((firstByte & 0b0001_1111) << 6) | (secondByte & 0b0011_1111); } // // Process third byte if (position + 2 >= utf8.length()) { return -2; } byte thirdByte = utf8.getByteUnchecked(position + 2); if (!isContinuationByte(thirdByte)) { return -2; } if (length == 3) { // 1110_xxxx 10xx_xxxx 10xx_xxxx int codePoint = ((firstByte & 0b0000_1111) << 12) | ((secondByte & 0b0011_1111) << 6) | (thirdByte & 0b0011_1111); // surrogates are invalid if (MIN_SURROGATE <= codePoint && codePoint <= MAX_SURROGATE) { return -3; } return codePoint; } // // Process forth byte if (position + 3 >= utf8.length()) { return -3; } byte forthByte = utf8.getByteUnchecked(position + 3); if (!isContinuationByte(forthByte)) { return -3; } if (length == 4) { // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx int codePoint = ((firstByte & 0b0000_0111) << 18) | ((secondByte & 0b0011_1111) << 12) | ((thirdByte & 0b0011_1111) << 6) | (forthByte & 0b0011_1111); // 4 byte code points have a limited valid range if (codePoint < 0x11_0000) { return codePoint; } return -4; } // // Process fifth byte if (position + 4 >= utf8.length()) { return -4; } byte fifthByte = utf8.getByteUnchecked(position + 4); if (!isContinuationByte(fifthByte)) { return -4; } if (length == 5) { // Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal return -5; } // // Process sixth byte if (position + 5 >= utf8.length()) { return -5; } byte sixthByte = utf8.getByteUnchecked(position + 5); if (!isContinuationByte(sixthByte)) { return -5; } if (length == 6) { // Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal return -6; } // for longer sequence, which can't happen return -1; } static int lengthOfCodePointFromStartByteSafe(byte startByte) { int unsignedStartByte = startByte & 0xFF; if (unsignedStartByte < 0b1000_0000) { // normal ASCII // 0xxx_xxxx return 1; } if (unsignedStartByte < 0b1100_0000) { // illegal bytes // 10xx_xxxx return -1; } if (unsignedStartByte < 0b1110_0000) { // 110x_xxxx 10xx_xxxx return 2; } if (unsignedStartByte < 0b1111_0000) { // 1110_xxxx 10xx_xxxx 10xx_xxxx return 3; } if (unsignedStartByte < 0b1111_1000) { // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx return 4; } if (unsignedStartByte < 0b1111_1100) { // 1111_10xx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx return 5; } if (unsignedStartByte < 0b1111_1110) { // 1111_110x 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx return 6; } return -1; } /** * Finds the index of the first byte of the code point at a position, or * {@code -1} if the position is not withing the slice. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int offsetOfCodePoint(Slice utf8, int codePointCount) { return offsetOfCodePoint(utf8, 0, codePointCount); } /** * Starting from {@code position} bytes in {@code utf8}, finds the * index of the first byte of the code point {@code codePointCount} * in the slice. If the slice does not contain * {@code codePointCount} code points after {@code position}, {@code -1} * is returned. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int offsetOfCodePoint(Slice utf8, int position, int codePointCount) { checkPositionIndex(position, utf8.length()); checkArgument(codePointCount >= 0, "codePointPosition is negative"); // Quick exit if we are sure that the position is after the end if (utf8.length() - position <= codePointCount) { return -1; } if (codePointCount == 0) { return position; } int correctIndex = codePointCount + position; // Length rounded to 8 bytes int length8 = utf8.length() & 0x7FFF_FFF8; // While we have enough bytes left and we need at least 8 characters process 8 bytes at once while (position < length8 && correctIndex >= position + 8) { // Count bytes which are NOT the start of a code point correctIndex += countContinuationBytes(utf8.getLongUnchecked(position)); position += 8; } // Length rounded to 4 bytes int length4 = utf8.length() & 0x7FFF_FFFC; // While we have enough bytes left and we need at least 4 characters process 4 bytes at once while (position < length4 && correctIndex >= position + 4) { // Count bytes which are NOT the start of a code point correctIndex += countContinuationBytes(utf8.getIntUnchecked(position)); position += 4; } // Do the rest one by one, always check the last byte to find the end of the code point while (position < utf8.length()) { // Count bytes which are NOT the start of a code point correctIndex += countContinuationBytes(utf8.getByteUnchecked(position)); if (position == correctIndex) { break; } position++; } if (position == correctIndex && correctIndex < utf8.length()) { return correctIndex; } return -1; } /** * Gets the UTF-8 sequence length of the code point at {@code position}. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int lengthOfCodePoint(Slice utf8, int position) { return lengthOfCodePointFromStartByte(utf8.getByte(position)); } /** * Gets the UTF-8 sequence length of the code point at {@code position}. *
* Truncated UTF-8 sequences, 5 and 6 byte sequences, and invalid code points * are handled by this method without throwing an exception. */ public static int lengthOfCodePointSafe(Slice utf8, int position) { int length = lengthOfCodePointFromStartByteSafe(utf8.getByte(position)); if (length < 0) { return -length; } if (length == 1 || position + 1 >= utf8.length() || !isContinuationByte(utf8.getByteUnchecked(position + 1))) { return 1; } if (length == 2 || position + 2 >= utf8.length() || !isContinuationByte(utf8.getByteUnchecked(position + 2))) { return 2; } if (length == 3 || position + 3 >= utf8.length() || !isContinuationByte(utf8.getByteUnchecked(position + 3))) { return 3; } if (length == 4 || position + 4 >= utf8.length() || !isContinuationByte(utf8.getByteUnchecked(position + 4))) { return 4; } if (length == 5 || position + 5 >= utf8.length() || !isContinuationByte(utf8.getByteUnchecked(position + 5))) { return 5; } if (length == 6) { return 6; } return 1; } /** * Gets the UTF-8 sequence length of the code point. * * @throws InvalidCodePointException if code point is not within a valid range */ public static int lengthOfCodePoint(int codePoint) { if (codePoint < 0) { throw new InvalidCodePointException(codePoint); } if (codePoint < 0x80) { // normal ASCII // 0xxx_xxxx return 1; } if (codePoint < 0x800) { return 2; } if (codePoint < 0x1_0000) { return 3; } if (codePoint < 0x11_0000) { return 4; } // Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal throw new InvalidCodePointException(codePoint); } /** * Gets the UTF-8 sequence length using the sequence start byte. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int lengthOfCodePointFromStartByte(byte startByte) { int unsignedStartByte = startByte & 0xFF; if (unsignedStartByte < 0x80) { // normal ASCII // 0xxx_xxxx return 1; } if (unsignedStartByte < 0xc0) { // illegal bytes // 10xx_xxxx throw new InvalidUtf8Exception("Illegal start 0x" + toHexString(unsignedStartByte).toUpperCase() + " of code point"); } if (unsignedStartByte < 0xe0) { // 110x_xxxx 10xx_xxxx return 2; } if (unsignedStartByte < 0xf0) { // 1110_xxxx 10xx_xxxx 10xx_xxxx return 3; } if (unsignedStartByte < 0xf8) { // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx return 4; } // Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal throw new InvalidUtf8Exception("Illegal start 0x" + toHexString(unsignedStartByte).toUpperCase() + " of code point"); } /** * Gets the UTF-8 encoded code point at the {@code position}. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int getCodePointAt(Slice utf8, int position) { int unsignedStartByte = utf8.getByte(position) & 0xFF; if (unsignedStartByte < 0x80) { // normal ASCII // 0xxx_xxxx return unsignedStartByte; } if (unsignedStartByte < 0xc0) { // illegal bytes // 10xx_xxxx throw new InvalidUtf8Exception("Illegal start 0x" + toHexString(unsignedStartByte).toUpperCase() + " of code point"); } if (unsignedStartByte < 0xe0) { // 110x_xxxx 10xx_xxxx if (position + 1 >= utf8.length()) { throw new InvalidUtf8Exception("UTF-8 sequence truncated"); } return ((unsignedStartByte & 0b0001_1111) << 6) | (utf8.getByte(position + 1) & 0b0011_1111); } if (unsignedStartByte < 0xf0) { // 1110_xxxx 10xx_xxxx 10xx_xxxx if (position + 2 >= utf8.length()) { throw new InvalidUtf8Exception("UTF-8 sequence truncated"); } return ((unsignedStartByte & 0b0000_1111) << 12) | ((utf8.getByteUnchecked(position + 1) & 0b0011_1111) << 6) | (utf8.getByteUnchecked(position + 2) & 0b0011_1111); } if (unsignedStartByte < 0xf8) { // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx if (position + 3 >= utf8.length()) { throw new InvalidUtf8Exception("UTF-8 sequence truncated"); } return ((unsignedStartByte & 0b0000_0111) << 18) | ((utf8.getByteUnchecked(position + 1) & 0b0011_1111) << 12) | ((utf8.getByteUnchecked(position + 2) & 0b0011_1111) << 6) | (utf8.getByteUnchecked(position + 3) & 0b0011_1111); } // Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal throw new InvalidUtf8Exception("Illegal start 0x" + toHexString(unsignedStartByte).toUpperCase() + " of code point"); } /** * Gets the UTF-8 encoded code point before the {@code position}. *
* Note: This method does not explicitly check for valid UTF-8, and may * return incorrect results or throw an exception for invalid UTF-8. */ public static int getCodePointBefore(Slice utf8, int position) { byte unsignedByte = utf8.getByte(position - 1); if (!isContinuationByte(unsignedByte)) { return unsignedByte & 0xFF; } if (!isContinuationByte(utf8.getByte(position - 2))) { return getCodePointAt(utf8, position - 2); } if (!isContinuationByte(utf8.getByte(position - 3))) { return getCodePointAt(utf8, position - 3); } if (!isContinuationByte(utf8.getByte(position - 4))) { return getCodePointAt(utf8, position - 4); } // Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal throw new InvalidUtf8Exception("UTF-8 is not well formed"); } private static boolean isContinuationByte(byte b) { return (b & 0b1100_0000) == 0b1000_0000; } /** * Convert the code point to UTF-8. *
* * @throws InvalidCodePointException if code point is not within a valid range */ public static Slice codePointToUtf8(int codePoint) { Slice utf8 = Slices.allocate(lengthOfCodePoint(codePoint)); setCodePointAt(codePoint, utf8, 0); return utf8; } /** * Sets the UTF-8 sequence for code point at the {@code position}. * * @throws InvalidCodePointException if code point is not within a valid range */ public static int setCodePointAt(int codePoint, Slice utf8, int position) { if (codePoint < 0) { throw new InvalidCodePointException(codePoint); } if (codePoint < 0x80) { // normal ASCII // 0xxx_xxxx utf8.setByte(position, codePoint); return 1; } if (codePoint < 0x800) { // 110x_xxxx 10xx_xxxx utf8.setByte(position, 0b1100_0000 | (codePoint >>> 6)); utf8.setByte(position + 1, 0b1000_0000 | (codePoint & 0b0011_1111)); return 2; } if (MIN_SURROGATE <= codePoint && codePoint <= MAX_SURROGATE) { throw new InvalidCodePointException(codePoint); } if (codePoint < 0x1_0000) { // 1110_xxxx 10xx_xxxx 10xx_xxxx utf8.setByte(position, 0b1110_0000 | ((codePoint >>> 12) & 0b0000_1111)); utf8.setByte(position + 1, 0b1000_0000 | ((codePoint >>> 6) & 0b0011_1111)); utf8.setByte(position + 2, 0b1000_0000 | (codePoint & 0b0011_1111)); return 3; } if (codePoint < 0x11_0000) { // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx utf8.setByte(position, 0b1111_0000 | ((codePoint >>> 18) & 0b0000_0111)); utf8.setByte(position + 1, 0b1000_0000 | ((codePoint >>> 12) & 0b0011_1111)); utf8.setByte(position + 2, 0b1000_0000 | ((codePoint >>> 6) & 0b0011_1111)); utf8.setByte(position + 3, 0b1000_0000 | (codePoint & 0b0011_1111)); return 4; } // Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal throw new InvalidCodePointException(codePoint); } private static int countContinuationBytes(byte i8) { // see below int value = i8 & 0xff; return (value >>> 7) & (~value >>> 6); } private static int countContinuationBytes(int i32) { // see below i32 = ((i32 & TOP_MASK32) >>> 1) & (~i32); return Integer.bitCount(i32); } private static int countContinuationBytes(long i64) { // Count the number of bytes that match 0b10xx_xxxx as follows: // 1. Mask off the 8th bit of every byte and shift it into the 7th position. // 2. Then invert the bytes, which turns the 0 in the 7th bit to a one. // 3. And together the restults of step 1 and 2, giving us a one in the 7th // position if the byte matched. // 4. Count the number of bits in the result, which is the number of bytes // that matched. i64 = ((i64 & TOP_MASK64) >>> 1) & (~i64); return Long.bitCount(i64); } } slice-0.16/src/main/java/io/airlift/slice/Slices.java 0000664 0000000 0000000 00000016032 12614743173 0022432 0 ustar 00root root 0000000 0000000 /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.airlift.slice; import sun.nio.ch.DirectBuffer; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.Charset; import static io.airlift.slice.Preconditions.checkNotNull; import static io.airlift.slice.Preconditions.checkPositionIndexes; import static java.nio.charset.StandardCharsets.UTF_8; import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; public final class Slices { /** * A slice with size {@code 0}. */ public static final Slice EMPTY_SLICE = new Slice(); private static final int SLICE_ALLOC_THRESHOLD = 524_288; // 2^19 private static final double SLICE_ALLOW_SKEW = 1.25; // must be > 1! private Slices() {} public static Slice ensureSize(Slice existingSlice, int minWritableBytes) { if (existingSlice == null) { return allocate(minWritableBytes); } if (minWritableBytes <= existingSlice.length()) { return existingSlice; } int newCapacity; if (existingSlice.length() == 0) { newCapacity = 1; } else { newCapacity = existingSlice.length(); } int minNewCapacity = minWritableBytes; while (newCapacity < minNewCapacity) { if (newCapacity < SLICE_ALLOC_THRESHOLD) { newCapacity <<= 1; } else { newCapacity *= SLICE_ALLOW_SKEW; } } Slice newSlice = allocate(newCapacity); newSlice.setBytes(0, existingSlice, 0, existingSlice.length()); return newSlice; } public static Slice allocate(int capacity) { if (capacity == 0) { return EMPTY_SLICE; } return new Slice(new byte[capacity]); } public static Slice allocateDirect(int capacity) { if (capacity == 0) { return EMPTY_SLICE; } return wrappedBuffer(ByteBuffer.allocateDirect(capacity)); } public static Slice copyOf(Slice slice) { return copyOf(slice, 0, slice.length()); } public static Slice copyOf(Slice slice, int offset, int length) { checkPositionIndexes(offset, offset + length, slice.length()); Slice copy = Slices.allocate(length); copy.setBytes(0, slice, offset, length); return copy; } /** * Wrap the visible portion of a {@link java.nio.ByteBuffer}. */ public static Slice wrappedBuffer(ByteBuffer buffer) { if (buffer instanceof DirectBuffer) { DirectBuffer direct = (DirectBuffer) buffer; return new Slice(null, direct.address() + buffer.position(), buffer.limit() - buffer.position(), buffer.capacity(), direct); } if (buffer.hasArray()) { int address = ARRAY_BYTE_BASE_OFFSET + buffer.arrayOffset() + buffer.position(); return new Slice(buffer.array(), address, buffer.limit() - buffer.position(), buffer.array().length, null); } throw new IllegalArgumentException("cannot wrap " + buffer.getClass().getName()); } public static Slice wrappedBuffer(byte... array) { if (array.length == 0) { return EMPTY_SLICE; } return new Slice(array); } public static Slice wrappedBuffer(byte[] array, int offset, int length) { if (length == 0) { return EMPTY_SLICE; } return new Slice(array, offset, length); } public static Slice wrappedBooleanArray(boolean... array) { return wrappedBooleanArray(array, 0, array.length); } public static Slice wrappedBooleanArray(boolean[] array, int offset, int length) { if (length == 0) { return EMPTY_SLICE; } return new Slice(array, offset, length); } public static Slice wrappedShortArray(short... array) { return wrappedShortArray(array, 0, array.length); } public static Slice wrappedShortArray(short[] array, int offset, int length) { if (length == 0) { return EMPTY_SLICE; } return new Slice(array, offset, length); } public static Slice wrappedIntArray(int... array) { return wrappedIntArray(array, 0, array.length); } public static Slice wrappedIntArray(int[] array, int offset, int length) { if (length == 0) { return EMPTY_SLICE; } return new Slice(array, offset, length); } public static Slice wrappedLongArray(long... array) { return wrappedLongArray(array, 0, array.length); } public static Slice wrappedLongArray(long[] array, int offset, int length) { if (length == 0) { return EMPTY_SLICE; } return new Slice(array, offset, length); } public static Slice wrappedFloatArray(float... array) { return wrappedFloatArray(array, 0, array.length); } public static Slice wrappedFloatArray(float[] array, int offset, int length) { if (length == 0) { return EMPTY_SLICE; } return new Slice(array, offset, length); } public static Slice wrappedDoubleArray(double... array) { return wrappedDoubleArray(array, 0, array.length); } public static Slice wrappedDoubleArray(double[] array, int offset, int length) { if (length == 0) { return EMPTY_SLICE; } return new Slice(array, offset, length); } public static Slice copiedBuffer(String string, Charset charset) { checkNotNull(string, "string is null"); checkNotNull(charset, "charset is null"); return wrappedBuffer(string.getBytes(charset)); } public static Slice utf8Slice(String string) { return copiedBuffer(string, UTF_8); } public static Slice mapFileReadOnly(File file) throws IOException { checkNotNull(file, "file is null"); if (!file.exists()) { throw new FileNotFoundException(file.toString()); } try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); FileChannel channel = randomAccessFile.getChannel()) { MappedByteBuffer byteBuffer = channel.map(MapMode.READ_ONLY, 0, file.length()); return wrappedBuffer(byteBuffer); } } } slice-0.16/src/main/java/io/airlift/slice/StringDecoder.java 0000664 0000000 0000000 00000005352 12614743173 0023747 0 ustar 00root root 0000000 0000000 /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.airlift.slice; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.util.IdentityHashMap; import java.util.Map; import static io.airlift.slice.Preconditions.checkNotNull; final class StringDecoder { private static final ThreadLocal