lib/bindata/io.rb in bindata-0.9.2 vs lib/bindata/io.rb in bindata-0.9.3
- old
+ new
@@ -29,11 +29,11 @@
end
@raw_io = io
# initial stream position if stream supports positioning
- @initial_pos = io.respond_to?(:pos) ? io.pos : 0
+ @initial_pos = positioning_supported? ? io.pos : 0
# bits when reading
@rnbits = 0
@rval = 0
@rendian = nil
@@ -48,11 +48,11 @@
attr_reader :raw_io
# Returns the current offset of the io stream. The exact value of
# the offset when reading bitfields is not defined.
def offset
- if @raw_io.respond_to?(:pos)
+ if positioning_supported?
@raw_io.pos - @initial_pos
else
# stream does not support positioning
0
end
@@ -77,55 +77,35 @@
raise EOFError, "End of file reached" if str.nil?
raise IOError, "data truncated" if str.size < n
str
end
- # Reads exactly +nbits+ bits from +io+. +endian+ specifies whether
- # the bits are stored in :big or :little endian format.
+ # Reads exactly +nbits+ bits from the stream. +endian+ specifies whether
+ # the bits are stored in +:big+ or +:little+ endian format.
def readbits(nbits, endian = :big)
if @rendian != endian
# don't mix bits of differing endian
@rnbits = 0
@rval = 0
@rendian = endian
end
- while nbits > @rnbits
- byte = @raw_io.read(1)
- raise EOFError, "End of file reached" if byte.nil?
- byte = byte.unpack('C').at(0) & 0xff
-
- if endian == :big
- @rval = (@rval << 8) | byte
- else
- @rval = @rval | (byte << @rnbits)
- end
-
- @rnbits += 8
- end
-
if endian == :big
- val = (@rval >> (@rnbits - nbits)) & ((1 << nbits) - 1)
- @rnbits -= nbits
- @rval &= ((1 << @rnbits) - 1)
+ read_be_bits(nbits)
else
- val = @rval & ((1 << nbits) - 1)
- @rnbits -= nbits
- @rval >>= nbits
+ read_le_bits(nbits)
end
-
- val
end
# Writes the given string of bytes to the io stream.
def writebytes(str)
flushbits
@raw_io.write(str)
end
- # Reads +nbits+ bits from +val+ to the stream. +endian+ specifies whether
- # the bits are to be stored in :big or :little endian format.
+ # Writes +nbits+ bits from +val+ to the stream. +endian+ specifies whether
+ # the bits are to be stored in +:big+ or +:little+ endian format.
def writebits(val, nbits, endian = :big)
# clamp val to range
val = val & ((1 << nbits) - 1)
if @wendian != endian
@@ -134,47 +114,13 @@
@wendian = endian
end
if endian == :big
- while nbits > 0
- bits_req = 8 - @wnbits
- if nbits >= bits_req
- msb_bits = (val >> (nbits - bits_req)) & ((1 << bits_req) - 1)
- nbits -= bits_req
- val &= (1 << nbits) - 1
-
- @wval = (@wval << bits_req) | msb_bits
- @raw_io.write(@wval.chr)
-
- @wval = 0
- @wnbits = 0
- else
- @wval = (@wval << nbits) | val
- @wnbits += nbits
- nbits = 0
- end
- end
+ write_be_bits(val, nbits)
else
- while nbits > 0
- bits_req = 8 - @wnbits
- if nbits >= bits_req
- lsb_bits = val & ((1 << bits_req) - 1)
- nbits -= bits_req
- val >>= bits_req
-
- @wval |= (lsb_bits << @wnbits)
- @raw_io.write(@wval.chr)
-
- @wval = 0
- @wnbits = 0
- else
- @wval |= (val << @wnbits)
- @wnbits += nbits
- nbits = 0
- end
- end
+ write_le_bits(val, nbits)
end
end
# To be called after all +writebits+ have been applied.
def flushbits
@@ -186,7 +132,104 @@
# do nothing
end
end
alias_method :flush, :flushbits
+ #---------------
+ private
+
+ # Checks if positioning is supported by the underlying io stream.
+ def positioning_supported?
+ unless defined? @positioning_supported
+ @positioning_supported = begin
+ @raw_io.pos
+ true
+ rescue NoMethodError, Errno::ESPIPE
+ false
+ end
+ end
+ @positioning_supported
+ end
+
+ # Reads exactly +nbits+ big endian bits from the stream.
+ def read_be_bits(nbits)
+ # ensure enough bits have accumulated
+ while nbits > @rnbits
+ byte = @raw_io.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
+
+ val = (@rval >> (@rnbits - nbits)) & ((1 << nbits) - 1)
+ @rnbits -= nbits
+ @rval &= ((1 << @rnbits) - 1)
+
+ val
+ end
+
+ # Reads exactly +nbits+ little endian bits from the stream.
+ def read_le_bits(nbits)
+ # ensure enough bits have accumulated
+ while nbits > @rnbits
+ byte = @raw_io.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
+
+ val = @rval & ((1 << nbits) - 1)
+ @rnbits -= nbits
+ @rval >>= nbits
+
+ val
+ end
+
+ # Writes +nbits+ bits from +val+ to the stream in big endian format.
+ def write_be_bits(val, nbits)
+ while nbits > 0
+ bits_req = 8 - @wnbits
+ if nbits >= bits_req
+ msb_bits = (val >> (nbits - bits_req)) & ((1 << bits_req) - 1)
+ nbits -= bits_req
+ val &= (1 << nbits) - 1
+
+ @wval = (@wval << bits_req) | msb_bits
+ @raw_io.write(@wval.chr)
+
+ @wval = 0
+ @wnbits = 0
+ else
+ @wval = (@wval << nbits) | val
+ @wnbits += nbits
+ nbits = 0
+ end
+ end
+ end
+
+ # Writes +nbits+ bits from +val+ to the stream in little endian format.
+ def write_le_bits(val, nbits)
+ while nbits > 0
+ bits_req = 8 - @wnbits
+ if nbits >= bits_req
+ lsb_bits = val & ((1 << bits_req) - 1)
+ nbits -= bits_req
+ val >>= bits_req
+
+ @wval |= (lsb_bits << @wnbits)
+ @raw_io.write(@wval.chr)
+
+ @wval = 0
+ @wnbits = 0
+ else
+ @wval |= (val << @wnbits)
+ @wnbits += nbits
+ nbits = 0
+ end
+ end
+ end
end
end