From 8010c47cab7ca2d0c2f217dde972300c5de3d692 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Mon, 1 Aug 2016 21:35:38 -0400 Subject: [PATCH] redo the bit reader shit --- bit.go | 117 --------------------------------------------- bit/reader.go | 52 ++++++++++++++++++++ bit/reader_test.go | 52 ++++++++++++++++++++ bit_test.go | 83 -------------------------------- glide.lock | 19 ++++++-- glide.yaml | 2 + message.go | 13 +---- 7 files changed, 123 insertions(+), 215 deletions(-) delete mode 100644 bit.go create mode 100644 bit/reader.go create mode 100644 bit/reader_test.go delete mode 100644 bit_test.go diff --git a/bit.go b/bit.go deleted file mode 100644 index 0e66d93..0000000 --- a/bit.go +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import ( - "fmt" - "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 -} - -func (b *bitBuffer) readByte() (out byte) { - if b.bits == 0 { - if b.index >= len(b.source) { - b.err = io.ErrUnexpectedEOF - return - } - out = b.source[b.index] - b.index += 1 - return - } - return byte(b.readBits(8)) -} - -func (b *bitBuffer) readBytes(n int) []byte { - if b.bits == 0 { - b.index += n - if b.index > len(b.source) { - b.err = io.ErrUnexpectedEOF - return []byte{} - } - return b.source[b.index-n : b.index] - } - buf := make([]byte, n) - for i := 0; i < n; i++ { - buf[i] = byte(b.readBits(8)) - } - return buf -} - -// readVarUint reads a variable-length uint32, encoded with some scheme that I -// can't find a standard for. first two bits are a length prefix, followed by a -// 4, 8, 12, or 32-bit wide uint. -func (b *bitBuffer) readVarUint() uint32 { - switch b.readBits(2) { - case 0: - return uint32(b.readBits(4)) - case 1: - return uint32(b.readBits(4) | b.readBits(4)<<4) - case 2: - return uint32(b.readBits(4) | b.readBits(8)<<4) - case 3: - return uint32(b.readBits(4) | b.readBits(28)<<4) - default: - // this switch is already exhaustive, the compiler just can't tell. - panic(fmt.Sprintf("invalid varuint prefix")) - } -} - -// readVarInt reads a varint-encoded value off of the front of the buffer. This -// is the varint encoding used in protobuf. That is: each byte utilizes a 7-bit -// group. the msb of each byte indicates whether there are more bytes to -// follow. -func (b *bitBuffer) readVarInt() uint64 { - var x, n uint64 - for shift := uint(0); shift < 64; shift += 7 { - n = b.readBits(8) - if n < 0x80 { - return x | n< r.bits { + b, err := r.src.ReadByte() + if err != nil { + r.err = err + return 0 + } + r.n <<= 8 + r.n |= uint64(b) + r.bits += 8 + } + n = (r.n >> (r.bits - bits)) & ((1 << bits) - 1) + r.bits -= bits + return +} + +func (r *Reader) Err() error { return r.err } diff --git a/bit/reader_test.go b/bit/reader_test.go new file mode 100644 index 0000000..4460c61 --- /dev/null +++ b/bit/reader_test.go @@ -0,0 +1,52 @@ +package bit + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + // 1000 1011 1010 1101 1111 0000 0000 1101 + badFood = []byte{0x8b, 0xad, 0xf0, 0x0d} +) + +func TestRead(t *testing.T) { + assert := assert.New(t) + + var r *Reader + + // aligned reading + r = NewBytesReader(badFood) + assert.Equal(uint64(0x8b), r.ReadBits(8)) + assert.Equal(uint64(0xad), r.ReadBits(8)) + assert.Equal(uint64(0xf0), r.ReadBits(8)) + assert.Equal(uint64(0x0d), r.ReadBits(8)) + + // misaligned reading + r = NewBytesReader(badFood) + + // 1000 1011 1010 1101 1111 0000 0000 1101 + // ^ + assert.Equal(uint64(0x01), r.ReadBits(1)) + + // 1000 1011 1010 1101 1111 0000 0000 1101 + // ^-^ + assert.Equal(uint64(0), r.ReadBits(3)) + + // 1000 1011 1010 1101 1111 0000 0000 1101 + // ^--^ + assert.Equal(uint64(0xb), r.ReadBits(4)) + + // 1000 1011 1010 1101 1111 0000 0000 1101 + // ^----^ + assert.Equal(uint64(0x15), r.ReadBits(5)) + + // 1000 1011 1010 1101 1111 0000 0000 1101 + // ^---------^ + assert.Equal(uint64(0x17c), r.ReadBits(9)) + + // 1000 1011 1010 1101 1111 0000 0000 1101 + // ^----------^ + assert.Equal(uint64(0xd), r.ReadBits(10)) +} diff --git a/bit_test.go b/bit_test.go deleted file mode 100644 index a8914f5..0000000 --- a/bit_test.go +++ /dev/null @@ -1,83 +0,0 @@ -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") - } - - buf = []byte{0x4} - bb = newBitBuffer(buf) - u := bb.readVarUint() - if u != 1 { - t.Errorf("feck. wanted %v, got %v", 1, u) - } - if bb.readBits(2); bb.err != nil { - t.Errorf("shouldn't have an error yet") - } - if bb.readBits(1); bb.err == nil { - t.Errorf("we should be at EOF now") - } - - buf = []byte{0x3c} - bb = newBitBuffer(buf) - u = bb.readVarUint() - if u != 15 { - t.Errorf("feck. wanted %v, got %v", 15, u) - } - if bb.readBits(2); bb.err != nil { - t.Errorf("shouldn't have an error yet") - } - if bb.readBits(1); bb.err == nil { - t.Errorf("we should be at EOF now") - } - - buf = []byte{0x48, 0x10} - // 0100 1000 0001 0000 - // 01 - prefix bits. indicates length 12 - // 00 10 - least significant four - // 00 0001 00 - most significant eight - // 00 - not read. - // - // 0000 0100 0010 - actual value (0x42, or 66) - bb = newBitBuffer(buf) - u = bb.readVarUint() - if u != 66 { - t.Errorf("feck. wanted %v, got %v", 66, u) - } - if bb.readBits(2); bb.err != nil { - t.Errorf("shouldn't have an error yet") - } - if bb.readBits(1); bb.err == nil { - t.Errorf("we should be at EOF now") - } -} diff --git a/glide.lock b/glide.lock index 2d333fd..9bcb723 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,23 @@ -hash: 9208b00dc0b7be6b23958e0642efb66c8584c0be1d97fd81a9de0df643ac2877 -updated: 2016-07-31T18:52:05.106198608-04:00 +hash: 3f7fbcf64c0749e5f78dc8188c594871ab368257d8a05f238cb2ff901d76f8f8 +updated: 2016-08-01T20:29:43.617478897-04:00 imports: - name: github.com/golang/protobuf version: c3cefd437628a0b7d31b34fe44b3a7a540e98527 subpackages: - proto + - protoc-gen-go/descriptor - name: github.com/golang/snappy version: d9eb7a3d35ec988b8585d4a0068e462c27d28380 -testImports: [] +- name: github.com/stretchr/testify + version: f390dcf405f7b83c997eac1b06768bb9f44dec18 + subpackages: + - assert +testImports: +- name: github.com/davecgh/go-spew + version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib diff --git a/glide.yaml b/glide.yaml index dc3b51c..3e4970d 100644 --- a/glide.yaml +++ b/glide.yaml @@ -2,3 +2,5 @@ package: github.com/jordanorelli/hyperstone import: - package: github.com/golang/protobuf - package: github.com/golang/snappy +- package: github.com/stretchr/testify + version: ^1.1.3 diff --git a/message.go b/message.go index 5fe7315..aa69865 100644 --- a/message.go +++ b/message.go @@ -42,18 +42,7 @@ func (m *message) check(dump bool) error { } if dump { - shit := packet.GetData()[:4] - bb := newBitBuffer(packet.GetData()) - type T struct { - t int32 - } - var v T - v.t = int32(bb.readVarUint()) - if bb.err != nil { - fmt.Printf("packet error: %v\n", bb.err) - } else { - fmt.Printf("{in: %d out: %d data: %v shit: %x}\n", packet.GetSequenceIn(), packet.GetSequenceOutAck(), v, shit) - } + fmt.Println("I broke packet dumping.") } return nil }