ByteUtilities/src/dorkbox/bytes/OptimizeUtilsByteArray.kt

401 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("UNUSED_CHANGED_VALUE", "NAME_SHADOWING")
package dorkbox.bytes
@Suppress("unused")
object OptimizeUtilsByteArray {
/**
* Gets the version number.
*/
const val version = BytesInfo.version
/**
* Returns the number of bytes that would be written with [.writeInt].
*
* @param optimizePositive
* true if you want to optimize the number of bytes needed to write the length value
*/
fun intLength(value: Int, optimizePositive: Boolean): Int {
return OptimizeUtilsByteBuf.intLength(value, optimizePositive)
}
/**
* FROM KRYO
*
*
* look at buffer, and see if we can read the length of the int off of it. (from the reader index)
*
* @param position where in the buffer to start reading
* @return 0 if we could not read anything, >0 for the number of bytes for the int on the buffer
*/
fun canReadInt(buffer: ByteArray, position: Int = 0): Boolean {
var position = position
val length = buffer.size
if (length >= 5) {
return true
}
if (position + 1 > length) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == length) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == length) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == length) {
return false
}
return if (buffer[position++].toInt() and 0x80 == 0) {
true
} else position != length
}
/**
* FROM KRYO
*
*
* Reads an int from the buffer that was optimized.
*
* @param position where in the buffer to start reading
*/
fun readInt(buffer: ByteArray, position: Int): Int {
return readInt(buffer, false, position)
}
/**
* FROM KRYO
*
*
* Reads an int from the buffer that was optimized.
*
* @param position where in the buffer to start reading
* @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 readInt(buffer: ByteArray, optimizePositive: Boolean = false, position: Int = 0): Int {
var position = position
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)
}
/**
* FROM KRYO
*
*
* Writes the specified int to the buffer using 1 to 5 bytes, depending on the size of the number.
*
* @param position where in the buffer to start writing
* @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: ByteArray, value: Int, optimizePositive: Boolean = false, position: Int = 0): Int {
var value = value
var position = position
if (!optimizePositive) {
value = value shl 1 xor (value shr 31)
}
if (value ushr 7 == 0) {
buffer[position++] = value.toByte()
return 1
}
if (value ushr 14 == 0) {
buffer[position++] = (value and 0x7F or 0x80).toByte()
buffer[position++] = (value ushr 7).toByte()
return 2
}
if (value ushr 21 == 0) {
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) {
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
}
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
}
/**
* Returns 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.
*/
fun longLength(value: Long, optimizePositive: Boolean): Int {
return OptimizeUtilsByteBuf.longLength(value, optimizePositive)
}
// long
/**
* FROM KRYO
*
*
* Reads a 1-9 byte long.
*
* @param position where in the buffer to start reading
* @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: ByteArray, optimizePositive: Boolean = false, position: Int = 0): Long {
var position = position
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 1L)
}
return result
}
/**
* FROM KRYO
*
*
* Writes a 1-9 byte long.
*
* @param position where in the buffer to start writing
* @param optimizePositive
* If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be inefficient (9
* bytes).
*
* @return the number of bytes written.
*/
fun writeLong(buffer: ByteArray, value: Long, optimizePositive: Boolean = false, position: Int = 0): Int {
var value = value
var position = position
if (!optimizePositive) {
value = value shl 1 xor (value shr 63)
}
if (value ushr 7 == 0L) {
buffer[position++] = value.toByte()
return 1
}
if (value ushr 14 == 0L) {
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7).toByte()
return 2
}
if (value ushr 21 == 0L) {
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7 or 0x80L).toByte()
buffer[position++] = (value ushr 14).toByte()
return 3
}
if (value ushr 28 == 0L) {
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7 or 0x80L).toByte()
buffer[position++] = (value ushr 14 or 0x80L).toByte()
buffer[position++] = (value ushr 21).toByte()
return 4
}
if (value ushr 35 == 0L) {
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7 or 0x80L).toByte()
buffer[position++] = (value ushr 14 or 0x80L).toByte()
buffer[position++] = (value ushr 21 or 0x80L).toByte()
buffer[position++] = (value ushr 28).toByte()
return 5
}
if (value ushr 42 == 0L) {
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7 or 0x80L).toByte()
buffer[position++] = (value ushr 14 or 0x80L).toByte()
buffer[position++] = (value ushr 21 or 0x80L).toByte()
buffer[position++] = (value ushr 28 or 0x80L).toByte()
buffer[position++] = (value ushr 35).toByte()
return 6
}
if (value ushr 49 == 0L) {
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7 or 0x80L).toByte()
buffer[position++] = (value ushr 14 or 0x80L).toByte()
buffer[position++] = (value ushr 21 or 0x80L).toByte()
buffer[position++] = (value ushr 28 or 0x80L).toByte()
buffer[position++] = (value ushr 35 or 0x80L).toByte()
buffer[position++] = (value ushr 42).toByte()
return 7
}
if (value ushr 56 == 0L) {
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7 or 0x80L).toByte()
buffer[position++] = (value ushr 14 or 0x80L).toByte()
buffer[position++] = (value ushr 21 or 0x80L).toByte()
buffer[position++] = (value ushr 28 or 0x80L).toByte()
buffer[position++] = (value ushr 35 or 0x80L).toByte()
buffer[position++] = (value ushr 42 or 0x80L).toByte()
buffer[position++] = (value ushr 49).toByte()
return 8
}
buffer[position++] = (value and 0x7FL or 0x80L).toByte()
buffer[position++] = (value ushr 7 or 0x80L).toByte()
buffer[position++] = (value ushr 14 or 0x80L).toByte()
buffer[position++] = (value ushr 21 or 0x80L).toByte()
buffer[position++] = (value ushr 28 or 0x80L).toByte()
buffer[position++] = (value ushr 35 or 0x80L).toByte()
buffer[position++] = (value ushr 42 or 0x80L).toByte()
buffer[position++] = (value ushr 49 or 0x80L).toByte()
buffer[position++] = (value ushr 56).toByte()
return 9
}
/**
* 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: ByteArray): Boolean {
val position = 0
return canReadLong(buffer, position)
}
/**
* FROM KRYO
*
*
* look at buffer, and see if we can read the length of the long off of it (from the reader index).
*
* @param position where in the buffer to start reading
* @return 0 if we could not read anything, >0 for the number of bytes for the long on the buffer
*/
private fun canReadLong(buffer: ByteArray, position: Int): Boolean {
var position = position
val limit = buffer.size
if (limit >= 9) {
return true
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == limit) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == limit) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == limit) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == limit) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == limit) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == limit) {
return false
}
if (buffer[position++].toInt() and 0x80 == 0) {
return true
}
if (position == limit) {
return false
}
return if (buffer[position++].toInt() and 0x80 == 0) {
true
} else position != limit
}
}