main.shark.internal.UnsortedByteEntries.kt Maven / Gradle / Ivy
package shark.internal
import shark.internal.aosp.ByteArrayTimSort
/**
* Wraps a byte array of entries where each entry is an id followed by bytes for the value.
* `id` is a long if [longIdentifiers] is true and an int otherwise. Each entry has [bytesPerValue]
* value bytes. Entries are appended into the array via [append]. Once done, the backing array
* is sorted and turned into a [SortedBytesMap] by calling [moveToSortedMap].
*/
internal class UnsortedByteEntries(
private val bytesPerValue: Int,
private val longIdentifiers: Boolean,
private val initialCapacity: Int = 4,
private val growthFactor: Double = 2.0
) {
private val bytesPerEntry = bytesPerValue + if (longIdentifiers) 8 else 4
private var entries: ByteArray? = null
private val subArray = MutableByteSubArray()
private var subArrayIndex = 0
private var assigned: Int = 0
private var currentCapacity = 0
fun append(
key: Long
): MutableByteSubArray {
if (entries == null) {
currentCapacity = initialCapacity
entries = ByteArray(currentCapacity * bytesPerEntry)
} else {
if (currentCapacity == assigned) {
val newCapacity = (currentCapacity * growthFactor).toInt()
growEntries(newCapacity)
currentCapacity = newCapacity
}
}
assigned++
subArrayIndex = 0
subArray.writeId(key)
return subArray
}
fun moveToSortedMap(): SortedBytesMap {
if (assigned == 0) {
return SortedBytesMap(longIdentifiers, bytesPerValue, ByteArray(0))
}
val entries = entries!!
// Sort entries by keys, which are ids of 4 or 8 bytes.
ByteArrayTimSort.sort(entries, 0, assigned, bytesPerEntry) {
entrySize, o1Array, o1Index, o2Array, o2Index ->
if (longIdentifiers) {
readLong(o1Array, o1Index * entrySize)
.compareTo(
readLong(o2Array, o2Index * entrySize)
)
} else {
readInt(o1Array, o1Index * entrySize)
.compareTo(
readInt(o2Array, o2Index * entrySize)
)
}
}
val sortedEntries = if (entries.size > assigned * bytesPerEntry) {
entries.copyOf(assigned * bytesPerEntry)
} else entries
this.entries = null
assigned = 0
return SortedBytesMap(
longIdentifiers, bytesPerValue, sortedEntries
)
}
private fun readInt(
array: ByteArray,
index: Int
): Int {
var pos = index
return (array[pos++] and 0xff shl 24
or (array[pos++] and 0xff shl 16)
or (array[pos++] and 0xff shl 8)
or (array[pos] and 0xff))
}
@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
private inline infix fun Byte.and(other: Long): Long = toLong() and other
@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
private inline infix fun Byte.and(other: Int): Int = toInt() and other
private fun readLong(
array: ByteArray,
index: Int
): Long {
var pos = index
return (array[pos++] and 0xffL shl 56
or (array[pos++] and 0xffL shl 48)
or (array[pos++] and 0xffL shl 40)
or (array[pos++] and 0xffL shl 32)
or (array[pos++] and 0xffL shl 24)
or (array[pos++] and 0xffL shl 16)
or (array[pos++] and 0xffL shl 8)
or (array[pos] and 0xffL))
}
private fun growEntries(newCapacity: Int) {
val newEntries = ByteArray(newCapacity * bytesPerEntry)
System.arraycopy(entries, 0, newEntries, 0, assigned * bytesPerEntry)
entries = newEntries
}
internal inner class MutableByteSubArray {
fun writeByte(value: Byte) {
val index = subArrayIndex
subArrayIndex++
require(index in 0..bytesPerEntry) {
"Index $index should be between 0 and $bytesPerEntry"
}
val valuesIndex = ((assigned - 1) * bytesPerEntry) + index
entries!![valuesIndex] = value
}
fun writeId(value: Long) {
if (longIdentifiers) {
writeLong(value)
} else {
writeInt(value.toInt())
}
}
fun writeInt(value: Int) {
val index = subArrayIndex
subArrayIndex += 4
require(index >= 0 && index <= bytesPerEntry - 4) {
"Index $index should be between 0 and ${bytesPerEntry - 4}"
}
var pos = ((assigned - 1) * bytesPerEntry) + index
val values = entries!!
values[pos++] = (value ushr 24 and 0xff).toByte()
values[pos++] = (value ushr 16 and 0xff).toByte()
values[pos++] = (value ushr 8 and 0xff).toByte()
values[pos] = (value and 0xff).toByte()
}
fun writeTruncatedLong(
value: Long,
byteCount: Int
) {
val index = subArrayIndex
subArrayIndex += byteCount
require(index >= 0 && index <= bytesPerEntry - byteCount) {
"Index $index should be between 0 and ${bytesPerEntry - byteCount}"
}
var pos = ((assigned - 1) * bytesPerEntry) + index
val values = entries!!
var shift = (byteCount - 1) * 8
while (shift >= 8) {
values[pos++] = (value ushr shift and 0xffL).toByte()
shift -= 8
}
values[pos] = (value and 0xffL).toByte()
}
fun writeLong(value: Long) {
val index = subArrayIndex
subArrayIndex += 8
require(index >= 0 && index <= bytesPerEntry - 8) {
"Index $index should be between 0 and ${bytesPerEntry - 8}"
}
var pos = ((assigned - 1) * bytesPerEntry) + index
val values = entries!!
values[pos++] = (value ushr 56 and 0xffL).toByte()
values[pos++] = (value ushr 48 and 0xffL).toByte()
values[pos++] = (value ushr 40 and 0xffL).toByte()
values[pos++] = (value ushr 32 and 0xffL).toByte()
values[pos++] = (value ushr 24 and 0xffL).toByte()
values[pos++] = (value ushr 16 and 0xffL).toByte()
values[pos++] = (value ushr 8 and 0xffL).toByte()
values[pos] = (value and 0xffL).toByte()
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy