diff options
-rw-r--r-- | formats/idxfile/decoder.go | 23 | ||||
-rw-r--r-- | formats/idxfile/encoder.go | 24 | ||||
-rw-r--r-- | formats/index/decoder.go | 107 | ||||
-rw-r--r-- | formats/packfile/scanner.go | 126 | ||||
-rw-r--r-- | utils/binary/read.go | 124 | ||||
-rw-r--r-- | utils/binary/read_test.go | 87 | ||||
-rw-r--r-- | utils/binary/write.go | 30 | ||||
-rw-r--r-- | utils/binary/writer_test.go | 43 |
8 files changed, 340 insertions, 224 deletions
diff --git a/formats/idxfile/decoder.go b/formats/idxfile/decoder.go index 57b508d..884d32b 100644 --- a/formats/idxfile/decoder.go +++ b/formats/idxfile/decoder.go @@ -2,11 +2,11 @@ package idxfile import ( "bytes" - "encoding/binary" "errors" "io" "gopkg.in/src-d/go-git.v4/core" + "gopkg.in/src-d/go-git.v4/utils/binary" ) var ( @@ -70,7 +70,7 @@ func validateHeader(r io.Reader) error { } func readVersion(idx *Idxfile, r io.Reader) error { - v, err := readInt32(r) + v, err := binary.ReadUint32(r) if err != nil { return err } @@ -80,21 +80,19 @@ func readVersion(idx *Idxfile, r io.Reader) error { } idx.Version = v - return nil } func readFanout(idx *Idxfile, r io.Reader) error { var err error - for i := 0; i < 255; i++ { - idx.Fanout[i], err = readInt32(r) + idx.Fanout[i], err = binary.ReadUint32(r) if err != nil { return err } } - idx.ObjectCount, err = readInt32(r) + idx.ObjectCount, err = binary.ReadUint32(r) return err } @@ -115,7 +113,7 @@ func readObjectNames(idx *Idxfile, r io.Reader) error { func readCRC32(idx *Idxfile, r io.Reader) error { c := int(idx.ObjectCount) for i := 0; i < c; i++ { - if err := binary.Read(r, binary.BigEndian, &idx.Entries[i].CRC32); err != nil { + if err := binary.Read(r, &idx.Entries[i].CRC32); err != nil { return err } } @@ -126,7 +124,7 @@ func readCRC32(idx *Idxfile, r io.Reader) error { func readOffsets(idx *Idxfile, r io.Reader) error { c := int(idx.ObjectCount) for i := 0; i < c; i++ { - o, err := readInt32(r) + o, err := binary.ReadUint32(r) if err != nil { return err } @@ -148,12 +146,3 @@ func readChecksums(idx *Idxfile, r io.Reader) error { return nil } - -func readInt32(r io.Reader) (uint32, error) { - var v uint32 - if err := binary.Read(r, binary.BigEndian, &v); err != nil { - return 0, err - } - - return v, nil -} diff --git a/formats/idxfile/encoder.go b/formats/idxfile/encoder.go index 0fe9ae6..164414a 100644 --- a/formats/idxfile/encoder.go +++ b/formats/idxfile/encoder.go @@ -2,13 +2,11 @@ package idxfile import ( "crypto/sha1" - "encoding/binary" - "fmt" "hash" "io" "sort" - "gopkg.in/src-d/go-git.v4/core" + "gopkg.in/src-d/go-git.v4/utils/binary" ) // An Encoder writes idx files to an output stream. @@ -56,13 +54,13 @@ func (e *Encoder) encodeHeader(idx *Idxfile) (int, error) { return c, err } - return c + 4, e.writeInt32(idx.Version) + return c + 4, binary.WriteUint32(e, idx.Version) } func (e *Encoder) encodeFanout(idx *Idxfile) (int, error) { fanout := idx.calculateFanout() for _, c := range fanout { - if err := e.writeInt32(c); err != nil { + if err := binary.WriteUint32(e, c); err != nil { return 0, err } } @@ -71,31 +69,23 @@ func (e *Encoder) encodeFanout(idx *Idxfile) (int, error) { } func (e *Encoder) encodeHashes(idx *Idxfile) (int, error) { - repet := make(map[core.Hash]int, 0) - sz := 0 for _, ent := range idx.Entries { i, err := e.Write(ent.Hash[:]) sz += i - repet[ent.Hash]++ if err != nil { return sz, err } } - for h, c := range repet { - if c > 1 { - fmt.Println(h, c) - } - } return sz, nil } func (e *Encoder) encodeCRC32(idx *Idxfile) (int, error) { sz := 0 for _, ent := range idx.Entries { - err := binary.Write(e, binary.BigEndian, ent.CRC32) + err := binary.Write(e, ent.CRC32) sz += 4 if err != nil { @@ -109,7 +99,7 @@ func (e *Encoder) encodeCRC32(idx *Idxfile) (int, error) { func (e *Encoder) encodeOffsets(idx *Idxfile) (int, error) { sz := 0 for _, ent := range idx.Entries { - if err := e.writeInt32(uint32(ent.Offset)); err != nil { + if err := binary.WriteUint32(e, uint32(ent.Offset)); err != nil { return sz, err } @@ -133,10 +123,6 @@ func (e *Encoder) encodeChecksums(idx *Idxfile) (int, error) { return 40, nil } -func (e *Encoder) writeInt32(value uint32) error { - return binary.Write(e, binary.BigEndian, value) -} - type EntryList []Entry func (p EntryList) Len() int { return len(p) } diff --git a/formats/index/decoder.go b/formats/index/decoder.go index 9bb25dd..8e37fd1 100644 --- a/formats/index/decoder.go +++ b/formats/index/decoder.go @@ -2,7 +2,6 @@ package index import ( "bytes" - "encoding/binary" "errors" "io" "io/ioutil" @@ -10,6 +9,7 @@ import ( "time" "gopkg.in/src-d/go-git.v4/core" + "gopkg.in/src-d/go-git.v4/utils/binary" ) var ( @@ -50,14 +50,14 @@ func NewDecoder(r io.Reader) *Decoder { // Decode reads the whole index object from its input and stores it in the // value pointed to by idx. func (d *Decoder) Decode(idx *Index) error { - version, err := validateHeader(d.r) + var err error + idx.Version, err = validateHeader(d.r) if err != nil { return err } - idx.Version = version - - if err := binary.Read(d.r, binary.BigEndian, &idx.EntryCount); err != nil { + idx.EntryCount, err = binary.ReadUint32(d.r) + if err != nil { return err } @@ -101,7 +101,7 @@ func (d *Decoder) readEntry(idx *Index) (*Entry, error) { &e.Flags, } - if err := readBinary(d.r, flow...); err != nil { + if err := binary.Read(d.r, flow...); err != nil { return nil, err } @@ -111,8 +111,8 @@ func (d *Decoder) readEntry(idx *Index) (*Entry, error) { e.Stage = Stage(e.Flags>>12) & 0x3 if e.Flags&EntryExtended != 0 { - var extended uint16 - if err := readBinary(d.r, &extended); err != nil { + extended, err := binary.ReadUint16(d.r) + if err != nil { return nil, err } @@ -150,7 +150,7 @@ func (d *Decoder) readEntryName(idx *Index, e *Entry) error { } func (d *Decoder) doReadEntryNameV4() (string, error) { - l, err := readVariableWidthInt(d.r) + l, err := binary.ReadVariableWidthInt(d.r) if err != nil { return "", err } @@ -160,7 +160,7 @@ func (d *Decoder) doReadEntryNameV4() (string, error) { base = d.lastEntry.Name[:len(d.lastEntry.Name)-int(l)] } - name, err := readUntil(d.r, '\x00') + name, err := binary.ReadUntil(d.r, '\x00') if err != nil { return "", err } @@ -172,7 +172,7 @@ func (d *Decoder) doReadEntryName(e *Entry) (string, error) { pLen := e.Flags & nameMask name := make([]byte, int64(pLen)) - if err := binary.Read(d.r, binary.BigEndian, &name); err != nil { + if err := binary.Read(d.r, &name); err != nil { return "", err } @@ -217,8 +217,8 @@ func (d *Decoder) readExtension(idx *Index) error { return err } - var len uint32 - if err := binary.Read(d.r, binary.BigEndian, &len); err != nil { + len, err := binary.ReadUint32(d.r) + if err != nil { return err } @@ -254,7 +254,8 @@ func validateHeader(r io.Reader) (version uint32, err error) { return 0, ErrMalformedSignature } - if err := binary.Read(r, binary.BigEndian, &version); err != nil { + version, err = binary.ReadUint32(r) + if err != nil { return 0, err } @@ -291,14 +292,14 @@ func (d *treeExtensionDecoder) Decode(t *Tree) error { func (d *treeExtensionDecoder) readEntry() (*TreeEntry, error) { e := &TreeEntry{} - path, err := readUntil(d.r, '\x00') + path, err := binary.ReadUntil(d.r, '\x00') if err != nil { return nil, err } e.Path = string(path) - count, err := readUntil(d.r, ' ') + count, err := binary.ReadUntil(d.r, ' ') if err != nil { return nil, err } @@ -315,7 +316,7 @@ func (d *treeExtensionDecoder) readEntry() (*TreeEntry, error) { } e.Entries = i - trees, err := readUntil(d.r, '\n') + trees, err := binary.ReadUntil(d.r, '\n') if err != nil { return nil, err } @@ -327,7 +328,7 @@ func (d *treeExtensionDecoder) readEntry() (*TreeEntry, error) { e.Trees = i - if err := binary.Read(d.r, binary.BigEndian, &e.Hash); err != nil { + if err := binary.Read(d.r, &e.Hash); err != nil { return nil, err } @@ -358,7 +359,7 @@ func (d *resolveUndoDecoder) readEntry() (*ResolveUndoEntry, error) { Stages: make(map[Stage]core.Hash, 0), } - path, err := readUntil(d.r, '\x00') + path, err := binary.ReadUntil(d.r, '\x00') if err != nil { return nil, err } @@ -373,7 +374,7 @@ func (d *resolveUndoDecoder) readEntry() (*ResolveUndoEntry, error) { for s := range e.Stages { var hash core.Hash - if err := binary.Read(d.r, binary.BigEndian, hash[:]); err != nil { + if err := binary.Read(d.r, hash[:]); err != nil { return nil, err } @@ -384,7 +385,7 @@ func (d *resolveUndoDecoder) readEntry() (*ResolveUndoEntry, error) { } func (d *resolveUndoDecoder) readStage(e *ResolveUndoEntry, s Stage) error { - ascii, err := readUntil(d.r, '\x00') + ascii, err := binary.ReadUntil(d.r, '\x00') if err != nil { return err } @@ -400,67 +401,3 @@ func (d *resolveUndoDecoder) readStage(e *ResolveUndoEntry, s Stage) error { return nil } - -func readBinary(r io.Reader, data ...interface{}) error { - for _, v := range data { - err := binary.Read(r, binary.BigEndian, v) - if err != nil { - return err - } - } - - return nil -} - -func readUntil(r io.Reader, delim byte) ([]byte, error) { - var buf [1]byte - value := make([]byte, 0, 16) - for { - if _, err := r.Read(buf[:]); err != nil { - if err == io.EOF { - return nil, err - } - - return nil, err - } - - if buf[0] == delim { - return value, nil - } - - value = append(value, buf[0]) - } -} - -// dheader[pos] = ofs & 127; -// while (ofs >>= 7) -// dheader[--pos] = 128 | (--ofs & 127); -// -func readVariableWidthInt(r io.Reader) (int64, error) { - var c byte - if err := readBinary(r, &c); err != nil { - return 0, err - } - - var v = int64(c & maskLength) - for moreBytesInLength(c) { - v++ - if err := readBinary(r, &c); err != nil { - return 0, err - } - - v = (v << lengthBits) + int64(c&maskLength) - } - - return v, nil -} - -const ( - maskContinue = uint8(128) // 1000 000 - maskLength = uint8(127) // 0111 1111 - lengthBits = uint8(7) // subsequent bytes has 7 bits to store the length -) - -func moreBytesInLength(c byte) bool { - return c&maskContinue > 0 -} diff --git a/formats/packfile/scanner.go b/formats/packfile/scanner.go index a5e4215..69cc7d0 100644 --- a/formats/packfile/scanner.go +++ b/formats/packfile/scanner.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "compress/zlib" - "encoding/binary" "fmt" "hash" "hash/crc32" @@ -12,6 +11,7 @@ import ( "io/ioutil" "gopkg.in/src-d/go-git.v4/core" + "gopkg.in/src-d/go-git.v4/utils/binary" ) var ( @@ -129,7 +129,7 @@ func (s *Scanner) isValidSignature(sig []byte) bool { // readVersion reads and returns the version field of a packfile. func (s *Scanner) readVersion() (uint32, error) { - return s.readInt32() + return binary.ReadUint32(s.r) } // isSupportedVersion returns whether version v is supported by the parser. @@ -140,17 +140,7 @@ func (s *Scanner) isSupportedVersion(v uint32) bool { // readCount reads and returns the count of objects field of a packfile. func (s *Scanner) readCount() (uint32, error) { - return s.readInt32() -} - -// ReadInt32 reads 4 bytes and returns them as a Big Endian int32. -func (s *Scanner) readInt32() (uint32, error) { - var v uint32 - if err := binary.Read(s.r, binary.BigEndian, &v); err != nil { - return 0, err - } - - return v, nil + return binary.ReadUint32(s.r) } // NextObjectHeader returns the ObjectHeader for the next object in the reader @@ -177,15 +167,15 @@ func (s *Scanner) NextObjectHeader() (*ObjectHeader, error) { switch h.Type { case core.OFSDeltaObject: - no, err := s.readNegativeOffset() + no, err := binary.ReadVariableWidthInt(s.r) if err != nil { return nil, err } - h.OffsetReference = h.Offset + no + h.OffsetReference = h.Offset - no case core.REFDeltaObject: var err error - h.Reference, err = s.readHash() + h.Reference, err = binary.ReadHash(s.r) if err != nil { return nil, err } @@ -240,10 +230,19 @@ func (s *Scanner) readObjectTypeAndLength() (core.ObjectType, int64, error) { return t, l, err } +const ( + maskType = uint8(112) // 0111 0000 + maskFirstLength = uint8(15) // 0000 1111 + maskContinue = uint8(128) // 1000 000 + firstLengthBits = uint8(4) // the first byte has 4 bits to store the length + maskLength = uint8(127) // 0111 1111 + lengthBits = uint8(7) // subsequent bytes has 7 bits to store the length +) + func (s *Scanner) readType() (core.ObjectType, byte, error) { var c byte var err error - if c, err = s.readByte(); err != nil { + if c, err = s.r.ReadByte(); err != nil { return core.ObjectType(0), 0, err } @@ -252,6 +251,10 @@ func (s *Scanner) readType() (core.ObjectType, byte, error) { return typ, c, nil } +func parseType(b byte) core.ObjectType { + return core.ObjectType((b & maskType) >> firstLengthBits) +} + // the length is codified in the last 4 bits of the first byte and in // the last 7 bits of subsequent bytes. Last byte has a 0 MSB. func (s *Scanner) readLength(first byte) (int64, error) { @@ -260,8 +263,8 @@ func (s *Scanner) readLength(first byte) (int64, error) { c := first shift := firstLengthBits var err error - for moreBytesInLength(c) { - if c, err = s.readByte(); err != nil { + for c&maskContinue > 0 { + if c, err = s.r.ReadByte(); err != nil { return 0, err } @@ -324,73 +327,7 @@ func (s *Scanner) Checksum() (core.Hash, error) { return core.ZeroHash, err } - return s.readHash() -} - -// ReadHash reads a hash. -func (s *Scanner) readHash() (core.Hash, error) { - var h core.Hash - if _, err := io.ReadFull(s.r, h[:]); err != nil { - return core.ZeroHash, err - } - - return h, nil -} - -// ReadNegativeOffset reads and returns an offset from a OFS DELTA -// object entry in a packfile. OFS DELTA offsets are specified in Git -// VLQ special format: -// -// Ordinary VLQ has some redundancies, example: the number 358 can be -// encoded as the 2-octet VLQ 0x8166 or the 3-octet VLQ 0x808166 or the -// 4-octet VLQ 0x80808166 and so forth. -// -// To avoid these redundancies, the VLQ format used in Git removes this -// prepending redundancy and extends the representable range of shorter -// VLQs by adding an offset to VLQs of 2 or more octets in such a way -// that the lowest possible value for such an (N+1)-octet VLQ becomes -// exactly one more than the maximum possible value for an N-octet VLQ. -// In particular, since a 1-octet VLQ can store a maximum value of 127, -// the minimum 2-octet VLQ (0x8000) is assigned the value 128 instead of -// 0. Conversely, the maximum value of such a 2-octet VLQ (0xff7f) is -// 16511 instead of just 16383. Similarly, the minimum 3-octet VLQ -// (0x808000) has a value of 16512 instead of zero, which means -// that the maximum 3-octet VLQ (0xffff7f) is 2113663 instead of -// just 2097151. And so forth. -// -// This is how the offset is saved in C: -// -// dheader[pos] = ofs & 127; -// while (ofs >>= 7) -// dheader[--pos] = 128 | (--ofs & 127); -// -func (s *Scanner) readNegativeOffset() (int64, error) { - var c byte - var err error - - if c, err = s.readByte(); err != nil { - return 0, err - } - - var offset = int64(c & maskLength) - for moreBytesInLength(c) { - offset++ - if c, err = s.readByte(); err != nil { - return 0, err - } - offset = (offset << lengthBits) + int64(c&maskLength) - } - - return -offset, nil -} - -func (s *Scanner) readByte() (byte, error) { - b, err := s.r.ReadByte() - if err != nil { - return 0, err - } - - return b, err + return binary.ReadHash(s.r) } // Close reads the reader until io.EOF @@ -399,23 +336,6 @@ func (s *Scanner) Close() error { return err } -func moreBytesInLength(c byte) bool { - return c&maskContinue > 0 -} - -var ( - maskContinue = uint8(128) // 1000 0000 - maskType = uint8(112) // 0111 0000 - maskFirstLength = uint8(15) // 0000 1111 - firstLengthBits = uint8(4) // the first byte has 4 bits to store the length - maskLength = uint8(127) // 0111 1111 - lengthBits = uint8(7) // subsequent bytes has 7 bits to store the length -) - -func parseType(b byte) core.ObjectType { - return core.ObjectType((b & maskType) >> firstLengthBits) -} - type trackableReader struct { count int64 io.Reader diff --git a/utils/binary/read.go b/utils/binary/read.go new file mode 100644 index 0000000..90ae41f --- /dev/null +++ b/utils/binary/read.go @@ -0,0 +1,124 @@ +// Package binary implements sintax-sugar functions on top of the standard +// library binary package +package binary + +import ( + "encoding/binary" + "io" + + "gopkg.in/src-d/go-git.v4/core" +) + +// Read reads structured binary data from r into data. Bytes are read and +// decoded in BigEndian order +// https://golang.org/pkg/encoding/binary/#Read +func Read(r io.Reader, data ...interface{}) error { + for _, v := range data { + if err := binary.Read(r, binary.BigEndian, v); err != nil { + return err + } + } + + return nil +} + +// ReadUntil reads from r untin delim is found +func ReadUntil(r io.Reader, delim byte) ([]byte, error) { + var buf [1]byte + value := make([]byte, 0, 16) + for { + if _, err := r.Read(buf[:]); err != nil { + if err == io.EOF { + return nil, err + } + + return nil, err + } + + if buf[0] == delim { + return value, nil + } + + value = append(value, buf[0]) + } +} + +// ReadVariableWidthInt reads and returns an int in Git VLQ special format: +// +// Ordinary VLQ has some redundancies, example: the number 358 can be +// encoded as the 2-octet VLQ 0x8166 or the 3-octet VLQ 0x808166 or the +// 4-octet VLQ 0x80808166 and so forth. +// +// To avoid these redundancies, the VLQ format used in Git removes this +// prepending redundancy and extends the representable range of shorter +// VLQs by adding an offset to VLQs of 2 or more octets in such a way +// that the lowest possible value for such an (N+1)-octet VLQ becomes +// exactly one more than the maximum possible value for an N-octet VLQ. +// In particular, since a 1-octet VLQ can store a maximum value of 127, +// the minimum 2-octet VLQ (0x8000) is assigned the value 128 instead of +// 0. Conversely, the maximum value of such a 2-octet VLQ (0xff7f) is +// 16511 instead of just 16383. Similarly, the minimum 3-octet VLQ +// (0x808000) has a value of 16512 instead of zero, which means +// that the maximum 3-octet VLQ (0xffff7f) is 2113663 instead of +// just 2097151. And so forth. +// +// This is how the offset is saved in C: +// +// dheader[pos] = ofs & 127; +// while (ofs >>= 7) +// dheader[--pos] = 128 | (--ofs & 127); +// +func ReadVariableWidthInt(r io.Reader) (int64, error) { + var c byte + if err := Read(r, &c); err != nil { + return 0, err + } + + var v = int64(c & maskLength) + for c&maskContinue > 0 { + v++ + if err := Read(r, &c); err != nil { + return 0, err + } + + v = (v << lengthBits) + int64(c&maskLength) + } + + return v, nil +} + +const ( + maskContinue = uint8(128) // 1000 000 + maskLength = uint8(127) // 0111 1111 + lengthBits = uint8(7) // subsequent bytes has 7 bits to store the length +) + +// ReadUint32 reads 4 bytes and returns them as a Big ndian uint32 +func ReadUint32(r io.Reader) (uint32, error) { + var v uint32 + if err := binary.Read(r, binary.BigEndian, &v); err != nil { + return 0, err + } + + return v, nil +} + +// ReadUint16 reads 2 bytes and returns them as a BigEndian uint16 +func ReadUint16(r io.Reader) (uint16, error) { + var v uint16 + if err := binary.Read(r, binary.BigEndian, &v); err != nil { + return 0, err + } + + return v, nil +} + +// ReadHash reads a core.Hash from r +func ReadHash(r io.Reader) (core.Hash, error) { + var h core.Hash + if err := binary.Read(r, binary.BigEndian, h[:]); err != nil { + return core.ZeroHash, err + } + + return h, nil +} diff --git a/utils/binary/read_test.go b/utils/binary/read_test.go new file mode 100644 index 0000000..6579ffb --- /dev/null +++ b/utils/binary/read_test.go @@ -0,0 +1,87 @@ +package binary + +import ( + "bytes" + "encoding/binary" + "testing" + + . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git.v3/core" +) + +func Test(t *testing.T) { TestingT(t) } + +type BinarySuite struct{} + +var _ = Suite(&BinarySuite{}) + +func (s *BinarySuite) TestRead(c *C) { + buf := bytes.NewBuffer(nil) + err := binary.Write(buf, binary.BigEndian, int64(42)) + c.Assert(err, IsNil) + err = binary.Write(buf, binary.BigEndian, int32(42)) + c.Assert(err, IsNil) + + var i64 int64 + var i32 int32 + err = Read(buf, &i64, &i32) + c.Assert(err, IsNil) + c.Assert(i64, Equals, int64(42)) + c.Assert(i32, Equals, int32(42)) +} + +func (s *BinarySuite) TestReadUntil(c *C) { + buf := bytes.NewBuffer([]byte("foo bar")) + + b, err := ReadUntil(buf, ' ') + c.Assert(err, IsNil) + c.Assert(b, HasLen, 3) + c.Assert(string(b), Equals, "foo") +} + +func (s *BinarySuite) TestReadVariableWidthInt(c *C) { + buf := bytes.NewBuffer([]byte{129, 110}) + + i, err := ReadVariableWidthInt(buf) + c.Assert(err, IsNil) + c.Assert(i, Equals, int64(366)) +} + +func (s *BinarySuite) TestReadVariableWidthIntShort(c *C) { + buf := bytes.NewBuffer([]byte{19}) + + i, err := ReadVariableWidthInt(buf) + c.Assert(err, IsNil) + c.Assert(i, Equals, int64(19)) +} + +func (s *BinarySuite) TestReadUint32(c *C) { + buf := bytes.NewBuffer(nil) + err := binary.Write(buf, binary.BigEndian, uint32(42)) + c.Assert(err, IsNil) + + i32, err := ReadUint32(buf) + c.Assert(err, IsNil) + c.Assert(i32, Equals, uint32(42)) +} + +func (s *BinarySuite) TestReadUint16(c *C) { + buf := bytes.NewBuffer(nil) + err := binary.Write(buf, binary.BigEndian, uint16(42)) + c.Assert(err, IsNil) + + i32, err := ReadUint16(buf) + c.Assert(err, IsNil) + c.Assert(i32, Equals, uint16(42)) +} + +func (s *BinarySuite) TestReadHash(c *C) { + expected := core.NewHash("43aec75c611f22c73b27ece2841e6ccca592f285") + buf := bytes.NewBuffer(nil) + err := binary.Write(buf, binary.BigEndian, expected) + c.Assert(err, IsNil) + + hash, err := ReadHash(buf) + c.Assert(err, IsNil) + c.Assert(hash.String(), Equals, expected.String()) +} diff --git a/utils/binary/write.go b/utils/binary/write.go new file mode 100644 index 0000000..3ea1d91 --- /dev/null +++ b/utils/binary/write.go @@ -0,0 +1,30 @@ +package binary + +import ( + "encoding/binary" + "io" +) + +// Write writes the binary representation of data into w, using BigEndian order +// https://golang.org/pkg/encoding/binary/#Write +func Write(w io.Writer, data ...interface{}) error { + for _, v := range data { + if err := binary.Write(w, binary.BigEndian, v); err != nil { + return err + } + } + + return nil +} + +// WriteUint32 writes the binary representation of a uint32 into w, in BigEndian +// order +func WriteUint32(w io.Writer, value uint32) error { + return binary.Write(w, binary.BigEndian, value) +} + +// WriteUint16 writes the binary representation of a uint16 into w, in BigEndian +// order +func WriteUint16(w io.Writer, value uint16) error { + return binary.Write(w, binary.BigEndian, value) +} diff --git a/utils/binary/writer_test.go b/utils/binary/writer_test.go new file mode 100644 index 0000000..88140a1 --- /dev/null +++ b/utils/binary/writer_test.go @@ -0,0 +1,43 @@ +package binary + +import ( + "bytes" + "encoding/binary" + + . "gopkg.in/check.v1" +) + +func (s *BinarySuite) TestWrite(c *C) { + expected := bytes.NewBuffer(nil) + err := binary.Write(expected, binary.BigEndian, int64(42)) + c.Assert(err, IsNil) + err = binary.Write(expected, binary.BigEndian, int32(42)) + c.Assert(err, IsNil) + + buf := bytes.NewBuffer(nil) + err = Write(buf, int64(42), int32(42)) + c.Assert(err, IsNil) + c.Assert(buf, DeepEquals, expected) +} + +func (s *BinarySuite) TestWriteUint32(c *C) { + expected := bytes.NewBuffer(nil) + err := binary.Write(expected, binary.BigEndian, int32(42)) + c.Assert(err, IsNil) + + buf := bytes.NewBuffer(nil) + err = WriteUint32(buf, 42) + c.Assert(err, IsNil) + c.Assert(buf, DeepEquals, expected) +} + +func (s *BinarySuite) TestWriteUint16(c *C) { + expected := bytes.NewBuffer(nil) + err := binary.Write(expected, binary.BigEndian, int16(42)) + c.Assert(err, IsNil) + + buf := bytes.NewBuffer(nil) + err = WriteUint16(buf, 42) + c.Assert(err, IsNil) + c.Assert(buf, DeepEquals, expected) +} |