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)
  raise EOFError, "End of file reached" if byte.nil?
  byte = byte.unpack('C').at(0) & 0xff
  @rval = (@rval << 8) | byte
  @rnbits += 8
end

def accumulate_little_endian_bits

def accumulate_little_endian_bits
  byte = read(1)
  raise EOFError, "End of file reached" if byte.nil?
  byte = byte.unpack('C').at(0) & 0xff
  @rval = @rval | (byte << @rnbits)
  @rnbits += 8
end

def buffer_limited_n(n)

def buffer_limited_n(n)
  if @buffer_end_pos
    max = @buffer_end_pos - offset
    n = max if n.nil? or n > max
  end
  n
end

def initialize(io)

def initialize(io)
  raise ArgumentError, "io must not be a BinData::IO::Read" if BinData::IO::Read === io
  # wrap strings in a StringIO
  if io.respond_to?(:to_str)
    io = BinData::IO.create_string_io(io.to_str)
  end
  @raw_io = io
  # bits when reading
  @rnbits  = 0
  @rval    = 0
  @rendian = nil
  @buffer_end_pos = nil
  extend seekable? ? SeekableStream : UnSeekableStream
end

def mask(nbits)

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

def read(n = nil)

def read(n = nil)
  read_raw(buffer_limited_n(n))
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
  str = read(n)
  raise EOFError, "End of file reached" if str.nil?
  raise IOError, "data truncated" if str.size < n
  str
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(n)

def seek(n)
  seek_raw(buffer_limited_n(n))
end

def seekable?

def seekable?
  @raw_io.pos
rescue NoMethodError, Errno::ESPIPE, Errno::EPIPE
  nil
end

def seekbytes(n)

Seek +n+ bytes from the current position in the io stream.
def seekbytes(n)
  reset_read_bits
  seek(n)
end

def with_buffer(n, &block)

calls inside the +block+ will be contained within this buffer.
Sets a buffer of +n+ bytes on the io stream. Any reading or seeking
def with_buffer(n, &block)
  prev = @buffer_end_pos
  if prev
    avail = prev - offset
    n = avail if n > avail
  end
  @buffer_end_pos = offset + n
  begin
    block.call
    read
  ensure
    @buffer_end_pos = prev
  end
end