All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dorkbox.util.bytes.ByteBuffer2 Maven / Gradle / Ivy

The newest version!
/*
 * 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.
 *
 * Modified by dorkbox, llc
 */
package dorkbox.util.bytes;

import java.nio.BufferUnderflowException;
import java.util.Arrays;

/**
 * A self-growing byte array wrapper.
 *
 * Utility methods are provided for efficiently writing primitive types and strings.
 *
 * Encoding of integers: BIG_ENDIAN is used for storing fixed native size integer values LITTLE_ENDIAN is used for a variable
 * length encoding of integer values
 *
 * @author Nathan Sweet 
 */
public class ByteBuffer2 {
    private int capacity;  // exactly how many bytes have been allocated
    private int maxCapacity;  // how large we can grow


    private int position;  // current pointer to the point where data is read/written

    private byte[] bytes; // the backing buffer
    private char[] chars = new char[32]; // small buffer for reading strings

    /**
     * Creates an uninitialized object. {@link #setBuffer(byte[], int)} must be called before the object is used.
     */
    public ByteBuffer2() {
    }

    /**
     * Creates a new object for writing to a byte array.
     *
     * @param bufferSize
     *            The initial and maximum size of the buffer. An exception is thrown if this size is exceeded.
     */
    public ByteBuffer2(int bufferSize) {
        this(bufferSize, bufferSize);
    }

    /**
     * Creates a new object for writing to a byte array.
     *
     * @param bufferSize
     *            The initial size of the buffer.
     * @param maxBufferSize
     *            The buffer is doubled as needed until it exceeds maxBufferSize and an exception is thrown. Can be -1
     *            for no maximum.
     */
    public ByteBuffer2(int bufferSize, int maxBufferSize) {
        if (maxBufferSize < -1) {
            throw new IllegalArgumentException("maxBufferSize cannot be < -1: " + maxBufferSize);
        }

        this.capacity = bufferSize;
        this.maxCapacity = maxBufferSize == -1 ? Integer.MAX_VALUE : maxBufferSize;
        this.bytes = new byte[bufferSize];
    }

    /**
     * Creates a new object for writing to a byte array.
     *
     * @see #setBuffer(byte[])
     */
    public ByteBuffer2(byte[] buffer) {
        this(buffer, buffer.length);
    }

    /**
     * Creates a new object for writing to a byte array.
     *
     * @see #setBuffer(byte[], int)
     */
    public ByteBuffer2(byte[] buffer, int maxBufferSize) {
        if (buffer == null) {
            throw new IllegalArgumentException("buffer cannot be null.");
        }
        setBuffer(buffer, maxBufferSize);
    }

    /**
     * Sets the buffer that will be written to. {@link #setBuffer(byte[], int)} is called with the specified buffer's
     * length as the maxBufferSize.
     */
    public void setBuffer(byte[] buffer) {
        setBuffer(buffer, buffer.length);
    }

    /**
     * Sets the buffer that will be written to. The position and total are reset, discarding any buffered bytes.
     *
     * @param maxBufferSize
     *            The buffer is doubled as needed until it exceeds maxBufferSize and an exception is thrown.
     */
    public void setBuffer(byte[] buffer, int maxBufferSize) {
        if (buffer == null) {
            throw new IllegalArgumentException("buffer cannot be null.");
        }

        if (maxBufferSize < -1) {
            throw new IllegalArgumentException("maxBufferSize cannot be < -1: " + maxBufferSize);
        }

        this.bytes = buffer;
        this.maxCapacity = maxBufferSize == -1 ? Integer.MAX_VALUE : maxBufferSize;
        this.capacity = buffer.length;
        this.position = 0;
    }

    /**
     * Returns the buffer. The bytes between zero and {@link #position()} are the data that has been written.
     */
    public byte[] getBuffer() {
        return this.bytes;
    }

    /**
     * Returns a new byte array containing the bytes currently in the buffer between zero and {@link #position()}.
     */
    public byte[] toBytes() {
        int position2 = this.position;
        byte[] newBuffer = new byte[position2];

        if (position2 > 0) {
            System.arraycopy(this.bytes, 0, newBuffer, 0, position2);
        }
        return newBuffer;
    }

    /**
     * Returns the remaining read/write bytes available before the end of the buffer
     */
    public int remaining() {
        return this.capacity - this.position;
    }

    /**
     * Returns the size of the backing byte buffer
     */
    public int capacity() {
        return this.capacity;
    }

    /**
     * Returns the current position in the buffer. This is the number of bytes that have not been flushed.
     */
    public int position() {
        return this.position;
    }

    /**
     * Sets the current position in the buffer.
     */
    public void setPosition(int position) {
        this.position = position;
    }

    /**
     * Sets the position to zero.
     */
    public void clear() {
        this.position = 0;
    }

    /**
     * Sets the position to zero.
     */
    public void rewind() {
        this.position = 0;
    }

    /**
     * Sets the position to zero, and write 0 to all bytes in the buffer
     */
    public void clearSecure() {
        this.position = 0;
        byte[] buffer = this.bytes;

        for (int i=0;i= required) {
            return false;
        }
        if (required > this.maxCapacity) {
            throw new RuntimeException("Buffer overflow. Max capacity: " + this.maxCapacity + ", required: " + required);
        }

        while (this.capacity - this.position < required) {
            if (this.capacity == this.maxCapacity) {
                throw new RuntimeException("Buffer overflow. Available: " + (this.capacity - this.position)
                        + ", required: " + required);
            }

            // Grow buffer.
            if (this.capacity == 0) {
                this.capacity = 1;
            }
            this.capacity = Math.min((int)(this.capacity * 1.6D), this.maxCapacity);
            if (this.capacity < 0) {
                this.capacity = this.maxCapacity;
            }
            byte[] newBuffer = new byte[this.capacity];
            System.arraycopy(this.bytes, 0, newBuffer, 0, this.position);
            this.bytes = newBuffer;
        }

        return true;
    }

    // byte

    /**
     * Writes a byte.
     */
    public void writeByte(byte value) {
        if (this.position == this.capacity) {
            require(1);
        }
        this.bytes[this.position++] = value;
    }

    /**
     * Writes a byte.
     */
    public void writeByte(int value) {
        if (this.position == this.capacity) {
            require(1);
        }
        this.bytes[this.position++] = (byte) value;
    }

    /**
     * Writes the bytes. Note the byte[] length is not written.
     */
    public void writeBytes(byte[] bytes) {
        if (bytes == null) {
            throw new IllegalArgumentException("bytes cannot be null.");
        }
        writeBytes(bytes, 0, bytes.length);
    }

    /**
     * Writes the bytes. Note the byte[] length is not written.
     */
    public void writeBytes(byte[] bytes, int offset, int count) {
        if (bytes == null) {
            throw new IllegalArgumentException("bytes cannot be null.");
        }

        int copyCount = Math.min(this.capacity - this.position, count);
        while (true) {
            System.arraycopy(bytes, offset, this.bytes, this.position, copyCount);
            this.position += copyCount;
            count -= copyCount;
            if (count == 0) {
                return;
            }
            offset += copyCount;
            copyCount = Math.min(this.capacity, count);
            require(copyCount);
        }
    }

    /**
     * Reads a single byte.
     */
    public byte readByte() {
        return this.bytes[this.position++];
    }

    /**
     * Reads a byte as an int from 0 to 255.
     */
    public int readByteUnsigned() {
        return this.bytes[this.position++] & 0xFF;
    }

    /**
     * Reads a single byte, does not advance the position
     */
    public byte readByte(int position) {
        return this.bytes[position];
    }

    /**
     * Reads a byte as an int from 0 to 255, does not advance the position
     */
    public int readByteUnsigned(int position) {
        return this.bytes[position] & 0xFF;
    }

    /**
     * Reads the specified number of bytes into a new byte[].
     */
    public byte[] readBytes(int length) {
        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) {
        readBytes(bytes, 0, bytes.length);
    }

    /**
     * Reads count bytes and writes them to the specified byte[], starting at offset in target byte array.
     */
    public void readBytes(byte[] bytes, int offset, int count) {
        if (bytes == null) {
            throw new IllegalArgumentException("bytes cannot be null.");
        }

        System.arraycopy(this.bytes, this.position, bytes, offset, count);
        this.position += count;
    }

    // int

    /**
     * Writes a 4 byte int. Uses BIG_ENDIAN byte order.
     */
    public void writeInt(int value) {
        require(4);

        byte[] buffer = this.bytes;
        buffer[this.position++] = (byte) (value >> 24);
        buffer[this.position++] = (byte) (value >> 16);
        buffer[this.position++] = (byte) (value >> 8);
        buffer[this.position++] = (byte) value;
    }

    /**
     * Writes a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not
     * guaranteed that a variable length encoding will be really used. The stream may decide to use native-sized integer
     * representation for efficiency reasons.
     *
     * @param optimizePositive
     *            If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be
     *            inefficient (5 bytes).
     */
    public int writeInt(int value, boolean optimizePositive) {
        return writeVarInt(value, optimizePositive);
    }

    /**
     * Writes a 1-5 byte int. It is guaranteed that a varible length encoding will be used.
     *
     * @param optimizePositive
     *            If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be
     *            inefficient (5 bytes).
     */
    public int writeVarInt(int value, boolean optimizePositive) {
        if (!optimizePositive) {
            value = value << 1 ^ value >> 31;
        }

        if (value >>> 7 == 0) {
            require(1);
            this.bytes[this.position++] = (byte) value;
            return 1;
        }
        if (value >>> 14 == 0) {
            require(2);
            byte[] buffer = this.bytes;
            buffer[this.position++] = (byte) (value & 0x7F | 0x80);
            buffer[this.position++] = (byte) (value >>> 7);
            return 2;
        }
        if (value >>> 21 == 0) {
            require(3);
            byte[] buffer = this.bytes;
            buffer[this.position++] = (byte) (value & 0x7F | 0x80);
            buffer[this.position++] = (byte) (value >>> 7 | 0x80);
            buffer[this.position++] = (byte) (value >>> 14);
            return 3;
        }
        if (value >>> 28 == 0) {
            require(4);
            byte[] buffer = this.bytes;
            buffer[this.position++] = (byte) (value & 0x7F | 0x80);
            buffer[this.position++] = (byte) (value >>> 7 | 0x80);
            buffer[this.position++] = (byte) (value >>> 14 | 0x80);
            buffer[this.position++] = (byte) (value >>> 21);
            return 4;
        }

        require(5);
        byte[] buffer = this.bytes;
        buffer[this.position++] = (byte) (value & 0x7F | 0x80);
        buffer[this.position++] = (byte) (value >>> 7 | 0x80);
        buffer[this.position++] = (byte) (value >>> 14 | 0x80);
        buffer[this.position++] = (byte) (value >>> 21 | 0x80);
        buffer[this.position++] = (byte) (value >>> 28);
        return 5;
    }

    /**
     * Reads a 4 byte int.
     */
    public int readInt() {
        byte[] buffer = this.bytes;
        int position = this.position;

        int value = (buffer[position] & 0xFF) << 24
                | (buffer[position + 1] & 0xFF) << 16
                | (buffer[position + 2] & 0xFF) << 8
                | buffer[position + 3] & 0xFF;

        this.position = position + 4;
        return value;
    }

    /**
     * Reads a 4 byte int, does not advance the position
     */
    public int readInt(int position) {
        byte[] buffer = this.bytes;
        int value = (buffer[position] & 0xFF) << 24
                | (buffer[position + 1] & 0xFF) << 16
                | (buffer[position + 2] & 0xFF) << 8
                | buffer[position + 3] & 0xFF;

        this.position = position + 4;
        return value;
    }

    /**
     * Reads a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not
     * guaranteed that a variable length encoding will be really used. The stream may decide to use native-sized integer
     * representation for efficiency reasons.
     */
    public int readInt(boolean optimizePositive) {
        return readVarInt(optimizePositive);
    }

    /**
     * Reads a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not
     * guaranteed that a variable length encoding will be really used. The stream may decide to use native-sized integer
     * representation for efficiency reasons.
     * 

* does not advance the position */ public int readInt(int position, boolean optimizePositive) { int pos = this.position; this.position = position; int value = readVarInt(optimizePositive); this.position = pos; return value; } /** * Reads a 1-5 byte int. It is guaranteed that a variable length encoding will be used. */ private int readVarInt(boolean optimizePositive) { byte[] buffer = this.bytes; if (this.capacity - this.position < 5) { return readInt_slow(optimizePositive); } int b = buffer[this.position++]; int result = b & 0x7F; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 28; } } } } return optimizePositive ? result : result >>> 1 ^ -(result & 1); } private int readInt_slow(boolean optimizePositive) { byte[] buffer = this.bytes; // The buffer is guaranteed to have at least 1 byte. int b = buffer[this.position++]; int result = b & 0x7F; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { b = buffer[this.position++]; 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() { if (this.capacity - this.position >= 5) { return true; } if (this.position + 1 > this.capacity) { return false; } byte[] buffer = this.bytes; int p = this.position; if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } return true; } /** * Returns true if enough bytes are available to read an int with {@link #readInt(boolean)}. */ public boolean canReadInt(int position) { if (this.capacity - position >= 5) { return true; } if (position + 1 > this.capacity) { return false; } byte[] buffer = this.bytes; int p = position; if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } return true; } // string /** * Writes the length and string, or null. Short strings are checked and if ASCII they are written more efficiently, * else they are written as UTF8. If a string is known to be ASCII, {@link ByteBuffer2#writeAscii(String)} may be used. The * string can be read using {@link ByteBuffer2#readString()} or {@link ByteBuffer2#readStringBuilder()}. * * @param value * May be null. */ @SuppressWarnings("deprecation") public void writeString(String value) { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); if (charCount == 0) { writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8. return; } // Detect ASCII. boolean ascii = false; if (charCount > 1 && charCount < 64) { ascii = true; for (int i = 0; i < charCount; i++) { int c = value.charAt(i); if (c > 127) { ascii = false; break; } } } if (ascii) { if (this.capacity - this.position < charCount) { writeAscii_slow(value, charCount); } else { value.getBytes(0, charCount, this.bytes, this.position); this.position += charCount; } this.bytes[this.position - 1] |= 0x80; } else { writeUtf8Length(charCount + 1); int charIndex = 0; if (this.capacity - this.position >= charCount) { // Try to write 8 bit chars. byte[] buffer = this.bytes; int position = this.position; for (; charIndex < charCount; charIndex++) { int c = value.charAt(charIndex); if (c > 127) { break; } buffer[position++] = (byte) c; } this.position = position; } if (charIndex < charCount) { writeString_slow(value, charCount, charIndex); } } } /** * Writes the length and CharSequence as UTF8, or null. The string can be read using {@link ByteBuffer2#readString()} or * {@link ByteBuffer2#readStringBuilder()}. * * @param value * May be null. */ public void writeString(CharSequence value) { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); if (charCount == 0) { writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8. return; } writeUtf8Length(charCount + 1); int charIndex = 0; if (this.capacity - this.position >= charCount) { // Try to write 8 bit chars. byte[] buffer = this.bytes; int position = this.position; for (; charIndex < charCount; charIndex++) { int c = value.charAt(charIndex); if (c > 127) { break; } buffer[position++] = (byte) c; } this.position = position; } if (charIndex < charCount) { writeString_slow(value, charCount, charIndex); } } /** * Writes a string that is known to contain only ASCII characters. Non-ASCII strings passed to this method will be * corrupted. Each byte is a 7 bit character with the remaining byte denoting if another character is available. * This is slightly more efficient than {@link ByteBuffer2#writeString(String)}. The string can be read using * {@link ByteBuffer2#readString()} or {@link ByteBuffer2#readStringBuilder()}. * * @param value * May be null. */ @SuppressWarnings("deprecation") public void writeAscii(String value) { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); switch (charCount) { case 0 : writeByte(1 | 0x80); // 1 is string length + 1, bit 8 means UTF8. return; case 1 : writeByte(2 | 0x80); // 2 is string length + 1, bit 8 means UTF8. writeByte(value.charAt(0)); return; } if (this.capacity - this.position < charCount) { writeAscii_slow(value, charCount); } else { value.getBytes(0, charCount, this.bytes, this.position); this.position += charCount; } this.bytes[this.position - 1] |= 0x80; // Bit 8 means end of ASCII. } /** * Writes the length of a string, which is a variable length encoded int except the first byte uses bit 8 to denote * UTF8 and bit 7 to denote if another byte is present. */ private void writeUtf8Length(int value) { if (value >>> 6 == 0) { require(1); this.bytes[this.position++] = (byte) (value | 0x80); // Set bit 8. } else if (value >>> 13 == 0) { require(2); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value | 0x40 | 0x80); // Set bit 7 and 8. buffer[this.position++] = (byte) (value >>> 6); } else if (value >>> 20 == 0) { require(3); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value | 0x40 | 0x80); // Set bit 7 and 8. buffer[this.position++] = (byte) (value >>> 6 | 0x80); // Set bit 8. buffer[this.position++] = (byte) (value >>> 13); } else if (value >>> 27 == 0) { require(4); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value | 0x40 | 0x80); // Set bit 7 and 8. buffer[this.position++] = (byte) (value >>> 6 | 0x80); // Set bit 8. buffer[this.position++] = (byte) (value >>> 13 | 0x80); // Set bit 8. buffer[this.position++] = (byte) (value >>> 20); } else { require(5); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value | 0x40 | 0x80); // Set bit 7 and 8. buffer[this.position++] = (byte) (value >>> 6 | 0x80); // Set bit 8. buffer[this.position++] = (byte) (value >>> 13 | 0x80); // Set bit 8. buffer[this.position++] = (byte) (value >>> 20 | 0x80); // Set bit 8. buffer[this.position++] = (byte) (value >>> 27); } } private void writeString_slow(CharSequence value, int charCount, int charIndex) { byte[] buffer = this.bytes; for (; charIndex < charCount; charIndex++) { if (this.position == this.capacity) { require(Math.min(this.capacity, charCount - charIndex)); buffer = this.bytes; } int c = value.charAt(charIndex); if (c <= 0x007F) { this.bytes[this.position++] = (byte) c; } else if (c > 0x07FF) { buffer[this.position++] = (byte) (0xE0 | c >> 12 & 0x0F); require(2); buffer = this.bytes; buffer[this.position++] = (byte) (0x80 | c >> 6 & 0x3F); buffer[this.position++] = (byte) (0x80 | c & 0x3F); } else { buffer[this.position++] = (byte) (0xC0 | c >> 6 & 0x1F); require(1); buffer = this.bytes; buffer[this.position++] = (byte) (0x80 | c & 0x3F); } } } @SuppressWarnings("deprecation") private void writeAscii_slow(String value, int charCount) { byte[] buffer = this.bytes; int charIndex = 0; int charsToWrite = Math.min(charCount, this.capacity - this.position); while (charIndex < charCount) { value.getBytes(charIndex, charIndex + charsToWrite, buffer, this.position); charIndex += charsToWrite; this.position += charsToWrite; charsToWrite = Math.min(charCount - charIndex, this.capacity); if (require(charsToWrite)) { buffer = this.bytes; } } } /** * Reads the length and string of UTF8 characters, or null. This can read strings written by * {@link ByteBuffer2#writeString(String)} , {@link ByteBuffer2#writeString(CharSequence)}, and * {@link ByteBuffer2#writeAscii(String)}. * * @return May be null. */ public String readString() { int available = this.capacity - this.position; int b = this.bytes[this.position++]; 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 (this.chars.length < charCount) { this.chars = new char[charCount]; } if (available < charCount) { throw new BufferUnderflowException(); } readUtf8(charCount); return new String(this.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. byte[] buffer = this.bytes; b = buffer[this.position++]; result |= (b & 0x7F) << 6; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 13; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 20; if ((b & 0x80) != 0) { b = buffer[this.position++]; 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. byte[] buffer = this.bytes; b = buffer[this.position++]; result |= (b & 0x7F) << 6; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 13; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 20; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 27; } } } } return result; } private void readUtf8(int charCount) { byte[] buffer = this.bytes; int position = this.position; char[] chars = this.chars; // Try to read 7 bit ASCII chars. int charIndex = 0; int spaceAvailable = this.capacity - this.position; int count = Math.min(spaceAvailable, charCount); int b; while (charIndex < count) { b = buffer[position++]; 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) { readUtf8_slow(charCount, charIndex); } } private void readUtf8_slow(int charCount, int charIndex) { char[] chars = this.chars; byte[] buffer = this.bytes; while (charIndex < charCount) { int b = buffer[this.position++] & 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 : chars[charIndex] = (char) ((b & 0x1F) << 6 | buffer[this.position++] & 0x3F); break; case 14 : chars[charIndex] = (char) ((b & 0x0F) << 12 | (buffer[this.position++] & 0x3F) << 6 | buffer[this.position++] & 0x3F); break; } charIndex++; } } private String readAscii() { byte[] buffer = this.bytes; int end = this.position; int start = end - 1; int limit = this.capacity; int b; do { if (end == limit) { return readAscii_slow(); } b = buffer[end++]; } while ((b & 0x80) == 0); buffer[end - 1] &= 0x7F; // Mask end of ascii bit. @SuppressWarnings("deprecation") String value = new String(buffer, 0, start, end - start); buffer[end - 1] |= 0x80; this.position = end; return value; } private String readAscii_slow() { this.position--; // Re-read the first byte. // Copy chars currently in buffer. int charCount = this.capacity - this.position; if (charCount > this.chars.length) { this.chars = new char[charCount * 2]; } char[] chars = this.chars; byte[] buffer = this.bytes; for (int i = this.position, ii = 0, n = this.capacity; i < n; i++, ii++) { chars[ii] = (char) buffer[i]; } this.position = this.capacity; // Copy additional chars one by one. while (true) { int b = buffer[this.position++]; 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 ByteBuffer2#writeString(String)} , {@link ByteBuffer2#writeString(CharSequence)}, and * {@link ByteBuffer2#writeAscii(String)}. * * @return May be null. */ public StringBuilder readStringBuilder() { int available = this.capacity - this.position; int b = this.bytes[this.position++]; 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 (this.chars.length < charCount) { this.chars = new char[charCount]; } readUtf8(charCount); StringBuilder builder = new StringBuilder(charCount); builder.append(this.chars, 0, charCount); return builder; } // float /** * Writes a 4 byte float. */ public void writeFloat(float value) { writeInt(Float.floatToIntBits(value)); } /** * Writes a 1-5 byte float with reduced precision. * * @param optimizePositive * If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (5 bytes). */ public int writeFloat(float value, float precision, boolean optimizePositive) { return writeInt((int) (value * precision), optimizePositive); } /** * Reads a 4 byte float. */ public float readFloat() { return Float.intBitsToFloat(readInt()); } /** * Reads a 1-5 byte float with reduced precision. */ public float readFloat(float precision, boolean optimizePositive) { return readInt(optimizePositive) / precision; } /** * Reads a 4 byte float, does not advance the position */ public float readFloat(int position) { return Float.intBitsToFloat(readInt(position)); } /** * Reads a 1-5 byte float with reduced precision, does not advance the position */ public float readFloat(int position, float precision, boolean optimizePositive) { return readInt(position, optimizePositive) / precision; } // short /** * Writes a 2 byte short. Uses BIG_ENDIAN byte order. */ public void writeShort(int value) { require(2); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value >>> 8); buffer[this.position++] = (byte) value; } /** * Reads a 2 byte short. */ public short readShort() { byte[] buffer = this.bytes; return (short) ((buffer[this.position++] & 0xFF) << 8 | buffer[this.position++] & 0xFF); } /** * Reads a 2 byte short as an int from 0 to 65535. */ public int readShortUnsigned() { byte[] buffer = this.bytes; return (buffer[this.position++] & 0xFF) << 8 | buffer[this.position++] & 0xFF; } /** * Reads a 2 byte short, does not advance the position */ public short readShort(int position) { byte[] buffer = this.bytes; return (short) ((buffer[position++] & 0xFF) << 8 | buffer[position] & 0xFF); } /** * Reads a 2 byte short as an int from 0 to 65535, does not advance the position */ public int readShortUnsigned(int position) { byte[] buffer = this.bytes; return (buffer[position++] & 0xFF) << 8 | buffer[position] & 0xFF; } // long /** * Writes an 8 byte long. Uses BIG_ENDIAN byte order. */ public void writeLong(long value) { require(8); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value >>> 56); buffer[this.position++] = (byte) (value >>> 48); buffer[this.position++] = (byte) (value >>> 40); buffer[this.position++] = (byte) (value >>> 32); buffer[this.position++] = (byte) (value >>> 24); buffer[this.position++] = (byte) (value >>> 16); buffer[this.position++] = (byte) (value >>> 8); buffer[this.position++] = (byte) value; } /** * Writes a 1-9 byte long. This stream may consider such a variable length encoding request as a hint. It is not * guaranteed that a variable length encoding will be really used. The stream may decide to use native-sized integer * representation for efficiency reasons. * * @param optimizePositive * If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (9 bytes). */ public int writeLong(long value, boolean optimizePositive) { return writeVarLong(value, optimizePositive); } /** * Writes a 1-9 byte long. It is guaranteed that a varible length encoding will be used. * * @param optimizePositive * If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (9 bytes). */ public int writeVarLong(long value, boolean optimizePositive) { if (!optimizePositive) { value = value << 1 ^ value >> 63; } if (value >>> 7 == 0) { require(1); this.bytes[this.position++] = (byte) value; return 1; } if (value >>> 14 == 0) { require(2); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7); return 2; } if (value >>> 21 == 0) { require(3); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7 | 0x80); buffer[this.position++] = (byte) (value >>> 14); return 3; } if (value >>> 28 == 0) { require(4); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7 | 0x80); buffer[this.position++] = (byte) (value >>> 14 | 0x80); buffer[this.position++] = (byte) (value >>> 21); return 4; } if (value >>> 35 == 0) { require(5); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7 | 0x80); buffer[this.position++] = (byte) (value >>> 14 | 0x80); buffer[this.position++] = (byte) (value >>> 21 | 0x80); buffer[this.position++] = (byte) (value >>> 28); return 5; } if (value >>> 42 == 0) { require(6); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7 | 0x80); buffer[this.position++] = (byte) (value >>> 14 | 0x80); buffer[this.position++] = (byte) (value >>> 21 | 0x80); buffer[this.position++] = (byte) (value >>> 28 | 0x80); buffer[this.position++] = (byte) (value >>> 35); return 6; } if (value >>> 49 == 0) { require(7); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7 | 0x80); buffer[this.position++] = (byte) (value >>> 14 | 0x80); buffer[this.position++] = (byte) (value >>> 21 | 0x80); buffer[this.position++] = (byte) (value >>> 28 | 0x80); buffer[this.position++] = (byte) (value >>> 35 | 0x80); buffer[this.position++] = (byte) (value >>> 42); return 7; } if (value >>> 56 == 0) { require(8); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7 | 0x80); buffer[this.position++] = (byte) (value >>> 14 | 0x80); buffer[this.position++] = (byte) (value >>> 21 | 0x80); buffer[this.position++] = (byte) (value >>> 28 | 0x80); buffer[this.position++] = (byte) (value >>> 35 | 0x80); buffer[this.position++] = (byte) (value >>> 42 | 0x80); buffer[this.position++] = (byte) (value >>> 49); return 8; } require(9); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value & 0x7F | 0x80); buffer[this.position++] = (byte) (value >>> 7 | 0x80); buffer[this.position++] = (byte) (value >>> 14 | 0x80); buffer[this.position++] = (byte) (value >>> 21 | 0x80); buffer[this.position++] = (byte) (value >>> 28 | 0x80); buffer[this.position++] = (byte) (value >>> 35 | 0x80); buffer[this.position++] = (byte) (value >>> 42 | 0x80); buffer[this.position++] = (byte) (value >>> 49 | 0x80); buffer[this.position++] = (byte) (value >>> 56); return 9; } /** * Returns true if enough bytes are available to read a long with {@link #readLong(boolean)}. */ public boolean canReadLong() { if (this.capacity - this.position >= 9) { return true; } if (this.position + 1 > this.capacity) { return false; } byte[] buffer = this.bytes; int p = this.position; if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } return true; } /** * Returns true if enough bytes are available to read a long with {@link #readLong(boolean)}. */ public boolean canReadLong(int position) { if (this.capacity - position >= 9) { return true; } if (position + 1 > this.capacity) { return false; } byte[] buffer = this.bytes; int p = position; if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } if ((buffer[p++] & 0x80) == 0) { return true; } if (p == this.capacity) { return false; } return true; } /** * Reads an 8 byte long. */ public long readLong() { byte[] buffer = this.bytes; return (long) buffer[this.position++] << 56 | (long) (buffer[this.position++] & 0xFF) << 48 | (long) (buffer[this.position++] & 0xFF) << 40 | (long) (buffer[this.position++] & 0xFF) << 32 | (long) (buffer[this.position++] & 0xFF) << 24 | (buffer[this.position++] & 0xFF) << 16 | (buffer[this.position++] & 0xFF) << 8 | buffer[this.position++] & 0xFF; } /** * Reads a 1-9 byte long. This stream may consider such a variable length encoding request as a hint. It is not * guaranteed that a variable length encoding will be really used. The stream may decide to use native-sized integer * representation for efficiency reasons. */ public long readLong(boolean optimizePositive) { return readVarLong(optimizePositive); } /** * Reads an 8 byte long, does not advance the position */ public long readLong(int position) { byte[] buffer = this.bytes; return (long) buffer[position++] << 56 | (long) (buffer[position++] & 0xFF) << 48 | (long) (buffer[position++] & 0xFF) << 40 | (long) (buffer[position++] & 0xFF) << 32 | (long) (buffer[position++] & 0xFF) << 24 | (buffer[position++] & 0xFF) << 16 | (buffer[position++] & 0xFF) << 8 | buffer[position++] & 0xFF; } /** * Reads a 1-9 byte long. This stream may consider such a variable length encoding request as a hint. It is not * guaranteed that a variable length encoding will be really used. The stream may decide to use native-sized integer * representation for efficiency reasons. *

* does not advance the position */ public long readLong(int position, boolean optimizePositive) { int pos = this.position; this.position = position; long value = readVarLong(optimizePositive); this.position = pos; return value; } /** * Reads a 1-9 byte long. It is guaranteed that a varible length encoding will be used. */ private long readVarLong(boolean optimizePositive) { if (this.capacity - this.position < 9) { return readLong_slow(optimizePositive); } int b = this.bytes[this.position++]; long result = b & 0x7F; if ((b & 0x80) != 0) { byte[] buffer = this.bytes; b = buffer[this.position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 28; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 35; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 42; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 49; if ((b & 0x80) != 0) { b = buffer[this.position++]; 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. byte[] buffer = this.bytes; int b = buffer[this.position++]; long result = b & 0x7F; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 28; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 35; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 42; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) (b & 0x7F) << 49; if ((b & 0x80) != 0) { b = buffer[this.position++]; result |= (long) b << 56; } } } } } } } } if (!optimizePositive) { result = result >>> 1 ^ -(result & 1); } return result; } // boolean /** * Writes a 1 byte boolean. */ public void writeBoolean(boolean value) { require(1); this.bytes[this.position++] = (byte) (value ? 1 : 0); } /** * Reads a 1 byte boolean. */ public boolean readBoolean () { return this.bytes[this.position++] == 1; } /** * Reads a 1 byte boolean, does not advance the position */ public boolean readBoolean (int position) { return this.bytes[position] == 1; } // char /** * Writes a 2 byte char. Uses BIG_ENDIAN byte order. */ public void writeChar(char value) { require(2); byte[] buffer = this.bytes; buffer[this.position++] = (byte) (value >>> 8); buffer[this.position++] = (byte) value; } /** * Reads a 2 byte char. */ public char readChar() { byte[] buffer = this.bytes; return (char) ((buffer[this.position++] & 0xFF) << 8 | buffer[this.position++] & 0xFF); } /** * Reads a 2 byte char, does not advance the position */ public char readChar(int position) { byte[] buffer = this.bytes; return (char) ((buffer[position++] & 0xFF) << 8 | buffer[position++] & 0xFF); } // double /** * Writes an 8 byte double. */ public void writeDouble(double value) { writeLong(Double.doubleToLongBits(value)); } /** * Writes a 1-9 byte double with reduced precision * * @param optimizePositive * If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (9 bytes). */ public int writeDouble(double value, double precision, boolean optimizePositive) { return writeLong((long) (value * precision), optimizePositive); } /** * Reads an 8 bytes double. */ public double readDouble() { return Double.longBitsToDouble(readLong()); } /** * Reads a 1-9 byte double with reduced precision. */ public double readDouble(double precision, boolean optimizePositive) { return readLong(optimizePositive) / precision; } /** * Reads an 8 bytes double, does not advance the position */ public double readDouble(int position) { return Double.longBitsToDouble(readLong(position)); } /** * Reads a 1-9 byte double with reduced precision, does not advance the position */ public double readDouble(int position, double precision, boolean optimizePositive) { return readLong(position, optimizePositive) / precision; } /** * Returns the number of bytes that would be written with {@link #writeInt(int, boolean)}. */ public static int intLength(int value, boolean optimizePositive) { if (!optimizePositive) { value = value << 1 ^ value >> 31; } if (value >>> 7 == 0) { return 1; } if (value >>> 14 == 0) { return 2; } if (value >>> 21 == 0) { return 3; } if (value >>> 28 == 0) { return 4; } return 5; } /** * Returns the number of bytes that would be written with {@link #writeLong(long, boolean)}. */ public static int longLength(long value, boolean optimizePositive) { if (!optimizePositive) { value = value << 1 ^ value >> 63; } if (value >>> 7 == 0) { return 1; } if (value >>> 14 == 0) { return 2; } if (value >>> 21 == 0) { return 3; } if (value >>> 28 == 0) { return 4; } if (value >>> 35 == 0) { return 5; } if (value >>> 42 == 0) { return 6; } if (value >>> 49 == 0) { return 7; } if (value >>> 56 == 0) { return 8; } return 9; } // Methods implementing bulk operations on arrays of primitive types /** * Bulk output of an int array. */ public void writeInts(int[] object, boolean optimizePositive) { for (int i = 0, n = object.length; i < n; i++) { writeInt(object[i], optimizePositive); } } /** * Bulk input of an int array. */ public int[] readInts(int length, boolean optimizePositive) { int[] array = new int[length]; for (int i = 0; i < length; i++) { array[i] = readInt(optimizePositive); } return array; } /** * Bulk output of an long array. */ public void writeLongs(long[] object, boolean optimizePositive) { for (int i = 0, n = object.length; i < n; i++) { writeLong(object[i], optimizePositive); } } /** * Bulk input of a long array. */ public long[] readLongs(int length, boolean optimizePositive) { long[] array = new long[length]; for (int i = 0; i < length; i++) { array[i] = readLong(optimizePositive); } return array; } /** * Bulk output of an int array. */ public void writeInts(int[] object) { for (int i = 0, n = object.length; i < n; i++) { writeInt(object[i]); } } /** * Bulk input of an int array. */ public int[] readInts(int length) { int[] array = new int[length]; for (int i = 0; i < length; i++) { array[i] = readInt(); } return array; } /** * Bulk output of an long array. */ public void writeLongs(long[] object) { for (int i = 0, n = object.length; i < n; i++) { writeLong(object[i]); } } /** * Bulk input of a long array. */ public long[] readLongs(int length) { long[] array = new long[length]; for (int i = 0; i < length; i++) { array[i] = readLong(); } return array; } /** * Bulk output of a float array. */ public void writeFloats(float[] object) { for (int i = 0, n = object.length; i < n; i++) { writeFloat(object[i]); } } /** * Bulk input of a float array. */ public float[] readFloats(int length) { float[] array = new float[length]; for (int i = 0; i < length; i++) { array[i] = readFloat(); } return array; } /** * Bulk output of a short array. */ public void writeShorts(short[] object) { for (int i = 0, n = object.length; i < n; i++) { writeShort(object[i]); } } /** * Bulk input of a short array. */ public short[] readShorts(int length) { short[] array = new short[length]; for (int i = 0; i < length; i++) { array[i] = readShort(); } return array; } /** * Bulk output of a char array. */ public void writeChars(char[] object) { for (int i = 0, n = object.length; i < n; i++) { writeChar(object[i]); } } /** * Bulk input of a char array. */ public char[] readChars(int length) { char[] array = new char[length]; for (int i = 0; i < length; i++) { array[i] = readChar(); } return array; } /** * Bulk output of a double array. */ public void writeDoubles(double[] object) { for (int i = 0, n = object.length; i < n; i++) { writeDouble(object[i]); } } /** * Bulk input of a double array */ public double[] readDoubles(int length) { double[] array = new double[length]; for (int i = 0; i < length; i++) { array[i] = readDouble(); } return array; } @Override public boolean equals(Object other) { if (!(other instanceof ByteBuffer2)) { return false; } // CANNOT be null, so we don't have to null check! return Arrays.equals(this.bytes, ((ByteBuffer2) other).bytes); } @Override public int hashCode() { // might be null for a thread because it's stale. who cares, get the value again return Arrays.hashCode(this.bytes); } @Override public String toString() { return "ByteBuffer2 " + java.util.Arrays.toString(this.bytes); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy