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,45 @@
1
+ require 'bit-struct/char-field'
2
+
3
+ class BitStruct
4
+ # Class for char fields that can be accessed with values like
5
+ # "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal 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.octets.
10
+ class OctetField < BitStruct::CharField
11
+ # Used in describe.
12
+ def self.class_name
13
+ @class_name ||= "octets"
14
+ end
15
+
16
+ SEPARATOR = "."
17
+ FORMAT = "%d"
18
+ BASE = 10
19
+
20
+ def add_accessors_to(cl, attr = name) # :nodoc:
21
+ attr_chars = "#{attr}_chars"
22
+ super(cl, attr_chars)
23
+ sep = self.class::SEPARATOR
24
+ base = self.class::BASE
25
+ fmt = self.class::FORMAT
26
+
27
+ cl.class_eval do
28
+ define_method attr do ||
29
+ ary = []
30
+ send(attr_chars).each_byte do |c|
31
+ ary << fmt % c
32
+ end
33
+ ary.join(sep)
34
+ end
35
+
36
+ old_writer = "#{attr_chars}="
37
+
38
+ define_method "#{attr}=" do |val|
39
+ data = val.split(sep).map{|s|s.to_i(base)}.pack("c*")
40
+ send(old_writer, data)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ class BitStruct
2
+ # Class for fixed length padding.
3
+ class PadField < Field
4
+ # Used in describe.
5
+ def self.class_name
6
+ @class_name ||= "padding"
7
+ end
8
+
9
+ def add_accessors_to(cl, attr = name) # :nodoc:
10
+ # No accessors for padding.
11
+ end
12
+
13
+ def inspectable?; false; end
14
+ end
15
+ end
@@ -0,0 +1,258 @@
1
+ class BitStruct
2
+ # Class for signed integers in network order, 1-16 bits, or 8n bits.
3
+ # Declared with BitStruct.signed.
4
+ class SignedField < Field
5
+ # Used in describe.
6
+ def self.class_name
7
+ @class_name ||= "signed"
8
+ end
9
+
10
+ def add_accessors_to(cl, attr = name) # :nodoc:
11
+ offset_byte = offset / 8
12
+ offset_bit = offset % 8
13
+
14
+ length_bit = offset_bit + length
15
+ length_byte = (length_bit/8.0).ceil
16
+ last_byte = offset_byte + length_byte - 1
17
+ max = 2**length-1
18
+ mid = 2**(length-1)
19
+ max_unsigned = 2**length
20
+ to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
21
+ # to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}
22
+
23
+ divisor = options[:fixed] || options["fixed"]
24
+ divisor_f = divisor && divisor.to_f
25
+ # if divisor and not divisor.is_a? Fixnum
26
+ # raise ArgumentError, "fixed-point divisor must be a fixnum"
27
+ # end
28
+
29
+ endian = (options[:endian] || options["endian"]).to_s
30
+ case endian
31
+ when "native"
32
+ ctl = length_byte <= 2 ? "s" : "l"
33
+ if length == 16 or length == 32
34
+ to_signed = proc {|n| n}
35
+ # with pack support, to_signed can be replaced with no-op
36
+ end
37
+ when "little"
38
+ ctl = length_byte <= 2 ? "v" : "V"
39
+ when "network", "big", ""
40
+ ctl = length_byte <= 2 ? "n" : "N"
41
+ else
42
+ raise ArgumentError,
43
+ "Unrecognized endian option: #{endian.inspect}"
44
+ end
45
+
46
+ data_is_big_endian =
47
+ ([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
48
+
49
+ if length_byte == 1
50
+ rest = 8 - length_bit
51
+ mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0]
52
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0]
53
+
54
+ cl.class_eval do
55
+ if divisor
56
+ define_method attr do ||
57
+ to_signed[(self[offset_byte] & mask) >> rest] / divisor_f
58
+ end
59
+
60
+ define_method "#{attr}=" do |val|
61
+ val = (val * divisor).round
62
+ self[offset_byte] =
63
+ (self[offset_byte] & mask2) | ((val<<rest) & mask)
64
+ end
65
+
66
+ else
67
+ define_method attr do ||
68
+ to_signed[(self[offset_byte] & mask) >> rest]
69
+ end
70
+
71
+ define_method "#{attr}=" do |val|
72
+ self[offset_byte] =
73
+ (self[offset_byte] & mask2) | ((val<<rest) & mask)
74
+ end
75
+ end
76
+ end
77
+
78
+ elsif offset_bit == 0 and length % 8 == 0
79
+ field_length = length
80
+ byte_range = offset_byte..last_byte
81
+
82
+ cl.class_eval do
83
+ case field_length
84
+ when 8
85
+ if divisor
86
+ define_method attr do ||
87
+ to_signed[self[offset_byte]] / divisor_f
88
+ end
89
+
90
+ define_method "#{attr}=" do |val|
91
+ val = (val * divisor).round
92
+ self[offset_byte] = val
93
+ end
94
+
95
+ else
96
+ define_method attr do ||
97
+ to_signed[self[offset_byte]]
98
+ end
99
+
100
+ define_method "#{attr}=" do |val|
101
+ self[offset_byte] = val
102
+ end
103
+ end
104
+
105
+ when 16, 32
106
+ if divisor
107
+ define_method attr do ||
108
+ to_signed[self[byte_range].unpack(ctl).first] / divisor_f
109
+ end
110
+
111
+ define_method "#{attr}=" do |val|
112
+ val = (val * divisor).round
113
+ self[byte_range] = [val].pack(ctl)
114
+ end
115
+
116
+ else
117
+ define_method attr do ||
118
+ to_signed[self[byte_range].unpack(ctl).first]
119
+ end
120
+
121
+ define_method "#{attr}=" do |val|
122
+ self[byte_range] = [val].pack(ctl)
123
+ end
124
+ end
125
+
126
+ else
127
+ reader_helper = proc do |substr|
128
+ bytes = substr.unpack("C*")
129
+ bytes.reverse! unless data_is_big_endian
130
+ bytes.inject do |sum, byte|
131
+ (sum << 8) + byte
132
+ end
133
+ end
134
+
135
+ writer_helper = proc do |val|
136
+ bytes = []
137
+ val += max_unsigned if val < 0
138
+ while val > 0
139
+ bytes.push val % 256
140
+ val = val >> 8
141
+ end
142
+ if bytes.length < length_byte
143
+ bytes.concat [0] * (length_byte - bytes.length)
144
+ end
145
+
146
+ bytes.reverse! if data_is_big_endian
147
+ bytes.pack("C*")
148
+ end
149
+
150
+ if divisor
151
+ define_method attr do ||
152
+ to_signed[reader_helper[self[byte_range]] / divisor_f]
153
+ end
154
+
155
+ define_method "#{attr}=" do |val|
156
+ self[byte_range] = writer_helper[(val * divisor).round]
157
+ end
158
+
159
+ else
160
+ define_method attr do ||
161
+ to_signed[reader_helper[self[byte_range]]]
162
+ end
163
+
164
+ define_method "#{attr}=" do |val|
165
+ self[byte_range] = writer_helper[val]
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ elsif length_byte == 2 # unaligned field that fits within two whole bytes
172
+ byte_range = offset_byte..last_byte
173
+ rest = 16 - length_bit
174
+
175
+ mask = ["0"*offset_bit + "1"*length + "0"*rest]
176
+ mask = mask.pack("B16").unpack(ctl).first
177
+
178
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
179
+ mask2 = mask2.pack("B16").unpack(ctl).first
180
+
181
+ cl.class_eval do
182
+ if divisor
183
+ define_method attr do ||
184
+ to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest] /
185
+ divisor_f
186
+ end
187
+
188
+ define_method "#{attr}=" do |val|
189
+ val = (val * divisor).round
190
+ x = (self[byte_range].unpack(ctl).first & mask2) |
191
+ ((val<<rest) & mask)
192
+ self[byte_range] = [x].pack(ctl)
193
+ end
194
+
195
+ else
196
+ define_method attr do ||
197
+ to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest]
198
+ end
199
+
200
+ define_method "#{attr}=" do |val|
201
+ x = (self[byte_range].unpack(ctl).first & mask2) |
202
+ ((val<<rest) & mask)
203
+ self[byte_range] = [x].pack(ctl)
204
+ end
205
+ end
206
+ end
207
+
208
+ elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
209
+ byte_range = offset_byte..last_byte
210
+ rest = 32 - length_bit
211
+
212
+ mask = ["0"*offset_bit + "1"*length + "0"*rest]
213
+ mask = mask.pack("B32").unpack(ctl).first
214
+
215
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
216
+ mask2 = mask2.pack("B32").unpack(ctl).first
217
+
218
+ cl.class_eval do
219
+ if divisor
220
+ define_method attr do ||
221
+ bytes = self[byte_range]
222
+ bytes << 0
223
+ to_signed[((bytes.unpack(ctl).first & mask) >> rest)] /
224
+ divisor_f
225
+ end
226
+
227
+ define_method "#{attr}=" do |val|
228
+ val = (val * divisor).round
229
+ bytes = self[byte_range]
230
+ bytes << 0
231
+ x = (bytes.unpack(ctl).first & mask2) |
232
+ ((val<<rest) & mask)
233
+ self[byte_range] = [x].pack(ctl)[0..2]
234
+ end
235
+
236
+ else
237
+ define_method attr do ||
238
+ bytes = self[byte_range]
239
+ bytes << 0
240
+ to_signed[(bytes.unpack(ctl).first & mask) >> rest]
241
+ end
242
+
243
+ define_method "#{attr}=" do |val|
244
+ bytes = self[byte_range]
245
+ bytes << 0
246
+ x = (bytes.unpack(ctl).first & mask2) |
247
+ ((val<<rest) & mask)
248
+ self[byte_range] = [x].pack(ctl)[0..2]
249
+ end
250
+ end
251
+ end
252
+
253
+ else
254
+ raise "unsupported: #{inspect}"
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,44 @@
1
+ class BitStruct
2
+ # Class for null-terminated printable text strings.
3
+ # Declared with BitStruct.text.
4
+ class TextField < Field
5
+ # Used in describe.
6
+ def self.class_name
7
+ @class_name ||= "text"
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 % 8 == 0
18
+ raise ArgumentError,
19
+ "Bad length, #{length}, for #{self.class} #{name}." +
20
+ " Must be multiple of 8."
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
+ val_byte_range = 0..length_byte-1
28
+
29
+ cl.class_eval do
30
+ define_method attr do ||
31
+ self[byte_range].sub(/\0*$/, "").to_s
32
+ end
33
+
34
+ define_method "#{attr}=" do |val|
35
+ val = val.to_s
36
+ if val.length < length_byte
37
+ val += "\0" * (length_byte - val.length)
38
+ end
39
+ self[byte_range] = val[val_byte_range]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,248 @@
1
+ class BitStruct
2
+ # Class for unsigned integers in network order, 1-16 bits, or 8n bits.
3
+ # Declared with BitStruct.unsigned.
4
+ class UnsignedField < Field
5
+ # Used in describe.
6
+ def self.class_name
7
+ @class_name ||= "unsigned"
8
+ end
9
+
10
+ def add_accessors_to(cl, attr = name) # :nodoc:
11
+ offset_byte = offset / 8
12
+ offset_bit = offset % 8
13
+
14
+ length_bit = offset_bit + length
15
+ length_byte = (length_bit/8.0).ceil
16
+ last_byte = offset_byte + length_byte - 1
17
+
18
+ divisor = options[:fixed] || options["fixed"]
19
+ divisor_f = divisor && divisor.to_f
20
+ # if divisor and not divisor.is_a? Fixnum
21
+ # raise ArgumentError, "fixed-point divisor must be a fixnum"
22
+ # end
23
+
24
+ endian = (options[:endian] || options["endian"]).to_s
25
+ case endian
26
+ when "native"
27
+ ctl = length_byte <= 2 ? "S" : "L"
28
+ when "little"
29
+ ctl = length_byte <= 2 ? "v" : "V"
30
+ when "network", "big", ""
31
+ ctl = length_byte <= 2 ? "n" : "N"
32
+ else
33
+ raise ArgumentError,
34
+ "Unrecognized endian option: #{endian.inspect}"
35
+ end
36
+
37
+ data_is_big_endian =
38
+ ([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
39
+
40
+ if length_byte == 1
41
+ rest = 8 - length_bit
42
+ mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0]
43
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0]
44
+
45
+ cl.class_eval do
46
+ if divisor
47
+ define_method attr do ||
48
+ ((self[offset_byte] & mask) >> rest) / divisor_f
49
+ end
50
+
51
+ define_method "#{attr}=" do |val|
52
+ val = (val * divisor).round
53
+ self[offset_byte] =
54
+ (self[offset_byte] & mask2) | ((val<<rest) & mask)
55
+ end
56
+
57
+ else
58
+ define_method attr do ||
59
+ (self[offset_byte] & mask) >> rest
60
+ end
61
+
62
+ define_method "#{attr}=" do |val|
63
+ self[offset_byte] =
64
+ (self[offset_byte] & mask2) | ((val<<rest) & mask)
65
+ end
66
+ end
67
+ end
68
+
69
+ elsif offset_bit == 0 and length % 8 == 0
70
+ field_length = length
71
+ byte_range = offset_byte..last_byte
72
+
73
+ cl.class_eval do
74
+ case field_length
75
+ when 8
76
+ if divisor
77
+ define_method attr do ||
78
+ self[offset_byte] / divisor_f
79
+ end
80
+
81
+ define_method "#{attr}=" do |val|
82
+ val = (val * divisor).round
83
+ self[offset_byte] = val
84
+ end
85
+
86
+ else
87
+ define_method attr do ||
88
+ self[offset_byte]
89
+ end
90
+
91
+ define_method "#{attr}=" do |val|
92
+ self[offset_byte] = val
93
+ end
94
+ end
95
+
96
+ when 16, 32
97
+ if divisor
98
+ define_method attr do ||
99
+ self[byte_range].unpack(ctl).first / divisor_f
100
+ end
101
+
102
+ define_method "#{attr}=" do |val|
103
+ val = (val * divisor).round
104
+ self[byte_range] = [val].pack(ctl)
105
+ end
106
+
107
+ else
108
+ define_method attr do ||
109
+ self[byte_range].unpack(ctl).first
110
+ end
111
+
112
+ define_method "#{attr}=" do |val|
113
+ self[byte_range] = [val].pack(ctl)
114
+ end
115
+ end
116
+
117
+ else
118
+ reader_helper = proc do |substr|
119
+ bytes = substr.unpack("C*")
120
+ bytes.reverse! unless data_is_big_endian
121
+ bytes.inject do |sum, byte|
122
+ (sum << 8) + byte
123
+ end
124
+ end
125
+
126
+ writer_helper = proc do |val|
127
+ bytes = []
128
+ while val > 0
129
+ bytes.push val % 256
130
+ val = val >> 8
131
+ end
132
+ if bytes.length < length_byte
133
+ bytes.concat [0] * (length_byte - bytes.length)
134
+ end
135
+
136
+ bytes.reverse! if data_is_big_endian
137
+ bytes.pack("C*")
138
+ end
139
+
140
+ if divisor
141
+ define_method attr do ||
142
+ reader_helper[self[byte_range]] / divisor_f
143
+ end
144
+
145
+ define_method "#{attr}=" do |val|
146
+ self[byte_range] = writer_helper[(val * divisor).round]
147
+ end
148
+
149
+ else
150
+ define_method attr do ||
151
+ reader_helper[self[byte_range]]
152
+ end
153
+
154
+ define_method "#{attr}=" do |val|
155
+ self[byte_range] = writer_helper[val]
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ elsif length_byte == 2 # unaligned field that fits within two whole bytes
162
+ byte_range = offset_byte..last_byte
163
+ rest = 16 - length_bit
164
+
165
+ mask = ["0"*offset_bit + "1"*length + "0"*rest]
166
+ mask = mask.pack("B16").unpack(ctl).first
167
+
168
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
169
+ mask2 = mask2.pack("B16").unpack(ctl).first
170
+
171
+ cl.class_eval do
172
+ if divisor
173
+ define_method attr do ||
174
+ ((self[byte_range].unpack(ctl).first & mask) >> rest) /
175
+ divisor_f
176
+ end
177
+
178
+ define_method "#{attr}=" do |val|
179
+ val = (val * divisor).round
180
+ x = (self[byte_range].unpack(ctl).first & mask2) |
181
+ ((val<<rest) & mask)
182
+ self[byte_range] = [x].pack(ctl)
183
+ end
184
+
185
+ else
186
+ define_method attr do ||
187
+ (self[byte_range].unpack(ctl).first & mask) >> rest
188
+ end
189
+
190
+ define_method "#{attr}=" do |val|
191
+ x = (self[byte_range].unpack(ctl).first & mask2) |
192
+ ((val<<rest) & mask)
193
+ self[byte_range] = [x].pack(ctl)
194
+ end
195
+ end
196
+ end
197
+
198
+ elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
199
+ byte_range = offset_byte..last_byte
200
+ rest = 32 - length_bit
201
+
202
+ mask = ["0"*offset_bit + "1"*length + "0"*rest]
203
+ mask = mask.pack("B32").unpack(ctl).first
204
+
205
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
206
+ mask2 = mask2.pack("B32").unpack(ctl).first
207
+
208
+ cl.class_eval do
209
+ if divisor
210
+ define_method attr do ||
211
+ bytes = self[byte_range]
212
+ bytes << 0
213
+ ((bytes.unpack(ctl).first & mask) >> rest) /
214
+ divisor_f
215
+ end
216
+
217
+ define_method "#{attr}=" do |val|
218
+ val = (val * divisor).round
219
+ bytes = self[byte_range]
220
+ bytes << 0
221
+ x = (bytes.unpack(ctl).first & mask2) |
222
+ ((val<<rest) & mask)
223
+ self[byte_range] = [x].pack(ctl)[0..2]
224
+ end
225
+
226
+ else
227
+ define_method attr do ||
228
+ bytes = self[byte_range]
229
+ bytes << 0
230
+ (bytes.unpack(ctl).first & mask) >> rest
231
+ end
232
+
233
+ define_method "#{attr}=" do |val|
234
+ bytes = self[byte_range]
235
+ bytes << 0
236
+ x = (bytes.unpack(ctl).first & mask2) |
237
+ ((val<<rest) & mask)
238
+ self[byte_range] = [x].pack(ctl)[0..2]
239
+ end
240
+ end
241
+ end
242
+
243
+ else
244
+ raise "unsupported: #{inspect}"
245
+ end
246
+ end
247
+ end
248
+ end