ByteUtilities/src/dorkbox/bytes/ByteArrayBuffer.kt

1865 lines
59 KiB
Kotlin

/*
* 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 <misc></misc>@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)
}
}