Class: BinData::IO

Inherits:
Object
  • Object
show all
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.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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]

Raises:

  • (ArgumentError)


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/bindata/io.rb', line 33

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 = positioning_supported? ? io.pos : 0

  # bits when reading
  @rnbits  = 0
  @rval    = 0
  @rendian = nil

  # bits when writing
  @wnbits  = 0
  @wval    = 0
  @wendian = nil
end

Instance Attribute Details

#raw_ioObject (readonly)

Access to the underlying raw io.



58
59
60
# File 'lib/bindata/io.rb', line 58

def raw_io
  @raw_io
end

Class Method Details

.create_string_io(str = "") ⇒ Object

Creates a StringIO around str.



9
10
11
12
13
14
# File 'lib/bindata/io.rb', line 9

def self.create_string_io(str = "")
  if RUBY_VERSION >= "1.9"
    str = str.dup.force_encoding(Encoding::BINARY)
  end
  StringIO.new(str)
end

Instance Method Details

#flushbitsObject Also known as: flush

To be called after all writebits have been applied.



147
148
149
150
151
152
153
# File 'lib/bindata/io.rb', line 147

def flushbits
  raise "Internal state error nbits = #{@wnbits}" if @wnbits >= 8

  if @wnbits > 0
    writebits(0, 8 - @wnbits, @wendian)
  end
end

#offsetObject

Returns the current offset of the io stream. The exact value of the offset when reading bitfields is not defined.



62
63
64
65
66
67
68
# File 'lib/bindata/io.rb', line 62

def offset
  if positioning_supported?
    @raw_io.pos - @initial_pos
  else
    0
  end
end

#read_all_bytesObject

Reads all remaining bytes from the stream.



93
94
95
96
# File 'lib/bindata/io.rb', line 93

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.



100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/bindata/io.rb', line 100

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.

Raises:

  • (EOFError)


83
84
85
86
87
88
89
90
# File 'lib/bindata/io.rb', line 83

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_bitsObject

Discards any read bits so the stream becomes aligned at the next byte boundary.



116
117
118
119
120
# File 'lib/bindata/io.rb', line 116

def reset_read_bits
  raise "Internal state error nbits = #{@rnbits}" if @rnbits >= 8
  @rnbits = 0
  @rval   = 0
end

#seekbytes(n) ⇒ Object

Seek n bytes from the current position in the io stream.



71
72
73
74
75
76
# File 'lib/bindata/io.rb', line 71

def seekbytes(n)
  reset_read_bits
  @raw_io.seek(n, ::IO::SEEK_CUR)
rescue 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.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/bindata/io.rb', line 130

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.



123
124
125
126
# File 'lib/bindata/io.rb', line 123

def writebytes(str)
  flushbits
  @raw_io.write(str)
end