feat(codec): add CodecException, ByteWriter and Crc32C
This commit is contained in:
1 parent
b54f41d3b5
commit
fccb54cd70
2 files changed
+127
No files matched your search
@@ -0,0 +1,113 @@
|
|||||||
|
package kestrel.store.codec
|
||||||
|
|
||||||
|
import kestrel.KestrelException
|
||||||
|
|
||||||
|
class CodecException(message: String) : KestrelException(message)
|
||||||
|
|
||||||
|
/** Growable little-endian-free byte sink. Multi-byte integers are written big-endian. */
|
||||||
|
class ByteWriter(initialCapacity: Int = 32) {
|
||||||
|
private var buffer = ByteArray(initialCapacity)
|
||||||
|
private var size = 0
|
||||||
|
|
||||||
|
val length: Int get() = size
|
||||||
|
|
||||||
|
private fun ensure(extra: Int) {
|
||||||
|
if (size + extra <= buffer.size) return
|
||||||
|
var newCapacity = buffer.size * 2
|
||||||
|
while (newCapacity < size + extra) newCapacity *= 2
|
||||||
|
buffer = buffer.copyOf(newCapacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeByte(value: Int): ByteWriter {
|
||||||
|
ensure(1)
|
||||||
|
buffer[size++] = value.toByte()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeBytes(bytes: ByteArray): ByteWriter {
|
||||||
|
ensure(bytes.size)
|
||||||
|
System.arraycopy(bytes, 0, buffer, size, bytes.size)
|
||||||
|
size += bytes.size
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFixedLong(value: Long): ByteWriter {
|
||||||
|
ensure(8)
|
||||||
|
for (i in 7 downTo 0) {
|
||||||
|
buffer[size++] = ((value ushr (i * 8)) and 0xFF).toByte()
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFixedDouble(value: Double): ByteWriter = writeFixedLong(java.lang.Double.doubleToLongBits(value))
|
||||||
|
|
||||||
|
/** Unsigned LEB128 varint. */
|
||||||
|
fun writeVarInt(value: Long): ByteWriter {
|
||||||
|
var remaining = value
|
||||||
|
while (true) {
|
||||||
|
val byte = (remaining and 0x7F).toInt()
|
||||||
|
remaining = remaining ushr 7
|
||||||
|
if (remaining == 0L) {
|
||||||
|
writeByte(byte)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
writeByte(byte or 0x80)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Zigzag-encoded signed varint, safe for negative numbers. */
|
||||||
|
fun writeZigZag(value: Long): ByteWriter = writeVarInt((value shl 1) xor (value shr 63))
|
||||||
|
|
||||||
|
fun writeLengthPrefixed(bytes: ByteArray): ByteWriter {
|
||||||
|
writeVarInt(bytes.size.toLong())
|
||||||
|
writeBytes(bytes)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toByteArray(): ByteArray = buffer.copyOf(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByteReader(private val data: ByteArray, private var position: Int = 0) {
|
||||||
|
val remaining: Int get() = data.size - position
|
||||||
|
|
||||||
|
fun hasRemaining(): Boolean = position < data.size
|
||||||
|
|
||||||
|
fun readByte(): Int {
|
||||||
|
if (position >= data.size) throw CodecException("unexpected end of buffer")
|
||||||
|
return data[position++].toInt() and 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readBytes(count: Int): ByteArray {
|
||||||
|
if (position + count > data.size) throw CodecException("unexpected end of buffer")
|
||||||
|
val slice = data.copyOfRange(position, position + count)
|
||||||
|
position += count
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFixedLong(): Long {
|
||||||
|
var result = 0L
|
||||||
|
repeat(8) { result = (result shl 8) or readByte().toLong() }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFixedDouble(): Double = java.lang.Double.longBitsToDouble(readFixedLong())
|
||||||
|
|
||||||
|
fun readVarInt(): Long {
|
||||||
|
var result = 0L
|
||||||
|
var shift = 0
|
||||||
|
while (true) {
|
||||||
|
val byte = readByte()
|
||||||
|
result = result or ((byte.toLong() and 0x7F) shl shift)
|
||||||
|
if (byte and 0x80 == 0) return result
|
||||||
|
shift += 7
|
||||||
|
if (shift > 63) throw CodecException("varint too long")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readZigZag(): Long {
|
||||||
|
val encoded = readVarInt()
|
||||||
|
return (encoded ushr 1) xor -(encoded and 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readLengthPrefixed(): ByteArray = readBytes(readVarInt().toInt())
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package kestrel.store.codec
|
||||||
|
|
||||||
|
import java.util.zip.CRC32C
|
||||||
|
|
||||||
|
/** Castagnoli CRC-32 checksums used to detect corruption of on-disk pages and records. */
|
||||||
|
object Crc32C {
|
||||||
|
fun of(bytes: ByteArray): Int = of(bytes, 0, bytes.size)
|
||||||
|
|
||||||
|
fun of(bytes: ByteArray, offset: Int, length: Int): Int {
|
||||||
|
val crc = CRC32C()
|
||||||
|
crc.update(bytes, offset, length)
|
||||||
|
return crc.value.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user