From a2fcfd9ef4ea066815ad2b7fd90d45fec0489e5a Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Mon, 25 Mar 2024 10:45:04 +0100 Subject: [PATCH] byte vec & bit vec (bit set) --- src/main/kotlin/blitz/Bits.kt | 10 +++ src/main/kotlin/blitz/Endian.kt | 39 +++++++++ src/main/kotlin/blitz/collections/Batched.kt | 33 ++++++++ src/main/kotlin/blitz/collections/BitVec.kt | 66 ++++++++++++++++ .../blitz/collections/ByteArrayUtils.kt | 5 ++ src/main/kotlin/blitz/collections/ByteVec.kt | 79 +++++++++++++++++++ .../kotlin/blitz/collections/IterUtils.kt | 10 +++ src/main/kotlin/blitz/collections/Vec.kt | 31 ++++++++ 8 files changed, 273 insertions(+) create mode 100644 src/main/kotlin/blitz/Bits.kt create mode 100644 src/main/kotlin/blitz/Endian.kt create mode 100644 src/main/kotlin/blitz/collections/BitVec.kt create mode 100644 src/main/kotlin/blitz/collections/ByteArrayUtils.kt create mode 100644 src/main/kotlin/blitz/collections/ByteVec.kt create mode 100644 src/main/kotlin/blitz/collections/IterUtils.kt create mode 100644 src/main/kotlin/blitz/collections/Vec.kt diff --git a/src/main/kotlin/blitz/Bits.kt b/src/main/kotlin/blitz/Bits.kt new file mode 100644 index 0000000..a9f1b5c --- /dev/null +++ b/src/main/kotlin/blitz/Bits.kt @@ -0,0 +1,10 @@ +package blitz + +fun Byte.toBool(): Boolean = + (this.toInt() != 0) + +fun Boolean.toByte(): Byte = + if (this) 1 else 0 + +fun Boolean.toBit2(): Char = + if (this) '1' else '0' \ No newline at end of file diff --git a/src/main/kotlin/blitz/Endian.kt b/src/main/kotlin/blitz/Endian.kt new file mode 100644 index 0000000..6bd3212 --- /dev/null +++ b/src/main/kotlin/blitz/Endian.kt @@ -0,0 +1,39 @@ +package blitz + +enum class Endian { + LITTLE, + BIG + ; + + infix fun encodeLittle(little: ByteArray) = + if (this == BIG) little.reversedArray() + else little +} + +fun Long.toBytes(endian: Endian) = + endian encodeLittle + toInt().toBytes(Endian.LITTLE) + + toInt().shr(32).toBytes(Endian.LITTLE) + +fun Int.toBytes(endian: Endian) = + endian encodeLittle byteArrayOf( + this.and(0xFF).toByte(), + this.shr(8).and(0xFF).toByte(), + this.shr(16).and(0xFF).toByte(), + this.shr(24).and(0xFF).toByte() + ) + +fun UInt.toBytes(endian: Endian) = + toInt().toBytes(endian) + +fun Short.toBytes(endian: Endian) = + endian encodeLittle toInt().toBytes(Endian.LITTLE).copyOf(2) + +fun UShort.toBytes(endian: Endian) = + toShort().toBytes(endian) + +fun Byte.toBytes() = + byteArrayOf(this) + +fun UByte.toBytes() = + toByte().toBytes() \ No newline at end of file diff --git a/src/main/kotlin/blitz/collections/Batched.kt b/src/main/kotlin/blitz/collections/Batched.kt index c9f30b6..5c424ff 100644 --- a/src/main/kotlin/blitz/collections/Batched.kt +++ b/src/main/kotlin/blitz/collections/Batched.kt @@ -15,6 +15,39 @@ interface ByteBatchIterator: BatchIterator { fun nextBytes(dest: ByteArray): Int } +fun BatchIterator.asByteBatchIterator(): ByteBatchIterator = + object : ByteBatchIterator { + override fun nextBytes(dest: ByteArray): Int { + val temp = Array(dest.size) { 0 } + val am = this@asByteBatchIterator.next(temp) + temp.copyInto(dest) + return am + } + + override fun next(dest: Array): Int = + this@asByteBatchIterator.next(dest) + + override fun next(limit: Int): List = + this@asByteBatchIterator.next(limit) + + override fun next(dest: MutableList, limit: Int) = + this@asByteBatchIterator.next(dest, limit) + + override fun nextBytes(limit: Int): ByteArray { + val temp = Array(limit) { 0 } + val am = this@asByteBatchIterator.next(temp) + val o = ByteArray(am) + temp.copyInto(o) + return o + } + + override fun next(): Byte = + this@asByteBatchIterator.next() + + override fun hasNext(): Boolean = + this@asByteBatchIterator.hasNext() + } + interface BatchSequence: Sequence { override fun iterator(): BatchIterator } diff --git a/src/main/kotlin/blitz/collections/BitVec.kt b/src/main/kotlin/blitz/collections/BitVec.kt new file mode 100644 index 0000000..c0576c8 --- /dev/null +++ b/src/main/kotlin/blitz/collections/BitVec.kt @@ -0,0 +1,66 @@ +package blitz.collections + +import blitz.toBit2 +import blitz.toBool +import blitz.toByte +import kotlin.math.ceil + +// TODO: make it hybrid to a real bitset if a lot of elements + +class BitVec private constructor( + private val byteVec: ByteVec +): Vec { + constructor(initCap: Int = 0): this(ByteVec(initCap)) + + override val size: Int + get() = byteVec.size + + // TODO: implement better + fun toBytes(): ByteArray = + toString() + .padEnd(ceil(byteVec.size.toFloat() / 8).toInt(), '0') + .chunked(8) + .map { it.toByte(2) } + .toByteArray() + + override fun flip() = + byteVec.flip() + + override fun copy(): Vec = + BitVec(byteVec) + + override fun reserve(amount: Int) = + byteVec.reserve(amount) + + override fun popBack(): Boolean = + byteVec.popBack().toBool() + + override fun pushBack(elem: Boolean) = + byteVec.pushBack(elem.toByte()) + + override fun get(index: Int): Boolean = + byteVec[index].toBool() + + override fun iterator(): Iterator = + byteVec.iterator().mapModifier { it.toBool() } + + override fun toString(): String = + joinToString(separator = "") { it.toBit2().toString() } + + override fun set(index: Int, value: Boolean) { + byteVec[index] = value.toByte() + } + + companion object { + // TODO: implement better + fun from(bytes: ByteArray): BitVec = + BitVec(ByteVec.from( + bytes.asSequence() + .map { it + .toString(2) + .map { c -> (c == '1').toByte() } + } + .flatten() + )) + } +} \ No newline at end of file diff --git a/src/main/kotlin/blitz/collections/ByteArrayUtils.kt b/src/main/kotlin/blitz/collections/ByteArrayUtils.kt new file mode 100644 index 0000000..29e33ee --- /dev/null +++ b/src/main/kotlin/blitz/collections/ByteArrayUtils.kt @@ -0,0 +1,5 @@ +package blitz.collections + +fun Array.copyInto(dest: ByteArray) { + forEachIndexed { i, byte -> dest[i] = byte } +} \ No newline at end of file diff --git a/src/main/kotlin/blitz/collections/ByteVec.kt b/src/main/kotlin/blitz/collections/ByteVec.kt new file mode 100644 index 0000000..59dbf14 --- /dev/null +++ b/src/main/kotlin/blitz/collections/ByteVec.kt @@ -0,0 +1,79 @@ +package blitz.collections + +class ByteVec(initCap: Int = 0): Vec, ByteBatchSequence { + override var size = 0 + private var cap = initCap + private var array = ByteArray(initCap) + + fun copyAsArray(): ByteArray = + array.copyOfRange(0, size) + + fun copyIntoArray(arr: ByteArray, destOff: Int = 0, startOff: Int = 0) = + array.copyInto(arr, destOff, startOff, size) + + override fun copy(): ByteVec = + ByteVec(size).also { + copyIntoArray(it.array) + } + + override fun reserve(amount: Int) { + if (amount > 0 && cap - size >= amount) + return + array = array.copyOf(size + amount) + cap = size + amount + } + + override fun popBack(): Byte = + array[size - 1].also { + reserve(-1) + size -- + } + + fun popBack(dest: ByteArray, destOff: Int = 0) { + copyIntoArray(dest, destOff, size - dest.size) + reserve(-dest.size) + size -= dest.size + } + + override fun get(index: Int): Byte = + array[index] + + override fun flip() { + array = array.reversedArray() + } + + fun pushBack(arr: ByteArray) { + reserve(arr.size) + arr.copyInto(array, size) + size += arr.size + } + + override fun pushBack(elem: Byte) { + reserve(8) + array[size] = elem + size ++ + } + + override fun iterator(): ByteBatchIterator = + array.asSequence().asBatch().iterator().asByteBatchIterator() + + override fun toString(): String = + joinToString(prefix = "[", postfix = "]") { "0x${it.toUByte().toString(16)}" } + + override fun set(index: Int, value: Byte) { + array[index] = value + } + + companion object { + fun from(bytes: ByteArray) = + ByteVec(bytes.size).also { + bytes.copyInto(it.array) + it.size += bytes.size + } + + fun from(bytes: Sequence) = + ByteVec().also { bv -> + bytes.forEach(bv::pushBack) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/blitz/collections/IterUtils.kt b/src/main/kotlin/blitz/collections/IterUtils.kt new file mode 100644 index 0000000..bcf56f3 --- /dev/null +++ b/src/main/kotlin/blitz/collections/IterUtils.kt @@ -0,0 +1,10 @@ +package blitz.collections + +fun Iterator.mapModifier(fn: (T) -> R): Iterator = + object : Iterator { + override fun next(): R = + fn(this@mapModifier.next()) + + override fun hasNext(): Boolean = + this@mapModifier.hasNext() + } \ No newline at end of file diff --git a/src/main/kotlin/blitz/collections/Vec.kt b/src/main/kotlin/blitz/collections/Vec.kt new file mode 100644 index 0000000..075ce8f --- /dev/null +++ b/src/main/kotlin/blitz/collections/Vec.kt @@ -0,0 +1,31 @@ +package blitz.collections + +interface Vec: IndexableSequence { + val size: Int + + fun flip() + + fun copy(): Vec + + fun reserve(amount: Int) + + fun pushBack(elem: T) + fun pushBack(elems: Array) { + reserve(elems.size) + elems.forEach(::pushBack) + } + fun pushBack(elems: Collection) { + reserve(elems.size) + elems.forEach(::pushBack) + } + + fun popBack(): T + fun popBack(dest: Array) { + var writer = 0 + repeat(dest.size) { + dest[writer ++] = popBack() + } + } + + operator fun set(index: Int, value: T) +} \ No newline at end of file