jbangert-bindata 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/BSDL +22 -0
- data/COPYING +52 -0
- data/ChangeLog.rdoc +204 -0
- data/Gemfile +2 -0
- data/INSTALL +11 -0
- data/NEWS.rdoc +164 -0
- data/README.md +54 -0
- data/Rakefile +13 -0
- data/bindata.gemspec +31 -0
- data/doc/manual.haml +407 -0
- data/doc/manual.md +1649 -0
- data/examples/NBT.txt +149 -0
- data/examples/gzip.rb +161 -0
- data/examples/ip_address.rb +22 -0
- data/examples/list.rb +124 -0
- data/examples/nbt.rb +178 -0
- data/lib/bindata.rb +33 -0
- data/lib/bindata/alignment.rb +83 -0
- data/lib/bindata/array.rb +335 -0
- data/lib/bindata/base.rb +388 -0
- data/lib/bindata/base_primitive.rb +214 -0
- data/lib/bindata/bits.rb +87 -0
- data/lib/bindata/choice.rb +216 -0
- data/lib/bindata/count_bytes_remaining.rb +35 -0
- data/lib/bindata/deprecated.rb +50 -0
- data/lib/bindata/dsl.rb +312 -0
- data/lib/bindata/float.rb +80 -0
- data/lib/bindata/int.rb +184 -0
- data/lib/bindata/io.rb +274 -0
- data/lib/bindata/lazy.rb +105 -0
- data/lib/bindata/offset.rb +91 -0
- data/lib/bindata/params.rb +135 -0
- data/lib/bindata/primitive.rb +135 -0
- data/lib/bindata/record.rb +110 -0
- data/lib/bindata/registry.rb +92 -0
- data/lib/bindata/rest.rb +35 -0
- data/lib/bindata/sanitize.rb +290 -0
- data/lib/bindata/skip.rb +48 -0
- data/lib/bindata/string.rb +145 -0
- data/lib/bindata/stringz.rb +96 -0
- data/lib/bindata/struct.rb +388 -0
- data/lib/bindata/trace.rb +94 -0
- data/lib/bindata/version.rb +3 -0
- data/setup.rb +1585 -0
- data/spec/alignment_spec.rb +61 -0
- data/spec/array_spec.rb +331 -0
- data/spec/base_primitive_spec.rb +238 -0
- data/spec/base_spec.rb +376 -0
- data/spec/bits_spec.rb +163 -0
- data/spec/choice_spec.rb +263 -0
- data/spec/count_bytes_remaining_spec.rb +38 -0
- data/spec/deprecated_spec.rb +31 -0
- data/spec/example.rb +21 -0
- data/spec/float_spec.rb +37 -0
- data/spec/int_spec.rb +216 -0
- data/spec/io_spec.rb +352 -0
- data/spec/lazy_spec.rb +217 -0
- data/spec/primitive_spec.rb +202 -0
- data/spec/record_spec.rb +530 -0
- data/spec/registry_spec.rb +108 -0
- data/spec/rest_spec.rb +26 -0
- data/spec/skip_spec.rb +27 -0
- data/spec/spec_common.rb +58 -0
- data/spec/string_spec.rb +300 -0
- data/spec/stringz_spec.rb +118 -0
- data/spec/struct_spec.rb +350 -0
- data/spec/system_spec.rb +380 -0
- data/tasks/manual.rake +36 -0
- data/tasks/rspec.rake +17 -0
- metadata +208 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'bindata/base_primitive'
|
2
|
+
|
3
|
+
module BinData
|
4
|
+
# Defines a number of classes that contain a floating point number.
|
5
|
+
# The float is defined by precision and endian.
|
6
|
+
|
7
|
+
module FloatingPoint #:nodoc: all
|
8
|
+
class << self
|
9
|
+
def define_methods(float_class, precision, endian)
|
10
|
+
float_class.module_eval <<-END
|
11
|
+
def do_num_bytes
|
12
|
+
#{create_num_bytes_code(precision)}
|
13
|
+
end
|
14
|
+
|
15
|
+
#---------------
|
16
|
+
private
|
17
|
+
|
18
|
+
def sensible_default
|
19
|
+
0.0
|
20
|
+
end
|
21
|
+
|
22
|
+
def value_to_binary_string(val)
|
23
|
+
#{create_to_binary_s_code(precision, endian)}
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_and_return_value(io)
|
27
|
+
#{create_read_code(precision, endian)}
|
28
|
+
end
|
29
|
+
END
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_num_bytes_code(precision)
|
33
|
+
(precision == :single) ? 4 : 8
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_read_code(precision, endian)
|
37
|
+
if precision == :single
|
38
|
+
unpack = (endian == :little) ? 'e' : 'g'
|
39
|
+
nbytes = 4
|
40
|
+
else # double_precision
|
41
|
+
unpack = (endian == :little) ? 'E' : 'G'
|
42
|
+
nbytes = 8
|
43
|
+
end
|
44
|
+
|
45
|
+
"io.readbytes(#{nbytes}).unpack('#{unpack}').at(0)"
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_to_binary_s_code(precision, endian)
|
49
|
+
if precision == :single
|
50
|
+
pack = (endian == :little) ? 'e' : 'g'
|
51
|
+
else # double_precision
|
52
|
+
pack = (endian == :little) ? 'E' : 'G'
|
53
|
+
end
|
54
|
+
|
55
|
+
"[val].pack('#{pack}')"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Single precision floating point number in little endian format
|
62
|
+
class FloatLe < BinData::BasePrimitive
|
63
|
+
FloatingPoint.define_methods(self, :single, :little)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Single precision floating point number in big endian format
|
67
|
+
class FloatBe < BinData::BasePrimitive
|
68
|
+
FloatingPoint.define_methods(self, :single, :big)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Double precision floating point number in little endian format
|
72
|
+
class DoubleLe < BinData::BasePrimitive
|
73
|
+
FloatingPoint.define_methods(self, :double, :little)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Double precision floating point number in big endian format
|
77
|
+
class DoubleBe < BinData::BasePrimitive
|
78
|
+
FloatingPoint.define_methods(self, :double, :big)
|
79
|
+
end
|
80
|
+
end
|
data/lib/bindata/int.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'bindata/base_primitive'
|
2
|
+
|
3
|
+
module BinData
|
4
|
+
# Defines a number of classes that contain an integer. The integer
|
5
|
+
# is defined by endian, signedness and number of bytes.
|
6
|
+
|
7
|
+
module Int #:nodoc: all
|
8
|
+
class << self
|
9
|
+
def define_class(nbits, endian, signed)
|
10
|
+
name = class_name(nbits, endian, signed)
|
11
|
+
unless BinData.const_defined?(name)
|
12
|
+
BinData.module_eval <<-END
|
13
|
+
class #{name} < BinData::BasePrimitive
|
14
|
+
Int.define_methods(self, #{nbits}, :#{endian}, :#{signed})
|
15
|
+
end
|
16
|
+
END
|
17
|
+
end
|
18
|
+
|
19
|
+
BinData.const_get(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def class_name(nbits, endian, signed)
|
23
|
+
endian_str = (endian == :big) ? "be" : "le"
|
24
|
+
base = (signed == :signed) ? "Int" : "Uint"
|
25
|
+
|
26
|
+
"#{base}#{nbits}#{endian_str}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_methods(int_class, nbits, endian, signed)
|
30
|
+
raise "nbits must be divisible by 8" unless (nbits % 8).zero?
|
31
|
+
|
32
|
+
int_class.module_eval <<-END
|
33
|
+
def assign(val)
|
34
|
+
#{create_clamp_code(nbits, signed)}
|
35
|
+
super(val)
|
36
|
+
end
|
37
|
+
|
38
|
+
def do_num_bytes
|
39
|
+
#{nbits / 8}
|
40
|
+
end
|
41
|
+
|
42
|
+
#---------------
|
43
|
+
private
|
44
|
+
|
45
|
+
def sensible_default
|
46
|
+
0
|
47
|
+
end
|
48
|
+
|
49
|
+
def value_to_binary_string(val)
|
50
|
+
#{create_clamp_code(nbits, signed)}
|
51
|
+
#{create_int2uint_code(nbits) if signed == :signed}
|
52
|
+
#{create_to_binary_s_code(nbits, endian)}
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_and_return_value(io)
|
56
|
+
val = #{create_read_code(nbits, endian)}
|
57
|
+
#{create_uint2int_code(nbits) if signed == :signed}
|
58
|
+
val
|
59
|
+
end
|
60
|
+
END
|
61
|
+
end
|
62
|
+
|
63
|
+
#-------------
|
64
|
+
private
|
65
|
+
|
66
|
+
def create_clamp_code(nbits, signed)
|
67
|
+
if signed == :signed
|
68
|
+
max = (1 << (nbits - 1)) - 1
|
69
|
+
min = -(max + 1)
|
70
|
+
else
|
71
|
+
min = 0
|
72
|
+
max = (1 << nbits) - 1
|
73
|
+
end
|
74
|
+
|
75
|
+
"val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_int2uint_code(nbits)
|
79
|
+
"val = val & #{(1 << nbits) - 1}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def create_uint2int_code(nbits)
|
83
|
+
"val = val - #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_read_code(nbits, endian)
|
87
|
+
bits_per_word = bytes_per_word(nbits) * 8
|
88
|
+
nwords = nbits / bits_per_word
|
89
|
+
nbytes = nbits / 8
|
90
|
+
|
91
|
+
idx = (0 ... nwords).to_a
|
92
|
+
idx.reverse! if (endian == :big)
|
93
|
+
|
94
|
+
parts = (0 ... nwords).collect do |i|
|
95
|
+
if i.zero?
|
96
|
+
"a.at(#{idx[i]})"
|
97
|
+
else
|
98
|
+
"(a.at(#{idx[i]}) << #{bits_per_word * i})"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
unpack_str = "a = io.readbytes(#{nbytes}).unpack('#{pack_directive(nbits, endian)}')"
|
103
|
+
assemble_str = parts.join(" + ")
|
104
|
+
|
105
|
+
"(#{unpack_str}; #{assemble_str})"
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_to_binary_s_code(nbits, endian)
|
109
|
+
# special case 8bit integers for speed
|
110
|
+
return "val.chr" if nbits == 8
|
111
|
+
|
112
|
+
bits_per_word = bytes_per_word(nbits) * 8
|
113
|
+
nwords = nbits / bits_per_word
|
114
|
+
mask = (1 << bits_per_word) - 1
|
115
|
+
|
116
|
+
vals = (0 ... nwords).collect do |i|
|
117
|
+
i.zero? ? "val" : "(val >> #{bits_per_word * i})"
|
118
|
+
end
|
119
|
+
vals.reverse! if (endian == :big)
|
120
|
+
|
121
|
+
parts = (0 ... nwords).collect { |i| "#{vals[i]} & #{mask}" }
|
122
|
+
array_str = "[" + parts.join(", ") + "]"
|
123
|
+
|
124
|
+
"#{array_str}.pack('#{pack_directive(nbits, endian)}')"
|
125
|
+
end
|
126
|
+
|
127
|
+
def bytes_per_word(nbits)
|
128
|
+
(nbits % 32).zero? ? 4 : (nbits % 16).zero? ? 2 : 1
|
129
|
+
end
|
130
|
+
|
131
|
+
def pack_directive(nbits, endian)
|
132
|
+
bits_per_word = bytes_per_word(nbits) * 8
|
133
|
+
nwords = nbits / bits_per_word
|
134
|
+
|
135
|
+
if (nbits % 32).zero?
|
136
|
+
d = (endian == :big) ? 'N' : 'V'
|
137
|
+
elsif (nbits % 16).zero?
|
138
|
+
d = (endian == :big) ? 'n' : 'v'
|
139
|
+
else
|
140
|
+
d = 'C'
|
141
|
+
end
|
142
|
+
|
143
|
+
d * nwords
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
# Unsigned 1 byte integer.
|
150
|
+
class Uint8 < BinData::BasePrimitive
|
151
|
+
Int.define_methods(self, 8, :little, :unsigned)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Signed 1 byte integer.
|
155
|
+
class Int8 < BinData::BasePrimitive
|
156
|
+
Int.define_methods(self, 8, :little, :signed)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Create classes on demand
|
160
|
+
class << self
|
161
|
+
alias_method :const_missing_without_int, :const_missing
|
162
|
+
def const_missing_with_int(name)
|
163
|
+
name = name.to_s
|
164
|
+
mappings = {
|
165
|
+
/^Uint(\d+)be$/ => [:big, :unsigned],
|
166
|
+
/^Uint(\d+)le$/ => [:little, :unsigned],
|
167
|
+
/^Int(\d+)be$/ => [:big, :signed],
|
168
|
+
/^Int(\d+)le$/ => [:little, :signed],
|
169
|
+
}
|
170
|
+
|
171
|
+
mappings.each_pair do |regex, args|
|
172
|
+
if regex =~ name
|
173
|
+
nbits = $1.to_i
|
174
|
+
if (nbits % 8).zero?
|
175
|
+
return Int.define_class(nbits, *args)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
const_missing_without_int(name)
|
181
|
+
end
|
182
|
+
alias_method :const_missing, :const_missing_with_int
|
183
|
+
end
|
184
|
+
end
|
data/lib/bindata/io.rb
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module BinData
|
4
|
+
# A wrapper around an IO object. The wrapper provides a consistent
|
5
|
+
# interface for BinData objects to use when accessing the IO.
|
6
|
+
class IO
|
7
|
+
|
8
|
+
# The underlying IO is unseekable
|
9
|
+
class Unseekable < StandardError; end
|
10
|
+
|
11
|
+
# Creates a StringIO around +str+.
|
12
|
+
def self.create_string_io(str = "")
|
13
|
+
if str.respond_to?(:force_encoding)
|
14
|
+
str = str.dup.force_encoding(Encoding::BINARY)
|
15
|
+
end
|
16
|
+
StringIO.new(str)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create a new IO wrapper around +io+. +io+ must support #read if used
|
20
|
+
# for reading, #write if used for writing, #pos if reading the current
|
21
|
+
# stream position and #seek if setting the current stream position. If
|
22
|
+
# +io+ is a string it will be automatically wrapped in an StringIO object.
|
23
|
+
#
|
24
|
+
# The IO can handle bitstreams in either big or little endian format.
|
25
|
+
#
|
26
|
+
# M byte1 L M byte2 L
|
27
|
+
# S 76543210 S S fedcba98 S
|
28
|
+
# B B B B
|
29
|
+
#
|
30
|
+
# In big endian format:
|
31
|
+
# readbits(6), readbits(5) #=> [765432, 10fed]
|
32
|
+
#
|
33
|
+
# In little endian format:
|
34
|
+
# readbits(6), readbits(5) #=> [543210, a9876]
|
35
|
+
#
|
36
|
+
def initialize(io)
|
37
|
+
raise ArgumentError, "io must not be a BinData::IO" if BinData::IO === io
|
38
|
+
|
39
|
+
# wrap strings in a StringIO
|
40
|
+
if io.respond_to?(:to_str)
|
41
|
+
io = BinData::IO.create_string_io(io.to_str)
|
42
|
+
end
|
43
|
+
|
44
|
+
@raw_io = io
|
45
|
+
|
46
|
+
# initial stream position if stream supports positioning
|
47
|
+
@initial_pos = current_position rescue 0
|
48
|
+
|
49
|
+
# bits when reading
|
50
|
+
@rnbits = 0
|
51
|
+
@rval = 0
|
52
|
+
@rendian = nil
|
53
|
+
|
54
|
+
# bits when writing
|
55
|
+
@wnbits = 0
|
56
|
+
@wval = 0
|
57
|
+
@wendian = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Access to the underlying raw io.
|
61
|
+
attr_reader :raw_io
|
62
|
+
|
63
|
+
# Returns the current offset of the io stream. The exact value of
|
64
|
+
# the offset when reading bitfields is not defined.
|
65
|
+
def offset
|
66
|
+
current_position - @initial_pos
|
67
|
+
rescue Unseekable
|
68
|
+
0
|
69
|
+
end
|
70
|
+
|
71
|
+
# The number of bytes remaining in the input stream.
|
72
|
+
def num_bytes_remaining
|
73
|
+
pos = current_position
|
74
|
+
@raw_io.seek(0, ::IO::SEEK_END)
|
75
|
+
bytes_remaining = current_position - pos
|
76
|
+
@raw_io.seek(pos, ::IO::SEEK_SET)
|
77
|
+
|
78
|
+
bytes_remaining
|
79
|
+
rescue Unseekable
|
80
|
+
0
|
81
|
+
end
|
82
|
+
|
83
|
+
# Seek +n+ bytes from the current position in the io stream.
|
84
|
+
def seekbytes(n)
|
85
|
+
reset_read_bits
|
86
|
+
@raw_io.seek(n, ::IO::SEEK_CUR)
|
87
|
+
rescue NoMethodError, Errno::ESPIPE, Errno::EPIPE
|
88
|
+
skipbytes(n)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Reads exactly +n+ bytes from +io+.
|
92
|
+
#
|
93
|
+
# If the data read is nil an EOFError is raised.
|
94
|
+
#
|
95
|
+
# If the data read is too short an IOError is raised.
|
96
|
+
def readbytes(n)
|
97
|
+
reset_read_bits
|
98
|
+
|
99
|
+
str = @raw_io.read(n)
|
100
|
+
raise EOFError, "End of file reached" if str.nil?
|
101
|
+
raise IOError, "data truncated" if str.size < n
|
102
|
+
str
|
103
|
+
end
|
104
|
+
|
105
|
+
# Reads all remaining bytes from the stream.
|
106
|
+
def read_all_bytes
|
107
|
+
reset_read_bits
|
108
|
+
@raw_io.read
|
109
|
+
end
|
110
|
+
|
111
|
+
# Reads exactly +nbits+ bits from the stream. +endian+ specifies whether
|
112
|
+
# the bits are stored in +:big+ or +:little+ endian format.
|
113
|
+
def readbits(nbits, endian)
|
114
|
+
if @rendian != endian
|
115
|
+
# don't mix bits of differing endian
|
116
|
+
reset_read_bits
|
117
|
+
@rendian = endian
|
118
|
+
end
|
119
|
+
|
120
|
+
if endian == :big
|
121
|
+
read_big_endian_bits(nbits)
|
122
|
+
else
|
123
|
+
read_little_endian_bits(nbits)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Discards any read bits so the stream becomes aligned at the
|
128
|
+
# next byte boundary.
|
129
|
+
def reset_read_bits
|
130
|
+
@rnbits = 0
|
131
|
+
@rval = 0
|
132
|
+
end
|
133
|
+
|
134
|
+
# Writes the given string of bytes to the io stream.
|
135
|
+
def writebytes(str)
|
136
|
+
flushbits
|
137
|
+
@raw_io.write(str)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Writes +nbits+ bits from +val+ to the stream. +endian+ specifies whether
|
141
|
+
# the bits are to be stored in +:big+ or +:little+ endian format.
|
142
|
+
def writebits(val, nbits, endian)
|
143
|
+
if @wendian != endian
|
144
|
+
# don't mix bits of differing endian
|
145
|
+
flushbits
|
146
|
+
@wendian = endian
|
147
|
+
end
|
148
|
+
|
149
|
+
clamped_val = val & mask(nbits)
|
150
|
+
|
151
|
+
if endian == :big
|
152
|
+
write_big_endian_bits(clamped_val, nbits)
|
153
|
+
else
|
154
|
+
write_little_endian_bits(clamped_val, nbits)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# To be called after all +writebits+ have been applied.
|
159
|
+
def flushbits
|
160
|
+
raise "Internal state error nbits = #{@wnbits}" if @wnbits >= 8
|
161
|
+
|
162
|
+
if @wnbits > 0
|
163
|
+
writebits(0, 8 - @wnbits, @wendian)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
alias_method :flush, :flushbits
|
167
|
+
|
168
|
+
#---------------
|
169
|
+
private
|
170
|
+
|
171
|
+
def current_position
|
172
|
+
@raw_io.pos
|
173
|
+
rescue NoMethodError, Errno::ESPIPE
|
174
|
+
raise Unseekable
|
175
|
+
end
|
176
|
+
|
177
|
+
def skipbytes(n)
|
178
|
+
# skip over data in 8k blocks
|
179
|
+
while n > 0
|
180
|
+
bytes_to_read = [n, 8192].min
|
181
|
+
@raw_io.read(bytes_to_read)
|
182
|
+
n -= bytes_to_read
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def read_big_endian_bits(nbits)
|
187
|
+
while @rnbits < nbits
|
188
|
+
accumulate_big_endian_bits
|
189
|
+
end
|
190
|
+
|
191
|
+
val = (@rval >> (@rnbits - nbits)) & mask(nbits)
|
192
|
+
@rnbits -= nbits
|
193
|
+
@rval &= mask(@rnbits)
|
194
|
+
|
195
|
+
val
|
196
|
+
end
|
197
|
+
|
198
|
+
def accumulate_big_endian_bits
|
199
|
+
byte = @raw_io.read(1)
|
200
|
+
raise EOFError, "End of file reached" if byte.nil?
|
201
|
+
byte = byte.unpack('C').at(0) & 0xff
|
202
|
+
|
203
|
+
@rval = (@rval << 8) | byte
|
204
|
+
@rnbits += 8
|
205
|
+
end
|
206
|
+
|
207
|
+
def read_little_endian_bits(nbits)
|
208
|
+
while @rnbits < nbits
|
209
|
+
accumulate_little_endian_bits
|
210
|
+
end
|
211
|
+
|
212
|
+
val = @rval & mask(nbits)
|
213
|
+
@rnbits -= nbits
|
214
|
+
@rval >>= nbits
|
215
|
+
|
216
|
+
val
|
217
|
+
end
|
218
|
+
|
219
|
+
def accumulate_little_endian_bits
|
220
|
+
byte = @raw_io.read(1)
|
221
|
+
raise EOFError, "End of file reached" if byte.nil?
|
222
|
+
byte = byte.unpack('C').at(0) & 0xff
|
223
|
+
|
224
|
+
@rval = @rval | (byte << @rnbits)
|
225
|
+
@rnbits += 8
|
226
|
+
end
|
227
|
+
|
228
|
+
def write_big_endian_bits(val, nbits)
|
229
|
+
while nbits > 0
|
230
|
+
bits_req = 8 - @wnbits
|
231
|
+
if nbits >= bits_req
|
232
|
+
msb_bits = (val >> (nbits - bits_req)) & mask(bits_req)
|
233
|
+
nbits -= bits_req
|
234
|
+
val &= mask(nbits)
|
235
|
+
|
236
|
+
@wval = (@wval << bits_req) | msb_bits
|
237
|
+
@raw_io.write(@wval.chr)
|
238
|
+
|
239
|
+
@wval = 0
|
240
|
+
@wnbits = 0
|
241
|
+
else
|
242
|
+
@wval = (@wval << nbits) | val
|
243
|
+
@wnbits += nbits
|
244
|
+
nbits = 0
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def write_little_endian_bits(val, nbits)
|
250
|
+
while nbits > 0
|
251
|
+
bits_req = 8 - @wnbits
|
252
|
+
if nbits >= bits_req
|
253
|
+
lsb_bits = val & mask(bits_req)
|
254
|
+
nbits -= bits_req
|
255
|
+
val >>= bits_req
|
256
|
+
|
257
|
+
@wval = @wval | (lsb_bits << @wnbits)
|
258
|
+
@raw_io.write(@wval.chr)
|
259
|
+
|
260
|
+
@wval = 0
|
261
|
+
@wnbits = 0
|
262
|
+
else
|
263
|
+
@wval = @wval | (val << @wnbits)
|
264
|
+
@wnbits += nbits
|
265
|
+
nbits = 0
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def mask(nbits)
|
271
|
+
(1 << nbits) - 1
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|