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