bin_struct 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of BinStruct
4
+ # see https://github.com/lemontree55/bin_struct for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # Copyright (C) 2024 LemonTree55 <lenontree@proton.me>
7
+ # This program is published under MIT license.
8
+
9
+ module BinStruct
10
+ # @abstract Base enum class to handle binary integers with limited
11
+ # authorized values
12
+ # An {Enum} type is used to handle an {Int} field with limited
13
+ # and named values.
14
+ #
15
+ # == Simple example
16
+ # enum = Int8Enum.new('low' => 0, 'medium' => 1, 'high' => 2})
17
+ # In this example, +enum+ is a 8-bit field 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
+ # Setting an unknown value will raise an exception:
25
+ # enum.value = 4 # => raise!
26
+ # enum.value = 'unknown' # => raise!
27
+ # But {#read} will not raise when reading an outbound value. This
28
+ # to enable decoding (or forging) of bad packets.
29
+ # @author Sylvain Daubert
30
+ class Enum < Int
31
+ # @return [Hash]
32
+ attr_reader :enum
33
+
34
+ # @param [Hash] options
35
+ # @see Int#initialize
36
+ # @option options [Hash] enum enumerated values. Default value is taken from
37
+ # first element unless given. This option is mandatory.
38
+ # @option options [Integer,String] :default
39
+ # @author LemonTree55
40
+ def initialize(options = {})
41
+ enum = options[:enum]
42
+ raise TypeError, 'enum must be defined as a Hash' unless enum.is_a?(Hash)
43
+
44
+ options[:default] ||= enum[enum.keys.first]
45
+ super
46
+ @enum = enum
47
+ end
48
+
49
+ # Setter for value attribute
50
+ # @param [#to_i, String,nil] value value as an Integer or as a String
51
+ # from enumration
52
+ # @return [Integer]
53
+ # @raise [ArgumentError] String value is unknown
54
+ def value=(value)
55
+ ival = case value
56
+ when NilClass
57
+ nil
58
+ when ::String
59
+ raise ArgumentError, "#{value.inspect} not in enumeration" unless @enum.key?(value)
60
+
61
+ @enum[value]
62
+ else
63
+ value.to_i
64
+ end
65
+ @value = ival
66
+ end
67
+
68
+ # To handle human API: set value from a String
69
+ alias from_human value=
70
+
71
+ # Get human readable value (enum name)
72
+ # @return [String]
73
+ def to_human
74
+ @enum.key(to_i) || "<unknown:#{@value}>"
75
+ end
76
+
77
+ def format_inspect
78
+ format_str % [to_human, to_i]
79
+ end
80
+ end
81
+
82
+ # Enumeration on one byte. See {Enum}.
83
+ # @author LemonTree55
84
+ class Int8Enum < Enum
85
+ # @param [Hash] options
86
+ # @option options [Hash] :enum
87
+ # @option options [Integer,::String] :value
88
+ # @option options [Integer,::String] :default
89
+ def initialize(options = {})
90
+ opts = options.slice(:enum, :value, :default)
91
+ opts[:width] = 1
92
+ opts[:endian] = nil
93
+ super(opts)
94
+ @packstr = { nil => 'C' }
95
+ end
96
+ end
97
+
98
+ # Enumeration on 2-byte integer. See {Enum}.
99
+ # @author LemonTree55
100
+ class Int16Enum < Enum
101
+ # @param [Hash] options
102
+ # @option options [Hash] :enum
103
+ # @option options [:big,:little] :endian
104
+ # @option options [Integer,::String] :value
105
+ # @option options [Integer,::String] :default
106
+ def initialize(options = {})
107
+ opts = options.slice(:enum, :endian, :default, :value)
108
+ opts[:endian] ||= :big
109
+ opts[:width] = 2
110
+ super(opts)
111
+ @packstr = { big: 'n', little: 'v' }
112
+ end
113
+ end
114
+
115
+ # Enumeration on big endian 2-byte integer. See {Enum}.
116
+ # @author Sylvain Daubert
117
+ class Int16beEnum < Int16Enum
118
+ undef endian=
119
+
120
+ # @param [Hash] options
121
+ # @option options [Hash] :enum
122
+ # @option options [Integer,::String] :value
123
+ # @option options [Integer,::String] :default
124
+ # @author LemonTree55
125
+ def initialize(options = {})
126
+ opts = options.slice(:enum, :default, :value)
127
+ opts[:endian] = :big
128
+ super(opts)
129
+ end
130
+ end
131
+
132
+ # Enumeration on big endian 2-byte integer. See {Enum}.
133
+ # @author Sylvain Daubert
134
+ class Int16leEnum < Int16Enum
135
+ undef endian=
136
+
137
+ # @param [Hash] options
138
+ # @option options [Hash] :enum
139
+ # @option options [Integer,::String] :value
140
+ # @option options [Integer,::String] :default
141
+ # @author LemonTree55
142
+ def initialize(options = {})
143
+ opts = options.slice(:enum, :default, :value)
144
+ opts[:endian] = :little
145
+ super(opts)
146
+ end
147
+ end
148
+
149
+ # Enumeration on 4-byte integer. See {Enum}.
150
+ # @author LemonTree55
151
+ class Int32Enum < Enum
152
+ # @param [Hash] options
153
+ # @option options [Hash] :enum
154
+ # @option options [:big,:little] :endian
155
+ # @option options [Integer,::String] :value
156
+ # @option options [Integer,::String] :default
157
+ def initialize(options = {})
158
+ opts = options.slice(:enum, :endian, :default, :value)
159
+ opts[:endian] ||= :big
160
+ opts[:width] = 4
161
+ super(opts)
162
+ @packstr = { big: 'N', little: 'V' }
163
+ end
164
+ end
165
+
166
+ # Enumeration on big endian 4-byte integer. See {Enum}.
167
+ # @author Sylvain Daubert
168
+ class Int32beEnum < Int32Enum
169
+ undef endian=
170
+
171
+ # @param [Hash] options
172
+ # @option options [Hash] :enum
173
+ # @option options [Integer,::String] :value
174
+ # @option options [Integer,::String] :default
175
+ # @author LemonTree55
176
+ def initialize(options = {})
177
+ opts = options.slice(:enum, :default, :value)
178
+ opts[:endian] = :big
179
+ super(opts)
180
+ end
181
+ end
182
+
183
+ # Enumeration on big endian 4-byte integer. See {Enum}.
184
+ # @author Sylvain Daubert
185
+ # @since 2.1.3
186
+ class Int32leEnum < Int32Enum
187
+ undef endian=
188
+
189
+ # @param [Hash] options
190
+ # @option options [Hash] :enum
191
+ # @option options [Integer,::String] :value
192
+ # @option options [Integer,::String] :default
193
+ # @author LemonTree55
194
+ def initialize(options = {})
195
+ opts = options.slice(:enum, :default, :value)
196
+ opts[:endian] = :little
197
+ super(opts)
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of BinStruct
4
+ # see https://github.com/lemontree55/bin_struct for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # Copyright (C) 2024 LemonTree55 <lenontree@proton.me>
7
+ # This program is published under MIT license.
8
+
9
+ module BinStruct
10
+ # Mixin to define minimal API for a class to be embbeded as a field in
11
+ # {Fields} type.
12
+ #
13
+ # == Optional methods
14
+ # These methods may, optionally, be defined by fieldable types:
15
+ # * +from_human+ to load data from a human-readable string.
16
+ # @author Sylvain Daubert
17
+ # @since 3.1.6
18
+ module Fieldable
19
+ # Get type name
20
+ # @return [String]
21
+ def type_name
22
+ self.class.to_s.split('::').last
23
+ end
24
+
25
+ # rubocop:disable Lint/UselessMethodDefinition
26
+ # These methods are defined for documentation.
27
+
28
+ # Populate object from a binary string
29
+ # @param [String] str
30
+ # @return [Fields] self
31
+ # @abstract subclass should overload it.
32
+ def read(str)
33
+ super
34
+ end
35
+
36
+ # Return object as a binary string
37
+ # @return [String]
38
+ # @abstract subclass should overload it.
39
+ def to_s
40
+ super
41
+ end
42
+
43
+ # Size of object as binary string
44
+ # @return [Integer]
45
+ def sz
46
+ to_s.size
47
+ end
48
+
49
+ # Return a human-readbale string
50
+ # @return [String]
51
+ # @abstract subclass should overload it.
52
+ def to_human
53
+ super
54
+ end
55
+
56
+ # rubocop:enable Lint/UselessMethodDefinition
57
+
58
+ # Format object when inspecting a {Fields} object
59
+ # @return [String]
60
+ def format_inspect
61
+ to_human
62
+ end
63
+ end
64
+ end