bin_struct 0.4.0 → 0.5.0

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: b6e644878c67b11f8006b130b0964a2349f0aa66645f2706cb97a20bf1ec6b77
4
+ data.tar.gz: 0bbf06e91de1bc410888f9c0e79f8caa0b7bb7f7082b5c775186c41c4a3d04db
5
5
  SHA512:
6
- metadata.gz: ecf6acc2f5c406556598212d22ad6257d22ed646c1128a145cff1730dc537bb7acfd40903f211aad28b11fa30c11324a98b98ecebb70ad7b84ea991bcf181f25
7
- data.tar.gz: 1dcfbb20a54f7c08d2794e0e8391b70c633d65f6ccbc41c372e46c77da6fb036fbfa9d1548462030b86603e3e41b132019da70b3c2d603f7f21116bdc842ec14
6
+ metadata.gz: c1bd8b95ce395af08b53ea41385929e9d06490d4ab21588dd46e02d8ba1d66207879174ec2f3a98400667446956042b581a032d93e8b9b97cc2d8448b20ced44
7
+ data.tar.gz: b81c04e4ddcf9a4bbe3af1b36735a5221014f69f43ef5d11c166d33e595aa989f0feb436ed2ba5547f42372c636383a5196c302f54548d0525d49057576f2467
data/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
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.0 - 2025-02-17
7
+
8
+ ### Added
9
+
10
+ - Add `String#b` to mimic Ruby's `String`
11
+ - Add a lot of examples in YARD documentation. These examples are checked using yard-doctest.
12
+
13
+ ### Deprecated
14
+
15
+ - Deprecate `BinStruct.force_binary` and `Struct.force_binary` in favor of Ruby's `String#b`
16
+
17
+ ### Fixed
18
+
19
+ - Fix `String#to_s` when static_length is set. `#to_s` was not aware of static length option.
20
+
6
21
  ## 0.4.0 - 2025-02-13
7
22
 
8
23
  ### 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
@@ -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:
@@ -443,12 +443,11 @@ module BinStruct
443
443
  def read(str)
444
444
  return self if str.nil?
445
445
 
446
- force_binary(str)
447
446
  start = 0
448
447
  attributes.each do |attr|
449
448
  next unless present?(attr)
450
449
 
451
- obj = self[attr].read(str[start..])
450
+ obj = self[attr].read(str.b[start..])
452
451
  start += self[attr].sz
453
452
  self[attr] = obj unless obj == self[attr]
454
453
  end
@@ -480,7 +479,7 @@ module BinStruct
480
479
  # @return [String]
481
480
  def to_s
482
481
  attributes.select { |attr| present?(attr) }
483
- .map! { |attr| force_binary @attributes[attr].to_s }.join
482
+ .map! { |attr| @attributes[attr].to_s.b }.join
484
483
  end
485
484
 
486
485
  # Size of object as binary string
@@ -531,6 +530,7 @@ module BinStruct
531
530
  # Force str to binary encoding
532
531
  # @param [String] str
533
532
  # @return [String]
533
+ # @deprecated Prefer use of Ruby's {::String#b}
534
534
  def force_binary(str)
535
535
  BinStruct.force_binary(str)
536
536
  end
@@ -8,5 +8,5 @@
8
8
 
9
9
  module BinStruct
10
10
  # BinStruct version
11
- VERSION = '0.4.0'
11
+ VERSION = '0.5.0'
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.0
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-02-17 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.