Class: BinData::IO
- Inherits:
-
Object
- Object
- BinData::IO
- Defined in:
- lib/bindata/io.rb
Overview
A wrapper around an IO object. The wrapper provides a consistent interface for BinData objects to use when accessing the IO.
Defined Under Namespace
Classes: Unseekable
Instance Attribute Summary collapse
-
#raw_io ⇒ Object
readonly
Access to the underlying raw io.
Class Method Summary collapse
-
.create_string_io(str = "") ⇒ Object
Creates a StringIO around
str
.
Instance Method Summary collapse
-
#flushbits ⇒ Object
(also: #flush)
To be called after all
writebits
have been applied. -
#initialize(io) ⇒ IO
constructor
Create a new IO wrapper around
io
. -
#num_bytes_remaining ⇒ Object
The number of bytes remaining in the input stream.
-
#offset ⇒ Object
Returns the current offset of the io stream.
-
#read_all_bytes ⇒ Object
Reads all remaining bytes from the stream.
-
#readbits(nbits, endian) ⇒ Object
Reads exactly
nbits
bits from the stream. -
#readbytes(n) ⇒ Object
Reads exactly
n
bytes fromio
. -
#reset_read_bits ⇒ Object
Discards any read bits so the stream becomes aligned at the next byte boundary.
-
#seekbytes(n) ⇒ Object
Seek
n
bytes from the current position in the io stream. -
#writebits(val, nbits, endian) ⇒ Object
Writes
nbits
bits fromval
to the stream. -
#writebytes(str) ⇒ Object
Writes the given string of bytes to the io stream.
Constructor Details
#initialize(io) ⇒ IO
Create a new IO wrapper around io
. io
must support #read if used for reading, #write if used for writing, #pos if reading the current stream position and #seek if setting the current stream position. If io
is a string it will be automatically wrapped in an StringIO object.
The IO can handle bitstreams in either big or little endian format.
M byte1 L M byte2 L
S 76543210 S S fedcba98 S
B B B B
In big endian format:
readbits(6), readbits(5) #=> [765432, 10fed]
In little endian format:
readbits(6), readbits(5) #=> [543210, a9876]
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/bindata/io.rb', line 36 def initialize(io) raise ArgumentError, "io must not be a BinData::IO" if BinData::IO === 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 # initial stream position if stream supports positioning @initial_pos = current_position rescue 0 # bits when reading @rnbits = 0 @rval = 0 @rendian = nil # bits when writing @wnbits = 0 @wval = 0 @wendian = nil end |
Instance Attribute Details
#raw_io ⇒ Object (readonly)
Access to the underlying raw io.
61 62 63 |
# File 'lib/bindata/io.rb', line 61 def raw_io @raw_io end |
Class Method Details
.create_string_io(str = "") ⇒ Object
Creates a StringIO around str
.
12 13 14 15 16 17 |
# File 'lib/bindata/io.rb', line 12 def self.create_string_io(str = "") if str.respond_to?(:force_encoding) str = str.dup.force_encoding(Encoding::BINARY) end StringIO.new(str) end |
Instance Method Details
#flushbits ⇒ Object Also known as: flush
To be called after all writebits
have been applied.
159 160 161 162 163 164 165 |
# File 'lib/bindata/io.rb', line 159 def flushbits raise "Internal state error nbits = #{@wnbits}" if @wnbits >= 8 if @wnbits > 0 writebits(0, 8 - @wnbits, @wendian) end end |
#num_bytes_remaining ⇒ Object
The number of bytes remaining in the input stream.
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/bindata/io.rb', line 72 def num_bytes_remaining pos = current_position @raw_io.seek(0, ::IO::SEEK_END) bytes_remaining = current_position - pos @raw_io.seek(pos, ::IO::SEEK_SET) bytes_remaining rescue Unseekable 0 end |
#offset ⇒ Object
Returns the current offset of the io stream. The exact value of the offset when reading bitfields is not defined.
65 66 67 68 69 |
# File 'lib/bindata/io.rb', line 65 def offset current_position - @initial_pos rescue Unseekable 0 end |
#read_all_bytes ⇒ Object
Reads all remaining bytes from the stream.
106 107 108 109 |
# File 'lib/bindata/io.rb', line 106 def read_all_bytes reset_read_bits @raw_io.read end |
#readbits(nbits, endian) ⇒ Object
Reads exactly nbits
bits from the stream. endian
specifies whether the bits are stored in :big
or :little
endian format.
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/bindata/io.rb', line 113 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 |
#readbytes(n) ⇒ Object
Reads exactly n
bytes from io
.
If the data read is nil an EOFError is raised.
If the data read is too short an IOError is raised.
96 97 98 99 100 101 102 103 |
# File 'lib/bindata/io.rb', line 96 def readbytes(n) reset_read_bits str = @raw_io.read(n) raise EOFError, "End of file reached" if str.nil? raise IOError, "data truncated" if str.size < n str end |
#reset_read_bits ⇒ Object
Discards any read bits so the stream becomes aligned at the next byte boundary.
129 130 131 132 |
# File 'lib/bindata/io.rb', line 129 def reset_read_bits @rnbits = 0 @rval = 0 end |
#seekbytes(n) ⇒ Object
Seek n
bytes from the current position in the io stream.
84 85 86 87 88 89 |
# File 'lib/bindata/io.rb', line 84 def seekbytes(n) reset_read_bits @raw_io.seek(n, ::IO::SEEK_CUR) rescue NoMethodError, Errno::ESPIPE, Errno::EPIPE skipbytes(n) end |
#writebits(val, nbits, endian) ⇒ Object
Writes nbits
bits from val
to the stream. endian
specifies whether the bits are to be stored in :big
or :little
endian format.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/bindata/io.rb', line 142 def writebits(val, nbits, endian) if @wendian != endian # don't mix bits of differing endian flushbits @wendian = endian end clamped_val = val & mask(nbits) if endian == :big write_big_endian_bits(clamped_val, nbits) else write_little_endian_bits(clamped_val, nbits) end end |
#writebytes(str) ⇒ Object
Writes the given string of bytes to the io stream.
135 136 137 138 |
# File 'lib/bindata/io.rb', line 135 def writebytes(str) flushbits @raw_io.write(str) end |