bytepack 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE +20 -0
- data/README.md +344 -0
- data/lib/bytepack.rb +75 -0
- data/lib/bytepack/any_type.rb +21 -0
- data/lib/bytepack/basic.rb +43 -0
- data/lib/bytepack/basic/fixed_size.rb +28 -0
- data/lib/bytepack/basic/fixed_size/decimal.rb +48 -0
- data/lib/bytepack/basic/fixed_size/float.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type.rb +18 -0
- data/lib/bytepack/basic/fixed_size/integer_type/byte.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type/integer.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type/long.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type/short.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type/u_byte.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type/u_integer.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type/u_long.rb +7 -0
- data/lib/bytepack/basic/fixed_size/integer_type/u_short.rb +7 -0
- data/lib/bytepack/basic/fixed_size/null.rb +11 -0
- data/lib/bytepack/basic/fixed_size/timestamp.rb +22 -0
- data/lib/bytepack/basic/string.rb +17 -0
- data/lib/bytepack/basic/symbol.rb +13 -0
- data/lib/bytepack/basic/varbinary.rb +34 -0
- data/lib/bytepack/complex.rb +9 -0
- data/lib/bytepack/complex/array.rb +22 -0
- data/lib/bytepack/complex/hash.rb +17 -0
- data/lib/bytepack/compound.rb +8 -0
- data/lib/bytepack/compound/single_type_array.rb +41 -0
- data/lib/bytepack/custom_data.rb +37 -0
- data/lib/bytepack/extensions.rb +18 -0
- data/lib/bytepack/type_info.rb +34 -0
- data/lib/bytepack/version.rb +3 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5638d36e9962d27928e960c35999f7f7bea8d7b76b9cbe73258be0051c21b782
|
4
|
+
data.tar.gz: 2c03181ab1a272ac8caf74f1866673759948744dc3c29147ff4b5c426f145f1f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34bd43f7e60a131d50b3e883acfdd1d7dab5c931b8ee1d1723dba21848e86d61e11006126fa1cf54fed3c856aeeae45c48e0920cda12c54868c8d9ef61bacca0
|
7
|
+
data.tar.gz: 03055a552a8240b5a837db21e55db3aa0249293ad08b4dc48a3545288ed194cf0c42206b3a03a2088f9003482a3e70454885ba33449b90482fd875ec6fe9eb28
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,344 @@
|
|
1
|
+
# Bytepack
|
2
|
+
|
3
|
+
### Tool for byte-serialization of various Ruby data structures
|
4
|
+
|
5
|
+
#### Packing & unpacking various Ruby data to/from a byte string, incl. arrays, hashes and custom data structures
|
6
|
+
|
7
|
+
## Compatibility
|
8
|
+
|
9
|
+
Compatible with Ruby **MRI 2.4**> & **JRuby 9.2**>
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
$ gem install bytepack
|
14
|
+
|
15
|
+
## Basic usage
|
16
|
+
|
17
|
+
Require Gem
|
18
|
+
|
19
|
+
require 'bytepack'
|
20
|
+
|
21
|
+
Packing specific datatype returns a byteset:
|
22
|
+
|
23
|
+
Bytepack::String.pack("test") # One argument as source value
|
24
|
+
=> "\x03\x04test"
|
25
|
+
|
26
|
+
Unpacking specific datatype returns an Array consists of Ruby object and resulted bytes offset as integer:
|
27
|
+
|
28
|
+
bytes = Bytepack::String.pack("test")
|
29
|
+
=> "\x03\x04test"
|
30
|
+
Bytepack::String.unpack(bytes) # Two arguments: byteset & offset as integer (optional)
|
31
|
+
=> ["test", 6]
|
32
|
+
|
33
|
+
Testing unpacking authenticity *(pack & unpack)*:
|
34
|
+
|
35
|
+
Bytepack::String.testpacking("test")
|
36
|
+
=> ["test", 6]
|
37
|
+
|
38
|
+
## Ruby Standard Library basic datatypes
|
39
|
+
|
40
|
+
### Byte Integer
|
41
|
+
|
42
|
+
8-bit Integer in range [-127..127]
|
43
|
+
|
44
|
+
Bytepack::Byte.pack(34)
|
45
|
+
=> "\""
|
46
|
+
|
47
|
+
Bytepack::Byte.unpack("\"".b)
|
48
|
+
=> [34, 1]
|
49
|
+
|
50
|
+
8-bit Unsigned Integer in range [1..127]
|
51
|
+
|
52
|
+
Bytepack::UByte.pack(34)
|
53
|
+
=> "\""
|
54
|
+
|
55
|
+
Bytepack::UByte.unpack("\"".b)
|
56
|
+
=> [34, 1]
|
57
|
+
|
58
|
+
### Short Integer
|
59
|
+
|
60
|
+
16-bit Integer in range [-32767..32767]
|
61
|
+
|
62
|
+
Bytepack::Short.pack(23423)
|
63
|
+
=> "[\x7F"
|
64
|
+
|
65
|
+
Bytepack::Short.unpack("[\x7F".b)
|
66
|
+
=> [23423, 2]
|
67
|
+
|
68
|
+
16-bit Unsigned Integer in range [1..32767]
|
69
|
+
|
70
|
+
Bytepack::UShort.pack(23423)
|
71
|
+
=> "[\x7F"
|
72
|
+
|
73
|
+
Bytepack::UShort.unpack("[\x7F".b)
|
74
|
+
=> [23423, 2]
|
75
|
+
|
76
|
+
### Integer
|
77
|
+
|
78
|
+
32-bit Integer in range [-2147483647..2147483647]
|
79
|
+
|
80
|
+
Bytepack::Integer.pack(12323423)
|
81
|
+
=> "\x00\xBC\n_"
|
82
|
+
|
83
|
+
Bytepack::Integer.unpack("\x00\xBC\n_".b)
|
84
|
+
=> [12323423, 4]
|
85
|
+
|
86
|
+
32-bit Unsigned Integer in range [1..2147483647]
|
87
|
+
|
88
|
+
Bytepack::UInteger.pack(12323423)
|
89
|
+
=> "\x00\xBC\n_"
|
90
|
+
|
91
|
+
Bytepack::UInteger.unpack("\x00\xBC\n_".b)
|
92
|
+
=> [12323423, 4]
|
93
|
+
|
94
|
+
### Long Integer
|
95
|
+
|
96
|
+
64-bit Integer in range [-9223372036854775807..9223372036854775807]
|
97
|
+
|
98
|
+
Bytepack::Long.pack(98712323423)
|
99
|
+
=> "\x00\x00\x00\x16\xFB\xB6\x85_"
|
100
|
+
|
101
|
+
Bytepack::Long.unpack("\x00\x00\x00\x16\xFB\xB6\x85_".b)
|
102
|
+
=> [98712323423, 8]
|
103
|
+
|
104
|
+
64-bit Unsigned Integer in range [1..9223372036854775807]
|
105
|
+
|
106
|
+
Bytepack::ULong.pack(98712323423)
|
107
|
+
=> "\x00\x00\x00\x16\xFB\xB6\x85_"
|
108
|
+
|
109
|
+
Bytepack::ULong.unpack("\x00\x00\x00\x16\xFB\xB6\x85_".b)
|
110
|
+
=> [98712323423, 8]
|
111
|
+
|
112
|
+
### Various length Integer
|
113
|
+
|
114
|
+
128-bit Long Long signed Integer
|
115
|
+
|
116
|
+
Bytepack::Basic.intToBytes(16, 2345980343453498712323423) # Two arguments: bytesize & value
|
117
|
+
=> "\x00\x00\x00\x00\x00\x01\xF0\xC7\xD9hbd>\f\xF5_"
|
118
|
+
|
119
|
+
Bytepack::Basic.bytesToInt(16, "\x00\x00\x00\x00\x00\x01\xF0\xC7\xD9hbd>\f\xF5_".b) # Three arguments: bytesize, byteset, offset as integer (optional)
|
120
|
+
=> [2345980343453498712323423, 8]
|
121
|
+
|
122
|
+
### The shortest length Integer
|
123
|
+
|
124
|
+
Use universal **Bytepack::AnyType** class for that
|
125
|
+
|
126
|
+
Bytepack::AnyType.pack(8934)
|
127
|
+
=> "\x04\"\xE6" # Packed as 1 meta-byte & 16-bit short Integer (total 3 bytes)
|
128
|
+
|
129
|
+
Bytepack::AnyType.unpack("\x04\"\xE6".b)
|
130
|
+
=> [8934, 3]
|
131
|
+
|
132
|
+
### Float
|
133
|
+
|
134
|
+
Bytepack::Float.pack(3.1415926)
|
135
|
+
=> "@\t!\xFBM\x12\xD8J"
|
136
|
+
|
137
|
+
Bytepack::Float.unpack("@\t!\xFBM\x12\xD8J".b)
|
138
|
+
=> [3.1415926, 8]
|
139
|
+
|
140
|
+
### BigDecimal
|
141
|
+
|
142
|
+
value = BigDecimal('3.1415926')
|
143
|
+
=> 0.31415926e1
|
144
|
+
|
145
|
+
Bytepack::Decimal.pack(value)
|
146
|
+
=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xDBu\x82\xCD\xC0"
|
147
|
+
|
148
|
+
Bytepack::Decimal.unpack("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xDBu\x82\xCD\xC0".b)
|
149
|
+
=> [0.31415926e1, 16]
|
150
|
+
|
151
|
+
### NilClass
|
152
|
+
|
153
|
+
Bytepack::Null.pack(nil)
|
154
|
+
=> "\x80"
|
155
|
+
|
156
|
+
Bytepack::Null.unpack("\x80".b)
|
157
|
+
=> [nil, 1]
|
158
|
+
|
159
|
+
### String ASCII-8BIT encoded (Binary data)
|
160
|
+
|
161
|
+
Pack ASCII-8BIT encoded string:
|
162
|
+
|
163
|
+
value = "\x04v\x1A\xE8wev\xD6".b
|
164
|
+
|
165
|
+
Bytepack::Varbinary.pack(value)
|
166
|
+
=> "\x03\b\x04v\x1A\xE8wev\xD6" # includes 1 meta-byte, 1-8 bytes of value's length integer and a byteset of value
|
167
|
+
|
168
|
+
Bytepack::Varbinary.unpack("\x03\b\x04v\x1A\xE8wev\xD6".b)
|
169
|
+
=> ["\x04v\x1A\xE8wev\xD6", 10]
|
170
|
+
|
171
|
+
By default, value's length serialized by *Byteset::AnyType* as a shortest integer possible (Byte, Short, Int or Long) and allways 2 bytes or more. You can override length datatype globally and make it static:
|
172
|
+
|
173
|
+
Bytepack::Varbinary.config(:LENGTH_TYPE, Bytepack::Integer)
|
174
|
+
value = "\x04v\x1A\xE8wev\xD6".b
|
175
|
+
Bytepack::Varbinary.pack(value)
|
176
|
+
=> "\x00\x00\x00\b\x04v\x1A\xE8wev\xD6" # includes 1 meta-byte, 4 bytes of value's length and a byteset of value
|
177
|
+
|
178
|
+
Byteset size now is *2 bytes more*, but allways **4 bytes (32-bit)**. That's useful in cases where byteset length make sense in the current project.
|
179
|
+
|
180
|
+
### String UTF-8 encoded (Regular string)
|
181
|
+
|
182
|
+
Pack UTF-8 encoded string:
|
183
|
+
|
184
|
+
Bytepack::String.pack("Words like violence")
|
185
|
+
=> "\x03\x13Words like violence" # includes 1 meta-byte, 1-8 bytes of value's length integer and a byteset of value
|
186
|
+
|
187
|
+
Bytepack::String.unpack("\x03\x13Words like violence".b)
|
188
|
+
=> ["Words like violence", 21]
|
189
|
+
|
190
|
+
By default, value's length serialized the same way as *Bytepack::Varbinary*, but you can specify length datatype and make it static:
|
191
|
+
|
192
|
+
Bytepack::String.config(:LENGTH_TYPE, Bytepack::Integer)
|
193
|
+
Bytepack::String.pack("Words like violence")
|
194
|
+
=> "\x00\x00\x00\x13Words like violence" # includes 1 meta-byte, 4 bytes of value's length and a byteset of value
|
195
|
+
|
196
|
+
### Symbol
|
197
|
+
|
198
|
+
Works almost the same way as String serialization do:
|
199
|
+
|
200
|
+
Bytepack::Symbol.pack(:key_this_value)
|
201
|
+
=> "\x03\x0Ekey_this_value" # includes 1 meta-byte, 1-8 bytes of value's length integer and a byteset of value
|
202
|
+
|
203
|
+
Bytepack::Symbol.unpack("\x03\x0Ekey_this_value".b)
|
204
|
+
=> [:key_this_value, 18]
|
205
|
+
|
206
|
+
By default, value's length serialized the same way as *Bytepack::Varbinary*, but you can specify length datatype and make it static:
|
207
|
+
|
208
|
+
Bytepack::Symbol.config(:LENGTH_TYPE, Bytepack::Integer)
|
209
|
+
Bytepack::Symbol.pack(:key_this_value)
|
210
|
+
=> "\x00\x00\x00\x0Ekey_this_value" # includes 1 meta-byte, 4 bytes of value's length and a byteset of value
|
211
|
+
|
212
|
+
### Time
|
213
|
+
|
214
|
+
All objects are represented as *Bytepack::Long* values (64-bit). The signed integer represents the number of microseconds before or after Unix epoch (Jan. 1 1970 00:00:00 GMT).
|
215
|
+
|
216
|
+
Bytepack::Timestamp.pack(Time.now)
|
217
|
+
=> "\x00\x05\x89\x02H\xF8\xDA\x9B"
|
218
|
+
|
219
|
+
Bytepack::Timestamp.unpack("\x00\x05\x89\x02H\xF8\xDA\x9B".b)
|
220
|
+
=> [2019-05-16 17:43:10 +0300, 8]
|
221
|
+
|
222
|
+
## Arrays and hashes
|
223
|
+
|
224
|
+
Arrays can be serialized in two modes:
|
225
|
+
|
226
|
+
### Single Type Array
|
227
|
+
|
228
|
+
Arrays consists of elements belongs to one datatype:
|
229
|
+
|
230
|
+
array = [1,2,3,4,5,6,4,3,2,123,3223,-23,0,12,89,100] # All elements are integers
|
231
|
+
|
232
|
+
You can pass specific datatype as the first array's element:
|
233
|
+
|
234
|
+
byteset = Bytepack::SingleTypeArray.pack([Bytepack::Short, *array])
|
235
|
+
=> "\x04\x03\x10\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x04\x00\x03\x00\x02\x00{\f\x97\xFF\xE9\x00\x00\x00\f\x00Y\x00d"
|
236
|
+
|
237
|
+
Bytepack::SingleTypeArray.unpack(byteset)
|
238
|
+
=> [[1, 2, 3, 4, 5, 6, 4, 3, 2, 123, 3223, -23, 0, 12, 89, 100], 35]
|
239
|
+
|
240
|
+
Or you wouldn't do it, it recognizes automatically by the longest integer:
|
241
|
+
|
242
|
+
byteset = Bytepack::SingleTypeArray.pack(array)
|
243
|
+
=> "\x04\x03\x10\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x04\x00\x03\x00\x02\x00{\f\x97\xFF\xE9\x00\x00\x00\f\x00Y\x00d"
|
244
|
+
|
245
|
+
Bytepack::SingleTypeArray.unpack(byteset)
|
246
|
+
=> [[1, 2, 3, 4, 5, 6, 4, 3, 2, 123, 3223, -23, 0, 12, 89, 100], 35]
|
247
|
+
|
248
|
+
By default, array's size serialized by *Byteset::AnyType* as a shortest integer possible (Byte, Short, Int or Long), you can override length datatype globally and make it static:
|
249
|
+
|
250
|
+
Bytepack::SingleTypeArray.config(:LENGTH_TYPE, Bytepack::Byte)
|
251
|
+
byteset = Bytepack::SingleTypeArray.pack(array)
|
252
|
+
Bytepack::SingleTypeArray.unpack(byteset)
|
253
|
+
=> [[1, 2, 3, 4, 5, 6, 4, 3, 2, 123, 3223, -23, 0, 12, 89, 100], 34]
|
254
|
+
|
255
|
+
Byteset size now is **34** instead of **35**. Why, because in previous example length packed as *2-bytes Byteset::AnyType, including 1 meta-byte and 1 byte integer itself*. Setting explicitly the length type as *Byteset::Byte*, it just 1 byte. That's useful in cases where byteset length make sense in the current project.
|
256
|
+
|
257
|
+
### Various Type Array (Regular array)
|
258
|
+
|
259
|
+
Arrays consists of elements belongs to different datatypes:
|
260
|
+
|
261
|
+
array = [1,2,"3",4,:"five",6,[7,8,9],10,123,3223,-23,0,12,89,100] # Chaos array
|
262
|
+
|
263
|
+
byteset = Bytepack::Array.pack(array)
|
264
|
+
=> "\x03\x0F\x03\x01\x03\x02\t\x03\x013\x03\x04\n\x03\x04five\x03\x06\x9E\x03\x03\a\b\t\x03\n\x03{\x04\f\x97\x03\xE9\x03\x00\x03\f\x03Y\x03d"
|
265
|
+
|
266
|
+
Bytepack::Array.unpack(byteset)
|
267
|
+
=> [[1, 2, "3", 4, :five, 6, [7, 8, 9], 10, 123, 3223, -23, 0, 12, 89, 100], 44]
|
268
|
+
|
269
|
+
By default, array's size serialized by *Byteset::AnyType* as a shortest integer possible (Byte, Short, Int or Long), you can override length datatype globally and make it static:
|
270
|
+
|
271
|
+
Bytepack::Array.config(:LENGTH_TYPE, Bytepack::Byte)
|
272
|
+
byteset = Bytepack::Array.pack(array)
|
273
|
+
Bytepack::Array.unpack(byteset)
|
274
|
+
=> [[1, 2, "3", 4, :five, 6, [7, 8, 9], 10, 123, 3223, -23, 0, 12, 89, 100], 43]
|
275
|
+
|
276
|
+
Byteset size now is **43** instead of **44**.
|
277
|
+
|
278
|
+
### Hash
|
279
|
+
|
280
|
+
Technically Hash serialized as two arrays: keys and values. Serialization uses length types of current *Array and SingleTypeArray* settings. Let's say we have mashed hash.
|
281
|
+
|
282
|
+
hash = {:key1 => 1, :key2 => "2", "key3" => "key3", :key4 => 4, :key5 => :key5, :array => [1,2,3,"text",:sym, {:nil => nil, :foo => "bar"}]}
|
283
|
+
byteset = Bytepack::Hash.pack(hash)
|
284
|
+
=> "\x9D\x06\n\x03\x04key1\n\x03\x04key2\t\x03\x04key3\n\x03\x04key4\n\x03\x04key5\n\x03\x05array\x9D\x06\x03\x01\t\x03\x012\t\x03\x04key3\x03\x04\n\x03\x04key5\x9D\x06\x03\x01\x03\x02\x03\x03\t\x03\x04text\n\x03\x03sym\xA7\x9E\n\x02\x03\x03nil\x03\x03foo\x9D\x02\x01\x80\t\x03\x03bar"
|
285
|
+
|
286
|
+
Hash serialized into 114 bytes. Not, recover it:
|
287
|
+
|
288
|
+
Bytepack::Hash.unpack(byteset)
|
289
|
+
=> [{:key1=>1, :key2=>"2", "key3"=>"key3", :key4=>4, :key5=>:key5, :array=>[1, 2, 3, "text", :sym, {:nil=>nil, :foo=>"bar"}]}, 114]
|
290
|
+
|
291
|
+
## Custom datatypes
|
292
|
+
|
293
|
+
Of course, not all available data structures are implemented out of the box. You can serialize any type of data and do it in a shorter way than *Marshal* does. For these purposes, use **Bytepack::CustomData**.
|
294
|
+
|
295
|
+
1) Create class inherited from **Bytepack::CustomData** class.
|
296
|
+
2) Class must include constant **TYPE_CODE** as a Byte integer [-127..127]. Value must be unique and not in the list of *Bytepack::TypeInfo.codes.keys* (reserved by Gem itselt)
|
297
|
+
3) Class must include constant **RUBY_TYPE** as a class in available namespace.
|
298
|
+
4) Like all OOB structures class must include the class method **pack()** which accepts one required argument as input value. Method returns the byteset as a result of serialization.
|
299
|
+
5) Like all OOB structures class must include the class method **unpack()** which accepts one required and one optional arguments:
|
300
|
+
- byteset as a *String* object;
|
301
|
+
- offset as an *Integer* object (optional, default=0).
|
302
|
+
The **unpack()** method returns two element array, where the first element is deserialized Ruby object and the second one is resulted offset. The return offset must be correct! For what every Gem's structures returns the same dataset when unpacking.
|
303
|
+
|
304
|
+
Example of serialization of **ActiveSupport::Duration** objects:
|
305
|
+
|
306
|
+
class DurationBytePack < Bytepack::CustomData
|
307
|
+
TYPE_CODE = 26
|
308
|
+
RUBY_TYPE = ActiveSupport::Duration
|
309
|
+
|
310
|
+
DIRECTIVE = 'cl>'
|
311
|
+
DURATION_PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds] # see ActiveSupport::Duration
|
312
|
+
|
313
|
+
class << self
|
314
|
+
def pack(val)
|
315
|
+
parts = val.parts
|
316
|
+
format = DIRECTIVE * parts.size
|
317
|
+
Bytepack::Byte.pack(parts.size) + parts.map {|part| [DURATION_PARTS.find_index(part[0]), part[1]]}.flatten.pack(format)
|
318
|
+
end
|
319
|
+
|
320
|
+
def unpack(bytes, offset = 0)
|
321
|
+
length, offset = *Bytepack::Byte.unpack(bytes, offset)
|
322
|
+
unpacked = bytes.unpack("@#{offset}#{"cl>"*length}").each_slice(2).sum do |idx, value|
|
323
|
+
offset += 5
|
324
|
+
value.send(DURATION_PARTS[idx])
|
325
|
+
end
|
326
|
+
[unpacked, offset]
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
Try it now in the Rails console:
|
333
|
+
|
334
|
+
DurationBytePack.pack(3.days)
|
335
|
+
=> "\x01\x03\x00\x00\x00\x03"
|
336
|
+
|
337
|
+
DurationBytePack.unpack("\x01\x03\x00\x00\x00\x03".b)
|
338
|
+
=> [3 days, 6]
|
339
|
+
|
340
|
+
Bytepack::Array.pack([1,2,3,4,5.days])
|
341
|
+
=> "\x00\x05\x03\x01\x03\x02\x03\x03\x03\x04\x1A\x01\x03\x00\x00\x00\x05"
|
342
|
+
|
343
|
+
Bytepack::Array.unpack("\x00\x05\x03\x01\x03\x02\x03\x03\x03\x04\x1A\x01\x03\x00\x00\x00\x05".b)
|
344
|
+
=> [[1, 2, 3, 4, 5 days], 17]
|
data/lib/bytepack.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
module Bytepack
|
2
|
+
|
3
|
+
class Struct
|
4
|
+
class << self
|
5
|
+
def config(const, value)
|
6
|
+
begin
|
7
|
+
remove_const(const) if const_defined?(const)
|
8
|
+
rescue NameError
|
9
|
+
end
|
10
|
+
const_set(const, value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def packingDataType(val) # Ruby data type conversion
|
14
|
+
case val
|
15
|
+
when ::Array then single_type_array?(val) ? SingleTypeArray : Array
|
16
|
+
when ::Hash then Hash
|
17
|
+
when ::NilClass then Null # Byte::NULL_INDICATOR
|
18
|
+
when ::Integer then
|
19
|
+
case val.bit_length
|
20
|
+
when (0..7) then Byte
|
21
|
+
when (8..15) then Short
|
22
|
+
when (16..31) then Integer
|
23
|
+
else
|
24
|
+
Long if val.bit_length >= 32
|
25
|
+
end
|
26
|
+
when ::Float then Float
|
27
|
+
when ::String then
|
28
|
+
val.encoding.name == "UTF-8" ? String : Varbinary # See "sometext".encoding
|
29
|
+
when ::Symbol then Symbol
|
30
|
+
when ::Time then Timestamp
|
31
|
+
when ::BigDecimal then Decimal
|
32
|
+
else # CustomData
|
33
|
+
CustomData.struct_by_ruby_type(val)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def single_type_array?(array)
|
38
|
+
first_type = array[0].class
|
39
|
+
begin
|
40
|
+
array.each {|e| raise(Exception) unless e.is_a?(first_type)}
|
41
|
+
rescue Exception
|
42
|
+
false
|
43
|
+
else
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def classifyDataType(val)
|
49
|
+
case val
|
50
|
+
when Class then val if val < Struct
|
51
|
+
when ::String, ::Symbol then
|
52
|
+
begin
|
53
|
+
Bytepack.const_get("#{val}")
|
54
|
+
rescue
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def testpacking(val)
|
61
|
+
unpack(pack(val))
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
require 'bytepack/any_type'
|
70
|
+
require 'bytepack/basic'
|
71
|
+
require 'bytepack/complex'
|
72
|
+
require 'bytepack/compound'
|
73
|
+
require 'bytepack/custom_data'
|
74
|
+
require 'bytepack/extensions'
|
75
|
+
require 'bytepack/type_info'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class AnyType < Struct
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def pack(val)
|
6
|
+
dataType = packingDataType(val)
|
7
|
+
TypeInfo.pack(dataType) + dataType.pack(val)
|
8
|
+
end
|
9
|
+
|
10
|
+
def unpack(bytes, offset = 0)
|
11
|
+
dataType, offset = *TypeInfo.unpack(bytes, offset)
|
12
|
+
if dataType.nil?
|
13
|
+
[nil, offset]
|
14
|
+
else
|
15
|
+
dataType.unpack(bytes, offset)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class Basic < Struct
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def preprocess(bytes, offset, format, length = 0)
|
6
|
+
format += length.to_s if !(self <= FixedSize) && length > 1
|
7
|
+
format = offset > 0 ? "@#{offset}#{format}" : format
|
8
|
+
offset += length
|
9
|
+
[offset, format]
|
10
|
+
end
|
11
|
+
|
12
|
+
def intToBytes(length, value)
|
13
|
+
bitlength = length*8
|
14
|
+
div = nil
|
15
|
+
x = [[32, 'N'], [16, 'n'], [8, 'C']].find {|a| (div = bitlength.divmod(a[0])) && div[0] != 0 && div[1] == 0}
|
16
|
+
b = (2**x[0])-1
|
17
|
+
([value & b] + (2..div[0]).map {|i| (value >> x[0]*(i-1)) & b}).reverse.pack(x[1]*div[0])
|
18
|
+
end
|
19
|
+
|
20
|
+
def bytesToInt(length, bytes, offset = 0) # bytes = array of 8-bit unsigned
|
21
|
+
format = "C#{length}"
|
22
|
+
format.prepend("@#{offset}") if offset > 0
|
23
|
+
bytes = bytes.unpack(format)
|
24
|
+
most_significant_bit = 1 << 7
|
25
|
+
negative = (bytes[0] & most_significant_bit) != 0
|
26
|
+
unscaled_value = -(bytes[0] & most_significant_bit) << length*8-8
|
27
|
+
# Clear the highest bit
|
28
|
+
# Unleash the powers of the butterfly
|
29
|
+
bytes[0] &= ~most_significant_bit
|
30
|
+
# Get the 2's complement
|
31
|
+
(0..length-1).each {|i| unscaled_value += bytes[i] << ((length-1 - i) * 8)}
|
32
|
+
unscaled_value * -1 if negative
|
33
|
+
unscaled_value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
require 'bytepack/basic/fixed_size'
|
41
|
+
require 'bytepack/basic/varbinary'
|
42
|
+
require 'bytepack/basic/string'
|
43
|
+
require 'bytepack/basic/symbol'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class FixedSize < Basic
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def pack(val)
|
6
|
+
val ||= self::NULL_INDICATOR
|
7
|
+
[val].pack(self::DIRECTIVE)
|
8
|
+
end
|
9
|
+
|
10
|
+
def unpack(bytes, offset = 0)
|
11
|
+
offset, format = *preprocess(bytes, offset, self::DIRECTIVE, self::LENGTH)
|
12
|
+
unpacked = bytes.unpack1(format)
|
13
|
+
if unpacked == self::NULL_INDICATOR
|
14
|
+
[nil, offset]
|
15
|
+
else
|
16
|
+
[unpacked, offset]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'bytepack/basic/fixed_size/integer_type'
|
25
|
+
require 'bytepack/basic/fixed_size/float'
|
26
|
+
require 'bytepack/basic/fixed_size/timestamp' # Date
|
27
|
+
require 'bytepack/basic/fixed_size/decimal'
|
28
|
+
require 'bytepack/basic/fixed_size/null'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'bigdecimal/util'
|
3
|
+
|
4
|
+
module Bytepack
|
5
|
+
class Decimal < FixedSize
|
6
|
+
DIRECTIVE = 'C16'
|
7
|
+
NULL_INDICATOR = -170141183460469231731687303715884105728 # NULL indicator for object type serializations
|
8
|
+
LENGTH = 16
|
9
|
+
PRECISION = 38
|
10
|
+
SCALE = 12
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def pack(val)
|
14
|
+
(val == self::NULL_INDICATOR || val.nil?) ? intToBytes(self::LENGTH, self::NULL_INDICATOR) : serialize(val)
|
15
|
+
end
|
16
|
+
|
17
|
+
def unpack(bytes, offset = 0)
|
18
|
+
[deserialize(bytes, offset), offset + self::LENGTH]
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize(val)
|
22
|
+
num = case val
|
23
|
+
when BigDecimal then val
|
24
|
+
else val.to_d
|
25
|
+
end
|
26
|
+
sign, digits, base, exponent = *num.split
|
27
|
+
scale = digits.size - exponent
|
28
|
+
precision = digits.size
|
29
|
+
raise(::ArgumentError, "Scale of this decimal is #{scale} and the max is #{self::SCALE}") if scale > self::SCALE
|
30
|
+
rest = precision - scale
|
31
|
+
raise(::ArgumentError, "Precision to the left of the decimal point is #{rest} and the max is #{self::PRECISION-self::SCALE}") if rest > 26
|
32
|
+
scale_factor = self::SCALE - scale
|
33
|
+
unscaled_int = sign * digits.to_i * base ** scale_factor # Unscaled integer
|
34
|
+
intToBytes(self::LENGTH, unscaled_int)
|
35
|
+
end
|
36
|
+
|
37
|
+
def deserialize(val, offset = 0)
|
38
|
+
unscaled = bytesToInt(self::LENGTH, val, offset)
|
39
|
+
if unscaled != self::NULL_INDICATOR
|
40
|
+
unscaled = unscaled.to_s
|
41
|
+
scaled = unscaled.insert(unscaled.size - self::SCALE, ".")
|
42
|
+
BigDecimal(scaled)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class IntegerType < FixedSize
|
3
|
+
# Reserved class
|
4
|
+
|
5
|
+
class << self
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'bytepack/basic/fixed_size/integer_type/byte'
|
12
|
+
require 'bytepack/basic/fixed_size/integer_type/u_byte'
|
13
|
+
require 'bytepack/basic/fixed_size/integer_type/short'
|
14
|
+
require 'bytepack/basic/fixed_size/integer_type/u_short'
|
15
|
+
require 'bytepack/basic/fixed_size/integer_type/integer'
|
16
|
+
require 'bytepack/basic/fixed_size/integer_type/u_integer'
|
17
|
+
require 'bytepack/basic/fixed_size/integer_type/long'
|
18
|
+
require 'bytepack/basic/fixed_size/integer_type/u_long'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class Timestamp < Long
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def pack(val)
|
6
|
+
# All dates are represented as Long values. This signed number represents the number of microseconds before or after Jan. 1 1970 00:00:00 GMT, the Unix epoch. Note that the units are microseconds, not milliseconds.
|
7
|
+
val = case val
|
8
|
+
when ::Integer then val
|
9
|
+
when ::Time then val.to_i*1000000 + val.usec # Microseconds
|
10
|
+
end
|
11
|
+
super(val)
|
12
|
+
end
|
13
|
+
|
14
|
+
def unpack(bytes, offset = 0)
|
15
|
+
unpacked = super(bytes, offset)
|
16
|
+
unpacked[0] = Time.at(unpacked[0]/1000000.to_f) if unpacked[0] # Microseconds
|
17
|
+
unpacked
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class String < Varbinary
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def unpack(bytes, offset = 0)
|
6
|
+
vb = super(bytes, offset)
|
7
|
+
vb[0] = vb[0].force_encoding("utf-8") unless vb[0].nil?
|
8
|
+
vb
|
9
|
+
end
|
10
|
+
|
11
|
+
def convert_input(val)
|
12
|
+
val.to_s.encode("utf-8")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class Varbinary < Basic
|
3
|
+
DIRECTIVE = 'a'
|
4
|
+
NULL_INDICATOR = -1 # NULL indicator for object type serializations
|
5
|
+
LENGTH_TYPE = AnyType
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def pack(val)
|
9
|
+
if val.nil?
|
10
|
+
AnyType.pack(self::NULL_INDICATOR)
|
11
|
+
else
|
12
|
+
val = convert_input(val)
|
13
|
+
self::LENGTH_TYPE.pack(val.bytesize) + val
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def unpack(bytes, offset = 0)
|
18
|
+
length, offset = *self::LENGTH_TYPE.unpack(bytes, offset)
|
19
|
+
case length
|
20
|
+
when self::NULL_INDICATOR then [nil, offset]
|
21
|
+
when 0 then ["", offset]
|
22
|
+
else
|
23
|
+
offset, format = *preprocess(bytes, offset, self::DIRECTIVE, length)
|
24
|
+
[bytes.unpack1(format), offset]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert_input(val)
|
29
|
+
val.to_s.encode("ascii-8bit")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class Array < Complex
|
3
|
+
LENGTH_TYPE = AnyType
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def pack(array = [])
|
7
|
+
elements_count = array.size
|
8
|
+
self::LENGTH_TYPE.pack(elements_count) + array.map {|val| AnyType.pack(val)}.join
|
9
|
+
end
|
10
|
+
|
11
|
+
def unpack(bytes, offset = 0)
|
12
|
+
elements_count, offset = *self::LENGTH_TYPE.unpack(bytes, offset)
|
13
|
+
elements = elements_count.times.map do
|
14
|
+
element, offset = *AnyType.unpack(bytes, offset)
|
15
|
+
element
|
16
|
+
end
|
17
|
+
[elements, offset]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class Hash < Complex
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def pack(hash = {})
|
6
|
+
AnyType.pack(hash.keys) + AnyType.pack(hash.values)
|
7
|
+
end
|
8
|
+
|
9
|
+
def unpack(bytes, offset = 0)
|
10
|
+
keys, offset = *AnyType.unpack(bytes, offset)
|
11
|
+
values, offset = *AnyType.unpack(bytes, offset)
|
12
|
+
[::Hash[keys.map.with_index {|key, index| [key, values[index]]}], offset]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class SingleTypeArray < Compound
|
3
|
+
LENGTH_TYPE = AnyType
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def pack(val = []) # First element is a data type indicator (Integer, Struct, String/Symbol)
|
7
|
+
array = val[1..-1]
|
8
|
+
dataType = classifyDataType(val[0])
|
9
|
+
unless dataType # No indicator recognized
|
10
|
+
array = val
|
11
|
+
dataType = autodetect_dataType(array)
|
12
|
+
end
|
13
|
+
TypeInfo.pack(dataType) + self::LENGTH_TYPE.pack(array.size) + array.map {|e| dataType.pack(e)}.join
|
14
|
+
end
|
15
|
+
|
16
|
+
def unpack(bytes, offset = 0)
|
17
|
+
dataType, offset = *TypeInfo.unpack(bytes, offset)
|
18
|
+
if dataType.nil?
|
19
|
+
array = nil
|
20
|
+
else
|
21
|
+
array_size, offset = *self::LENGTH_TYPE.unpack(bytes, offset)
|
22
|
+
array = array_size.times.map do
|
23
|
+
element, offset = *dataType.unpack(bytes, offset)
|
24
|
+
element
|
25
|
+
end
|
26
|
+
end
|
27
|
+
[array, offset]
|
28
|
+
end
|
29
|
+
|
30
|
+
def autodetect_dataType(array)
|
31
|
+
if array[0].is_a?(::Integer)
|
32
|
+
max_int = array.select {|e| e.is_a?(::Integer)}.max
|
33
|
+
packingDataType(max_int)
|
34
|
+
else
|
35
|
+
packingDataType(array[0])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class CustomData < Struct
|
3
|
+
|
4
|
+
@subclasses = []
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def inherited(child)
|
8
|
+
@subclasses << child
|
9
|
+
end
|
10
|
+
|
11
|
+
def subclasses(&block)
|
12
|
+
selected = nil
|
13
|
+
@subclasses.each do |child| # .find() is too slow
|
14
|
+
if yield(child)
|
15
|
+
selected = child
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end
|
19
|
+
selected if block_given?
|
20
|
+
end
|
21
|
+
|
22
|
+
def struct_by_ruby_type(val)
|
23
|
+
subclasses {|child| val.is_a?(child::RUBY_TYPE)}
|
24
|
+
end
|
25
|
+
|
26
|
+
def code_by_struct(struct)
|
27
|
+
struct::TYPE_CODE if struct < Struct
|
28
|
+
end
|
29
|
+
|
30
|
+
def struct_by_code(code)
|
31
|
+
subclasses {|child| child::TYPE_CODE == code}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Bytepack
|
2
|
+
module Extensions
|
3
|
+
|
4
|
+
module CodeValuesHash
|
5
|
+
attr_reader :codes, :code_values
|
6
|
+
|
7
|
+
def inherited(child)
|
8
|
+
child.instance_variable_set(:@codes, Hash[@codes.map {|a| [a[0], a[1].dup]}]) if instance_variable_defined?("@codes")
|
9
|
+
end
|
10
|
+
|
11
|
+
def hash_codes(*arrays)
|
12
|
+
@codes ||= ::Hash[arrays]
|
13
|
+
@code_values ||= ::Hash[arrays.map(&:reverse)]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Bytepack
|
2
|
+
class TypeInfo < Byte
|
3
|
+
extend Extensions::CodeValuesHash
|
4
|
+
|
5
|
+
hash_codes [-99, Array], # ARRAY
|
6
|
+
[-98, SingleTypeArray], # SingleTypeArray
|
7
|
+
[-89, Hash], # Hash
|
8
|
+
[1, Null], # NULL
|
9
|
+
[3, Byte], # TINYINT
|
10
|
+
[4, Short], # SMALLINT
|
11
|
+
[5, Integer], # INTEGER
|
12
|
+
[6, Long], # BIGINT
|
13
|
+
[8, Float], # FLOAT
|
14
|
+
[9, String], # STRING
|
15
|
+
[10, Symbol], # Symbol
|
16
|
+
[11, Timestamp], # TIMESTAMP
|
17
|
+
[22, Decimal], # DECIMAL
|
18
|
+
[25, Varbinary] # VARBINARY
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def pack(val)
|
22
|
+
val = val.is_a?(::Integer) ? val : code_values[val]||CustomData.code_by_struct(val)
|
23
|
+
super(val)
|
24
|
+
end
|
25
|
+
|
26
|
+
def unpack(bytes, offset = 0)
|
27
|
+
unpacked = super(bytes, offset)
|
28
|
+
unpacked[0] = codes[unpacked[0]]||CustomData.struct_by_code(unpacked[0]) if unpacked[0]
|
29
|
+
unpacked
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bytepack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Valery Kvon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-05-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.11'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.11'
|
27
|
+
description: Packing & unpacking various Ruby data to/from a byte string, incl. arrays,
|
28
|
+
hashes and custom data structures
|
29
|
+
email: addagger@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- CHANGELOG.md
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- lib/bytepack.rb
|
38
|
+
- lib/bytepack/any_type.rb
|
39
|
+
- lib/bytepack/basic.rb
|
40
|
+
- lib/bytepack/basic/fixed_size.rb
|
41
|
+
- lib/bytepack/basic/fixed_size/decimal.rb
|
42
|
+
- lib/bytepack/basic/fixed_size/float.rb
|
43
|
+
- lib/bytepack/basic/fixed_size/integer_type.rb
|
44
|
+
- lib/bytepack/basic/fixed_size/integer_type/byte.rb
|
45
|
+
- lib/bytepack/basic/fixed_size/integer_type/integer.rb
|
46
|
+
- lib/bytepack/basic/fixed_size/integer_type/long.rb
|
47
|
+
- lib/bytepack/basic/fixed_size/integer_type/short.rb
|
48
|
+
- lib/bytepack/basic/fixed_size/integer_type/u_byte.rb
|
49
|
+
- lib/bytepack/basic/fixed_size/integer_type/u_integer.rb
|
50
|
+
- lib/bytepack/basic/fixed_size/integer_type/u_long.rb
|
51
|
+
- lib/bytepack/basic/fixed_size/integer_type/u_short.rb
|
52
|
+
- lib/bytepack/basic/fixed_size/null.rb
|
53
|
+
- lib/bytepack/basic/fixed_size/timestamp.rb
|
54
|
+
- lib/bytepack/basic/string.rb
|
55
|
+
- lib/bytepack/basic/symbol.rb
|
56
|
+
- lib/bytepack/basic/varbinary.rb
|
57
|
+
- lib/bytepack/complex.rb
|
58
|
+
- lib/bytepack/complex/array.rb
|
59
|
+
- lib/bytepack/complex/hash.rb
|
60
|
+
- lib/bytepack/compound.rb
|
61
|
+
- lib/bytepack/compound/single_type_array.rb
|
62
|
+
- lib/bytepack/custom_data.rb
|
63
|
+
- lib/bytepack/extensions.rb
|
64
|
+
- lib/bytepack/type_info.rb
|
65
|
+
- lib/bytepack/version.rb
|
66
|
+
homepage: https://github.com/addagger/bytepack
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.4.0
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.3.6
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.0.3
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Tool for byte-serialization of various Ruby data structures
|
89
|
+
test_files: []
|