824 lines
29 KiB
Kotlin
824 lines
29 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.
|
|
*/
|
|
package dorkbox.bytes
|
|
|
|
import com.esotericsoftware.kryo.KryoException
|
|
import com.esotericsoftware.kryo.io.Output
|
|
import com.esotericsoftware.kryo.util.Util
|
|
import io.netty.buffer.ByteBuf
|
|
import io.netty.buffer.Unpooled
|
|
import java.io.IOException
|
|
import java.io.OutputStream
|
|
|
|
/**
|
|
* An [OutputStream] which writes data to a [ByteBuf].
|
|
*
|
|
*
|
|
* A write operation against this stream will occur at the `writerIndex`
|
|
* of its underlying buffer and the `writerIndex` will increase during
|
|
* the write operation.
|
|
*
|
|
*
|
|
* This stream implements [DataOutput] for your convenience.
|
|
* The endianness of the stream is not always big endian but depends on
|
|
* the endianness of the underlying buffer.
|
|
*
|
|
*
|
|
*
|
|
* Utility methods are provided for efficiently reading primitive types and strings.
|
|
*
|
|
* Modified from KRYO to use ByteBuf.
|
|
*/
|
|
@Suppress("MemberVisibilityCanBePrivate", "NAME_SHADOWING")
|
|
class ByteBufOutput : Output {
|
|
companion object {
|
|
/**
|
|
* Gets the version number.
|
|
*/
|
|
const val version = BytesInfo.version
|
|
}
|
|
|
|
/**
|
|
* Returns the buffer. The bytes between zero and [.position] are the data that has been written.
|
|
*/
|
|
// NOTE: capacity IS NOT USED!
|
|
lateinit var byteBuf: ByteBuf
|
|
private set
|
|
|
|
private var initialReaderIndex = 0
|
|
private var initialWriterIndex = 0
|
|
|
|
/**
|
|
* Creates an uninitialized Output, [.setBuffer] must be called before the Output is used.
|
|
*/
|
|
constructor() {}
|
|
|
|
/**
|
|
* Creates a new Output for writing to a direct ByteBuffer.
|
|
*
|
|
* @param bufferSize The size of the buffer. An exception is thrown if more bytes than this are written and [.flush] does not empty
|
|
* the buffer.
|
|
*
|
|
* @param maxBufferSize If [.flush] does not empty the buffer, 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" }
|
|
maxCapacity = if (maxBufferSize == -1) Util.maxArraySize else maxBufferSize
|
|
byteBuf = Unpooled.buffer(bufferSize)
|
|
}
|
|
|
|
/**
|
|
* Creates a new Output for writing to a ByteBuffer.
|
|
*/
|
|
constructor(buffer: ByteBuf) {
|
|
setBuffer(buffer)
|
|
}
|
|
|
|
/**
|
|
* Creates a new Output for writing to a ByteBuffer.
|
|
*
|
|
* @param maxBufferSize If [.flush] does not empty the buffer, the buffer is doubled as needed until it exceeds maxBufferSize and
|
|
* an exception is thrown. Can be -1 for no maximum.
|
|
*/
|
|
constructor(buffer: ByteBuf, maxBufferSize: Int) {
|
|
setBuffer(buffer, maxBufferSize)
|
|
}
|
|
|
|
/**
|
|
* @see Output.Output
|
|
*/
|
|
constructor(outputStream: OutputStream) : this(4096, 4096) {
|
|
this.outputStream = outputStream
|
|
}
|
|
|
|
/**
|
|
* @see Output.Output
|
|
*/
|
|
constructor(outputStream: OutputStream, bufferSize: Int) : this(bufferSize, bufferSize) {
|
|
this.outputStream = outputStream
|
|
}
|
|
|
|
override fun getOutputStream(): OutputStream {
|
|
return outputStream
|
|
}
|
|
|
|
/**
|
|
* Throws [UnsupportedOperationException] because this output uses a ByteBuffer, not a byte[].
|
|
*
|
|
* @see .getByteBuf
|
|
*/
|
|
@Deprecated("This buffer does not used a byte[], see #byteBuf.",
|
|
ReplaceWith("byteBuf")
|
|
)
|
|
override fun getBuffer(): ByteArray {
|
|
throw UnsupportedOperationException("This buffer does not used a byte[], see #byteBuf.")
|
|
}
|
|
|
|
/**
|
|
* Throws [UnsupportedOperationException] because this output uses a ByteBuffer, not a byte[].
|
|
*
|
|
* @see .getByteBuf
|
|
*/
|
|
override fun setBuffer(buffer: ByteArray) {
|
|
setBuffer(Unpooled.wrappedBuffer(buffer))
|
|
}
|
|
|
|
/**
|
|
* Allocates a new direct ByteBuffer with the specified bytes and sets it as the new buffer.
|
|
*
|
|
* @see .setBuffer
|
|
*/
|
|
override fun setBuffer(buffer: ByteArray, maxBufferSize: Int) {
|
|
setBuffer(Unpooled.wrappedBuffer(buffer))
|
|
}
|
|
|
|
/**
|
|
* Allocates a new direct ByteBuffer with the specified bytes and sets it as the new buffer.
|
|
*
|
|
* @see .setBuffer
|
|
*/
|
|
fun setBuffer(bytes: ByteArray, offset: Int, count: Int) {
|
|
setBuffer(Unpooled.wrappedBuffer(bytes, offset, count))
|
|
}
|
|
|
|
/**
|
|
* Sets a new buffer to write to. The max size is the buffer's length.
|
|
*
|
|
* @see .setBuffer
|
|
*/
|
|
fun setBuffer(buffer: ByteBuf) {
|
|
setBuffer(buffer, buffer.capacity())
|
|
}
|
|
|
|
/**
|
|
* Sets a new buffer to write to. The bytes are not copied, the old buffer is discarded and the new buffer used in its place.
|
|
*
|
|
* The position and capacity are set to match the specified buffer. The total is reset. The
|
|
* [OutputStream][.setOutputStream] is set to null.
|
|
*
|
|
* @param maxBufferSize If [.flush] does not empty the buffer, the buffer is doubled as needed until it exceeds
|
|
* maxBufferSize and an exception is thrown. Can be -1 for no maximum.
|
|
*/
|
|
fun setBuffer(buffer: ByteBuf, maxBufferSize: Int) {
|
|
require(maxBufferSize >= -1) { "maxBufferSize cannot be < -1: $maxBufferSize" }
|
|
initialReaderIndex = buffer.readerIndex()
|
|
initialWriterIndex = buffer.writerIndex()
|
|
byteBuf = buffer
|
|
maxCapacity = if (maxBufferSize == -1) Util.maxArraySize else maxBufferSize
|
|
position = initialWriterIndex
|
|
total = 0
|
|
outputStream = null
|
|
}
|
|
|
|
override fun toBytes(): ByteArray {
|
|
val newBuffer = ByteArray(position)
|
|
byteBuf.readerIndex(initialReaderIndex)
|
|
byteBuf.getBytes(initialReaderIndex, newBuffer, 0, position)
|
|
return newBuffer
|
|
}
|
|
|
|
override fun setPosition(position: Int) {
|
|
this.position = position
|
|
byteBuf.writerIndex(position)
|
|
}
|
|
|
|
override fun reset() {
|
|
super.reset()
|
|
byteBuf.setIndex(initialReaderIndex, initialWriterIndex)
|
|
}
|
|
|
|
/**
|
|
* Ensures the buffer is large enough to read the specified number of bytes.
|
|
* @return true if the buffer has been resized.
|
|
*/
|
|
@Throws(KryoException::class)
|
|
override fun require(required: Int): Boolean {
|
|
if (byteBuf.isWritable(required)) {
|
|
return false
|
|
}
|
|
var origCode = byteBuf.ensureWritable(required, true)
|
|
if (origCode == 0) {
|
|
// 0 if the buffer has enough writable bytes, and its capacity is unchanged.
|
|
return false
|
|
} else if (origCode == 2) {
|
|
// 2 if the buffer has enough writable bytes, and its capacity has been increased.
|
|
return true
|
|
} else if (origCode == 3) {
|
|
// 3 if the buffer does not have enough bytes, but its capacity has been increased to its maximum.
|
|
return true
|
|
} else {
|
|
// flush and try again.
|
|
flush()
|
|
}
|
|
|
|
// only got here because we were unable to resize the buffer! So we flushed it first to try again!
|
|
origCode = byteBuf.ensureWritable(required, true)
|
|
return if (origCode == 0) {
|
|
// 0 if the buffer has enough writable bytes, and its capacity is unchanged.
|
|
false
|
|
} else if (origCode == 1) {
|
|
// 1 if the buffer does not have enough bytes, and its capacity is unchanged.
|
|
throw KryoException("Buffer overflow. Max capacity: $maxCapacity, required: $required")
|
|
} else if (origCode == 2) {
|
|
// 2 if the buffer has enough writable bytes, and its capacity has been increased.
|
|
true
|
|
} else if (origCode == 3) {
|
|
// 3 if the buffer does not have enough bytes, but its capacity has been increased to its maximum.
|
|
true
|
|
} else {
|
|
throw KryoException("Unknown buffer resize code: $origCode")
|
|
}
|
|
}
|
|
|
|
// OutputStream:
|
|
@Throws(KryoException::class)
|
|
override fun flush() {
|
|
if (outputStream == null) return
|
|
try {
|
|
val tmp = ByteArray(position)
|
|
byteBuf.getBytes(initialReaderIndex, tmp)
|
|
byteBuf.readerIndex(initialReaderIndex)
|
|
outputStream.write(tmp, 0, position)
|
|
} catch (ex: IOException) {
|
|
throw KryoException(ex)
|
|
}
|
|
total += position.toLong()
|
|
position = 0
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun close() {
|
|
flush()
|
|
if (outputStream != null) {
|
|
try {
|
|
outputStream.close()
|
|
} catch (ignored: IOException) {
|
|
}
|
|
}
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun write(value: Int) {
|
|
require(1)
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
position++
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun write(bytes: ByteArray) {
|
|
requireNotNull(bytes) { "bytes cannot be null." }
|
|
writeBytes(bytes, 0, bytes.size)
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun write(bytes: ByteArray, offset: Int, length: Int) {
|
|
writeBytes(bytes, offset, length)
|
|
}
|
|
|
|
// byte:
|
|
@Throws(KryoException::class)
|
|
override fun writeByte(value: Byte) {
|
|
require(1)
|
|
byteBuf.writeByte(value.toInt())
|
|
position++
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeByte(value: Int) {
|
|
require(1)
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
position++
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeBytes(bytes: ByteArray) {
|
|
requireNotNull(bytes) { "bytes cannot be null." }
|
|
writeBytes(bytes, 0, bytes.size)
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeBytes(bytes: ByteArray, offset: Int, count: Int) {
|
|
requireNotNull(bytes) { "bytes cannot be null." }
|
|
require(count)
|
|
byteBuf.writeBytes(bytes, offset, count)
|
|
position += count
|
|
}
|
|
|
|
// int:
|
|
@Throws(KryoException::class)
|
|
override fun writeInt(value: Int) {
|
|
require(4)
|
|
position += 4
|
|
byteBuf.writeInt(value)
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override 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)
|
|
position++
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
return 1
|
|
}
|
|
if (value ushr 14 == 0) {
|
|
require(2)
|
|
position += 2
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7).toByte().toInt())
|
|
return 2
|
|
}
|
|
if (value ushr 21 == 0) {
|
|
require(3)
|
|
position += 3
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14).toByte().toInt())
|
|
return 3
|
|
}
|
|
if (value ushr 28 == 0) {
|
|
require(4)
|
|
position += 4
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21).toByte().toInt())
|
|
return 4
|
|
}
|
|
require(5)
|
|
position += 5
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 28).toByte().toInt())
|
|
return 5
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeVarIntFlag(flag: Boolean, value: Int, optimizePositive: Boolean): Int {
|
|
var value = value
|
|
if (!optimizePositive) value = value shl 1 xor (value shr 31)
|
|
val first = value and 0x3F or if (flag) 0x80 else 0 // Mask first 6 bits, bit 8 is the flag.
|
|
if (value ushr 6 == 0) {
|
|
require(1)
|
|
byteBuf.writeByte(first.toByte().toInt())
|
|
position++
|
|
return 1
|
|
}
|
|
if (value ushr 13 == 0) {
|
|
require(2)
|
|
position += 2
|
|
byteBuf.writeByte((first or 0x40).toByte().toInt()) // Set bit 7.
|
|
byteBuf.writeByte((value ushr 6).toByte().toInt())
|
|
return 2
|
|
}
|
|
if (value ushr 20 == 0) {
|
|
require(3)
|
|
position += 3
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((first or 0x40).toByte().toInt()) // Set bit 7.
|
|
byteBuf.writeByte((value ushr 6 or 0x80).toByte().toInt()) // Set bit 8.
|
|
byteBuf.writeByte((value ushr 13).toByte().toInt())
|
|
return 3
|
|
}
|
|
if (value ushr 27 == 0) {
|
|
require(4)
|
|
position += 4
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((first or 0x40).toByte().toInt()) // Set bit 7.
|
|
byteBuf.writeByte((value ushr 6 or 0x80).toByte().toInt()) // Set bit 8.
|
|
byteBuf.writeByte((value ushr 13 or 0x80).toByte().toInt()) // Set bit 8.
|
|
byteBuf.writeByte((value ushr 20).toByte().toInt())
|
|
return 4
|
|
}
|
|
require(5)
|
|
position += 5
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((first or 0x40).toByte().toInt()) // Set bit 7.
|
|
byteBuf.writeByte((value ushr 6 or 0x80).toByte().toInt()) // Set bit 8.
|
|
byteBuf.writeByte((value ushr 13 or 0x80).toByte().toInt()) // Set bit 8.
|
|
byteBuf.writeByte((value ushr 20 or 0x80).toByte().toInt()) // Set bit 8.
|
|
byteBuf.writeByte((value ushr 27).toByte().toInt())
|
|
return 5
|
|
}
|
|
|
|
// long:
|
|
@Throws(KryoException::class)
|
|
override fun writeLong(value: Long) {
|
|
require(8)
|
|
position += 8
|
|
byteBuf.writeLong(value)
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeVarLong(value: Long, optimizePositive: Boolean): Int {
|
|
var value = value
|
|
if (!optimizePositive) value = value shl 1 xor (value shr 63)
|
|
if (value ushr 7 == 0L) {
|
|
require(1)
|
|
position++
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
return 1
|
|
}
|
|
if (value ushr 14 == 0L) {
|
|
require(2)
|
|
position += 2
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7).toByte().toInt())
|
|
return 2
|
|
}
|
|
if (value ushr 21 == 0L) {
|
|
require(3)
|
|
position += 3
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14).toByte().toInt())
|
|
return 3
|
|
}
|
|
if (value ushr 28 == 0L) {
|
|
require(4)
|
|
position += 4
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21).toByte().toInt())
|
|
return 4
|
|
}
|
|
if (value ushr 35 == 0L) {
|
|
require(5)
|
|
position += 5
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 28).toByte().toInt())
|
|
return 5
|
|
}
|
|
if (value ushr 42 == 0L) {
|
|
require(6)
|
|
position += 6
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 28 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 35).toByte().toInt())
|
|
return 6
|
|
}
|
|
if (value ushr 49 == 0L) {
|
|
require(7)
|
|
position += 7
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 28 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 35 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 42).toByte().toInt())
|
|
return 7
|
|
}
|
|
if (value ushr 56 == 0L) {
|
|
require(8)
|
|
position += 8
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 28 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 35 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 42 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 49).toByte().toInt())
|
|
return 8
|
|
}
|
|
require(9)
|
|
position += 9
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeByte((value and 0x7F or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 7 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 14 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 21 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 28 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 35 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 42 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 49 or 0x80).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 56).toByte().toInt())
|
|
return 9
|
|
}
|
|
|
|
// float:
|
|
@Throws(KryoException::class)
|
|
override fun writeFloat(value: Float) {
|
|
require(4)
|
|
val byteBuf = byteBuf
|
|
position += 4
|
|
byteBuf.writeFloat(value)
|
|
}
|
|
|
|
// double:
|
|
@Throws(KryoException::class)
|
|
override fun writeDouble(value: Double) {
|
|
require(8)
|
|
position += 8
|
|
val byteBuf = byteBuf
|
|
byteBuf.writeDouble(value)
|
|
}
|
|
|
|
// short:
|
|
@Throws(KryoException::class)
|
|
override fun writeShort(value: Int) {
|
|
require(2)
|
|
position += 2
|
|
byteBuf.writeShort(value)
|
|
}
|
|
|
|
// char:
|
|
@Throws(KryoException::class)
|
|
override fun writeChar(value: Char) {
|
|
require(2)
|
|
position += 2
|
|
byteBuf.writeChar(value.code)
|
|
}
|
|
|
|
// boolean:
|
|
@Throws(KryoException::class)
|
|
override fun writeBoolean(value: Boolean) {
|
|
require(1)
|
|
byteBuf.writeByte((if (value) 1 else 0).toByte().toInt())
|
|
position++
|
|
}
|
|
|
|
// String:
|
|
@Throws(KryoException::class)
|
|
override 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
|
|
}
|
|
require(charCount) // must be able to write this number of chars
|
|
|
|
// 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
|
|
var i = 0
|
|
val n = value.length
|
|
while (i < n) {
|
|
byteBuf.writeByte(value[i].code.toByte().toInt())
|
|
++i
|
|
}
|
|
position += charCount
|
|
|
|
// 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 = (byteBuf.getByte(position - 1).toInt() or 0x80).toByte()
|
|
byteBuf.setByte(position - 1, value1.toInt())
|
|
return
|
|
}
|
|
}
|
|
|
|
// UTF8 (or ASCII with length 1 or length > 32
|
|
writeVarIntFlag(true, charCount + 1, true)
|
|
var charIndex = 0
|
|
// Try to write 7 bit chars.
|
|
val byteBuf = byteBuf
|
|
while (true) {
|
|
val c = value[charIndex].code
|
|
if (c > 127) break
|
|
byteBuf.writeByte(c.toByte().toInt())
|
|
charIndex++
|
|
if (charIndex == charCount) {
|
|
position = byteBuf.writerIndex()
|
|
return
|
|
}
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
if (charIndex < charCount) writeUtf8_slow(value, charCount, charIndex)
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeAscii(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
|
|
}
|
|
require(charCount) // must be able to write this number of chars
|
|
val byteBuf = byteBuf
|
|
var i = 0
|
|
val n = value.length
|
|
while (i < n) {
|
|
byteBuf.writeByte(value[i].code.toByte().toInt())
|
|
++i
|
|
}
|
|
position += charCount
|
|
byteBuf.setByte(position - 1, (byteBuf.getByte(position - 1).toInt() or 0x80).toByte().toInt()) // Bit 8 means end of ASCII.
|
|
}
|
|
|
|
private fun writeUtf8_slow(value: String, charCount: Int, charIndex: Int) {
|
|
@Suppress("NAME_SHADOWING")
|
|
var charIndex = charIndex
|
|
while (charIndex < charCount) {
|
|
val c = value[charIndex].code
|
|
|
|
if (c <= 0x007F) {
|
|
writeByte(c.toByte())
|
|
} else if (c > 0x07FF) {
|
|
require(3)
|
|
byteBuf.writeByte((0xE0 or (c shr 12 and 0x0F)).toByte().toInt())
|
|
byteBuf.writeByte((0x80 or (c shr 6 and 0x3F)).toByte().toInt())
|
|
byteBuf.writeByte((0x80 or (c and 0x3F)).toByte().toInt())
|
|
position += 3
|
|
} else {
|
|
require(2)
|
|
byteBuf.writeByte((0xC0 or (c shr 6 and 0x1F)).toByte().toInt())
|
|
byteBuf.writeByte((0x80 or (c and 0x3F)).toByte().toInt())
|
|
position += 2
|
|
}
|
|
charIndex++
|
|
}
|
|
}
|
|
|
|
// Primitive arrays:
|
|
@Throws(KryoException::class)
|
|
override fun writeInts(array: IntArray, offset: Int, count: Int) {
|
|
var offset = offset
|
|
require(count shl 2)
|
|
val byteBuf = byteBuf
|
|
val n = offset + count
|
|
while (offset < n) {
|
|
val value = array[offset]
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
byteBuf.writeByte((value shr 8).toByte().toInt())
|
|
byteBuf.writeByte((value shr 16).toByte().toInt())
|
|
byteBuf.writeByte((value shr 24).toByte().toInt())
|
|
offset++
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeLongs(array: LongArray, offset: Int, count: Int) {
|
|
var offset = offset
|
|
require(count shl 3)
|
|
val byteBuf = byteBuf
|
|
val n = offset + count
|
|
while (offset < n) {
|
|
val value = array[offset]
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
byteBuf.writeByte((value ushr 8).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 16).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 24).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 32).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 40).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 48).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 56).toByte().toInt())
|
|
offset++
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeFloats(array: FloatArray, offset: Int, count: Int) {
|
|
var offset = offset
|
|
require(count shl 2)
|
|
val byteBuf = byteBuf
|
|
val n = offset + count
|
|
while (offset < n) {
|
|
val value = java.lang.Float.floatToIntBits(array[offset])
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
byteBuf.writeByte((value shr 8).toByte().toInt())
|
|
byteBuf.writeByte((value shr 16).toByte().toInt())
|
|
byteBuf.writeByte((value shr 24).toByte().toInt())
|
|
offset++
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeDoubles(array: DoubleArray, offset: Int, count: Int) {
|
|
var offset = offset
|
|
require(count shl 3)
|
|
val byteBuf = byteBuf
|
|
val n = offset + count
|
|
while (offset < n) {
|
|
val value = java.lang.Double.doubleToLongBits(array[offset])
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
byteBuf.writeByte((value ushr 8).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 16).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 24).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 32).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 40).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 48).toByte().toInt())
|
|
byteBuf.writeByte((value ushr 56).toByte().toInt())
|
|
offset++
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeShorts(array: ShortArray, offset: Int, count: Int) {
|
|
var offset = offset
|
|
require(count shl 1)
|
|
val n = offset + count
|
|
while (offset < n) {
|
|
val value = array[offset].toInt()
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
byteBuf.writeByte((value ushr 8).toByte().toInt())
|
|
offset++
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeChars(array: CharArray, offset: Int, count: Int) {
|
|
var offset = offset
|
|
require(count shl 1)
|
|
val n = offset + count
|
|
while (offset < n) {
|
|
val value = array[offset].code
|
|
byteBuf.writeByte(value.toByte().toInt())
|
|
byteBuf.writeByte((value ushr 8).toByte().toInt())
|
|
offset++
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
}
|
|
|
|
@Throws(KryoException::class)
|
|
override fun writeBooleans(array: BooleanArray, offset: Int, count: Int) {
|
|
var offset = offset
|
|
require(count)
|
|
val n = offset + count
|
|
while (offset < n) {
|
|
byteBuf.writeByte(if (array[offset]) 1 else 0)
|
|
offset++
|
|
}
|
|
position = byteBuf.writerIndex()
|
|
}
|
|
}
|