lynon started
This commit is contained in:
parent
5848adca61
commit
987b80e44d
61
lynglib/src/commonMain/kotlin/net/sergeych/lynon/BitInput.kt
Normal file
61
lynglib/src/commonMain/kotlin/net/sergeych/lynon/BitInput.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
49
lynglib/src/jvmTest/kotlin/LynonTests.kt
Normal file
49
lynglib/src/jvmTest/kotlin/LynonTests.kt
Normal 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user