work on bit reader

This commit is contained in:
Alex Yatskov 2019-02-09 16:00:49 -08:00
parent 2581573252
commit a67b06a511
2 changed files with 79 additions and 48 deletions

View File

@ -6,8 +6,8 @@ import (
type BitReader struct { type BitReader struct {
reader io.Reader reader io.Reader
offset int bitOffset int
buffer [1]byte tailByte byte
} }
func NewBitReader(reader io.Reader) *BitReader { func NewBitReader(reader io.Reader) *BitReader {
@ -66,31 +66,65 @@ func (r *BitReader) ReadInt64(count int) (int64, error) {
} }
func (r *BitReader) ReadUint64(count int) (uint64, error) { func (r *BitReader) ReadUint64(count int) (uint64, error) {
var value uint64 buffer, bitOffset, err := r.readBytes(count)
for count > 0 { if err != nil {
bitOffset := r.offset % 8
bitsLeft := 8 - bitOffset
if bitsLeft == 8 {
if _, err := r.reader.Read(r.buffer[:]); err != nil {
return 0, err return 0, err
} }
var result uint64
remainder := count
for byteOffset := 0; remainder > 0; byteOffset++ {
bitsRead := 8 - bitOffset
if bitsRead > remainder {
bitsRead = remainder
} }
bitsRead := count bufferByte := buffer[byteOffset]
if bitsRead > bitsLeft { bufferByte >>= uint(bitOffset)
bitsRead = bitsLeft bufferByte &= ^(0xff << uint(bitsRead))
result |= (uint64(bufferByte) << uint(count-remainder))
remainder -= bitsRead
bitOffset = 0
} }
buffer := r.buffer[0] return result, nil
buffer <<= uint(bitOffset) }
buffer >>= (uint(bitOffset) + uint(bitsLeft-bitsRead))
func (r *BitReader) readBytes(count int) ([]byte, int, error) {
value <<= uint(bitsRead) if count == 0 {
value |= uint64(buffer) return nil, 0, nil
}
r.offset += bitsRead
count -= bitsRead var (
} bitOffsetInByte = r.bitOffset % 8
bitsLeftInByte = 8 - bitOffsetInByte
return value, nil bytesNeeded = 1
)
if bitsLeftInByte < count {
bitsOverrun := count - bitsLeftInByte
bytesNeeded += bitsOverrun / 8
if bitsOverrun%8 != 0 {
bytesNeeded++
}
}
buffer := make([]byte, bytesNeeded)
bufferToRead := buffer
if bitsLeftInByte < 8 {
buffer[0] = r.tailByte
bufferToRead = buffer[1:]
}
if _, err := io.ReadAtLeast(r.reader, bufferToRead, len(bufferToRead)); err != nil {
return nil, 0, err
}
r.bitOffset += count
r.tailByte = buffer[bytesNeeded-1]
return buffer, bitOffsetInByte, nil
} }

View File

@ -2,40 +2,37 @@ package streaming
import ( import (
"bytes" "bytes"
"fmt"
"testing" "testing"
) )
func TestBitReader(t *testing.T) { func TestBitReader(t *testing.T) {
data := []byte{ data := []byte{
0x69, // 01101001 0x01, // 00000001
0x96, // 10010110 0x23, // 00100011
0xf0, // 11110000 0x45, // 01000101
0xaa, // 10101010 0x67, // 01100111
0x00, // 00000000 0x89, // 01100111
0xff, // 11111111 0xAB, // 10101011
0xCD, // 11001101
0xEF, // 11101111
} }
r := NewBitReader(bytes.NewReader(data)) r := NewBitReader(bytes.NewReader(data))
readPass := func(c int, v uint64) { readPass := func(c int, v uint64) {
if value, err := r.ReadUint64(c); value != v || err != nil { if value, err := r.ReadUint64(c); value != v || err != nil {
t.Fail() fmt.Printf("%.16x (expected %.16x)\n", value, v)
}
}
readFail := func(c int) {
if value, err := r.ReadUint64(c); value != 0 || err == nil {
t.Fail() t.Fail()
} }
} }
readPass(0, 0x00) readPass(0, 0x00)
readPass(2, 0x01) readPass(8, 0x01)
readPass(2, 0x02) readPass(16, 0x4523)
readPass(3, 0x04) readPass(3, 0x67&0x07)
readPass(1, 0x01) readPass(13, 0x8967>>3)
readPass(12, 0x096f) readPass(13, 0xcdab&0x1fff)
readPass(8, 0x000a) readPass(2, (0xcdab>>13)&3)
readPass(20, 0x0a00ff) readPass(9, 0xefcdab>>15)
readFail(1)
} }