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