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

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)


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/bindata/io.rb', line 23

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 = StringIO.new(io)
  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.



48
49
50
# File 'lib/bindata/io.rb', line 48

def raw_io
  @raw_io
end

Instance Method Details

#flushbitsObject Also known as: flush

To be called after all writebits have been applied.



133
134
135
136
137
138
139
# File 'lib/bindata/io.rb', line 133

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.



52
53
54
55
56
57
58
# File 'lib/bindata/io.rb', line 52

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

#read_all_bytesObject

Reads all remaining bytes from the stream.



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

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

  @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.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/bindata/io.rb', line 92

def readbits(nbits, endian)
  if @rendian != endian
    # don't mix bits of differing endian
    @rnbits  = 0
    @rval    = 0
    @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)


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

def readbytes(n)
  raise "Internal state error nbits = #{@rnbits}" if @rnbits >= 8
  @rnbits = 0
  @rval   = 0

  str = @raw_io.read(n)
  raise EOFError, "End of file reached" if str.nil?
  raise IOError, "data truncated" if str.size < n
  str
end

#seekbytes(n) ⇒ Object

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



61
62
63
# File 'lib/bindata/io.rb', line 61

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



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/bindata/io.rb', line 115

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.



108
109
110
111
# File 'lib/bindata/io.rb', line 108

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