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
writebitshave 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
nbitsbits from the stream. -
#readbytes(n) ⇒ Object
Reads exactly
nbytes fromio. -
#reset_read_bits ⇒ Object
Discards any read bits so the stream becomes aligned at the next byte boundary.
-
#seekbytes(n) ⇒ Object
Seek
nbytes from the current position in the io stream. -
#writebits(val, nbits, endian) ⇒ Object
Writes
nbitsbits fromvalto 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 |