lynon started

This commit is contained in:
Sergey Chernov 2025-07-11 05:55:29 +03:00
parent 5848adca61
commit 987b80e44d
6 changed files with 241 additions and 0 deletions

View File

@ -0,0 +1,61 @@
package net.sergeych.lynon
abstract class BitInput {
/**
* Return next byte, int in 0..255 range, or -1 if end of stream reached
*/
abstract fun getByte(): Int
private var accumulator = 0
var isEndOfStream: Boolean = false
private set
private var mask = 0
fun getBitOrNull(): Int? {
if (isEndOfStream) return null
if (mask == 0) {
accumulator = getByte()
if (accumulator == -1) {
isEndOfStream = true
return null
}
mask = 0x80
}
val result = if (0 == accumulator and mask) 0 else 1
mask = mask shr 1
return result
}
fun getBitsOrNull(count: Int): ULong? {
var result = 0UL
var mask = 1UL
for( i in 0 ..< count) {
when(getBitOrNull()) {
null -> return null
1 -> result = result or mask
0 -> {}
}
mask = mask shl 1
}
return result
}
fun getBits(count: Int): ULong {
return getBitsOrNull(count) ?: throw IllegalStateException("Unexpected end of stream")
}
fun unpackUnsigned(): ULong {
val tetrades = getBits(4).toInt()
var result = 0UL
var shift = 0
for (i in 0..<tetrades) {
result = result or (getBits(4) shl shift)
shift += 4
}
return result
}
}

View File

@ -0,0 +1,70 @@
package net.sergeych.lynon
abstract class BitOutput {
abstract fun outputByte(byte: UByte)
private var accumulator = 0
private var accumulatorBits = 0
fun putBits(bits: ULong, count: Int) {
require( count <= 64 )
var x = bits
for( i in 0 ..< count ) {
putBit( (x and 1u).toInt() )
x = x shr 1
}
}
fun putBits(bits: Int, count: Int) {
require( count <= 32 )
var x = bits
for( i in 0 ..< count ) {
putBit( (x and 1) )
x = x shr 1
}
}
fun putBit(bit: Int) {
accumulator = (accumulator shl 1) or bit
if( ++accumulatorBits >= 8 ) {
outputByte(accumulator.toUByte())
accumulator = accumulator shr 0
accumulatorBits = 0
}
}
fun packUnsigned(value: ULong) {
val tetrades = sizeInTetrades(value)
putBits(tetrades, 4)
var rest = value
for( i in 0..<tetrades ) {
putBits( rest and 0xFu, 4 )
rest = rest shr 4
}
}
@Suppress("unused")
fun putSigned(value: Long) {
if( value < 0 ) {
putBit(1)
packUnsigned((-value).toULong())
}
else {
putBit(0)
packUnsigned(value.toULong())
}
}
var isClosed = false
private set
fun close() {
if( !isClosed ) {
if (accumulatorBits > 0) {
while (accumulatorBits != 0) putBit(0)
}
isClosed = true
}
}
}

View File

@ -0,0 +1,14 @@
package net.sergeych.lynon
class MemoryBitInput(val packedBits: UByteArray): BitInput() {
private var index = 0
override fun getByte(): Int {
if( index < packedBits.size ) {
return packedBits[index++].toInt()
} else {
return -1
}
}
}

View File

@ -0,0 +1,14 @@
package net.sergeych.lynon
class MemoryBitOutput: BitOutput() {
private val buffer = mutableListOf<UByte>()
fun toUByteArray(): UByteArray {
close()
return buffer.toTypedArray().toUByteArray()
}
override fun outputByte(byte: UByte) {
buffer.add(byte)
}
}

View File

@ -0,0 +1,33 @@
package net.sergeych.lynon
/**
* Hoq many tetrades needed to store the value. It is faster to use this function
* than to use sizeInBits
*
* Size for 0 is 1
*/
fun sizeInTetrades(value: ULong): Int {
if( value == 0UL ) return 1
var size = 0
var rest = value
while( rest != 0UL ) {
size++
rest = rest shr 4
}
return size
}
/**
* How many bits needed to store the value. Size for 0 is 1,
*/
@Suppress("unused")
fun sizeInBits(value: ULong): Int {
if( value == 0UL ) return 1
var size = 0
var rest = value
while( rest != 0UL ) {
size++
rest = rest shr 1
}
return size
}

View File

@ -0,0 +1,49 @@
import junit.framework.TestCase.assertEquals
import net.sergeych.bintools.toDump
import net.sergeych.lynon.MemoryBitInput
import net.sergeych.lynon.MemoryBitOutput
import net.sergeych.lynon.sizeInTetrades
import kotlin.test.Test
class LynonTests {
@Test
fun testSizeInTetrades() {
assertEquals(1, sizeInTetrades(0u))
assertEquals(1, sizeInTetrades(1u))
assertEquals(1, sizeInTetrades(15u))
assertEquals(2, sizeInTetrades(16u))
assertEquals(2, sizeInTetrades(254u))
assertEquals(2, sizeInTetrades(255u))
assertEquals(3, sizeInTetrades(256u))
assertEquals(3, sizeInTetrades(257u))
}
@Test
fun testBitStreams() {
val bout = MemoryBitOutput()
bout.putBits(2, 3)
bout.putBits(1, 7)
bout.putBits( 197, 8)
bout.putBits( 3, 4)
bout.close()
val bin = MemoryBitInput(bout.toUByteArray())
assertEquals(2UL, bin.getBits(3))
assertEquals(1UL, bin.getBits(7))
assertEquals(197UL, bin.getBits(8))
assertEquals(3UL, bin.getBits(4))
}
@Test
fun testPackInteger() {
val bout = MemoryBitOutput()
bout.packUnsigned(147179UL)
bout.close()
println(bout.toUByteArray().toDump())
val bin = MemoryBitInput(bout.toUByteArray())
assertEquals(147179UL, bin.unpackUnsigned())
}
}