class BinData::IO::Read


readbits(6), readbits(5) #=> [543210, a9876]
In little endian format:
readbits(6), readbits(5) #=> [765432, 10fed]
In big endian format:
B B B B
S 76543210 S S fedcba98 S
M byte1 L M byte2 L
The IO can handle bitstreams in either big or little endian format.
wrapped in an StringIO object.
current stream position. If io is a string it will be automatically
#pos if reading the current stream position and #seek if setting the
Create a new IO Read wrapper around io. io must provide #read,

def accumulate_big_endian_bits

def accumulate_big_endian_bits
  byte = read(1).unpack1('C') & 0xff
  @rval = (@rval << 8) | byte
  @rnbits += 8
end

def accumulate_little_endian_bits

def accumulate_little_endian_bits
  byte = read(1).unpack1('C') & 0xff
  @rval = @rval | (byte << @rnbits)
  @rnbits += 8
end

def initialize(io)

def initialize(io)
  if self.class === io
    raise ArgumentError, "io must not be a #{self.class}"
  end
  # wrap strings in a StringIO
  if io.respond_to?(:to_str)
    io = BinData::IO.create_string_io(io.to_str)
  end
  @io = RawIO.new(io)
  # bits when reading
  @rnbits  = 0
  @rval    = 0
  @rendian = nil
end

def mask(nbits)

def mask(nbits)
  (1 << nbits) - 1
end

def num_bytes_remaining

The number of bytes remaining in the io steam.
def num_bytes_remaining
  @io.num_bytes_remaining
end

def read(n = nil)

def read(n = nil)
  str = @io.read(n)
  if n
    raise EOFError, "End of file reached" if str.nil?
    raise IOError, "data truncated" if str.size < n
  end
  str
end

def read_all_bytes

Reads all remaining bytes from the stream.
def read_all_bytes
  reset_read_bits
  read
end

def read_big_endian_bits(nbits)

def read_big_endian_bits(nbits)
  while @rnbits < nbits
    accumulate_big_endian_bits
  end
  val     = (@rval >> (@rnbits - nbits)) & mask(nbits)
  @rnbits -= nbits
  @rval   &= mask(@rnbits)
  val
end

def read_little_endian_bits(nbits)

def read_little_endian_bits(nbits)
  while @rnbits < nbits
    accumulate_little_endian_bits
  end
  val     = @rval & mask(nbits)
  @rnbits -= nbits
  @rval   >>= nbits
  val
end

def readbits(nbits, endian)

the bits are stored in +:big+ or +:little+ endian format.
Reads exactly +nbits+ bits from the stream. +endian+ specifies whether
def readbits(nbits, endian)
  if @rendian != endian
    # don't mix bits of differing endian
    reset_read_bits
    @rendian = endian
  end
  if endian == :big
    read_big_endian_bits(nbits)
  else
    read_little_endian_bits(nbits)
  end
end

def readbytes(n)

If the data read is too short an IOError is raised.

If the data read is nil an EOFError is raised.

Reads exactly +n+ bytes from +io+.
def readbytes(n)
  reset_read_bits
  read(n)
end

def reset_read_bits

next byte boundary.
Discards any read bits so the stream becomes aligned at the
def reset_read_bits
  @rnbits = 0
  @rval   = 0
end

def seek_to_abs_offset(n)

Seek to an absolute offset within the io stream.
def seek_to_abs_offset(n)
  reset_read_bits
  @io.seek_abs(n)
end

def skipbytes(n)

Seek +n+ bytes from the current position in the io stream.
def skipbytes(n)
  reset_read_bits
  @io.skip(n)
end

def transform(io)

yields +self+ and +io+ to the given block

+io+ must be an instance of +Transform+.

See +BinData::Buffer+ as an example.
Allow transforming data in the input stream.
def transform(io)
  reset_read_bits
  saved = @io
  @io = io.prepend_to_chain(@io)
  yield(self, io)
  io.after_read_transform
ensure
  @io = saved
end