github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/helpers/bincontainer/decoder.go (about) 1 package bincontainer 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "io" 7 ) 8 9 // A Decoder is able to reconstruct chunks of binary data from a stream which was 10 // previously encoded by an Encoder. 11 // NOTE: A maximum length of 2**32 (~4GB) is supported. 12 type Decoder struct { 13 r io.Reader 14 } 15 16 // ErrIncomplete is thrown whenever the Reader is unable to supply further data, 17 // although the protocol assumes more. 18 var ErrIncomplete = errors.New("incomplete chunk") 19 20 // NewDecoder returns a new Decoder instance. 21 func NewDecoder(r io.Reader) *Decoder { 22 return &Decoder{r: r} 23 } 24 25 // ReadChunk attempts to read the next chunk of data from the underlying reader. 26 // It either returns the full chunk or an error. 27 // If the underlying writer is closed after reading a complete chunk, EOF is returned. 28 // In all other cases, another error (such as ErrIncomplete) is returned. 29 func (e *Decoder) ReadChunk() ([]byte, error) { 30 length, err := e.readLength() 31 if err != nil { 32 return nil, err 33 } 34 return e.readData(length) 35 } 36 37 // readData attempts to read the requested number of bytes from the reader and 38 // returns it. 39 func (e *Decoder) readData(length uint32) ([]byte, error) { 40 chunk := make([]byte, length) 41 read, err := e.r.Read(chunk) 42 if uint32(read) != length { 43 return nil, ErrIncomplete 44 } 45 if err != nil && err != io.EOF { 46 return nil, err 47 } 48 return chunk, nil 49 } 50 51 // readLength reads the length-prefix tag from the reader and returns its 52 // uint32 value. 53 func (e *Decoder) readLength() (uint32, error) { 54 binLength := make([]byte, lengthSpecSize) 55 read, err := e.r.Read(binLength) 56 if read == 0 && err == io.EOF { 57 return 0, err 58 } 59 if read != lengthSpecSize { 60 return 0, ErrIncomplete 61 } 62 if err != nil && err != io.EOF { 63 return 0, err 64 } 65 66 return binary.LittleEndian.Uint32(binLength), nil 67 }