github.com/m3db/m3@v1.5.0/src/dbnode/encoding/istream.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package encoding
    22  
    23  import (
    24  	"io"
    25  
    26  	"github.com/m3db/m3/src/dbnode/x/xio"
    27  )
    28  
    29  // IStream encapsulates a readable stream.
    30  type IStream struct {
    31  	r         xio.Reader64
    32  	current   uint64 // current uint64 we are working off of
    33  	index     int    // current index within data slice
    34  	remaining uint8  // bits remaining in current to be read
    35  }
    36  
    37  var (
    38  	_ io.ByteReader = (*IStream)(nil)
    39  	_ io.Reader     = (*IStream)(nil)
    40  )
    41  
    42  // NewIStream creates a new IStream
    43  func NewIStream(reader64 xio.Reader64) *IStream {
    44  	return &IStream{r: reader64}
    45  }
    46  
    47  // Read reads len(b) bytes.
    48  func (is *IStream) Read(b []byte) (int, error) {
    49  	var i int
    50  	for ; i < len(b); i++ {
    51  		res, err := is.ReadBits(8)
    52  		if err != nil {
    53  			return i, err
    54  		}
    55  		b[i] = byte(res)
    56  	}
    57  	return i, nil
    58  }
    59  
    60  // ReadByte reads the next Byte.
    61  func (is *IStream) ReadByte() (byte, error) {
    62  	res, err := is.ReadBits(8)
    63  	return byte(res), err
    64  }
    65  
    66  // ReadBit reads the next Bit.
    67  func (is *IStream) ReadBit() (Bit, error) {
    68  	res, err := is.ReadBits(1)
    69  	return Bit(res), err
    70  }
    71  
    72  // ReadBits reads the next Bits.
    73  func (is *IStream) ReadBits(numBits uint8) (uint64, error) {
    74  	res := is.current >> (64 - numBits)
    75  	remaining := is.remaining
    76  	if numBits <= remaining {
    77  		// Have enough bits buffered.
    78  		is.current <<= numBits
    79  		is.remaining -= numBits
    80  		return res, nil
    81  	}
    82  
    83  	// Not enough bits buffered, read next word from the stream.
    84  	bitsNeeded := numBits - remaining
    85  
    86  	current, n, err := is.r.Read64()
    87  	if err != nil {
    88  		return 0, err
    89  	}
    90  	n *= 8
    91  	if n < bitsNeeded {
    92  		return 0, io.EOF
    93  	}
    94  
    95  	is.current = current << bitsNeeded
    96  	is.remaining = n - bitsNeeded
    97  	return res | current>>(64-bitsNeeded), nil
    98  }
    99  
   100  // PeekBits looks at the next Bits, but doesn't move the pos.
   101  func (is *IStream) PeekBits(numBits uint8) (uint64, error) {
   102  	if numBits <= is.remaining {
   103  		return readBitsInWord(is.current, numBits), nil
   104  	}
   105  	res := readBitsInWord(is.current, numBits)
   106  	bitsNeeded := numBits - is.remaining
   107  	next, bytes, err := is.r.Peek64()
   108  	if err != nil {
   109  		return 0, err
   110  	}
   111  	if rem := 8 * bytes; rem < bitsNeeded {
   112  		return 0, io.EOF
   113  	}
   114  	return res | readBitsInWord(next, bitsNeeded), nil
   115  }
   116  
   117  // RemainingBitsInCurrentByte returns the number of bits remaining to be read in the current byte.
   118  func (is *IStream) RemainingBitsInCurrentByte() uint {
   119  	return uint(is.remaining % 8)
   120  }
   121  
   122  // readBitsInWord reads the first numBits in word w.
   123  func readBitsInWord(w uint64, numBits uint8) uint64 {
   124  	return w >> (64 - numBits)
   125  }
   126  
   127  // Reset resets the IStream.
   128  func (is *IStream) Reset(reader xio.Reader64) {
   129  	is.current = 0
   130  	is.remaining = 0
   131  	is.index = 0
   132  	is.r = reader
   133  }