bin_struct 0.4.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aeb290e624ce4004c20cfaf40fc60ae43938c80eab8fd1df1e8aa908a75e26cf
4
- data.tar.gz: 8cb5dd0abdce7421b8269a4d35c14886f57cc6440a50c924b0937e5b30fe20ce
3
+ metadata.gz: f7d87b0168273fd5ed8cb81c89ecbc1726d93544bf70d57467490d5732033ebc
4
+ data.tar.gz: 5f14f873d4d45ee090b81033a952d62e3673334444b61a62825c9925d9574af6
5
5
  SHA512:
6
- metadata.gz: ecf6acc2f5c406556598212d22ad6257d22ed646c1128a145cff1730dc537bb7acfd40903f211aad28b11fa30c11324a98b98ecebb70ad7b84ea991bcf181f25
7
- data.tar.gz: 1dcfbb20a54f7c08d2794e0e8391b70c633d65f6ccbc41c372e46c77da6fb036fbfa9d1548462030b86603e3e41b132019da70b3c2d603f7f21116bdc842ec14
6
+ metadata.gz: f82248767c36240d4774ebab9cffb25c05efb328981a068349256442034f5ff510abc72693398b6e320b9ab8f59ed3b9d87e8b6d9f44f30884ff62afb960c531
7
+ data.tar.gz: 80a67a76677dbad046463541b7e9d8208033243c9a0f4633d48e25e1375adc0085f261d50e3b53fdc34b2c89152652b30f13d3432ce59c5ebe0f3918bf071b4a
data/CHANGELOG.md CHANGED
@@ -3,6 +3,27 @@
3
3
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
4
4
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## 0.5.1 - 2025-04-21
7
+
8
+ ### Fixed
9
+
10
+ - Struct defined with bit attributes could not be cloned.
11
+
12
+ ## 0.5.0 - 2025-02-17
13
+
14
+ ### Added
15
+
16
+ - Add `String#b` to mimic Ruby's `String`
17
+ - Add a lot of examples in YARD documentation. These examples are checked using yard-doctest.
18
+
19
+ ### Deprecated
20
+
21
+ - Deprecate `BinStruct.force_binary` and `Struct.force_binary` in favor of Ruby's `String#b`
22
+
23
+ ### Fixed
24
+
25
+ - Fix `String#to_s` when static_length is set. `#to_s` was not aware of static length option.
26
+
6
27
  ## 0.4.0 - 2025-02-13
7
28
 
8
29
  ### Added
@@ -6,13 +6,10 @@
6
6
  # Copyright (C) 2024 LemonTree55 <lenontree@proton.me>
7
7
  # This program is published under MIT license.
8
8
 
9
- # BinStruct module
10
- # @author LemonTree55
11
9
  module BinStruct
12
10
  # @abstract Base class to define type-length-value data.
13
11
  #
14
- # ===Usage
15
- # To simply define a new TLV class, do:
12
+ # You have to define a concrete class from AbstractTLV
16
13
  # MyTLV = BinStruct::AbstractTLV.create
17
14
  # MyTLV.define_type_enum 'one' => 1, 'two' => 2
18
15
  # This will define a new +MyTLV+ class, subclass of {AbstractTLV}. This class will
@@ -23,26 +20,33 @@ module BinStruct
23
20
  # +.define_type_enum+ is, here, necessary to define enum hash to be used
24
21
  # for +#type+ accessor, as this one is defined as an {Enum}.
25
22
  #
26
- # This new defined class may now be easily used:
23
+ # @example Basic usage
24
+ # MyTLV = BinStruct::AbstractTLV.create
25
+ # MyTLV.define_type_enum 'one' => 1, 'two' => 2
26
+ #
27
27
  # tlv = MyTLV.new(type: 1, value: 'abcd') # automagically set #length from value
28
28
  # tlv.type #=> 1
29
29
  # tlv.human_type #=> 'one'
30
30
  # tlv.length #=> 4
31
31
  # tlv.value #=> "abcd"
32
32
  #
33
- # ===Advanced usage
34
- # Each attribute's type may be changed at generating TLV class:
33
+ # @example Change attribute types
34
+ # # Change type for each attribute
35
+ # # Type and length are 16-bit big endian integers
36
+ # # Value is a OUI
35
37
  # MyTLV = BinStruct::AbstractTLV.create(type_class: BinStruct::Int16,
36
38
  # length_class: BinStruct::Int16,
37
- # value_class: PacketGen::Header::IP::Addr)
38
- # tlv = MyTLV.new(type: 1, value: '1.2.3.4')
39
+ # value_class: BinStruct::OUI)
40
+ # tlv = MyTLV.new(type: 1, value: '01:02:03')
39
41
  # tlv.type #=> 1
40
- # tlv.length #=> 4
41
- # tlv.value #=> '1.2.3.4'
42
- # tlv.to_s #=> "\x00\x01\x00\x04\x01\x02\x03\x04"
42
+ # tlv.length #=> 3
43
+ # tlv.value #=> '01:02:03'
44
+ # tlv.to_s #=> "\x00\x01\x00\x03\x01\x02\x03"
43
45
  #
44
- # Some aliases may also be defined. For example, to create a TLV type
45
- # whose +type+ attribute should be named +code+:
46
+ # @example Using aliases
47
+ # # Type and length are 16-bit big endian integers
48
+ # # Value is a string
49
+ # # code is an alias for type
46
50
  # MyTLV = BinStruct::AbstractTLV.create(type_class: BinStruct::Int16,
47
51
  # length_class: BinStruct::Int16,
48
52
  # aliases: { code: :type })
@@ -87,6 +87,23 @@ module BinStruct
87
87
 
88
88
  # @param [Hash] options
89
89
  # @option options [Int] counter Int object used as a counter for this set
90
+ # @example counter example
91
+ # # Define a counter
92
+ # counter = BinStruct::Int8.new
93
+ # counter.to_i # => 0
94
+ #
95
+ # # Define an array with associated counter
96
+ # ary = BinStruct::ArrayOfInt8.new(counter: counter)
97
+ # # Add 2 elements to arry, increment counter twice
98
+ # ary.read([1, 2])
99
+ # counter.to_i #=> 2
100
+ # # Add a third element
101
+ # ary << BinStruct::Int8.new(value: 42)
102
+ # counter.to_i #=> 3
103
+ #
104
+ # # push does not increment the counter
105
+ # ary.push(BinStruct::Int8.new(value: 100))
106
+ # counter.to_i #=> 3
90
107
  def initialize(options = {})
91
108
  @counter = options[:counter]
92
109
  @array = []
@@ -271,30 +288,65 @@ module BinStruct
271
288
  end
272
289
 
273
290
  # Specialized {Array} to handle serie of {Int8}.
291
+ # @example
292
+ # ary = BinStruct::ArrayOfInt8.new
293
+ # ary.read([0, 1, 2])
294
+ # ary.to_s #=> "\x00\x01\x02".b
295
+ #
296
+ # ary.read("\x05\x06")
297
+ # ary.map(&:to_i) #=> [5, 6]
274
298
  class ArrayOfInt8 < Array
275
299
  include ArrayOfIntMixin
276
300
  set_of Int8
277
301
  end
278
302
 
279
303
  # Specialized {Array} to handle serie of {Int16}.
304
+ # @example
305
+ # ary = BinStruct::ArrayOfInt16.new
306
+ # ary.read([0, 1, 2])
307
+ # ary.to_s #=> "\x00\x00\x00\x01\x00\x02".b
308
+ #
309
+ # ary.read("\x05\x06")
310
+ # ary.map(&:to_i) #=> [0x0506]
280
311
  class ArrayOfInt16 < Array
281
312
  include ArrayOfIntMixin
282
313
  set_of Int16
283
314
  end
284
315
 
285
316
  # Specialized {Array} to handle serie of {Int16le}.
317
+ # @example
318
+ # ary = BinStruct::ArrayOfInt16le.new
319
+ # ary.read([0, 1, 2])
320
+ # ary.to_s #=> "\x00\x00\x01\x00\x02\x00".b
321
+ #
322
+ # ary.read("\x05\x06")
323
+ # ary.map(&:to_i) #=> [0x0605]
286
324
  class ArrayOfInt16le < Array
287
325
  include ArrayOfIntMixin
288
326
  set_of Int16le
289
327
  end
290
328
 
291
329
  # Specialized {Array} to handle serie of {Int32}.
330
+ # @example
331
+ # ary = BinStruct::ArrayOfInt32.new
332
+ # ary.read([0, 1, 2])
333
+ # ary.to_s #=> "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02".b
334
+ #
335
+ # ary.read("\x00\x00\x05\x06")
336
+ # ary.map(&:to_i) #=> [0x00000506]
292
337
  class ArrayOfInt32 < BinStruct::Array
293
338
  include ArrayOfIntMixin
294
339
  set_of Int32
295
340
  end
296
341
 
297
342
  # Specialized {Array} to handle serie of {Int32le}.
343
+ # @example
344
+ # ary = BinStruct::ArrayOfInt32le.new
345
+ # ary.read([0, 1, 2])
346
+ # ary.to_s #=> "\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00".b
347
+ #
348
+ # ary.read("\x00\x00\x05\x06")
349
+ # ary.map(&:to_i) #=> [0x06050000]
298
350
  class ArrayOfInt32le < BinStruct::Array
299
351
  include ArrayOfIntMixin
300
352
  set_of Int32le
@@ -7,13 +7,23 @@
7
7
  require 'digest'
8
8
 
9
9
  module BinStruct
10
- # Define a bitfield attribute to embed in a {Struct}.
10
+ # Define a bitfield attribute to embed in a {Struct}. Use it through {Struct.define_bit_attr}
11
11
  #
12
+ # @example
12
13
  # class MyStruct < BinStruct::Struct
13
14
  # # Create a 32-bit bitfield attribute, with fields a (16 bits), b and c (4 bits each) and d (8 bits).
14
15
  # # a is the leftmost field in bitfield, and d the rightmost one.
15
- # define_attr :int32, BinStruct::BitAttr.create(width: 32, a: 16, b: 4, c: 4, d:8)
16
+ # define_bit_attr :int32, width: 32, a: 16, b: 4, c: 4, d:8
16
17
  # end
18
+ #
19
+ # s1 = MyStruct.new(int32: 0x12345678)
20
+ # s1.a #=> 0x1234
21
+ # s1.b #=> 5
22
+ # s1.c #=> 6
23
+ # s1.d #=> 0x78
24
+ #
25
+ # s2 = MyStruct.new(a: 0x1234, d: 0x42)
26
+ # s2.to_s #=> "\x12\x34\x00\x42".b
17
27
  # @since 0.3.0
18
28
  # @abstract Subclasses must de derived using {.create}.
19
29
  # @author LemonTree55
@@ -22,8 +32,6 @@ module BinStruct
22
32
 
23
33
  # @return [Integer] width in bits of bit attribute
24
34
  attr_reader :width
25
- # @return [::Array[Symbol]]
26
- attr_reader :bit_methods
27
35
 
28
36
  # @private
29
37
  Parameters = Struct.new(:width, :fields, :int)
@@ -35,6 +43,10 @@ module BinStruct
35
43
  # @return [Parameters]
36
44
  attr_reader :parameters
37
45
 
46
+ # @private
47
+ # @return [::Array[Symbol]]
48
+ attr_reader :bit_methods
49
+
38
50
  # Create a new {BitAttr} subclass with specified parameters
39
51
  # @param [Integer] width size of bitfields in bits. Must be a size of an {Int} (8, 16, 24, 32 or 64 bits).
40
52
  # @param [:big,:little,:native] endian endianess of bit attribute as an integer
@@ -54,10 +66,7 @@ module BinStruct
54
66
  total_size = fields.reduce(0) { |acc, ary| acc + ary.last }
55
67
  raise ArgumentError, "sum of bitfield sizes is not equal to #{width}" unless total_size == width
56
68
 
57
- cache[hsh] = Class.new(self) do
58
- int_klass = BinStruct.const_get("Int#{width}")
59
- @parameters = Parameters.new(width, fields, int_klass.new(endian: endian)).freeze
60
- end
69
+ cache[hsh] = create_subclass(width, endian, fields.dup.freeze)
61
70
  end
62
71
 
63
72
  private
@@ -74,6 +83,39 @@ module BinStruct
74
83
  def compute_hash(*params)
75
84
  Digest::MD5.digest(Marshal.dump(params))
76
85
  end
86
+
87
+ def create_subclass(width, endian, fields)
88
+ klass = Class.new(self) do
89
+ int_klass = BinStruct.const_get("Int#{width}")
90
+ @parameters = Parameters.new(width, fields, int_klass.new(endian: endian)).freeze
91
+ @bit_methods = []
92
+ end
93
+
94
+ define_methods(klass, fields)
95
+ klass
96
+ end
97
+
98
+ # @param [Class] {BitAttr} subclass
99
+ # @param [Hash{Symbol => Integer}] fields
100
+ # @return [void]
101
+ def define_methods(klass, fields)
102
+ define_str = +''
103
+ fields.each do |name, size|
104
+ define_str << "def #{name}; @data[#{name.inspect}]; end\n"
105
+ klass.bit_methods << name
106
+ klass.bit_methods << :"#{name}="
107
+
108
+ if size == 1
109
+ define_str << "def #{name}?; @data[#{name.inspect}] != 0; end\n"
110
+ klass.bit_methods << :"#{name}?"
111
+ define_str << "def #{name}=(val); v = case val when TrueClass; 1 when FalseClass; 0 else val end; " \
112
+ "@data[#{name.inspect}] = v; end\n"
113
+ else
114
+ define_str << "def #{name}=(val); @data[#{name.inspect}] = val; end\n"
115
+ end
116
+ end
117
+ klass.class_eval(define_str)
118
+ end
77
119
  end
78
120
 
79
121
  # Initialize bit attribute
@@ -91,13 +133,21 @@ module BinStruct
91
133
  @data = {}
92
134
  @bit_methods = []
93
135
 
94
- parameters.fields.each do |name, size|
136
+ parameters.fields.each_key do |name|
95
137
  @data[name] = opts[name] || 0
96
- define_methods(name, size)
97
138
  end
98
139
  @bit_methods.freeze
99
140
  end
100
141
 
142
+ def initialize_copy(_other)
143
+ @data = @data.dup
144
+ end
145
+
146
+ # @return [::Array[Symbol]]
147
+ def bit_methods
148
+ self.class.bit_methods
149
+ end
150
+
101
151
  # Get type name
102
152
  # @return [::String]
103
153
  def type_name
@@ -165,24 +215,5 @@ module BinStruct
165
215
 
166
216
  self
167
217
  end
168
-
169
- # @param [Symbol] name
170
- # @return [void]
171
- def define_methods(name, size)
172
- instance_eval "def #{name}; @data[#{name.inspect}]; end\n", __FILE__, __LINE__ # def name; data[:name]; end
173
- bit_methods << name
174
- bit_methods << :"#{name}="
175
-
176
- # rubocop:disable Style/DocumentDynamicEvalDefinition
177
- if size == 1
178
- instance_eval "def #{name}?; @data[#{name.inspect}] != 0; end\n", __FILE__, __LINE__
179
- instance_eval "def #{name}=(val); v = case val when TrueClass; 1 when FalseClass; 0 else val end; " \
180
- "@data[#{name.inspect}] = v; end", __FILE__, __LINE__ - 1
181
- bit_methods << :"#{name}?"
182
- else
183
- instance_eval "def #{name}=(val); @data[#{name.inspect}] = val; end", __FILE__, __LINE__
184
- end
185
- # rubocop:enable Style/DocumentDynamicEvalDefinition
186
- end
187
218
  end
188
219
  end
@@ -10,6 +10,9 @@ require 'forwardable'
10
10
 
11
11
  module BinStruct
12
12
  # This class handles null-terminated strings (aka C strings).
13
+ # # @example
14
+ # cstr = BinStruct::CString.new(value: 'abcd')
15
+ # cstr.to_s #=> "abcd\x00".b
13
16
  # @author Sylvain Daubert (2016-2024)
14
17
  # @author LemonTree55
15
18
  class CString
@@ -102,7 +105,7 @@ module BinStruct
102
105
  else
103
106
  s = "#{string}\x00"
104
107
  end
105
- BinStruct.force_binary(s)
108
+ s.b
106
109
  end
107
110
 
108
111
  # Append the given string to CString
@@ -134,7 +137,7 @@ module BinStruct
134
137
  # @param [::String] str
135
138
  # @return [self]
136
139
  def from_human(str)
137
- read str
140
+ read(str)
138
141
  end
139
142
 
140
143
  # Get human-readable string
@@ -146,8 +149,7 @@ module BinStruct
146
149
  private
147
150
 
148
151
  def register_internal_string(str)
149
- @string = str
150
- BinStruct.force_binary(@string)
152
+ @string = str.b
151
153
  end
152
154
 
153
155
  def remove_null_character
@@ -12,21 +12,23 @@ module BinStruct
12
12
  # An {Enum} type is used to handle an {Int} attribute with limited
13
13
  # and named values.
14
14
  #
15
- # == Simple example
16
- # enum = Int8Enum.new('low' => 0, 'medium' => 1, 'high' => 2})
17
- # In this example, +enum+ is a 8-bit attribute which may take one
18
- # among three values: +low+, +medium+ or +high+:
19
- # enum.value = 'high'
20
- # enum.value # => 2
21
- # enum.value = 1
22
- # enum.value # => 1
23
- # enum.to_human # => "medium"
24
15
  # Setting an unknown name will raise an exception:
25
16
  # enum.value = 'unknown' # => raise!
26
17
  # But {#read} and {#value=} will not raise when reading/setting an out-of-bound integer. This
27
18
  # to enable decoding (or forging) of bad packets.
28
19
  # enum.read("\x05".b).value # => 5
29
20
  # enum.value = 4 # => 4
21
+ #
22
+ # @example Simple example
23
+ # # Define an enum on 8-bit integer. It may take one among
24
+ # # three values: low, medium or high
25
+ # enum = BinStruct::Int8Enum.new(enum: {'low' => 0, 'medium' => 1, 'high' => 2})
26
+ # enum.value = 'high'
27
+ # enum.value # => 2
28
+ # enum.value = 1
29
+ # enum.value # => 1
30
+ # enum.to_human # => "medium"
31
+ #
30
32
  # @author Sylvain Daubert (2016-2024)
31
33
  # @author LemonTree55
32
34
  class Enum < Int
@@ -9,7 +9,7 @@
9
9
  module BinStruct
10
10
  # This module is a mixin adding +length_from+ capacity to a type.
11
11
  # +length_from+ capacity is the capacity, for a type, to gets its
12
- # length from another object.
12
+ # length from another object. For an example, see {String}.
13
13
  # @author Sylvain Daubert (2016-2024)
14
14
  # @author LemonTree55
15
15
  module LengthFrom
@@ -30,7 +30,7 @@ module BinStruct
30
30
  # @param [#to_s] str
31
31
  # @return [::String]
32
32
  def read_with_length_from(str)
33
- s = BinStruct.force_binary(str.to_s)
33
+ s = str.to_s.b
34
34
  s[0, sz_to_read]
35
35
  end
36
36
 
@@ -8,10 +8,11 @@
8
8
 
9
9
  module BinStruct
10
10
  # OUI type, defined as a set of 3 bytes
11
- # oui = OUI.new
11
+ # @example
12
+ # oui = BinStruct::OUI.new
12
13
  # oui.from_human('00:01:02')
13
14
  # oui.to_human # => "00:01:02"
14
- # oui.to_s # => "\x00\x01\x03"
15
+ # oui.to_s # => "\x00\x01\x02".b
15
16
  # @author Sylvain Daubert (2016-2024)
16
17
  # @author LemonTree55
17
18
  class OUI < Struct
@@ -10,6 +10,33 @@ require 'forwardable'
10
10
 
11
11
  module BinStruct
12
12
  # This class mimics regular String, but it is {Structable}.
13
+ #
14
+ # It may take its length from another field ({LengthFrom} capacity). It may also has a static length
15
+ # (i.e. string has always the same length, whatever its content is).
16
+ #
17
+ # @example Basic example
18
+ # str = BinStruct::String.new
19
+ # str.read("abc")
20
+ # str.to_s #=> "abc".b
21
+ #
22
+ # @example LengthFrom example
23
+ # class StrLen < BinStruct::Struct
24
+ # define_attr :length, BinStruct::Int8
25
+ # define_attr :str, BinStruct::String, builder: ->(h, t) { t.new(length_from: h[:length]) }
26
+ # end
27
+ #
28
+ # # Length is 3, but rest of data is 4 byte long. Only 3 bytes will be read.
29
+ # s = StrLen.new.read("\x03abcd")
30
+ # s.length #=> 3
31
+ # s.str.to_s #=> "abc".b
32
+ # s.to_s # => "\x03abc".b
33
+ #
34
+ # @example static length example
35
+ # s = BinStruct::String.new(static_length: 10)
36
+ # s.sz #=> 10
37
+ # s.to_s #=> "\0\0\0\0\0\0\0\0\0\0".b
38
+ # s.read("01234567890123456789")
39
+ # s.to_s #=> "0123456789".b
13
40
  # @author Sylvain Daubert (2016-2024)
14
41
  # @author LemonTree55
15
42
  class String
@@ -19,7 +46,7 @@ module BinStruct
19
46
 
20
47
  def_delegators :@string, :[], :length, :size, :inspect, :==,
21
48
  :unpack, :force_encoding, :encoding, :index, :empty?,
22
- :encode, :slice, :slice!, :[]=
49
+ :encode, :slice, :slice!, :[]=, :b
23
50
 
24
51
  # Underlying Ruby String
25
52
  # @return [::String]
@@ -83,25 +110,29 @@ module BinStruct
83
110
  # @param [#to_s] str
84
111
  # @return [self]
85
112
  def <<(str)
86
- @string << BinStruct.force_binary(str.to_s)
113
+ @string << str.to_s.b
87
114
  self
88
115
  end
89
116
 
90
117
  # Generate "binary" string
91
118
  # @return [::String]
92
119
  def to_s
93
- @string
120
+ if static_length?
121
+ s = @string[0, static_length]
122
+ s << ("\x00" * (static_length - s.length))
123
+ s.b
124
+ else
125
+ @string.b
126
+ end
94
127
  end
95
128
 
96
- alias sz length
97
129
  alias to_human to_s
98
130
  alias from_human read
99
131
 
100
132
  private
101
133
 
102
134
  def register_internal_string(str)
103
- @string = str
104
- BinStruct.force_binary(@string)
135
+ @string = str.b
105
136
  end
106
137
  end
107
138
  end
@@ -32,8 +32,8 @@ module BinStruct
32
32
  # Attributes may also be accessed through {#[]} ans {#[]=}. These methods give access
33
33
  # to type object:
34
34
  # mybs = MyBinaryStructure.new
35
- # mybs.attr1 # => Integer
36
- # mybs[:attr1] # => BinStruct::Int8
35
+ # mybs.attr1.class # => Integer
36
+ # mybs[:attr1].class # => BinStruct::Int8
37
37
  #
38
38
  # {#initialize} accepts an option hash to populate attributes. Keys are attribute
39
39
  # name symbols, and values are those expected by writer accessor.
@@ -57,7 +57,7 @@ module BinStruct
57
57
  # +#body+, +#body=+, +#mac_addr+ and +#mac_addr=+.
58
58
  #
59
59
  # {.define_attr} has many options (third optional Hash argument):
60
- # * +:default+ gives default attribute value. It may be a simple value (an Integer
60
+ # * +:default+ to define default attribute value. It may be a simple value (an Integer
61
61
  # for an Int attribute, for example) or a lambda,
62
62
  # * +:builder+ to give a builder/constructor lambda to create attribute. The lambda
63
63
  # takes 2 arguments: {Struct} subclass object owning attribute, and type class as passes
@@ -97,7 +97,7 @@ module BinStruct
97
97
  # * {.define_attr_before} and {.define_bit_attr_before} to define a new attribute before an existing one,
98
98
  # * {.define_attr_after} and {.define_bit_attr_after} to define a new attribute after an existing onr,
99
99
  # * {.remove_attr} to remove an existing attribute,
100
- # * {.uptade_attr} to change options of an attribute (but not its type),
100
+ # * {.update_attr} to change options of an attribute (but not its type),
101
101
  #
102
102
  # @author Sylvain Daubert (2016-2024)
103
103
  # @author LemonTree55
@@ -247,7 +247,7 @@ module BinStruct
247
247
  # class MyHeader < BinStruct::Struct
248
248
  # # define a 16-bit attribute named :flag
249
249
  # # flag1, flag2 and flag3 are 1-bit attributes
250
- # # type and stype are 3-bit attributes. reserved is a 7-bit attribute
250
+ # # type and stype are 3-bit attributes, reserved is a 7-bit attribute
251
251
  # define_bit_attr :flags, flag1: 1, flag2: 1, flag3: 1, type: 3, stype: 3, reserved: 7
252
252
  # end
253
253
  # A bit attribute of size 1 bit defines 3 methods:
@@ -268,13 +268,15 @@ module BinStruct
268
268
  bit_attr_klass = BitAttr.create(width: width, endian: endian, **fields)
269
269
  define_attr(attr, bit_attr_klass, default: default)
270
270
  fields.each_key { |field| register_bit_attr_field(attr, field) }
271
+ define_str = +''
271
272
  bit_attr_klass.new.bit_methods.each do |meth|
272
- if meth.to_s.end_with?('=')
273
- define_method(meth) { |value| self[attr].send(meth, value) }
274
- else
275
- define_method(meth) { self[attr].send(meth) }
276
- end
273
+ define_str << if meth.to_s.end_with?('=')
274
+ "def #{meth}(value); self[:#{attr}].#{meth}(value); end\n"
275
+ else
276
+ "def #{meth}; self[:#{attr}].#{meth}; end\n"
277
+ end
277
278
  end
279
+ class_eval(define_str)
278
280
  end
279
281
 
280
282
  # Define a bit attribute, before another attribute
@@ -443,12 +445,11 @@ module BinStruct
443
445
  def read(str)
444
446
  return self if str.nil?
445
447
 
446
- force_binary(str)
447
448
  start = 0
448
449
  attributes.each do |attr|
449
450
  next unless present?(attr)
450
451
 
451
- obj = self[attr].read(str[start..])
452
+ obj = self[attr].read(str.b[start..])
452
453
  start += self[attr].sz
453
454
  self[attr] = obj unless obj == self[attr]
454
455
  end
@@ -480,7 +481,7 @@ module BinStruct
480
481
  # @return [String]
481
482
  def to_s
482
483
  attributes.select { |attr| present?(attr) }
483
- .map! { |attr| force_binary @attributes[attr].to_s }.join
484
+ .map! { |attr| @attributes[attr].to_s.b }.join
484
485
  end
485
486
 
486
487
  # Size of object as binary string
@@ -531,6 +532,7 @@ module BinStruct
531
532
  # Force str to binary encoding
532
533
  # @param [String] str
533
534
  # @return [String]
535
+ # @deprecated Prefer use of Ruby's {::String#b}
534
536
  def force_binary(str)
535
537
  BinStruct.force_binary(str)
536
538
  end
@@ -8,5 +8,5 @@
8
8
 
9
9
  module BinStruct
10
10
  # BinStruct version
11
- VERSION = '0.4.0'
11
+ VERSION = '0.5.1'
12
12
  end
data/lib/bin_struct.rb CHANGED
@@ -8,7 +8,31 @@
8
8
 
9
9
  require_relative 'bin_struct/version'
10
10
 
11
- # BinStruct module
11
+ # BinStruct module provides classes to easily serialize/deserialize data to/from binary strings.
12
+ # @example Basic example
13
+ # class MyData < BinStruct::Struct
14
+ # # Define 2 attributes as a 8-bit integer
15
+ # define_attr :byte1, BinStruct::Int8
16
+ # define_attr :byte2, BinStruct::Int8
17
+ # # Define an attribute as a 16-bit big endian integer
18
+ # define_attr :word, BinStruct::Int16
19
+ # # Define a 32-bit little endian integer attribute
20
+ # define_attr :dword, BinStruct::Int32le
21
+ # # Define a string prepending with its length (8-bit integer)
22
+ # define_attr :str, BinStruct::IntString
23
+ # end
24
+ #
25
+ # # Generate binary data
26
+ # mydata = MyData.new(byte1: 1, byte2: 2, word: 3, dword: 4, str: 'abc')
27
+ # mydata.to_s #=> "\x01\x02\x00\x03\x04\x00\x00\x00\x03abc".b
28
+ #
29
+ # # Parse binary data
30
+ # mydata.read("\x00\xff\x01\x23\x11\x22\x33\x44\x00")
31
+ # mydata.byte1 #=> 0
32
+ # mydata.byte2 #=> 255
33
+ # mydata.word #=> 0x0123
34
+ # mydata.dword #=> 0x44332211
35
+ # mydata.str #=> ""
12
36
  # @author LemonTree55
13
37
  module BinStruct
14
38
  # BinStruct error class
@@ -17,8 +41,9 @@ module BinStruct
17
41
  # Force binary encoding for +str+
18
42
  # @param [String] str
19
43
  # @return [String] binary encoded string
44
+ # @deprecated Use {::String#b} instead of this method
20
45
  def self.force_binary(str)
21
- str.dup.force_encoding(Encoding::BINARY)
46
+ str.b
22
47
  end
23
48
  end
24
49
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bin_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - LemonTree55
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-13 00:00:00.000000000 Z
11
+ date: 2025-04-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'BinStruct is a binary dissector and generator. It eases manipulating
14
14
  complex binary data.