bit-struct 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
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