bit-struct 0.15.0 → 0.16

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.
@@ -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