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