bit-struct 0.13.1

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.
Files changed (58) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +102 -0
  3. data/README.txt +189 -0
  4. data/Rakefile +34 -0
  5. data/TODO +23 -0
  6. data/TODO-ALSO +71 -0
  7. data/examples/ara-player-data.rb +82 -0
  8. data/examples/bignum.rb +18 -0
  9. data/examples/bits.rb +19 -0
  10. data/examples/byte-bdy.rb +30 -0
  11. data/examples/field-ripper.rb +22 -0
  12. data/examples/fixed-point.rb +17 -0
  13. data/examples/ip.rb +81 -0
  14. data/examples/longlong.rb +30 -0
  15. data/examples/md.rb +23 -0
  16. data/examples/modular-def.rb +38 -0
  17. data/examples/native.rb +31 -0
  18. data/examples/nested.rb +33 -0
  19. data/examples/pad.rb +14 -0
  20. data/examples/ping-recv.rb +25 -0
  21. data/examples/ping.rb +73 -0
  22. data/examples/player-data.rb +75 -0
  23. data/examples/raw.rb +62 -0
  24. data/examples/rest.rb +30 -0
  25. data/examples/switch-endian.rb +49 -0
  26. data/examples/vector.rb +98 -0
  27. data/lib/bit-struct.rb +6 -0
  28. data/lib/bit-struct/bit-struct.rb +549 -0
  29. data/lib/bit-struct/char-field.rb +48 -0
  30. data/lib/bit-struct/fields.rb +273 -0
  31. data/lib/bit-struct/float-field.rb +61 -0
  32. data/lib/bit-struct/hex-octet-field.rb +20 -0
  33. data/lib/bit-struct/nested-field.rb +76 -0
  34. data/lib/bit-struct/octet-field.rb +45 -0
  35. data/lib/bit-struct/pad-field.rb +15 -0
  36. data/lib/bit-struct/signed-field.rb +258 -0
  37. data/lib/bit-struct/text-field.rb +44 -0
  38. data/lib/bit-struct/unsigned-field.rb +248 -0
  39. data/lib/bit-struct/vector-field.rb +77 -0
  40. data/lib/bit-struct/vector.rb +173 -0
  41. data/lib/bit-struct/yaml.rb +69 -0
  42. data/tasks/ann.rake +80 -0
  43. data/tasks/bones.rake +20 -0
  44. data/tasks/gem.rake +201 -0
  45. data/tasks/git.rake +40 -0
  46. data/tasks/notes.rake +27 -0
  47. data/tasks/post_load.rake +34 -0
  48. data/tasks/rdoc.rake +51 -0
  49. data/tasks/rubyforge.rake +55 -0
  50. data/tasks/setup.rb +292 -0
  51. data/tasks/spec.rake +54 -0
  52. data/tasks/svn.rake +47 -0
  53. data/tasks/test.rake +40 -0
  54. data/tasks/zentest.rake +36 -0
  55. data/test/test-endian.rb +39 -0
  56. data/test/test-vector.rb +38 -0
  57. data/test/test.rb +433 -0
  58. metadata +126 -0
@@ -0,0 +1,48 @@
1
+ class BitStruct
2
+ # Class for fixed length binary strings of characters.
3
+ # Declared with BitStruct.char.
4
+ class CharField < Field
5
+ #def self.default
6
+ # don't define this, since it must specify N nulls and we don't know N
7
+ #end
8
+
9
+ # Used in describe.
10
+ def self.class_name
11
+ @class_name ||= "char"
12
+ end
13
+
14
+ def add_accessors_to(cl, attr = name) # :nodoc:
15
+ unless offset % 8 == 0
16
+ raise ArgumentError,
17
+ "Bad offset, #{offset}, for #{self.class} #{name}." +
18
+ " Must be multiple of 8."
19
+ end
20
+
21
+ unless length % 8 == 0
22
+ raise ArgumentError,
23
+ "Bad length, #{length}, for #{self.class} #{name}." +
24
+ " Must be multiple of 8."
25
+ end
26
+
27
+ offset_byte = offset / 8
28
+ length_byte = length / 8
29
+ last_byte = offset_byte + length_byte - 1
30
+ byte_range = offset_byte..last_byte
31
+ val_byte_range = 0..length_byte-1
32
+
33
+ cl.class_eval do
34
+ define_method attr do ||
35
+ self[byte_range].to_s
36
+ end
37
+
38
+ define_method "#{attr}=" do |val|
39
+ val = val.to_s
40
+ if val.length < length_byte
41
+ val += "\0" * (length_byte - val.length)
42
+ end
43
+ self[byte_range] = val[val_byte_range]
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,273 @@
1
+ class BitStruct
2
+ class << self
3
+ # Define a char string field in the current subclass of BitStruct,
4
+ # with the given _name_ and _length_ (in bits). Trailing nulls _are_
5
+ # considered part of the string.
6
+ #
7
+ # If a class is provided, use it for the Field class.
8
+ # If a string is provided, use it for the display_name.
9
+ # If a hash is provided, use it for options.
10
+ #
11
+ # Note that the accessors have COPY semantics, not reference.
12
+ #
13
+ def char(name, length, *rest)
14
+ opts = parse_options(rest, name, CharField)
15
+ add_field(name, length, opts)
16
+ end
17
+ alias string char
18
+ autoload :CharField, "bit-struct/char-field"
19
+
20
+ # Define a floating point field in the current subclass of BitStruct,
21
+ # with the given _name_ and _length_ (in bits).
22
+ #
23
+ # If a class is provided, use it for the Field class.
24
+ # If a string is provided, use it for the display_name.
25
+ # If a hash is provided, use it for options.
26
+ #
27
+ # The <tt>:endian => :native</tt> option overrides the default of
28
+ # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
29
+ # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
30
+ # <tt>:little</tt>.
31
+ #
32
+ def float name, length, *rest
33
+ opts = parse_options(rest, name, FloatField)
34
+ add_field(name, length, opts)
35
+ end
36
+ autoload :FloatField, "bit-struct/float-field"
37
+
38
+ # Define an octet string field in the current subclass of BitStruct,
39
+ # with the given _name_ and _length_ (in bits). Trailing nulls are
40
+ # not considered part of the string. The field is accessed using
41
+ # period-separated hex digits.
42
+ #
43
+ # If a class is provided, use it for the Field class.
44
+ # If a string is provided, use it for the display_name.
45
+ # If a hash is provided, use it for options.
46
+ #
47
+ def hex_octets(name, length, *rest)
48
+ opts = parse_options(rest, name, HexOctetField)
49
+ add_field(name, length, opts)
50
+ end
51
+ autoload :HexOctetField, "bit-struct/hex-octet-field"
52
+
53
+ # Define a nested field in the current subclass of BitStruct,
54
+ # with the given _name_ and _nested_class_. Length is determined from
55
+ # _nested_class_.
56
+ #
57
+ # In _rest_:
58
+ #
59
+ # If a class is provided, use it for the Field class (i.e. <=NestedField).
60
+ # If a string is provided, use it for the display_name.
61
+ # If a hash is provided, use it for options.
62
+ #
63
+ # WARNING: the accessors have COPY semantics, not reference. When you call a
64
+ # reader method to get the nested structure, you get a *copy* of that data.
65
+ #
66
+ # For example:
67
+ #
68
+ # class Sub < BitStruct
69
+ # unsigned :x, 8
70
+ # end
71
+ #
72
+ # class A < BitStruct
73
+ # nest :n, Sub
74
+ # end
75
+ #
76
+ # a = A.new
77
+ #
78
+ # p a # ==> #<A n=#<Sub x=0>>
79
+ #
80
+ # # This fails to set x in a.
81
+ # a.n.x = 3
82
+ # p a # ==> #<A n=#<Sub x=0>>
83
+ #
84
+ # # This works
85
+ # n = a.n
86
+ # n.x = 3
87
+ # a.n = n
88
+ # p a # ==> #<A n=#<Sub x=3>>
89
+ #
90
+ def nest(name, nested_class, *rest)
91
+ opts = parse_options(rest, name, NestedField)
92
+ opts[:default] ||= nested_class.initial_value.dup
93
+ opts[:nested_class] = nested_class
94
+ field = add_field(name, nested_class.bit_length, opts)
95
+ field
96
+ end
97
+ alias struct nest
98
+ autoload :NestedField, "bit-struct/nested-field"
99
+
100
+ # Define an octet string field in the current subclass of BitStruct,
101
+ # with the given _name_ and _length_ (in bits). Trailing nulls are
102
+ # not considered part of the string. The field is accessed using
103
+ # period-separated decimal digits.
104
+ #
105
+ # If a class is provided, use it for the Field class.
106
+ # If a string is provided, use it for the display_name.
107
+ # If a hash is provided, use it for options.
108
+ #
109
+ def octets(name, length, *rest)
110
+ opts = parse_options(rest, name, OctetField)
111
+ add_field(name, length, opts)
112
+ end
113
+ autoload :OctetField, "bit-struct/octet-field"
114
+
115
+ # Define a padding field in the current subclass of BitStruct,
116
+ # with the given _name_ and _length_ (in bits).
117
+ #
118
+ # If a class is provided, use it for the Field class.
119
+ # If a string is provided, use it for the display_name.
120
+ # If a hash is provided, use it for options.
121
+ #
122
+ def pad(name, length, *rest)
123
+ opts = parse_options(rest, name, PadField)
124
+ add_field(name, length, opts)
125
+ end
126
+ alias padding pad
127
+ autoload :PadField, "bit-struct/pad-field"
128
+
129
+ # Define a signed integer field in the current subclass of BitStruct,
130
+ # with the given _name_ and _length_ (in bits).
131
+ #
132
+ # If a class is provided, use it for the Field class.
133
+ # If a string is provided, use it for the display_name.
134
+ # If a hash is provided, use it for options.
135
+ #
136
+ # SignedField adds the <tt>:fixed => divisor</tt> option, which specifies
137
+ # that the internally stored value is interpreted as a fixed point real
138
+ # number with the specified +divisor+.
139
+ #
140
+ # The <tt>:endian => :native</tt> option overrides the default of
141
+ # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
142
+ # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
143
+ # <tt>:little</tt>.
144
+ #
145
+ def signed name, length, *rest
146
+ opts = parse_options(rest, name, SignedField)
147
+ add_field(name, length, opts)
148
+ end
149
+ autoload :SignedField, "bit-struct/signed-field"
150
+
151
+ # Define a printable text string field in the current subclass of BitStruct,
152
+ # with the given _name_ and _length_ (in bits). Trailing nulls are
153
+ # _not_ considered part of the string.
154
+ #
155
+ # If a class is provided, use it for the Field class.
156
+ # If a string is provided, use it for the display_name.
157
+ # If a hash is provided, use it for options.
158
+ #
159
+ # Note that the accessors have COPY semantics, not reference.
160
+ #
161
+ def text(name, length, *rest)
162
+ opts = parse_options(rest, name, TextField)
163
+ add_field(name, length, opts)
164
+ end
165
+ autoload :TextField, "bit-struct/text-field"
166
+
167
+ # Define a unsigned integer field in the current subclass of BitStruct,
168
+ # with the given _name_ and _length_ (in bits).
169
+ #
170
+ # If a class is provided, use it for the Field class.
171
+ # If a string is provided, use it for the display_name.
172
+ # If a hash is provided, use it for options.
173
+ #
174
+ # UnsignedField adds the <tt>:fixed => divisor</tt> option, which specifies
175
+ # that the internally stored value is interpreted as a fixed point real
176
+ # number with the specified +divisor+.
177
+ #
178
+ # The <tt>:endian => :native</tt> option overrides the default of
179
+ # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
180
+ # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
181
+ # <tt>:little</tt>.
182
+ #
183
+ def unsigned name, length, *rest
184
+ opts = parse_options(rest, name, UnsignedField)
185
+ add_field(name, length, opts)
186
+ end
187
+ autoload :UnsignedField, "bit-struct/unsigned-field"
188
+
189
+ # Define a vector field in the current subclass of BitStruct,
190
+ # with the given _name_.
191
+ #
192
+ # If a class is provided, use it for the Vector class, otherwise
193
+ # the block must define the entry fields. The two forms looks like
194
+ # this:
195
+ #
196
+ # class Vec < BitStruct::Vector
197
+ # # these declarations apply to *each* entry in the vector:
198
+ # unsigned :x, 16
199
+ # signed :y, 32
200
+ # end
201
+ #
202
+ # class Packet < BitStruct
203
+ # # Using the Vec class defined above
204
+ # vector :v, Vec, "a vector", :length => 5
205
+ #
206
+ # # equivalently, using an anonymous subclass of BitStruct::Vector
207
+ # vector :v2, "a vector", :length => 5 do
208
+ # unsigned :x, 16
209
+ # signed :y, 32
210
+ # end
211
+ # end
212
+ #
213
+ # If a string is provided, use it for the display_name.
214
+ # If a hash is provided, use it for options.
215
+ #
216
+ # WARNING: the accessors have COPY semantics, not reference. When you call a
217
+ # reader method to get the vector structure, you get a *copy* of that data.
218
+ #
219
+ # For example, to modify the numeric fields in a Packet as defined above:
220
+ #
221
+ # pkt = Packet.new
222
+ # vec = pkt.v
223
+ # entry = vec[2]
224
+ # entry.x = 123
225
+ # entry.y = -456
226
+ # vec[2] = entry
227
+ # pkt.v = vec
228
+ #
229
+ def vector(name, *rest, &block)
230
+ opts = parse_options(rest, name, nil)
231
+ cl = opts[:field_class]
232
+ opts[:field_class] = VectorField
233
+
234
+ unless (block and not cl) or (cl and not block)
235
+ raise ArgumentError,
236
+ "vector must have either a class or a block, but not both"
237
+ end
238
+
239
+ case
240
+ when cl == nil
241
+ vector_class = Class.new(BitStruct::Vector)
242
+ vector_class.class_eval(&block)
243
+
244
+ when cl < BitStruct
245
+ vector_class = Class.new(BitStruct::Vector)
246
+ vector_class.struct_class cl
247
+
248
+ when cl < BitStruct::Vector
249
+ vector_class = cl
250
+
251
+ else raise ArgumentError, "Bad vector class: #{cl.inspect}"
252
+ end
253
+
254
+ vector_class.default_options default_options
255
+
256
+ length = opts[:length] ## what about :length => :lenfield
257
+ unless length
258
+ raise ArgumentError, "Must provide length as :length => N"
259
+ end
260
+
261
+ opts[:default] ||= vector_class.new(length) ## nil if variable length
262
+ opts[:vector_class] = vector_class
263
+
264
+ bit_length = vector_class.struct_class.round_byte_length * 8 * length
265
+
266
+ field = add_field(name, bit_length, opts)
267
+ field
268
+ end
269
+ autoload :VectorField, "bit-struct/vector-field"
270
+ end
271
+
272
+ autoload :Vector, "bit-struct/vector"
273
+ end
@@ -0,0 +1,61 @@
1
+ class BitStruct
2
+ # Class for floats (single and double precision) in network order.
3
+ # Declared with BitStruct.float.
4
+ class FloatField < Field
5
+ # Used in describe.
6
+ def self.class_name
7
+ @class_name ||= "float"
8
+ end
9
+
10
+ def add_accessors_to(cl, attr = name) # :nodoc:
11
+ unless offset % 8 == 0
12
+ raise ArgumentError,
13
+ "Bad offset, #{offset}, for #{self.class} #{name}." +
14
+ " Must be multiple of 8."
15
+ end
16
+
17
+ unless length == 32 or length == 64
18
+ raise ArgumentError,
19
+ "Bad length, #{length}, for #{self.class} #{name}." +
20
+ " Must be 32 or 64."
21
+ end
22
+
23
+ offset_byte = offset / 8
24
+ length_byte = length / 8
25
+ last_byte = offset_byte + length_byte - 1
26
+ byte_range = offset_byte..last_byte
27
+
28
+ endian = (options[:endian] || options["endian"]).to_s
29
+ case endian
30
+ when "native"
31
+ ctl = case length
32
+ when 32; "f"
33
+ when 64; "d"
34
+ end
35
+ when "little"
36
+ ctl = case length
37
+ when 32; "e"
38
+ when 64; "E"
39
+ end
40
+ when "network", "big", ""
41
+ ctl = case length
42
+ when 32; "g"
43
+ when 64; "G"
44
+ end
45
+ else
46
+ raise ArgumentError,
47
+ "Unrecognized endian option: #{endian.inspect}"
48
+ end
49
+
50
+ cl.class_eval do
51
+ define_method attr do ||
52
+ self[byte_range].unpack(ctl).first
53
+ end
54
+
55
+ define_method "#{attr}=" do |val|
56
+ self[byte_range] = [val].pack(ctl)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,20 @@
1
+ require 'bit-struct/char-field'
2
+
3
+ class BitStruct
4
+ # Class for char fields that can be accessed with values like
5
+ # "xx:xx:xx:xx", where each xx is up to 2 hex digits representing a
6
+ # single octet. The original string-based accessors are still available with
7
+ # the <tt>_chars</tt> suffix.
8
+ #
9
+ # Declared with BitStruct.hex_octets.
10
+ class HexOctetField < BitStruct::OctetField
11
+ # Used in describe.
12
+ def self.class_name
13
+ @class_name ||= "hex_octets"
14
+ end
15
+
16
+ SEPARATOR = ":"
17
+ FORMAT = "%02x"
18
+ BASE = 16
19
+ end
20
+ end
@@ -0,0 +1,76 @@
1
+ require 'bit-struct/bit-struct'
2
+
3
+ class BitStruct
4
+ # Class for nesting a BitStruct as a field within another BitStruct.
5
+ # Declared with BitStruct.nest.
6
+ class NestedField < Field
7
+ def initialize(*args)
8
+ super
9
+ end
10
+
11
+ # Used in describe.
12
+ def self.class_name
13
+ @class_name ||= "nest"
14
+ end
15
+
16
+ def class_name
17
+ @class_name ||= nested_class.name[/\w+$/]
18
+ end
19
+
20
+ def nested_class
21
+ @nested_class ||= options[:nested_class] || options["nested_class"]
22
+ end
23
+
24
+ def describe opts
25
+ if opts[:expand]
26
+ opts = opts.dup
27
+ opts[:byte_offset] = offset / 8
28
+ opts[:omit_header] = opts[:omit_footer] = true
29
+ nested_class.describe(nil, opts) {|desc| yield desc}
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def add_accessors_to(cl, attr = name) # :nodoc:
36
+ unless offset % 8 == 0
37
+ raise ArgumentError,
38
+ "Bad offset, #{offset}, for nested field #{name}." +
39
+ " Must be multiple of 8."
40
+ end
41
+
42
+ unless length % 8 == 0
43
+ raise ArgumentError,
44
+ "Bad length, #{length}, for nested field #{name}." +
45
+ " Must be multiple of 8."
46
+ end
47
+
48
+ offset_byte = offset / 8
49
+ length_byte = length / 8
50
+ last_byte = offset_byte + length_byte - 1
51
+ byte_range = offset_byte..last_byte
52
+
53
+ nc = nested_class
54
+
55
+ cl.class_eval do
56
+ define_method attr do ||
57
+ nc.new(self[byte_range])
58
+ end
59
+
60
+ define_method "#{attr}=" do |val|
61
+ if val.length != length_byte
62
+ raise ArgumentError, "Size mismatch in nested struct assignment " +
63
+ "to #{attr} with value #{val.inspect}"
64
+ end
65
+
66
+ if val.class != nc
67
+ warn "Type mismatch in nested struct assignment " +
68
+ "to #{attr} with value #{val.inspect}"
69
+ end
70
+
71
+ self[byte_range] = val
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end