bit-struct 0.15.0 → 0.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@ class BitStruct
5
5
  #def self.default
6
6
  # don't define this, since it must specify N nulls and we don't know N
7
7
  #end
8
-
8
+
9
9
  # Used in describe.
10
10
  def self.class_name
11
11
  @class_name ||= "char"
@@ -17,13 +17,13 @@ class BitStruct
17
17
  "Bad offset, #{offset}, for #{self.class} #{name}." +
18
18
  " Must be multiple of 8."
19
19
  end
20
-
20
+
21
21
  unless length % 8 == 0
22
22
  raise ArgumentError,
23
23
  "Bad length, #{length}, for #{self.class} #{name}." +
24
24
  " Must be multiple of 8."
25
25
  end
26
-
26
+
27
27
  offset_byte = offset / 8
28
28
  length_byte = length / 8
29
29
  last_byte = offset_byte + length_byte - 1
@@ -0,0 +1,94 @@
1
+
2
+ class BitStruct
3
+ class Field
4
+ # Offset of field in bits.
5
+ attr_reader :offset
6
+
7
+ # Length of field in bits.
8
+ attr_reader :length
9
+ alias size length
10
+
11
+ # Name of field (used for its accessors).
12
+ attr_reader :name
13
+
14
+ # Options, such as :default (varies for each field subclass).
15
+ # In general, options can be provided as strings or as symbols.
16
+ attr_reader :options
17
+
18
+ # Display name of field (used for printing).
19
+ attr_reader :display_name
20
+
21
+ # Default value.
22
+ attr_reader :default
23
+
24
+ # Format for printed value of field.
25
+ attr_reader :format
26
+
27
+ # Subclasses can override this to define a default for all fields of this
28
+ # class, not just the one currently being added to a BitStruct class, a
29
+ # "default default" if you will. The global default, if #default returns
30
+ # nil, is to fill the field with zero. Most field classes just let this
31
+ # default stand. The default can be overridden per-field when a BitStruct
32
+ # class is defined.
33
+ def self.default; nil; end
34
+
35
+ # Used in describe.
36
+ def self.class_name
37
+ @class_name ||= name[/\w+$/]
38
+ end
39
+
40
+ # Used in describe. Can be overridden per-subclass, as in NestedField.
41
+ def class_name
42
+ self.class.class_name
43
+ end
44
+
45
+ # Yield the description of this field, as an array of 5 strings: byte
46
+ # offset, type, name, size, and description. The opts hash may have:
47
+ #
48
+ # :expand :: if the value is true, expand complex fields
49
+ #
50
+ # (Subclass implementations may yield more than once for complex fields.)
51
+ #
52
+ def describe opts
53
+ bits = size
54
+ if bits > 32 and bits % 8 == 0
55
+ len_str = "%dB" % (bits/8)
56
+ else
57
+ len_str = "%db" % bits
58
+ end
59
+
60
+ byte_offset = offset / 8 + (opts[:byte_offset] || 0)
61
+
62
+ yield ["@%d" % byte_offset, class_name, name, len_str, display_name]
63
+ end
64
+
65
+ # Options are _display_name_, _default_, and _format_ (subclasses of Field
66
+ # may add other options).
67
+ def initialize(offset, length, name, opts = {})
68
+ @offset, @length, @name, @options =
69
+ offset, length, name, opts
70
+
71
+ @display_name = opts[:display_name] || opts["display_name"]
72
+ @default = opts[:default] || opts["default"] || self.class.default
73
+ @format = opts[:format] || opts["format"]
74
+ end
75
+
76
+ # Inspect the value of this field in the specified _obj_.
77
+ def inspect_in_object(obj, opts)
78
+ val = obj.send(name)
79
+ str =
80
+ begin
81
+ val.inspect_with_options(opts)
82
+ rescue NoMethodError
83
+ val.inspect
84
+ end
85
+ (f=@format) ? (f % str) : str
86
+ end
87
+
88
+ # Normally, all fields show up in inspect, but some, such as padding,
89
+ # should not.
90
+ def inspectable?; true; end
91
+ end
92
+ end
93
+
94
+
@@ -94,24 +94,24 @@ class BitStruct
94
94
  # n.x = 3
95
95
  # a.n = n
96
96
  # p a # ==> #<A n=#<Sub x=3>>
97
- #
97
+ #
98
98
  def nest(name, *rest, &block)
99
99
  nested_class = rest.grep(Class).find {|cl| cl <= BitStruct}
100
100
  rest.delete nested_class
101
101
  opts = parse_options(rest, name, NestedField)
102
102
  nested_class = opts[:nested_class] ||= nested_class
103
-
103
+
104
104
  unless (block and not nested_class) or (nested_class and not block)
105
105
  raise ArgumentError,
106
106
  "nested field must have either a nested_class option or a block," +
107
107
  " but not both"
108
108
  end
109
-
109
+
110
110
  unless nested_class
111
111
  nested_class = Class.new(BitStruct)
112
112
  nested_class.class_eval(&block)
113
113
  end
114
-
114
+
115
115
  opts[:default] ||= nested_class.initial_value.dup
116
116
  opts[:nested_class] = nested_class
117
117
  field = add_field(name, nested_class.bit_length, opts)
@@ -250,17 +250,17 @@ class BitStruct
250
250
  # entry.y = -456
251
251
  # vec[2] = entry
252
252
  # pkt.v = vec
253
- #
253
+ #
254
254
  def vector(name, *rest, &block)
255
255
  opts = parse_options(rest, name, nil)
256
256
  cl = opts[:field_class]
257
257
  opts[:field_class] = VectorField
258
-
258
+
259
259
  unless (block and not cl) or (cl and not block)
260
260
  raise ArgumentError,
261
261
  "vector must have either a class or a block, but not both"
262
262
  end
263
-
263
+
264
264
  case
265
265
  when cl == nil
266
266
  vector_class = Class.new(BitStruct::Vector)
@@ -272,12 +272,12 @@ class BitStruct
272
272
 
273
273
  when cl < BitStruct::Vector
274
274
  vector_class = cl
275
-
275
+
276
276
  else raise ArgumentError, "Bad vector class: #{cl.inspect}"
277
277
  end
278
-
278
+
279
279
  vector_class.default_options default_options
280
-
280
+
281
281
  length = opts[:length] || rest.grep(Integer).first
282
282
  ## what about :length => :lenfield
283
283
  unless length
@@ -287,14 +287,14 @@ class BitStruct
287
287
 
288
288
  opts[:default] ||= vector_class.new(length) ## nil if variable length
289
289
  opts[:vector_class] = vector_class
290
-
290
+
291
291
  bit_length = vector_class.struct_class.round_byte_length * 8 * length
292
-
292
+
293
293
  field = add_field(name, bit_length, opts)
294
294
  field
295
295
  end
296
296
  BitStruct.autoload :VectorField, "bit-struct/vector-field"
297
297
  end
298
-
298
+
299
299
  autoload :Vector, "bit-struct/vector"
300
300
  end
@@ -6,20 +6,20 @@ class BitStruct
6
6
  def self.class_name
7
7
  @class_name ||= "float"
8
8
  end
9
-
9
+
10
10
  def add_accessors_to(cl, attr = name) # :nodoc:
11
11
  unless offset % 8 == 0
12
12
  raise ArgumentError,
13
13
  "Bad offset, #{offset}, for #{self.class} #{name}." +
14
14
  " Must be multiple of 8."
15
15
  end
16
-
16
+
17
17
  unless length == 32 or length == 64
18
18
  raise ArgumentError,
19
19
  "Bad length, #{length}, for #{self.class} #{name}." +
20
20
  " Must be 32 or 64."
21
21
  end
22
-
22
+
23
23
  offset_byte = offset / 8
24
24
  length_byte = length / 8
25
25
  last_byte = offset_byte + length_byte - 1
@@ -46,7 +46,7 @@ class BitStruct
46
46
  raise ArgumentError,
47
47
  "Unrecognized endian option: #{endian.inspect}"
48
48
  end
49
-
49
+
50
50
  cl.class_eval do
51
51
  define_method attr do ||
52
52
  self[byte_range].unpack(ctl).first
@@ -1,18 +1,18 @@
1
- require 'bit-struct/char-field'
1
+ require 'bit-struct/octet-field'
2
2
 
3
3
  class BitStruct
4
4
  # Class for char fields that can be accessed with values like
5
5
  # "xx:xx:xx:xx", where each xx is up to 2 hex digits representing a
6
6
  # single octet. The original string-based accessors are still available with
7
7
  # the <tt>_chars</tt> suffix.
8
- #
8
+ #
9
9
  # Declared with BitStruct.hex_octets.
10
10
  class HexOctetField < BitStruct::OctetField
11
11
  # Used in describe.
12
12
  def self.class_name
13
13
  @class_name ||= "hex_octets"
14
14
  end
15
-
15
+
16
16
  SEPARATOR = ":"
17
17
  FORMAT = "%02x"
18
18
  BASE = 16
@@ -7,16 +7,16 @@ class BitStruct
7
7
  def initialize(*args)
8
8
  super
9
9
  end
10
-
10
+
11
11
  # Used in describe.
12
12
  def self.class_name
13
13
  @class_name ||= "nest"
14
14
  end
15
-
15
+
16
16
  def class_name
17
17
  @class_name ||= nested_class.name[/\w+$/]
18
18
  end
19
-
19
+
20
20
  def nested_class
21
21
  @nested_class ||= options[:nested_class] || options["nested_class"]
22
22
  end
@@ -38,20 +38,20 @@ class BitStruct
38
38
  "Bad offset, #{offset}, for nested field #{name}." +
39
39
  " Must be multiple of 8."
40
40
  end
41
-
41
+
42
42
  unless length % 8 == 0
43
43
  raise ArgumentError,
44
44
  "Bad length, #{length}, for nested field #{name}." +
45
45
  " Must be multiple of 8."
46
46
  end
47
-
47
+
48
48
  offset_byte = offset / 8
49
49
  length_byte = length / 8
50
50
  last_byte = offset_byte + length_byte - 1
51
51
  byte_range = offset_byte..last_byte
52
52
 
53
53
  nc = nested_class
54
-
54
+
55
55
  cl.class_eval do
56
56
  define_method attr do ||
57
57
  nc.new(self[byte_range])
@@ -62,12 +62,12 @@ class BitStruct
62
62
  raise ArgumentError, "Size mismatch in nested struct assignment " +
63
63
  "to #{attr} with value #{val.inspect}"
64
64
  end
65
-
65
+
66
66
  if val.class != nc
67
67
  warn "Type mismatch in nested struct assignment " +
68
68
  "to #{attr} with value #{val.inspect}"
69
69
  end
70
-
70
+
71
71
  self[byte_range] = val
72
72
  end
73
73
  end
@@ -5,14 +5,14 @@ class BitStruct
5
5
  # "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal digits representing a
6
6
  # single octet. The original string-based accessors are still available with
7
7
  # the <tt>_chars</tt> suffix.
8
- #
8
+ #
9
9
  # Declared with BitStruct.octets.
10
10
  class OctetField < BitStruct::CharField
11
11
  # Used in describe.
12
12
  def self.class_name
13
13
  @class_name ||= "octets"
14
14
  end
15
-
15
+
16
16
  SEPARATOR = "."
17
17
  FORMAT = "%d"
18
18
  BASE = 10
@@ -23,7 +23,7 @@ class BitStruct
23
23
  sep = self.class::SEPARATOR
24
24
  base = self.class::BASE
25
25
  fmt = self.class::FORMAT
26
-
26
+
27
27
  cl.class_eval do
28
28
  define_method attr do ||
29
29
  ary = []
@@ -32,7 +32,7 @@ class BitStruct
32
32
  end
33
33
  ary.join(sep)
34
34
  end
35
-
35
+
36
36
  old_writer = "#{attr_chars}="
37
37
 
38
38
  define_method "#{attr}=" do |val|
@@ -6,25 +6,20 @@ class BitStruct
6
6
  def self.class_name
7
7
  @class_name ||= "signed"
8
8
  end
9
-
9
+
10
10
  def add_accessors_to(cl, attr = name) # :nodoc:
11
11
  offset_byte = offset / 8
12
12
  offset_bit = offset % 8
13
-
13
+
14
14
  length_bit = offset_bit + length
15
15
  length_byte = (length_bit/8.0).ceil
16
16
  last_byte = offset_byte + length_byte - 1
17
- max = 2**length-1
18
17
  mid = 2**(length-1)
19
18
  max_unsigned = 2**length
20
19
  to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
21
- # to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}
22
-
20
+
23
21
  divisor = options[:fixed] || options["fixed"]
24
22
  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
23
 
29
24
  endian = (options[:endian] || options["endian"]).to_s
30
25
  case endian
@@ -42,15 +37,15 @@ class BitStruct
42
37
  raise ArgumentError,
43
38
  "Unrecognized endian option: #{endian.inspect}"
44
39
  end
45
-
40
+
46
41
  data_is_big_endian =
47
42
  ([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
48
-
43
+
49
44
  if length_byte == 1
50
45
  rest = 8 - length_bit
51
46
  mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord
52
47
  mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord
53
-
48
+
54
49
  cl.class_eval do
55
50
  if divisor
56
51
  define_method attr do ||
@@ -74,11 +69,11 @@ class BitStruct
74
69
  end
75
70
  end
76
71
  end
77
-
72
+
78
73
  elsif offset_bit == 0 and length % 8 == 0
79
74
  field_length = length
80
75
  byte_range = offset_byte..last_byte
81
-
76
+
82
77
  cl.class_eval do
83
78
  case field_length
84
79
  when 8
@@ -91,7 +86,7 @@ class BitStruct
91
86
  val = (val * divisor).round
92
87
  self[offset_byte] = val
93
88
  end
94
-
89
+
95
90
  else
96
91
  define_method attr do ||
97
92
  to_signed[self[offset_byte]]
@@ -101,7 +96,7 @@ class BitStruct
101
96
  self[offset_byte] = val
102
97
  end
103
98
  end
104
-
99
+
105
100
  when 16, 32
106
101
  if divisor
107
102
  define_method attr do ||
@@ -112,7 +107,7 @@ class BitStruct
112
107
  val = (val * divisor).round
113
108
  self[byte_range] = [val].pack(ctl)
114
109
  end
115
-
110
+
116
111
  else
117
112
  define_method attr do ||
118
113
  to_signed[self[byte_range].unpack(ctl).first]
@@ -122,7 +117,7 @@ class BitStruct
122
117
  self[byte_range] = [val].pack(ctl)
123
118
  end
124
119
  end
125
-
120
+
126
121
  else
127
122
  reader_helper = proc do |substr|
128
123
  bytes = substr.unpack("C*")
@@ -131,7 +126,7 @@ class BitStruct
131
126
  (sum << 8) + byte
132
127
  end
133
128
  end
134
-
129
+
135
130
  writer_helper = proc do |val|
136
131
  bytes = []
137
132
  val += max_unsigned if val < 0
@@ -146,21 +141,21 @@ class BitStruct
146
141
  bytes.reverse! if data_is_big_endian
147
142
  bytes.pack("C*")
148
143
  end
149
-
144
+
150
145
  if divisor
151
146
  define_method attr do ||
152
147
  to_signed[reader_helper[self[byte_range]] / divisor_f]
153
148
  end
154
-
149
+
155
150
  define_method "#{attr}=" do |val|
156
151
  self[byte_range] = writer_helper[(val * divisor).round]
157
152
  end
158
-
153
+
159
154
  else
160
155
  define_method attr do ||
161
156
  to_signed[reader_helper[self[byte_range]]]
162
157
  end
163
-
158
+
164
159
  define_method "#{attr}=" do |val|
165
160
  self[byte_range] = writer_helper[val]
166
161
  end
@@ -171,10 +166,10 @@ class BitStruct
171
166
  elsif length_byte == 2 # unaligned field that fits within two whole bytes
172
167
  byte_range = offset_byte..last_byte
173
168
  rest = 16 - length_bit
174
-
169
+
175
170
  mask = ["0"*offset_bit + "1"*length + "0"*rest]
176
171
  mask = mask.pack("B16").unpack(ctl).first
177
-
172
+
178
173
  mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
179
174
  mask2 = mask2.pack("B16").unpack(ctl).first
180
175
 
@@ -204,14 +199,14 @@ class BitStruct
204
199
  end
205
200
  end
206
201
  end
207
-
202
+
208
203
  elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
209
204
  byte_range = offset_byte..last_byte
210
205
  rest = 32 - length_bit
211
-
206
+
212
207
  mask = ["0"*offset_bit + "1"*length + "0"*rest]
213
208
  mask = mask.pack("B32").unpack(ctl).first
214
-
209
+
215
210
  mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
216
211
  mask2 = mask2.pack("B32").unpack(ctl).first
217
212
 
@@ -249,7 +244,7 @@ class BitStruct
249
244
  end
250
245
  end
251
246
  end
252
-
247
+
253
248
  else
254
249
  raise "unsupported: #{inspect}"
255
250
  end