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

dorkbox.bytes.ByteArrayBuffer.kt Maven / Gradle / Ivy

There is a newer version: 2.1
Show newest version
/*
 * Copyright 2023 dorkbox, llc
 *
 * 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.
 */

/*
* 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.bytes

import java.io.IOException
import java.nio.BufferUnderflowException
import java.util.*
import kotlin.experimental.and

 /**
 * 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 @n4te.com>
 */
@Suppress("unused", "DuplicatedCode", "DuplicatedCode", "MemberVisibilityCanBePrivate")
class ByteArrayBuffer {
    companion object {
        /**
         * Gets the version number.
         */
        const val version = BytesInfo.version

        /**
         * Returns the number of bytes that would be written with [.writeInt].
         */
        fun intLength(value: Int, optimizePositive: Boolean): Int {
            @Suppress("NAME_SHADOWING")
            var value = value
            if (!optimizePositive) {
                value = value shl 1 xor (value shr 31)
            }
            if (value ushr 7 == 0) {
                return 1
            }
            if (value ushr 14 == 0) {
                return 2
            }
            if (value ushr 21 == 0) {
                return 3
            }
            return if (value ushr 28 == 0) {
                4
            } else 5
        }

        /**
         * Returns the number of bytes that would be written with [.writeLong].
         */
        fun longLength(value: Long, optimizePositive: Boolean): Int {
            @Suppress("NAME_SHADOWING")
            var value = value
            if (!optimizePositive) {
                value = value shl 1 xor (value shr 63)
            }
            if (value ushr 7 == 0L) {
                return 1
            }
            if (value ushr 14 == 0L) {
                return 2
            }
            if (value ushr 21 == 0L) {
                return 3
            }
            if (value ushr 28 == 0L) {
                return 4
            }
            if (value ushr 35 == 0L) {
                return 5
            }
            if (value ushr 42 == 0L) {
                return 6
            }
            if (value ushr 49 == 0L) {
                return 7
            }
            return if (value ushr 56 == 0L) {
                8
            } else 9
        }
    }


    private var capacity = 0    // exactly how many bytes have been allocated
    private var maxCapacity = 0 // how large we can grow
    private var position = 0    // current pointer to the point where data is read/written
    private lateinit var bytes : ByteArray    // the backing buffer
    private var chars = CharArray(32) // small buffer for reading strings

    /**
     * Creates an uninitialized object. [.setBuffer] must be called before the object is used.
     */
    constructor()

    /**
     * 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.
     */
    constructor(bufferSize: Int, maxBufferSize: Int = bufferSize) {
        require(maxBufferSize >= -1) { "maxBufferSize cannot be < -1: $maxBufferSize" }
        capacity = bufferSize
        maxCapacity = if (maxBufferSize == -1) Int.MAX_VALUE else maxBufferSize
        bytes = ByteArray(bufferSize)
    }

    /**
     * Creates a new object for writing to a byte array.
     *
     * @see .setBuffer
     */
    constructor(buffer: ByteArray, maxBufferSize: Int = buffer.size) {
        setBuffer(buffer, maxBufferSize)
    }

    /**
     * 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.
     */
    fun setBuffer(buffer: ByteArray, maxBufferSize: Int) {
        require(maxBufferSize >= -1) { "maxBufferSize cannot be < -1: $maxBufferSize" }

        bytes = buffer
        maxCapacity = if (maxBufferSize == -1) Int.MAX_VALUE else maxBufferSize
        capacity = buffer.size
        position = 0
    }


    /**
     * Returns the buffer. The bytes between zero and [.position] are the data that has been written.
     */
    fun getBuffer(): ByteArray {
        return bytes
    }

    /**
     * Sets the buffer that will be written to. [.setBuffer] is called with the specified buffer's
     * length as the maxBufferSize.
     */
    fun setBuffer(buffer: ByteArray) {
        setBuffer(buffer, buffer.size)
    }

    /**
     * Returns a new byte array containing the bytes currently in the buffer between zero and [.position].
     */
    fun toBytes(): ByteArray {
        val newBuffer = ByteArray(position)

        if (position > 0) {
            System.arraycopy(bytes, 0, newBuffer, 0, position)
        }
        return newBuffer
    }

    /**
     * Returns the remaining read/write bytes available before the end of the buffer
     */
    fun remaining(): Int {
        return capacity - position
    }

    /**
     * Returns the size of the backing byte buffer
     */
    fun capacity(): Int {
        return capacity
    }

    /**
     * Returns the current position in the buffer. This is the number of bytes that have not been flushed.
     */
    fun position(): Int {
        return position
    }

    /**
     * Sets the current position in the buffer.
     */
    fun setPosition(position: Int) {
        this.position = position
    }

    /**
     * Sets the position to zero.
     */
    fun clear() {
        position = 0
    }

    /**
     * Sets the position to zero.
     */
    fun rewind() {
        position = 0
    }

    /**
     * Sets the position to zero, and write 0 to all bytes in the buffer
     */
    fun clearSecure() {
        position = 0
        val buffer = bytes
        for (i in 0 until capacity) {
            buffer[i] = 0
        }
    }

    /**
     * Discards the specified number of bytes.
     */
    fun skip(count: Int) {
        @Suppress("NAME_SHADOWING")
        var count = count
        var skipCount = Math.min(capacity - position, count)
        while (true) {
            position += skipCount
            count -= skipCount
            if (count == 0) {
                break
            }
            skipCount = Math.min(count, capacity)
            require(skipCount)
        }
    }

    /**
     * @return true if the buffer has been resized.
     */
    private fun require(required: Int): Boolean {
        if (capacity - position >= required) {
            return false
        }
        if (required > maxCapacity) {
            throw IOException("Buffer overflow. Max capacity: $maxCapacity, required: $required")
        }

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

            // Grow buffer.
            if (capacity == 0) {
                capacity = 1
            }

            capacity = (capacity * 1.6).toInt().coerceAtMost(maxCapacity)
            if (capacity < 0) {
                capacity = maxCapacity
            }

            val newBuffer = ByteArray(capacity)
            System.arraycopy(bytes, 0, newBuffer, 0, position)
            bytes = newBuffer
        }

        return true
    }


    // byte
    /**
     * Writes a byte.
     */
    fun writeByte(value: Byte) {
        if (position == capacity) {
            require(1)
        }
        bytes[position++] = value
    }

    /**
     * Writes a byte.
     */
    fun writeByte(value: Int) {
        if (position == capacity) {
            require(1)
        }
        bytes[position++] = value.toByte()
    }

    /**
     * Writes the bytes. Note the byte[] length is not written.
     */
    fun writeBytes(bytes: ByteArray) {
        writeBytes(bytes, 0, bytes.size)
    }

    /**
     * Writes the bytes. Note the byte[] length is not written.
     */
    fun writeBytes(bytes: ByteArray, offset: Int, count: Int) {
        @Suppress("NAME_SHADOWING")
        var offset = offset
        @Suppress("NAME_SHADOWING")
        var count = count
        var copyCount = (capacity - position).coerceAtMost(count)

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

    /**
     * Reads a single byte.
     */
    fun readByte(): Byte {
        return bytes[position++]
    }

    /**
     * Reads a byte as an int from 0 to 255.
     */
    fun readByteUnsigned(): Int {
        return bytes[position++].toInt() and 0xFF
    }

    /**
     * Reads a single byte, does not advance the position
     */
    fun readByte(position: Int): Byte {
        return bytes[position]
    }

    /**
     * Reads a byte as an int from 0 to 255, does not advance the position
     */
    fun readByteUnsigned(position: Int): Int {
        return bytes[position].toInt() and 0xFF
    }

    /**
     * Reads the specified number of bytes into a new byte[].
     */
    fun readBytes(length: Int): ByteArray {
        val bytes = ByteArray(length)
        readBytes(bytes, 0, length)
        return bytes
    }

    /**
     * Reads count bytes and writes them to the specified byte[], starting at offset (or 0) in target byte array.
     */
    fun readBytes(bytes: ByteArray, offset: Int = 0, count: Int = bytes.size) {
        System.arraycopy(this.bytes, position, bytes, offset, count)
        position += count
    }


    // int
    /**
     * Writes a 4 byte int. Uses BIG_ENDIAN byte order.
     */
    fun writeInt(value: Int) {
        require(4)
        val buffer = bytes
        buffer[position++] = (value shr 24).toByte()
        buffer[position++] = (value shr 16).toByte()
        buffer[position++] = (value shr 8).toByte()
        buffer[position++] = value.toByte()
    }

    /**
     * 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).
     */
    fun writeInt(value: Int, optimizePositive: Boolean): Int {
        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).
     */
    fun writeVarInt(value: Int, optimizePositive: Boolean): Int {
        @Suppress("NAME_SHADOWING")
        var value = value
        if (!optimizePositive) value = value shl 1 xor (value shr 31)
        if (value ushr 7 == 0) {
            require(1)
            bytes[position++] = value.toByte()
            return 1
        }
        if (value ushr 14 == 0) {
            require(2)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7).toByte()
            return 2
        }
        if (value ushr 21 == 0) {
            require(3)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14).toByte()
            return 3
        }
        if (value ushr 28 == 0) {
            require(4)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14 or 0x80).toByte()
            buffer[position++] = (value ushr 21).toByte()
            return 4
        }
        require(5)
        val buffer = bytes
        buffer[position++] = (value and 0x7F or 0x80).toByte()
        buffer[position++] = (value ushr 7 or 0x80).toByte()
        buffer[position++] = (value ushr 14 or 0x80).toByte()
        buffer[position++] = (value ushr 21 or 0x80).toByte()
        buffer[position++] = (value ushr 28).toByte()
        return 5
    }

    /**
     * Reads a 4 byte int.
     */
    fun readInt(): Int {
        val buffer = bytes
        val position = position
        val value: Int = buffer[position].toInt() and 0xFF shl 24 or (buffer[position + 1].toInt() and 0xFF shl 16
                ) or (buffer[position + 2].toInt() and 0xFF shl 8
                ) or (buffer[position + 3].toInt() and 0xFF)
        this.position = position + 4
        return value
    }

    /**
     * Reads a 4 byte int, does not advance the position
     */
    fun readInt(position: Int): Int {
        val buffer = bytes
        val value: Int = buffer[position].toInt() and 0xFF shl 24 or (buffer[position + 1].toInt() and 0xFF shl 16
                ) or (buffer[position + 2].toInt() and 0xFF shl 8
                ) or (buffer[position + 3].toInt() and 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.
     */
    fun readInt(optimizePositive: Boolean): Int {
        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
     */
    fun readInt(position: Int, optimizePositive: Boolean): Int {
        val pos = this.position
        this.position = position
        val 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 fun readVarInt(optimizePositive: Boolean): Int {
        val buffer = bytes
       
        var b = buffer[position++].toInt()
        var result = b and 0x7F
        if (b and 0x80 != 0) {
            b = buffer[position++].toInt()
            result = result or (b and 0x7F shl 7)
            if (b and 0x80 != 0) {
                b = buffer[position++].toInt()
                result = result or (b and 0x7F shl 14)
                if (b and 0x80 != 0) {
                    b = buffer[position++].toInt()
                    result = result or (b and 0x7F shl 21)
                    if (b and 0x80 != 0) {
                        b = buffer[position++].toInt()
                        result = result or (b and 0x7F shl 28)
                    }
                }
            }
        }
        return if (optimizePositive) result else result ushr 1 xor -(result and 1)
    }

    /**
     * Returns true if enough bytes are available to read an int with [.readInt].
     */
    fun canReadInt(): Boolean {
        if (capacity - position >= 5) {
            return true
        }
        if (position + 1 > capacity) {
            return false
        }
        val buffer = bytes
        var p = position
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        return if (buffer[p++].toInt() and 0x80 == 0) {
            true
        } else p != capacity
    }

    /**
     * Returns true if enough bytes are available to read an int with [.readInt].
     */
    fun canReadInt(position: Int): Boolean {
        if (capacity - position >= 5) {
            return true
        }
        if (position + 1 > capacity) {
            return false
        }
        val buffer = bytes
        var p = position
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        return if (buffer[p++].toInt() and 0x80 == 0) {
            true
        } else p != capacity
    }


    // 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, [ByteArrayBuffer.writeAscii] may be used. The
     * string can be read using [ByteArrayBuffer.readString] or [ByteArrayBuffer.readStringBuilder].
     *
     * @param value
     * May be null.
     */
    fun writeString(value: String?) {
        if (value == null) {
            writeByte(0x80) // 0 means null, bit 8 means UTF8.
            return
        }

        val charCount = value.length
        if (charCount == 0) {
            writeByte(1 or 0x80) // 1 means empty string, bit 8 means UTF8.
            return
        }

        // Detect ASCII, we only do this for small strings
        // since 1 char is used for bit-masking if we use for 1 char string, reading the string will not work!
        var permitAscii = charCount in 2..32
        if (permitAscii) {
            for (i in 0 until charCount) {
                if (value[i].code > 127) {
                    permitAscii = false
                    break // not ascii
                }
            }

            if (permitAscii) {
                // this is ascii
                if (capacity - position < charCount) {
                    writeAscii_slow(value, charCount)
                } else {
                    val stringBytes = value.encodeToByteArray(0, charCount)
                    stringBytes.copyInto(bytes, position)
//                    value.toByteArray(0, charCount, bytes, position)
                    position += charCount

//                    var i = 0
//                    val n = value.length
//                    while (i < n) {
//                        bytes[position++] = value[i].code.toByte()
//                        ++i
//                    }
                }

                // mod the last written byte with 0x80 so we can use that when reading ascii bytes to see what the end of the string is
                val value1: Byte = (bytes[position - 1].toInt() or 0x80).toByte()
                bytes[position - 1] = value1
                return
            }
        }

        writeUtf8Length(charCount + 1)
        var charIndex = 0
        if (capacity - position >= charCount) {
            // Try to write 8 bit chars.
            val buffer = bytes
            var position = position
            while (charIndex < charCount) {
                val c = value[charIndex].code
                if (c > 127) {
                    break
                }
                buffer[position++] = c.toByte()
                charIndex++
            }
            this.position = position
        }
        if (charIndex < charCount) {
            writeUtf8_slow(value, charCount, charIndex)
        }
    }

    /**
     * Writes the length and CharSequence as UTF8, or null. The string can be read using [ByteArrayBuffer.readString] or
     * [ByteArrayBuffer.readStringBuilder].
     *
     * @param value
     * May be null.
     */
    fun writeString(value: CharSequence?) {
        if (value == null) {
            writeByte(0x80) // 0 means null, bit 8 means UTF8.
            return
        }


        val charCount = value.length
        if (charCount == 0) {
            writeByte(1 or 0x80) // 1 means empty string, bit 8 means UTF8.
            return
        }


        writeUtf8Length(charCount + 1)
        var charIndex = 0
        if (capacity - position >= charCount) {
            // Try to write 8 bit chars.
            val buffer = bytes
            var position = position
            while (charIndex < charCount) {
                val c = value[charIndex].code
                if (c > 127) {
                    break
                }
                buffer[position++] = c.toByte()
                charIndex++
            }
            this.position = position
        }
        if (charIndex < charCount) {
            writeUtf8_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 [ByteArrayBuffer.writeString]. The string can be read using
     * [ByteArrayBuffer.readString] or [ByteArrayBuffer.readStringBuilder].
     *
     * @param value
     * May be null.
     */
    fun writeAscii(value: String?) {
        if (value == null) {
            writeByte(0x80) // 0 means null, bit 8 means UTF8.
            return
        }
        val charCount = value.length
        when (charCount) {
            0 -> {
                writeByte(1 or 0x80) // 1 is string length + 1, bit 8 means UTF8.
                return
            }
            1 -> {
                writeByte(2 or 0x80) // 2 is string length + 1, bit 8 means UTF8.
                writeByte(value[0].code)
                return
            }
        }
        if (capacity - position < charCount) {
            writeAscii_slow(value, charCount)
        } else {
            val stringBytes = value.encodeToByteArray(0, charCount)
            stringBytes.copyInto(bytes, position)
//            value.toByteArray(0, charCount, bytes, position)
            position += charCount
        }
        bytes[position - 1] = (bytes[position - 1].toInt() or 0x80).toByte() // 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 fun writeUtf8Length(value: Int) {
        if (value ushr 6 == 0) {
            require(1)
            bytes[position++] = (value or 0x80).toByte() // Set bit 8.
        } else if (value ushr 13 == 0) {
            require(2)
            val buffer = bytes
            buffer[position++] = (value or 0x40 or 0x80).toByte() // Set bit 7 and 8.
            buffer[position++] = (value ushr 6).toByte()
        } else if (value ushr 20 == 0) {
            require(3)
            val buffer = bytes
            buffer[position++] = (value or 0x40 or 0x80).toByte() // Set bit 7 and 8.
            buffer[position++] = (value ushr 6 or 0x80).toByte() // Set bit 8.
            buffer[position++] = (value ushr 13).toByte()
        } else if (value ushr 27 == 0) {
            require(4)
            val buffer = bytes
            buffer[position++] = (value or 0x40 or 0x80).toByte() // Set bit 7 and 8.
            buffer[position++] = (value ushr 6 or 0x80).toByte() // Set bit 8.
            buffer[position++] = (value ushr 13 or 0x80).toByte() // Set bit 8.
            buffer[position++] = (value ushr 20).toByte()
        } else {
            require(5)
            val buffer = bytes
            buffer[position++] = (value or 0x40 or 0x80).toByte() // Set bit 7 and 8.
            buffer[position++] = (value ushr 6 or 0x80).toByte() // Set bit 8.
            buffer[position++] = (value ushr 13 or 0x80).toByte() // Set bit 8.
            buffer[position++] = (value ushr 20 or 0x80).toByte() // Set bit 8.
            buffer[position++] = (value ushr 27).toByte()
        }
    }

    private fun writeUtf8_slow(value: CharSequence, charCount: Int, charIndex: Int) {
        @Suppress("NAME_SHADOWING")
        var charIndex = charIndex
        while (charIndex < charCount) {
            if (position == capacity) {
                require(capacity.coerceAtMost(charCount - charIndex))
            }
            val c = value[charIndex].code

            if (c <= 0x007F) {
                bytes[position++] = c.toByte()
            } else if (c > 0x07FF) {
                bytes[position++] = (0xE0 or (c shr 12 and 0x0F)).toByte()
                require(2)
                val buffer = bytes
                buffer[position++] = (0x80 or (c shr 6 and 0x3F)).toByte()
                buffer[position++] = (0x80 or (c and 0x3F)).toByte()
            } else {
                bytes[position++] = (0xC0 or (c shr 6 and 0x1F)).toByte()
                require(1)
                bytes[position++] = (0x80 or (c and 0x3F)).toByte()
            }
            charIndex++
        }
    }

    private fun writeAscii_slow(value: String, charCount: Int) {
        var buffer = bytes
        var charIndex = 0
        var charsToWrite = charCount.coerceAtMost(capacity - position)

        while (charIndex < charCount) {
            val stringBytes = value.encodeToByteArray(charIndex, charIndex + charsToWrite)
            stringBytes.copyInto(buffer, position)
//            value.toByteArray(charIndex, charIndex + charsToWrite, buffer, position)

            charIndex += charsToWrite
            position += charsToWrite
            charsToWrite = Math.min(charCount - charIndex, capacity)

            if (require(charsToWrite)) {
                buffer = bytes
            }
        }
    }

    /**
     * Reads the length and string of UTF8 characters, or null. This can read strings written by
     * [ByteArrayBuffer.writeString] , [ByteArrayBuffer.writeString], and
     * [ByteArrayBuffer.writeAscii].
     *
     * @return May be null.
     */
    fun readString(): String? {
        val available = capacity - position
        val b = bytes[position++].toInt()
        if (b and 0x80 == 0) {
            return readAscii() // ASCII.
        }

        // Null, empty, or UTF8.
        var charCount = if (available >= 5) readUtf8Length(b) else readUtf8Length_slow(b)
        when (charCount) {
            0 -> return null
            1 -> return ""
        }

        charCount--
        if (chars.size < charCount) {
            chars = CharArray(charCount)
        }

        if (available < charCount) {
            throw BufferUnderflowException()
        }
        readUtf8(charCount)
        return String(chars, 0, charCount)
    }

    private fun readUtf8Length(b: Int): Int {
        @Suppress("NAME_SHADOWING")
        var b = b
        var result = b and 0x3F // Mask all but first 6 bits.
        if (b and 0x40 != 0) { // Bit 7 means another byte, bit 8 means UTF8.
            val buffer = bytes
            b = buffer[position++].toInt()
            result = result or (b and 0x7F shl 6)
            if (b and 0x80 != 0) {
                b = buffer[position++].toInt()
                result = result or (b and 0x7F shl 13)
                if (b and 0x80 != 0) {
                    b = buffer[position++].toInt()
                    result = result or (b and 0x7F shl 20)
                    if (b and 0x80 != 0) {
                        b = buffer[position++].toInt()
                        result = result or (b and 0x7F shl 27)
                    }
                }
            }
        }
        return result
    }

    private fun readUtf8Length_slow(b: Int): Int {
        @Suppress("NAME_SHADOWING")
        var b = b
        var result = b and 0x3F // Mask all but first 6 bits.
        if (b and 0x40 != 0) { // Bit 7 means another byte, bit 8 means UTF8.
            val buffer = bytes
            b = buffer[position++].toInt()
            result = result or (b and 0x7F shl 6)
            if (b and 0x80 != 0) {
                b = buffer[position++].toInt()
                result = result or (b and 0x7F shl 13)
                if (b and 0x80 != 0) {
                    b = buffer[position++].toInt()
                    result = result or (b and 0x7F shl 20)
                    if (b and 0x80 != 0) {
                        b = buffer[position++].toInt()
                        result = result or (b and 0x7F shl 27)
                    }
                }
            }
        }
        return result
    }

    private fun readUtf8(charCount: Int) {
        val buffer = bytes
        var position = position
        val chars = chars

        // Try to read 7 bit ASCII chars.
        var charIndex = 0
        val spaceAvailable = capacity - this.position
        val count = Math.min(spaceAvailable, charCount)
        var b: Int
        while (charIndex < count) {
            b = buffer[position++].toInt()
            if (b < 0) {
                position--
                break
            }
            chars[charIndex++] = b.toChar()
        }
        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 fun readUtf8_slow(charCount: Int, charIndex: Int) {
        @Suppress("NAME_SHADOWING")
        var charIndex = charIndex
        val chars = chars
        val buffer = bytes

        while (charIndex < charCount) {
            val b: Int = buffer[position++].toInt() and 0xFF
            when (b shr 4) {
                0, 1, 2, 3, 4, 5, 6, 7 -> chars[charIndex] = b.toChar()
                12, 13 -> chars[charIndex] = (b and 0x1F shl 6 or (buffer[position++].toInt() and 0x3F)).toChar()
                14 -> chars[charIndex] =
                    (b and 0x0F shl 12 or (buffer[position++].toInt() and 0x3F shl 6) or (buffer[position++].toInt() and 0x3F)).toChar()
            }
            charIndex++
        }
    }

    private fun readAscii(): String {
        val buffer = bytes
        var end = position
        val start = end - 1
        val limit = capacity
        var b: Int

        do {
            if (end == limit) {
                return readAscii_slow()
            }
            b = buffer[end++].toInt()
        } while (b and 0x80 == 0)

        buffer[end - 1] = buffer[end - 1] and 0x7F // Mask end of ascii bit.
        val value = String(buffer, start, end - start)
        buffer[end - 1] = (buffer[end - 1].toInt() or 0x80).toByte()
        position = end
        return value
    }

    private fun readAscii_slow(): String {
        position-- // Re-read the first byte.

        // Copy chars currently in buffer.
        var charCount = capacity - position
        if (charCount > chars.size) {
            chars = CharArray(charCount * 2)
        }
        var chars = chars
        val buffer = bytes
        var i = position
        var ii = 0
        val n = capacity

        while (i < n) {
            chars[ii] = buffer[i].toInt().toChar()
            i++
            ii++
        }
        position = capacity

        // Copy additional chars one by one.
        while (true) {
            val b = buffer[position++].toInt()
            if (charCount == chars.size) {
                val newChars = CharArray(charCount * 2)
                System.arraycopy(chars, 0, newChars, 0, charCount)
                chars = newChars
                this.chars = newChars
            }
            if (b and 0x80 == 0x80) {
                chars[charCount++] = (b and 0x7F).toChar()
                break
            }
            chars[charCount++] = b.toChar()
        }
        return String(chars, 0, charCount)
    }

    /**
     * Reads the length and string of UTF8 characters, or null. This can read strings written by
     * [ByteArrayBuffer.writeString] , [ByteArrayBuffer.writeString], and
     * [ByteArrayBuffer.writeAscii].
     *
     * @return May be null.
     */
    fun readStringBuilder(): StringBuilder? {
        val available = capacity - position
        val b = bytes[position++].toInt()
        if (b and 0x80 == 0) {
            return StringBuilder(readAscii()) // ASCII.
        }

        // Null, empty, or UTF8.
        var charCount = if (available >= 5) readUtf8Length(b) else readUtf8Length_slow(b)
        when (charCount) {
            0 -> return null
            1 -> return StringBuilder()
        }
        charCount--
        if (chars.size < charCount) {
            chars = CharArray(charCount)
        }
        readUtf8(charCount)
        val builder = StringBuilder(charCount)
        builder.append(chars, 0, charCount)
        return builder
    }
    // float
    /**
     * Writes a 4 byte float.
     */
    fun writeFloat(value: Float) {
        writeInt(java.lang.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).
     */
    fun writeFloat(value: Float, precision: Float, optimizePositive: Boolean): Int {
        return writeInt((value * precision).toInt(), optimizePositive)
    }

    /**
     * Reads a 4 byte float.
     */
    fun readFloat(): Float {
        return java.lang.Float.intBitsToFloat(readInt())
    }

    /**
     * Reads a 1-5 byte float with reduced precision.
     */
    fun readFloat(precision: Float, optimizePositive: Boolean): Float {
        return readInt(optimizePositive) / precision
    }

    /**
     * Reads a 4 byte float, does not advance the position
     */
    fun readFloat(position: Int): Float {
        return java.lang.Float.intBitsToFloat(readInt(position))
    }

    /**
     * Reads a 1-5 byte float with reduced precision, does not advance the position
     */
    fun readFloat(position: Int, precision: Float, optimizePositive: Boolean): Float {
        return readInt(position, optimizePositive) / precision
    }
    // short
    /**
     * Writes a 2 byte short. Uses BIG_ENDIAN byte order.
     */
    fun writeShort(value: Int) {
        require(2)
        val buffer = bytes
        buffer[position++] = (value ushr 8).toByte()
        buffer[position++] = value.toByte()
    }

    /**
     * Reads a 2 byte short.
     */
    fun readShort(): Short {
        val buffer = bytes
        return ((buffer[position++].toInt() and 0xFF) shl 8 or (buffer[position++].toInt() and 0xFF)).toShort()
    }

    /**
     * Reads a 2 byte short as an int from 0 to 65535.
     */
    fun readShortUnsigned(): Int {
        val buffer = bytes
        return buffer[position++].toInt() and 0xFF shl 8 or buffer[position++].toInt() and 0xFF
    }

    /**
     * Reads a 2 byte short, does not advance the position
     */
    fun readShort(position: Int): Short {
        @Suppress("NAME_SHADOWING")
        var position = position
        val buffer = bytes
        return (buffer[position++].toInt() and 0xFF shl 8 or buffer[position].toInt() and 0xFF).toShort()
    }

    /**
     * Reads a 2 byte short as an int from 0 to 65535, does not advance the position
     */
    fun readShortUnsigned(position: Int): Int {
        @Suppress("NAME_SHADOWING")
        var position = position
        val buffer = bytes
        return buffer[position++].toInt() and 0xFF shl 8 or buffer[position].toInt() and 0xFF
    }
    // long
    /**
     * Writes an 8 byte long. Uses BIG_ENDIAN byte order.
     */
    fun writeLong(value: Long) {
        require(8)
        val buffer = bytes
        buffer[position++] = (value ushr 56).toByte()
        buffer[position++] = (value ushr 48).toByte()
        buffer[position++] = (value ushr 40).toByte()
        buffer[position++] = (value ushr 32).toByte()
        buffer[position++] = (value ushr 24).toByte()
        buffer[position++] = (value ushr 16).toByte()
        buffer[position++] = (value ushr 8).toByte()
        buffer[position++] = value.toByte()
    }

    /**
     * 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).
     */
    fun writeLong(value: Long, optimizePositive: Boolean): Int {
        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).
     */
    fun writeVarLong(value: Long, optimizePositive: Boolean): Int {
        @Suppress("NAME_SHADOWING")
        var value = value
        if (!optimizePositive) value = value shl 1 xor (value shr 63)
        if (value ushr 7 == 0L) {
            require(1)
            bytes[position++] = value.toByte()
            return 1
        }
        if (value ushr 14 == 0L) {
            require(2)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7).toByte()
            return 2
        }
        if (value ushr 21 == 0L) {
            require(3)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14).toByte()
            return 3
        }
        if (value ushr 28 == 0L) {
            require(4)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14 or 0x80).toByte()
            buffer[position++] = (value ushr 21).toByte()
            return 4
        }
        if (value ushr 35 == 0L) {
            require(5)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14 or 0x80).toByte()
            buffer[position++] = (value ushr 21 or 0x80).toByte()
            buffer[position++] = (value ushr 28).toByte()
            return 5
        }
        if (value ushr 42 == 0L) {
            require(6)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14 or 0x80).toByte()
            buffer[position++] = (value ushr 21 or 0x80).toByte()
            buffer[position++] = (value ushr 28 or 0x80).toByte()
            buffer[position++] = (value ushr 35).toByte()
            return 6
        }
        if (value ushr 49 == 0L) {
            require(7)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14 or 0x80).toByte()
            buffer[position++] = (value ushr 21 or 0x80).toByte()
            buffer[position++] = (value ushr 28 or 0x80).toByte()
            buffer[position++] = (value ushr 35 or 0x80).toByte()
            buffer[position++] = (value ushr 42).toByte()
            return 7
        }
        if (value ushr 56 == 0L) {
            require(8)
            val buffer = bytes
            buffer[position++] = (value and 0x7F or 0x80).toByte()
            buffer[position++] = (value ushr 7 or 0x80).toByte()
            buffer[position++] = (value ushr 14 or 0x80).toByte()
            buffer[position++] = (value ushr 21 or 0x80).toByte()
            buffer[position++] = (value ushr 28 or 0x80).toByte()
            buffer[position++] = (value ushr 35 or 0x80).toByte()
            buffer[position++] = (value ushr 42 or 0x80).toByte()
            buffer[position++] = (value ushr 49).toByte()
            return 8
        }
        require(9)
        val buffer = bytes
        buffer[position++] = (value and 0x7F or 0x80).toByte()
        buffer[position++] = (value ushr 7 or 0x80).toByte()
        buffer[position++] = (value ushr 14 or 0x80).toByte()
        buffer[position++] = (value ushr 21 or 0x80).toByte()
        buffer[position++] = (value ushr 28 or 0x80).toByte()
        buffer[position++] = (value ushr 35 or 0x80).toByte()
        buffer[position++] = (value ushr 42 or 0x80).toByte()
        buffer[position++] = (value ushr 49 or 0x80).toByte()
        buffer[position++] = (value ushr 56).toByte()
        return 9
    }

    /**
     * Returns true if enough bytes are available to read a long with [.readLong].
     */
    fun canReadLong(): Boolean {
        if (capacity - position >= 9) {
            return true
        }
        if (position + 1 > capacity) {
            return false
        }
        val buffer = bytes
        var p = position
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        return if (buffer[p++].toInt() and 0x80 == 0) {
            true
        } else p != capacity
    }

    /**
     * Returns true if enough bytes are available to read a long with [.readLong].
     */
    fun canReadLong(position: Int): Boolean {
        if (capacity - position >= 9) {
            return true
        }
        if (position + 1 > capacity) {
            return false
        }
        val buffer = bytes
        var p = position
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        if (buffer[p++].toInt() and 0x80 == 0) {
            return true
        }
        if (p == capacity) {
            return false
        }
        return if (buffer[p++].toInt() and 0x80 == 0) {
            true
        } else p != capacity
    }

    /**
     * Reads an 8 byte long.
     */
    fun readLong(): Long {
        val buffer = bytes
        return buffer[position++].toLong() shl 56 or ((buffer[position++].toInt() and 0xFF).toLong() shl 48
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 40
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 32
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 24
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 16
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 8
                ) or (buffer[position++].toInt() and 0xFF).toLong()
    }

    /**
     * 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.
     */
    fun readLong(optimizePositive: Boolean): Long {
        return readVarLong(optimizePositive)
    }

    /**
     * Reads an 8 byte long, does not advance the position
     */
    fun readLong(position: Int): Long {
        @Suppress("NAME_SHADOWING")
        var position = position
        val buffer = bytes
        return buffer[position++].toLong() shl 56 or ((buffer[position++].toInt() and 0xFF).toLong() shl 48
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 40
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 32
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 24
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 16
                ) or ((buffer[position++].toInt() and 0xFF).toLong() shl 8
                ) or (buffer[position].toInt() and 0xFF).toLong()
    }

    /**
     * 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
     */
    fun readLong(position: Int, optimizePositive: Boolean): Long {
        val pos = this.position
        this.position = position
        val 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 fun readVarLong(optimizePositive: Boolean): Long {
        if (capacity - position < 9) {
            return readLong_slow(optimizePositive)
        }
        var b = bytes[position++].toInt()
        var result = (b and 0x7F).toLong()
        if (b and 0x80 != 0) {
            val buffer = bytes
            b = buffer[position++].toInt()
            result = result or (b and 0x7F shl 7).toLong()
            if (b and 0x80 != 0) {
                b = buffer[position++].toInt()
                result = result or (b and 0x7F shl 14).toLong()
                if (b and 0x80 != 0) {
                    b = buffer[position++].toInt()
                    result = result or (b and 0x7F shl 21).toLong()
                    if (b and 0x80 != 0) {
                        b = buffer[position++].toInt()
                        result = result or ((b and 0x7F).toLong() shl 28)
                        if (b and 0x80 != 0) {
                            b = buffer[position++].toInt()
                            result = result or ((b and 0x7F).toLong() shl 35)
                            if (b and 0x80 != 0) {
                                b = buffer[position++].toInt()
                                result = result or ((b and 0x7F).toLong() shl 42)
                                if (b and 0x80 != 0) {
                                    b = buffer[position++].toInt()
                                    result = result or ((b and 0x7F).toLong() shl 49)
                                    if (b and 0x80 != 0) {
                                        b = buffer[position++].toInt()
                                        result = result or (b.toLong() shl 56)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if (!optimizePositive) {
            result = result ushr 1 xor -(result and 1)
        }
        return result
    }

    private fun readLong_slow(optimizePositive: Boolean): Long {
        // The buffer is guaranteed to have at least 1 byte.
        val buffer = bytes
        var b = buffer[position++].toInt()
        var result = (b and 0x7F).toLong()
        if (b and 0x80 != 0) {
            b = buffer[position++].toInt()
            result = result or (b and 0x7F shl 7).toLong()
            if (b and 0x80 != 0) {
                b = buffer[position++].toInt()
                result = result or (b and 0x7F shl 14).toLong()
                if (b and 0x80 != 0) {
                    b = buffer[position++].toInt()
                    result = result or (b and 0x7F shl 21).toLong()
                    if (b and 0x80 != 0) {
                        b = buffer[position++].toInt()
                        result = result or ((b and 0x7F).toLong() shl 28)
                        if (b and 0x80 != 0) {
                            b = buffer[position++].toInt()
                            result = result or ((b and 0x7F).toLong() shl 35)
                            if (b and 0x80 != 0) {
                                b = buffer[position++].toInt()
                                result = result or ((b and 0x7F).toLong() shl 42)
                                if (b and 0x80 != 0) {
                                    b = buffer[position++].toInt()
                                    result = result or ((b and 0x7F).toLong() shl 49)
                                    if (b and 0x80 != 0) {
                                        b = buffer[position++].toInt()
                                        result = result or (b.toLong() shl 56)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if (!optimizePositive) {
            result = result ushr 1 xor -(result and 1)
        }
        return result
    }
    // boolean
    /**
     * Writes a 1 byte boolean.
     */
    fun writeBoolean(value: Boolean) {
        require(1)
        bytes[position++] = (if (value) 1 else 0).toByte()
    }

    /**
     * Reads a 1 byte boolean.
     */
    fun readBoolean(): Boolean {
        return bytes[position++].toInt() == 1
    }

    /**
     * Reads a 1 byte boolean, does not advance the position
     */
    fun readBoolean(position: Int): Boolean {
        return bytes[position] .toInt()== 1
    }
    // char
    /**
     * Writes a 2 byte char. Uses BIG_ENDIAN byte order.
     */
    fun writeChar(value: Char) {
        require(2)
        val buffer = bytes
        buffer[position++] = (value.code ushr 8).toByte()
        buffer[position++] = value.code.toByte()
    }

    /**
     * Reads a 2 byte char.
     */
    fun readChar(): Char {
        val buffer = bytes
        return ((buffer[position++].toInt() and 0xFF) shl 8 or (buffer[position++].toInt() and 0xFF)).toChar()
    }

    /**
     * Reads a 2 byte char, does not advance the position
     */
    fun readChar(position: Int): Char {
        @Suppress("NAME_SHADOWING")
        var position = position
        val buffer = bytes
        return (buffer[position++].toInt() and 0xFF shl 8 or buffer[position].toInt() and 0xFF).toChar()
    }

    // double
    /**
     * Writes an 8 byte double.
     */
    fun writeDouble(value: Double) {
        writeLong(java.lang.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).
     */
    fun writeDouble(value: Double, precision: Double, optimizePositive: Boolean): Int {
        return writeLong((value * precision).toLong(), optimizePositive)
    }

    /**
     * Reads an 8 bytes double.
     */
    fun readDouble(): Double {
        return java.lang.Double.longBitsToDouble(readLong())
    }

    /**
     * Reads a 1-9 byte double with reduced precision.
     */
    fun readDouble(precision: Double, optimizePositive: Boolean): Double {
        return readLong(optimizePositive) / precision
    }

    /**
     * Reads an 8 bytes double, does not advance the position
     */
    fun readDouble(position: Int): Double {
        return java.lang.Double.longBitsToDouble(readLong(position))
    }

    /**
     * Reads a 1-9 byte double with reduced precision, does not advance the position
     */
    fun readDouble(position: Int, precision: Double, optimizePositive: Boolean): Double {
        return readLong(position, optimizePositive) / precision
    }

    // Methods implementing bulk operations on arrays of primitive types
    /**
     * Bulk output of an int array.
     */
    fun writeInts(`object`: IntArray, optimizePositive: Boolean) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeInt(`object`[i], optimizePositive)
            i++
        }
    }

    /**
     * Bulk input of an int array.
     */
    fun readInts(length: Int, optimizePositive: Boolean): IntArray {
        val array = IntArray(length)
        for (i in 0 until length) {
            array[i] = readInt(optimizePositive)
        }
        return array
    }

    /**
     * Bulk output of an long array.
     */
    fun writeLongs(`object`: LongArray, optimizePositive: Boolean) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeLong(`object`[i], optimizePositive)
            i++
        }
    }

    /**
     * Bulk input of a long array.
     */
    fun readLongs(length: Int, optimizePositive: Boolean): LongArray {
        val array = LongArray(length)
        for (i in 0 until length) {
            array[i] = readLong(optimizePositive)
        }
        return array
    }

    /**
     * Bulk output of an int array.
     */
    fun writeInts(`object`: IntArray) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeInt(`object`[i])
            i++
        }
    }

    /**
     * Bulk input of an int array.
     */
    fun readInts(length: Int): IntArray {
        val array = IntArray(length)
        for (i in 0 until length) {
            array[i] = readInt()
        }
        return array
    }

    /**
     * Bulk output of an long array.
     */
    fun writeLongs(`object`: LongArray) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeLong(`object`[i])
            i++
        }
    }

    /**
     * Bulk input of a long array.
     */
    fun readLongs(length: Int): LongArray {
        val array = LongArray(length)
        for (i in 0 until length) {
            array[i] = readLong()
        }
        return array
    }

    /**
     * Bulk output of a float array.
     */
    fun writeFloats(`object`: FloatArray) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeFloat(`object`[i])
            i++
        }
    }

    /**
     * Bulk input of a float array.
     */
    fun readFloats(length: Int): FloatArray {
        val array = FloatArray(length)
        for (i in 0 until length) {
            array[i] = readFloat()
        }
        return array
    }

    /**
     * Bulk output of a short array.
     */
    fun writeShorts(`object`: ShortArray) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeShort(`object`[i].toInt())
            i++
        }
    }

    /**
     * Bulk input of a short array.
     */
    fun readShorts(length: Int): ShortArray {
        val array = ShortArray(length)
        for (i in 0 until length) {
            array[i] = readShort()
        }
        return array
    }

    /**
     * Bulk output of a char array.
     */
    fun writeChars(`object`: CharArray) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeChar(`object`[i])
            i++
        }
    }

    /**
     * Bulk input of a char array.
     */
    fun readChars(length: Int): CharArray {
        val array = CharArray(length)
        for (i in 0 until length) {
            array[i] = readChar()
        }
        return array
    }

    /**
     * Bulk output of a double array.
     */
    fun writeDoubles(`object`: DoubleArray) {
        var i = 0
        val n = `object`.size
        while (i < n) {
            writeDouble(`object`[i])
            i++
        }
    }

    /**
     * Bulk input of a double array
     */
    fun readDoubles(length: Int): DoubleArray {
        val array = DoubleArray(length)
        for (i in 0 until length) {
            array[i] = readDouble()
        }
        return array
    }

    override fun equals(other: Any?): Boolean {
        return if (other !is ByteArrayBuffer) {
            false
        } else Arrays.equals(bytes, other.bytes)

        // CANNOT be null, so we don't have to null check!
    }

    override fun hashCode(): Int {
        // might be null for a thread because it's stale. who cares, get the value again
        return Arrays.hashCode(bytes)
    }

    override fun toString(): String {
        return "ByteBuffer2 " + Arrays.toString(bytes)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy