--- /dev/null
+/* Copyright (c) 2008, Nathan Sweet
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the distribution.
+ * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+package io.atomix.utils.serializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+
+import com.esotericsoftware.kryo.KryoException;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.util.UnsafeUtil;
+
+/**
+ * An InputStream that reads data from a byte array and optionally fills the byte array from another InputStream as needed.
+ * Utility methods are provided for efficiently reading primitive types and strings.
+ *
+ * @author Roman Levenstein <romixlev@gmail.com>
+ */
+public class ByteBufferInput extends Input {
+ protected ByteBuffer niobuffer;
+
+ protected boolean varIntsEnabled = true;
+
+ /* Default byte order is BIG_ENDIAN to be compatible to the base class */
+ ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
+
+ protected final static ByteOrder nativeOrder = ByteOrder.nativeOrder();
+
+ //See: https://stackoverflow.com/questions/3038392/do-java-arrays-have-a-maximum-size
+ static final int MAX_SAFE_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /** Creates an uninitialized Input. A buffer must be set before the Input is used.
+ * @see #setBuffer(ByteBuffer) */
+ public ByteBufferInput () {
+ }
+
+ /** Creates a new Input for reading from a byte array.
+ * @param bufferSize The size of the buffer. An exception is thrown if more bytes than this are read. */
+ public ByteBufferInput (int bufferSize) {
+ this.capacity = bufferSize;
+ niobuffer = ByteBuffer.allocateDirect(bufferSize);
+ niobuffer.order(byteOrder);
+ }
+
+ public ByteBufferInput (byte[] buffer) {
+ setBuffer(buffer);
+ }
+
+ /** Creates a new Input for reading from a ByteBuffer. */
+ public ByteBufferInput (ByteBuffer buffer) {
+ setBuffer(buffer);
+ }
+
+ /** Creates a new Input for reading from an InputStream with a buffer size of 4096. */
+ public ByteBufferInput (InputStream inputStream) {
+ this(4096);
+ if (inputStream == null) throw new IllegalArgumentException("inputStream cannot be null.");
+ this.inputStream = inputStream;
+ }
+
+ /** Creates a new Input for reading from an InputStream. */
+ public ByteBufferInput (InputStream inputStream, int bufferSize) {
+ this(bufferSize);
+ if (inputStream == null) throw new IllegalArgumentException("inputStream cannot be null.");
+ this.inputStream = inputStream;
+ }
+
+ public ByteOrder order () {
+ return byteOrder;
+ }
+
+ public void order (ByteOrder byteOrder) {
+ this.byteOrder = byteOrder;
+ }
+
+ /** Sets a new buffer, discarding any previous buffer. The position and total are reset. */
+ public void setBuffer (byte[] bytes) {
+ ByteBuffer directBuffer = ByteBuffer.allocateDirect(bytes.length);
+ directBuffer.put(bytes);
+ directBuffer.position(0);
+ directBuffer.limit(bytes.length);
+ directBuffer.order(byteOrder);
+ setBuffer(directBuffer);
+ }
+
+ /** Sets a new buffer, discarding any previous buffer. The byte order, position, limit and capacity are set to match the
+ * specified buffer. The total is reset. The {@link #setInputStream(InputStream) InputStream} is set to null. */
+ public void setBuffer (ByteBuffer buffer) {
+ if (buffer == null) throw new IllegalArgumentException("buffer cannot be null.");
+ niobuffer = buffer;
+ position = buffer.position();
+ limit = buffer.limit();
+ capacity = buffer.capacity();
+ byteOrder = buffer.order();
+ total = 0;
+ inputStream = null;
+ }
+
+ /** Releases a direct buffer. {@link #setBuffer(ByteBuffer)} must be called before any write operations can be performed. */
+ public void release () {
+ close();
+ UnsafeUtil.releaseBuffer(niobuffer);
+ niobuffer = null;
+ }
+
+ /** This constructor allows for creation of a direct ByteBuffer of a given size at a given address.
+ *
+ * <p>
+ * Typical usage could look like this snippet:
+ *
+ * <pre>
+ * // Explicitly allocate memory
+ * long bufAddress = UnsafeUtil.unsafe().allocateMemory(4096);
+ * // Create a ByteBufferInput using the allocated memory region
+ * ByteBufferInput buffer = new ByteBufferInput(bufAddress, 4096);
+ *
+ * // Do some operations on this buffer here
+ *
+ * // Say that ByteBuffer won't be used anymore
+ * buffer.release();
+ * // Release the allocated region
+ * UnsafeUtil.unsafe().freeMemory(bufAddress);
+ * </pre>
+ *
+ * @param address starting address of a memory region pre-allocated using Unsafe.allocateMemory() */
+ public ByteBufferInput (long address, int size) {
+ setBuffer(UnsafeUtil.getDirectBufferAt(address, size));
+ }
+
+ public ByteBuffer getByteBuffer () {
+ return niobuffer;
+ }
+
+ public InputStream getInputStream () {
+ return inputStream;
+ }
+
+ /** Sets a new InputStream. The position and total are reset, discarding any buffered bytes.
+ * @param inputStream May be null. */
+ public void setInputStream (InputStream inputStream) {
+ this.inputStream = inputStream;
+ limit = 0;
+ rewind();
+ }
+
+ public void rewind () {
+ super.rewind();
+ niobuffer.position(0);
+ }
+
+ /** Fills the buffer with more bytes. Can be overridden to fill the bytes from a source other than the InputStream. */
+ protected int fill (ByteBuffer buffer, int offset, int count) throws KryoException {
+ if (inputStream == null) return -1;
+ try {
+ byte[] tmp = new byte[count];
+ int result = inputStream.read(tmp, 0, count);
+ buffer.position(offset);
+ if (result >= 0) {
+ buffer.put(tmp, 0, result);
+ buffer.position(offset);
+ }
+ return result;
+ } catch (IOException ex) {
+ throw new KryoException(ex);
+ }
+ }
+
+ /** @param required Must be > 0. The buffer is filled until it has at least this many bytes.
+ * @return the number of bytes remaining.
+ * @throws KryoException if EOS is reached before required bytes are read (buffer underflow). */
+ final protected int require (int required) throws KryoException {
+ int remaining = limit - position;
+ if (remaining >= required) return remaining;
+ if (required > capacity) throw new KryoException("Buffer too small: capacity: " + capacity + ", required: " + required);
+
+ int count;
+ // Try to fill the buffer.
+ if (remaining > 0) {
+ count = fill(niobuffer, limit, capacity - limit);
+ if (count == -1) throw new KryoException("Buffer underflow.");
+ remaining += count;
+ if (remaining >= required) {
+ limit += count;
+ return remaining;
+ }
+ }
+
+ // Compact. Position after compaction can be non-zero
+ niobuffer.position(position);
+ niobuffer.compact();
+ total += position;
+ position = 0;
+
+ while (true) {
+ count = fill(niobuffer, remaining, capacity - remaining);
+ if (count == -1) {
+ if (remaining >= required) break;
+ throw new KryoException("Buffer underflow.");
+ }
+ remaining += count;
+ if (remaining >= required) break; // Enough has been read.
+ }
+ limit = remaining;
+ niobuffer.position(0);
+ return remaining;
+ }
+
+ /** @param optional Try to fill the buffer with this many bytes.
+ * @return the number of bytes remaining, but not more than optional, or -1 if the EOS was reached and the buffer is empty. */
+ private int optional (int optional) throws KryoException {
+ int remaining = limit - position;
+ if (remaining >= optional) return optional;
+ optional = Math.min(optional, capacity);
+
+ // Try to fill the buffer.
+ int count = fill(niobuffer, limit, capacity - limit);
+ if (count == -1) return remaining == 0 ? -1 : Math.min(remaining, optional);
+ remaining += count;
+ if (remaining >= optional) {
+ limit += count;
+ return optional;
+ }
+
+ // Compact.
+ niobuffer.compact();
+ total += position;
+ position = 0;
+
+ while (true) {
+ count = fill(niobuffer, remaining, capacity - remaining);
+ if (count == -1) break;
+ remaining += count;
+ if (remaining >= optional) break; // Enough has been read.
+ }
+ limit = remaining;
+ niobuffer.position(position);
+ return remaining == 0 ? -1 : Math.min(remaining, optional);
+ }
+
+ // InputStream
+
+ /** Reads a single byte as an int from 0 to 255, or -1 if there are no more bytes are available. */
+ public int read () throws KryoException {
+ if (optional(1) <= 0) return -1;
+ niobuffer.position(position);
+ position++;
+ return niobuffer.get() & 0xFF;
+ }
+
+ /** Reads bytes.length bytes or less and writes them to the specified byte[], starting at 0, and returns the number of bytes
+ * read. */
+ public int read (byte[] bytes) throws KryoException {
+ niobuffer.position(position);
+ return read(bytes, 0, bytes.length);
+ }
+
+ /** Reads count bytes or less and writes them to the specified byte[], starting at offset, and returns the number of bytes read
+ * or -1 if no more bytes are available. */
+ public int read (byte[] bytes, int offset, int count) throws KryoException {
+ niobuffer.position(position);
+ if (bytes == null) throw new IllegalArgumentException("bytes cannot be null.");
+ int startingCount = count;
+ int copyCount = Math.min(limit - position, count);
+ while (true) {
+ niobuffer.get(bytes, offset, copyCount);
+ position += copyCount;
+ count -= copyCount;
+ if (count == 0) break;
+ offset += copyCount;
+ copyCount = optional(count);
+ if (copyCount == -1) {
+ // End of data.
+ if (startingCount == count) return -1;
+ break;
+ }
+ if (position == limit) break;
+ }
+ return startingCount - count;
+ }
+
+ public void setPosition (int position) {
+ this.position = position;
+ this.niobuffer.position(position);
+ }
+
+ /** Sets the limit in the buffer. */
+ public void setLimit (int limit) {
+ this.limit = limit;
+ this.niobuffer.limit(limit);
+ }
+
+ public void skip (int count) throws KryoException {
+ super.skip(count);
+ niobuffer.position(this.position());
+ }
+
+ /** Discards the specified number of bytes. */
+ public long skip (long count) throws KryoException {
+ long remaining = count;
+ while (remaining > 0) {
+ int skip = (int)Math.min(MAX_SAFE_ARRAY_SIZE, remaining);
+ skip(skip);
+ remaining -= skip;
+ }
+ return count;
+ }
+
+ /** Closes the underlying InputStream, if any. */
+ public void close () throws KryoException {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ // byte
+
+ /** Reads a single byte. */
+ public byte readByte () throws KryoException {
+ niobuffer.position(position);
+ require(1);
+ position++;
+ return niobuffer.get();
+ }
+
+ /** Reads a byte as an int from 0 to 255. */
+ public int readByteUnsigned () throws KryoException {
+ // buffer.position(position);
+ require(1);
+ position++;
+ return niobuffer.get() & 0xFF;
+ }
+
+ /** Reads the specified number of bytes into a new byte[]. */
+ public byte[] readBytes (int length) throws KryoException {
+ byte[] bytes = new byte[length];
+ readBytes(bytes, 0, length);
+ return bytes;
+ }
+
+ /** Reads bytes.length bytes and writes them to the specified byte[], starting at index 0. */
+ public void readBytes (byte[] bytes) throws KryoException {
+ readBytes(bytes, 0, bytes.length);
+ }
+
+ /** Reads count bytes and writes them to the specified byte[], starting at offset. */
+ public void readBytes (byte[] bytes, int offset, int count) throws KryoException {
+ if (bytes == null) throw new IllegalArgumentException("bytes cannot be null.");
+ int copyCount = Math.min(limit - position, count);
+ while (true) {
+ niobuffer.get(bytes, offset, copyCount);
+ position += copyCount;
+ count -= copyCount;
+ if (count == 0) break;
+ offset += copyCount;
+ copyCount = Math.min(count, capacity);
+ require(copyCount);
+ }
+ }
+
+ public int readInt () throws KryoException {
+ require(4);
+ position += 4;
+ return niobuffer.getInt();
+ }
+
+ public int readInt (boolean optimizePositive) throws KryoException {
+ if (varIntsEnabled)
+ return readVarInt(optimizePositive);
+ else
+ return readInt();
+ }
+
+ public int readVarInt (boolean optimizePositive) throws KryoException {
+ niobuffer.position(position);
+ if (require(1) < 5) return readInt_slow(optimizePositive);
+ position++;
+ int b = niobuffer.get();
+ int result = b & 0x7F;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 7;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 14;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 21;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 28;
+ }
+ }
+ }
+ }
+ return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
+ }
+
+ private int readInt_slow (boolean optimizePositive) {
+ // The buffer is guaranteed to have at least 1 byte.
+ position++;
+ int b = niobuffer.get();
+ int result = b & 0x7F;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 7;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 14;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 21;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 28;
+ }
+ }
+ }
+ }
+ return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
+ }
+
+ /** Returns true if enough bytes are available to read an int with {@link #readInt(boolean)}. */
+ public boolean canReadInt () throws KryoException {
+ if (limit - position >= 5) return true;
+ if (optional(5) <= 0) return false;
+ int p = position;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ return true;
+ }
+
+ /** Returns true if enough bytes are available to read a long with {@link #readLong(boolean)}. */
+ public boolean canReadLong () throws KryoException {
+ if (limit - position >= 9) return true;
+ if (optional(5) <= 0) return false;
+ int p = position;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ if ((niobuffer.get(p++) & 0x80) == 0) return true;
+ if (p == limit) return false;
+ return true;
+ }
+
+ /** Reads the length and string of UTF8 characters, or null. This can read strings written by
+ * {@link Output#writeString(String)} , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
+ * @return May be null. */
+ public String readString () {
+ niobuffer.position(position);
+ int available = require(1);
+ position++;
+ int b = niobuffer.get();
+ if ((b & 0x80) == 0) return readAscii(); // ASCII.
+ // Null, empty, or UTF8.
+ int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b);
+ switch (charCount) {
+ case 0:
+ return null;
+ case 1:
+ return "";
+ }
+ charCount--;
+ if (chars.length < charCount) chars = new char[charCount];
+ readUtf8(charCount);
+ return new String(chars, 0, charCount);
+ }
+
+ private int readUtf8Length (int b) {
+ int result = b & 0x3F; // Mask all but first 6 bits.
+ if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 6;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 13;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 20;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 27;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private int readUtf8Length_slow (int b) {
+ int result = b & 0x3F; // Mask all but first 6 bits.
+ if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 6;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 13;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 20;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 27;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private void readUtf8 (int charCount) {
+ char[] chars = this.chars;
+ // Try to read 7 bit ASCII chars.
+ int charIndex = 0;
+ int count = Math.min(require(1), charCount);
+ int position = this.position;
+ int b;
+ while (charIndex < count) {
+ position++;
+ b = niobuffer.get();
+ if (b < 0) {
+ position--;
+ break;
+ }
+ chars[charIndex++] = (char)b;
+ }
+ this.position = position;
+ // If buffer didn't hold all chars or any were not ASCII, use slow path for remainder.
+ if (charIndex < charCount) {
+ niobuffer.position(position);
+ readUtf8_slow(charCount, charIndex);
+ }
+ }
+
+ private void readUtf8_slow (int charCount, int charIndex) {
+ char[] chars = this.chars;
+ while (charIndex < charCount) {
+ if (position == limit) require(1);
+ position++;
+ int b = niobuffer.get() & 0xFF;
+ switch (b >> 4) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ chars[charIndex] = (char)b;
+ break;
+ case 12:
+ case 13:
+ if (position == limit) require(1);
+ position++;
+ chars[charIndex] = (char)((b & 0x1F) << 6 | niobuffer.get() & 0x3F);
+ break;
+ case 14:
+ require(2);
+ position += 2;
+ int b2 = niobuffer.get();
+ int b3 = niobuffer.get();
+ chars[charIndex] = (char)((b & 0x0F) << 12 | (b2 & 0x3F) << 6 | b3 & 0x3F);
+ break;
+ }
+ charIndex++;
+ }
+ }
+
+ private String readAscii () {
+ int end = position;
+ int start = end - 1;
+ int limit = this.limit;
+ int b;
+ do {
+ if (end == limit) return readAscii_slow();
+ end++;
+ b = niobuffer.get();
+ } while ((b & 0x80) == 0);
+ niobuffer.put(end - 1, (byte)(niobuffer.get(end - 1) & 0x7F)); // Mask end of ascii bit.
+ byte[] tmp = new byte[end - start];
+ niobuffer.position(start);
+ niobuffer.get(tmp);
+ String value = new String(tmp, 0, 0, end - start);
+ niobuffer.put(end - 1, (byte)(niobuffer.get(end - 1) | 0x80));
+ position = end;
+ niobuffer.position(position);
+ return value;
+ }
+
+ private String readAscii_slow () {
+ position--; // Re-read the first byte.
+ // Copy chars currently in buffer.
+ int charCount = limit - position;
+ if (charCount > chars.length) chars = new char[charCount * 2];
+ char[] chars = this.chars;
+ for (int i = position, ii = 0, n = limit; i < n; i++, ii++)
+ chars[ii] = (char)niobuffer.get(i);
+ position = limit;
+ // Copy additional chars one by one.
+ while (true) {
+ require(1);
+ position++;
+ int b = niobuffer.get();
+ if (charCount == chars.length) {
+ char[] newChars = new char[charCount * 2];
+ System.arraycopy(chars, 0, newChars, 0, charCount);
+ chars = newChars;
+ this.chars = newChars;
+ }
+ if ((b & 0x80) == 0x80) {
+ chars[charCount++] = (char)(b & 0x7F);
+ break;
+ }
+ chars[charCount++] = (char)b;
+ }
+ return new String(chars, 0, charCount);
+ }
+
+ /** Reads the length and string of UTF8 characters, or null. This can read strings written by
+ * {@link Output#writeString(String)} , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
+ * @return May be null. */
+ public StringBuilder readStringBuilder () {
+ niobuffer.position(position);
+ int available = require(1);
+ position++;
+ int b = niobuffer.get();
+ if ((b & 0x80) == 0) return new StringBuilder(readAscii()); // ASCII.
+ // Null, empty, or UTF8.
+ int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b);
+ switch (charCount) {
+ case 0:
+ return null;
+ case 1:
+ return new StringBuilder("");
+ }
+ charCount--;
+ if (chars.length < charCount) chars = new char[charCount];
+ readUtf8(charCount);
+ StringBuilder builder = new StringBuilder(charCount);
+ builder.append(chars, 0, charCount);
+ return builder;
+ }
+
+ /** Reads a 4 byte float. */
+ public float readFloat () throws KryoException {
+ require(4);
+ position += 4;
+ return niobuffer.getFloat();
+ }
+
+ /** Reads a 1-5 byte float with reduced precision. */
+ public float readFloat (float precision, boolean optimizePositive) throws KryoException {
+ return readInt(optimizePositive) / (float)precision;
+ }
+
+ /** Reads a 2 byte short. */
+ public short readShort () throws KryoException {
+ require(2);
+ position += 2;
+ return niobuffer.getShort();
+ }
+
+ /** Reads a 2 byte short as an int from 0 to 65535. */
+ public int readShortUnsigned () throws KryoException {
+ require(2);
+ position += 2;
+ return niobuffer.getShort();
+ }
+
+ /** Reads an 8 byte long. */
+ public long readLong () throws KryoException {
+ require(8);
+ position += 8;
+ return niobuffer.getLong();
+ }
+
+ /** {@inheritDoc} */
+ public long readLong (boolean optimizePositive) throws KryoException {
+ if (varIntsEnabled)
+ return readVarLong(optimizePositive);
+ else
+ return readLong();
+ }
+
+ /** {@inheritDoc} */
+ public long readVarLong (boolean optimizePositive) throws KryoException {
+ niobuffer.position(position);
+ if (require(1) < 9) return readLong_slow(optimizePositive);
+ position++;
+ int b = niobuffer.get();
+ long result = b & 0x7F;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 7;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 14;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 21;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 28;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 35;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 42;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 49;
+ if ((b & 0x80) != 0) {
+ position++;
+ b = niobuffer.get();
+ result |= (long)b << 56;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!optimizePositive) result = (result >>> 1) ^ -(result & 1);
+ return result;
+ }
+
+ private long readLong_slow (boolean optimizePositive) {
+ // The buffer is guaranteed to have at least 1 byte.
+ position++;
+ int b = niobuffer.get();
+ long result = b & 0x7F;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 7;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 14;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (b & 0x7F) << 21;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 28;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 35;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 42;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (long)(b & 0x7F) << 49;
+ if ((b & 0x80) != 0) {
+ require(1);
+ position++;
+ b = niobuffer.get();
+ result |= (long)b << 56;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!optimizePositive) result = (result >>> 1) ^ -(result & 1);
+ return result;
+ }
+
+ /** Reads a 1 byte boolean. */
+ public boolean readBoolean () throws KryoException {
+ require(1);
+ position++;
+ return niobuffer.get() == 1 ? true : false;
+ }
+
+ /** Reads a 2 byte char. */
+ public char readChar () throws KryoException {
+ require(2);
+ position += 2;
+ return niobuffer.getChar();
+ }
+
+ /** Reads an 8 bytes double. */
+ public double readDouble () throws KryoException {
+ require(8);
+ position += 8;
+ return niobuffer.getDouble();
+ }
+
+ /** Reads a 1-9 byte double with reduced precision. */
+ public double readDouble (double precision, boolean optimizePositive) throws KryoException {
+ return readLong(optimizePositive) / (double)precision;
+ }
+
+ // Methods implementing bulk operations on arrays of primitive types
+
+ /** Bulk input of an int array. */
+ public int[] readInts (int length) throws KryoException {
+ if (capacity - position >= length * 4 && isNativeOrder()) {
+ int[] array = new int[length];
+ IntBuffer buf = niobuffer.asIntBuffer();
+ buf.get(array);
+ position += length * 4;
+ niobuffer.position(position);
+ return array;
+ } else
+ return super.readInts(length);
+ }
+
+ /** Bulk input of a long array. */
+ public long[] readLongs (int length) throws KryoException {
+ if (capacity - position >= length * 8 && isNativeOrder()) {
+ long[] array = new long[length];
+ LongBuffer buf = niobuffer.asLongBuffer();
+ buf.get(array);
+ position += length * 8;
+ niobuffer.position(position);
+ return array;
+ } else
+ return super.readLongs(length);
+ }
+
+ /** Bulk input of a float array. */
+ public float[] readFloats (int length) throws KryoException {
+ if (capacity - position >= length * 4 && isNativeOrder()) {
+ float[] array = new float[length];
+ FloatBuffer buf = niobuffer.asFloatBuffer();
+ buf.get(array);
+ position += length * 4;
+ niobuffer.position(position);
+ return array;
+ } else
+ return super.readFloats(length);
+ }
+
+ /** Bulk input of a short array. */
+ public short[] readShorts (int length) throws KryoException {
+ if (capacity - position >= length * 2 && isNativeOrder()) {
+ short[] array = new short[length];
+ ShortBuffer buf = niobuffer.asShortBuffer();
+ buf.get(array);
+ position += length * 2;
+ niobuffer.position(position);
+ return array;
+ } else
+ return super.readShorts(length);
+ }
+
+ /** Bulk input of a char array. */
+ public char[] readChars (int length) throws KryoException {
+ if (capacity - position >= length * 2 && isNativeOrder()) {
+ char[] array = new char[length];
+ CharBuffer buf = niobuffer.asCharBuffer();
+ buf.get(array);
+ position += length * 2;
+ niobuffer.position(position);
+ return array;
+ } else
+ return super.readChars(length);
+ }
+
+ /** Bulk input of a double array. */
+ public double[] readDoubles (int length) throws KryoException {
+ if (capacity - position >= length * 8 && isNativeOrder()) {
+ double[] array = new double[length];
+ DoubleBuffer buf = niobuffer.asDoubleBuffer();
+ buf.get(array);
+ position += length * 8;
+ niobuffer.position(position);
+ return array;
+ } else
+ return super.readDoubles(length);
+ }
+
+ private boolean isNativeOrder () {
+ return byteOrder == nativeOrder;
+ }
+
+ /** Return current setting for variable length encoding of integers
+ * @return current setting for variable length encoding of integers */
+ public boolean getVarIntsEnabled () {
+ return varIntsEnabled;
+ }
+
+ /** Controls if a variable length encoding for integer types should be used when serializers suggest it.
+ *
+ * @param varIntsEnabled */
+ public void setVarIntsEnabled (boolean varIntsEnabled) {
+ this.varIntsEnabled = varIntsEnabled;
+ }
+}