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  }