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