ByteUtilities/src/dorkbox/bytes/OptimizeUtilsByteBuf.kt

403 lines
15 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.
*/
@file:Suppress("NAME_SHADOWING")
package dorkbox.bytes
import io.netty.buffer.ByteBuf
@Suppress("unused")
object OptimizeUtilsByteBuf {
/**
* Gets the version number.
*/
const val version = BytesInfo.version
// int
/**
* FROM KRYO
*
*
* Returns the number of bytes that would be written with [.writeInt].
*
* @param optimizePositive
* If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be inefficient (5
* bytes). This ultimately means that it will use fewer bytes for positive numbers.
*/
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
}
/**
* FROM KRYO
*
*
* look at buffer, and see if we can read the length of the int off of it. (from the reader index)
*
* @return 0 if we could not read anything, >0 for the number of bytes for the int on the buffer
*/
fun canReadInt(buffer: ByteBuf): Int {
val startIndex = buffer.readerIndex()
return try {
var remaining = buffer.readableBytes()
var offset = 0
var count = 1
while (offset < 32 && remaining > 0) {
val b = buffer.readByte().toInt()
if (b and 0x80 == 0) {
return count
}
offset += 7
remaining--
count++
}
0
} finally {
buffer.readerIndex(startIndex)
}
}
fun canReadVarInt(buffer: ByteBuf): Boolean {
val startIndex = buffer.readerIndex()
try {
var remaining = buffer.readableBytes()
if (remaining >= 5) return true
var p = startIndex
if (buffer.getByte(p++).toInt() and 0x80 == 0) return true
if (p == remaining) return false
if (buffer.getByte(p++).toInt() and 0x80 == 0) return true
if (p == remaining) return false
if (buffer.getByte(p++).toInt() and 0x80 == 0) return true
if (p == remaining) return false
if (buffer.getByte(p++).toInt() and 0x80 == 0) return true
return if (p == remaining) false else true
} finally {
buffer.readerIndex(startIndex)
}
}
/**
* FROM KRYO
*
*
* Reads an int from the buffer that was optimized.
*
* @param optimizePositive
* If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be inefficient (5
* bytes). This ultimately means that it will use fewer bytes for positive numbers.
*
* @return the number of bytes written.
*/
fun readInt(buffer: ByteBuf, optimizePositive: Boolean = false): Int {
var b = buffer.readByte().toInt()
var result = b and 0x7F
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b and 0x7F shl 7)
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b and 0x7F shl 14)
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b and 0x7F shl 21)
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b and 0x7F shl 28)
}
}
}
}
return if (optimizePositive) result else result ushr 1 xor -(result and 1)
}
/**
* FROM KRYO
*
*
* Writes the specified int to the buffer using 1 to 5 bytes, depending on the size of the number.
*
* @param optimizePositive
* If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be inefficient (5
* bytes). This ultimately means that it will use fewer bytes for positive numbers.
*
* @return the number of bytes written.
*/
fun writeInt(buffer: ByteBuf, value: Int, optimizePositive: Boolean): Int {
var value = value
if (!optimizePositive) {
value = value shl 1 xor (value shr 31)
}
if (value ushr 7 == 0) {
buffer.writeByte(value.toByte().toInt())
return 1
}
if (value ushr 14 == 0) {
buffer.writeByte((value and 0x7F or 0x80).toByte().toInt())
buffer.writeByte((value ushr 7).toByte().toInt())
return 2
}
if (value ushr 21 == 0) {
buffer.writeByte((value and 0x7F or 0x80).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80).toByte().toInt())
buffer.writeByte((value ushr 14).toByte().toInt())
return 3
}
if (value ushr 28 == 0) {
buffer.writeByte((value and 0x7F or 0x80).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80).toByte().toInt())
buffer.writeByte((value ushr 21).toByte().toInt())
return 4
}
buffer.writeByte((value and 0x7F or 0x80).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80).toByte().toInt())
buffer.writeByte((value ushr 21 or 0x80).toByte().toInt())
buffer.writeByte((value ushr 28).toByte().toInt())
return 5
}
// long
/**
* Returns the 1-9 bytes that would be written with [.writeLong].
*
* @param optimizePositive
* If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be inefficient (9
* bytes). This ultimately means that it will use fewer bytes for positive numbers.
*/
@JvmStatic
fun longLength(value: Long, optimizePositive: Boolean): Int {
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
}
/**
* FROM KRYO
*
*
* Reads a 1-9 byte long.
*
* @param optimizePositive
* If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be inefficient (9
* bytes). This ultimately means that it will use fewer bytes for positive numbers.
*/
fun readLong(buffer: ByteBuf, optimizePositive: Boolean): Long {
var b = buffer.readByte().toInt()
var result = (b and 0x7F).toLong()
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b and 0x7F shl 7).toLong()
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b and 0x7F shl 14).toLong()
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b and 0x7F shl 21).toLong()
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or ((b and 0x7F).toLong() shl 28)
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or ((b and 0x7F).toLong() shl 35)
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or ((b and 0x7F).toLong() shl 42)
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or ((b and 0x7F).toLong() shl 49)
if (b and 0x80 != 0) {
b = buffer.readByte().toInt()
result = result or (b.toLong() shl 56)
}
}
}
}
}
}
}
}
if (!optimizePositive) {
result = result ushr 1 xor -(result and 1L)
}
return result
}
/**
* FROM KRYO
*
*
* Writes a 1-9 byte long.
*
* @param optimizePositive
* If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be inefficient (9
* bytes). This ultimately means that it will use fewer bytes for positive numbers.
*
* @return the number of bytes written.
*/
fun writeLong(buffer: ByteBuf, value: Long, optimizePositive: Boolean): Int {
var value = value
if (!optimizePositive) {
value = value shl 1 xor (value shr 63)
}
if (value ushr 7 == 0L) {
buffer.writeByte(value.toByte().toInt())
return 1
}
if (value ushr 14 == 0L) {
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7).toByte().toInt())
return 2
}
if (value ushr 21 == 0L) {
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 14).toByte().toInt())
return 3
}
if (value ushr 28 == 0L) {
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 21).toByte().toInt())
return 4
}
if (value ushr 35 == 0L) {
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 21 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 28).toByte().toInt())
return 5
}
if (value ushr 42 == 0L) {
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 21 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 28 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 35).toByte().toInt())
return 6
}
if (value ushr 49 == 0L) {
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 21 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 28 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 35 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 42).toByte().toInt())
return 7
}
if (value ushr 56 == 0L) {
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 21 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 28 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 35 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 42 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 49).toByte().toInt())
return 8
}
buffer.writeByte((value and 0x7FL or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 7 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 14 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 21 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 28 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 35 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 42 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 49 or 0x80L).toByte().toInt())
buffer.writeByte((value ushr 56).toByte().toInt())
return 9
}
/**
* FROM KRYO
*
*
* look at buffer, and see if we can read the length of the long off of it (from the reader index).
*
* @return 0 if we could not read anything, >0 for the number of bytes for the long on the buffer
*/
fun canReadLong(buffer: ByteBuf): Int {
val position = buffer.readerIndex()
return try {
var remaining = buffer.readableBytes()
var offset = 0
var count = 1
while (offset < 64 && remaining > 0) {
val b = buffer.readByte().toInt()
if (b and 0x80 == 0) {
return count
}
offset += 7
remaining--
count++
}
0
} finally {
buffer.readerIndex(position)
}
}
}