diff --git a/bit.go b/bit.go new file mode 100644 index 0000000..15532b6 --- /dev/null +++ b/bit.go @@ -0,0 +1,51 @@ +package main + +import ( + "io" +) + +// a bitBuffer is a buffer of raw data that is not necessarily byte-aligned, +// for performing bitwise reads and manipulation. The bulk of this source is +// adapted from the bitReader defined in the standard library bzip2 package. +type bitBuffer struct { + source []byte // the source data to be read. This slice is never modified + index int // position of the last byte read out of source + scratch uint64 // scratch register of bits worthy of manipulation + bits uint // bit width of the scratch register + err error // stored error +} + +func newBitBuffer(buf []byte) *bitBuffer { + return &bitBuffer{source: buf} +} + +func (b *bitBuffer) readBits(bits uint) uint64 { + for bits > b.bits { + if b.index >= len(b.source) { + b.err = io.ErrUnexpectedEOF + return 0 + } + b.scratch <<= 8 + b.scratch |= uint64(b.source[b.index]) + b.index += 1 + b.bits += 8 + } + + // b.scratch looks like this (assuming that b.bits = 14 and bits = 6): + // Bit: 111111 + // 5432109876543210 + // + // (6 bits, the desired output) + // |-----| + // V V + // 0101101101001110 + // ^ ^ + // |------------| + // b.bits (num valid bits) + // + // This the next line right shifts the desired bits into the + // least-significant places and masks off anything above. + n := (b.scratch >> (b.bits - bits)) & ((1 << bits) - 1) + b.bits -= bits + return n +} diff --git a/bit_test.go b/bit_test.go new file mode 100644 index 0000000..79d7a4f --- /dev/null +++ b/bit_test.go @@ -0,0 +1,37 @@ +package main + +import ( + "io" + "testing" +) + +func TestBits(t *testing.T) { + buf := []byte{0x00} + bb := newBitBuffer(buf) + for i := 0; i < 8; i++ { + if bb.readBits(1) != 0x00 { + t.Error("hahha what") + } + if bb.err != nil { + t.Errorf("oh weird error: %v", bb.err) + } + } + if bb.readBits(1) != 0x00 { + t.Error("hahha what") + } + if bb.err != io.ErrUnexpectedEOF { + t.Errorf("oh weird error: %v", bb.err) + } + + buf = []byte{0x10} + bb = newBitBuffer(buf) + if n := bb.readBits(4); n != 0x01 { + t.Errorf("shit. wanted %v, got %v", 0x01, n) + } + if n := bb.readBits(4); n != 0x00 { + t.Errorf("poop. wanted %v, got %v", 0x00, n) + } + if bb.err != nil { + t.Errorf("fuck") + } +} diff --git a/parser.go b/parser.go index ac5c30f..9039435 100644 --- a/parser.go +++ b/parser.go @@ -154,8 +154,10 @@ func (p *parser) readMessage() (*message, error) { if _, err := io.ReadFull(p.source, buf); err != nil { return nil, wrap(err, "readMessage couldn't read message body") } + // TODO: pool these! return &message{cmd, int64(tick), compressed, buf}, nil } + // TODO: pool these! return &message{cmd, int64(tick), compressed, nil}, nil }