bin_struct 0.4.0 → 0.5.0

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