bare-rb 0.0.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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/lib/bare-rb.rb +112 -0
  3. data/lib/exceptions.rb +40 -0
  4. data/lib/types.rb +544 -0
  5. metadata +45 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: adea5476cd77af659cb3329602479f8b80b59d0b7459dc7cf343165b7096d871
4
+ data.tar.gz: 2e3f86206cc9519cd654dfcf28972a5c5c5b4fc5966143699d74f22209373088
5
+ SHA512:
6
+ metadata.gz: e19625e2170ed457390a6f6d06d68a67443c91f057b5310363e49d6a8c5102bb41ae068fba67f3eaecbe11f2f984bc54eae81f02c39a5222f07887920e39f8af
7
+ data.tar.gz: 0e502b379a427ac5c493f0f02a8b17ab6821560adf248fccbcfac2dc31e406f08c1f29816970a50b5d82589ffdbb6f4fca2940b4e2eacda39633a95b50d58b69
@@ -0,0 +1,112 @@
1
+ require 'set'
2
+ require_relative "types"
3
+
4
+ class Bare
5
+ def self.encode(msg, schema)
6
+ return schema.encode(msg)
7
+ end
8
+
9
+ def self.decode(msg, schema)
10
+ return schema.decode(msg)[:value]
11
+ end
12
+
13
+ # These classes are wrapped in methods for ergonomics.
14
+ # Isn't Bare.Array(Bare.U8) nicer than Bare::Array.new(Bare::U8.new)?
15
+
16
+ def self.Int
17
+ return BareTypes::Int.new
18
+ end
19
+
20
+ def self.Void
21
+ return BareTypes::Void.new
22
+ end
23
+
24
+ def self.F32
25
+ return BareTypes::F32.new
26
+ end
27
+
28
+ def self.F64
29
+ return BareTypes::F64.new
30
+ end
31
+
32
+ def self.String
33
+ return BareTypes::String.new
34
+ end
35
+
36
+ def self.U8
37
+ return BareTypes::U8.new
38
+ end
39
+
40
+ def self.U16
41
+ return BareTypes::U16.new
42
+ end
43
+
44
+ def self.U32
45
+ return BareTypes::U32.new
46
+ end
47
+
48
+ def self.U64
49
+ return BareTypes::U64.new
50
+ end
51
+
52
+ def self.I8
53
+ return BareTypes::I8.new
54
+ end
55
+
56
+ def self.I16
57
+ return BareTypes::I16.new
58
+ end
59
+
60
+ def self.I32
61
+ return BareTypes::I32.new
62
+ end
63
+
64
+ def self.I64
65
+ return BareTypes::I64.new
66
+ end
67
+
68
+ def self.Optional(*opts)
69
+ return BareTypes::Optional.new(*opts)
70
+ end
71
+
72
+ def self.Map(*opts)
73
+ return BareTypes::Map.new(*opts)
74
+ end
75
+
76
+ def self.Union(*opts)
77
+ return BareTypes::Union.new(*opts)
78
+ end
79
+
80
+ def self.DataFixedLen(*opts)
81
+ return BareTypes::DataFixedLen.new(*opts)
82
+ end
83
+
84
+ def self.Data
85
+ return BareTypes::Data.new
86
+ end
87
+
88
+ def self.Uint
89
+ return BareTypes::Uint.new
90
+ end
91
+
92
+ def self.Bool
93
+ return BareTypes::Bool.new
94
+ end
95
+
96
+ def self.Struct(*opts)
97
+ return BareTypes::Struct.new(*opts)
98
+ end
99
+
100
+ def self.Array(*opts)
101
+ return BareTypes::Array.new(*opts)
102
+ end
103
+
104
+ def self.ArrayFixedLen(*opts)
105
+ return BareTypes::ArrayFixedLen.new(*opts)
106
+ end
107
+
108
+ def self.Enum(*opts)
109
+ return BareTypes::Enum.new(*opts)
110
+ end
111
+ end
112
+
@@ -0,0 +1,40 @@
1
+ class BareException < StandardError
2
+ def initialize(msg=nil)
3
+ super
4
+ end
5
+ end
6
+
7
+ class VoidUsedOutsideTaggedSet < BareException
8
+ def initialize(msg = "Any type which is ultimately a void type (either directly or through user-defined types) may not be used as an optional type, struct member, array member, or map key or value. Void types may only be used as members of the set of types in a tagged union.")
9
+ super
10
+ end
11
+ end
12
+
13
+ class MinimumSizeError < BareException
14
+ def initialize(msg = "Schema object has minimum size of 1")
15
+ super
16
+ end
17
+ end
18
+ class MaximumSizeError < BareException
19
+ def initialize(msg = "Object too large to encode as given type")
20
+ super
21
+ end
22
+ end
23
+
24
+ class MapKeyError < BareException
25
+ def initialize(msg = "Map keys must be a none data or data[len] primitive")
26
+ super
27
+ end
28
+ end
29
+
30
+ class EnumValueError < BareException
31
+ def initialize(msg = "Enums cannot have two identical values")
32
+ super
33
+ end
34
+ end
35
+
36
+ class SchemaMismatch < BareException
37
+ def initialize(msg = "Some mismatch between data and schema has occurred")
38
+ super
39
+ end
40
+ end
@@ -0,0 +1,544 @@
1
+ require_relative './exceptions'
2
+
3
+ class BareTypes
4
+
5
+ class BaseType
6
+ end
7
+
8
+ class BarePrimitive < BaseType
9
+ # Types which are always equivalent to another instantiation of themselves
10
+ # Eg. Uint.new == Uint.new
11
+ # But Union.new(types1) != Union.new(types2)
12
+ # since unions could have different sets of types
13
+ def ==(other)
14
+ self.class == other.class
15
+ end
16
+ end
17
+
18
+ class Int < BarePrimitive
19
+ # https://developers.google.com/protocol-buffers/docs/encoding
20
+ # Easy to just convert to signed and re-use uint code
21
+ def encode(msg)
22
+ mappedInteger = msg < 0 ? -2 * msg - 1 : msg * 2
23
+ return Uint.new.encode(mappedInteger)
24
+ end
25
+ def decode(msg)
26
+ output = Uint.new.decode(msg)
27
+ unmapped = output[:value]
28
+ unmapped = unmapped.odd? ? (unmapped + 1) / - 2 : unmapped / 2
29
+ return { value: unmapped, rest: output[:rest] }
30
+ end
31
+ end
32
+
33
+ class Void < BarePrimitive
34
+ def encode(msg)
35
+ return "".b
36
+ end
37
+ def decode(msg)
38
+ return {value: nil, rest: msg}
39
+ end
40
+ end
41
+
42
+ class F32 < BarePrimitive
43
+ def encode(msg)
44
+ return [msg].pack("e")
45
+ end
46
+ def decode(msg)
47
+ return {value: msg.unpack("e")[0], rest: msg[4..msg.size]}
48
+ end
49
+ end
50
+
51
+ class F64 < BarePrimitive
52
+ def encode(msg)
53
+ return [msg].pack("E")
54
+ end
55
+ def decode(msg)
56
+ return {value: msg.unpack("E")[0], rest: msg[8..msg.size]}
57
+ end
58
+ end
59
+
60
+ class String < BarePrimitive
61
+ def encode(msg)
62
+ encodedString = nil
63
+ begin
64
+ encodedString = msg.encode("UTF-8").b
65
+ rescue Encoding::UndefinedConversionError => error
66
+ raise error.class, "Unable to convert string to UTF-8=, BARE strings are encoded as UTF8. If you can't convert your string to UTF-8 you can encode it with binary data"
67
+ end
68
+ bytes = Uint.new.encode(encodedString.size)
69
+ bytes << encodedString
70
+ return bytes
71
+ end
72
+ def decode(msg)
73
+ output = Uint.new.decode(msg)
74
+ strLen = output[:value]
75
+ string = output[:rest][0..strLen-1]
76
+ return {value: string.force_encoding("utf-8"), rest: output[:rest][strLen..output[:rest].size] }
77
+ end
78
+ end
79
+
80
+ class Optional < BaseType
81
+ def ==(otherType)
82
+ return otherType.class == BareTypes::Optional && otherType.optionalType == @optionalType
83
+ end
84
+
85
+ def optionalType
86
+ @optionalType
87
+ end
88
+
89
+ def initialize(optionalType)
90
+ raise VoidUsedOutsideTaggedSet() if optionalType.class == BareTypes::Void
91
+ @optionalType = optionalType
92
+ end
93
+
94
+ def encode(msg)
95
+ if msg.nil?
96
+ return "\x00".b
97
+ else
98
+ bytes = "\xFF".b
99
+ bytes << @optionalType.encode(msg)
100
+ return bytes
101
+ end
102
+ end
103
+
104
+ def decode(msg)
105
+ if msg.unpack("C")[0] == 0
106
+ return {value: nil, rest: msg[1..msg.size]}
107
+ else
108
+ return @optionalType.decode(msg[1..msg.size])
109
+ end
110
+ end
111
+ end
112
+
113
+ class Map < BaseType
114
+ def ==(otherType)
115
+ return otherType.class == BareTypes::Map && otherType.from == @from && otherType.to == @to
116
+ end
117
+
118
+ def initialize(fromType, toType)
119
+ raise VoidUsedOutsideTaggedSet if fromType.class == BareTypes::Void or toType.class == BareTypes::Void
120
+ raise MapKeyError("Map keys must use a primitive type which is not data or data<length>.") if !fromType.class.ancestors.include?(BarePrimitive) || fromType.is_a?(BareTypes::Data) || fromType.is_a?(BareTypes::DataFixedLen)
121
+ @from = fromType
122
+ @to = toType
123
+ end
124
+
125
+ def from
126
+ @from
127
+ end
128
+
129
+ def to
130
+ @to
131
+ end
132
+
133
+ def encode(msg)
134
+ bytes = Uint.new.encode(msg.size)
135
+ msg.each do |from, to|
136
+ bytes << @from.encode(from)
137
+ bytes << @to.encode(to)
138
+ end
139
+ return bytes
140
+ end
141
+
142
+ def decode(msg)
143
+ hash = Hash.new
144
+ output = Uint.new.decode(msg)
145
+ mapSize = output[:value]
146
+ (mapSize - 1).to_i.downto(0) do
147
+ output = @from.decode(output[:rest])
148
+ key = output[:value]
149
+ output = @to.decode(output[:rest])
150
+ hash[key] = output[:value]
151
+ hash[key] = output[:value]
152
+ end
153
+ return {value: hash, rest: output[:rest]}
154
+ end
155
+ end
156
+
157
+ class Union < BarePrimitive
158
+ def intToType
159
+ @intToType
160
+ end
161
+
162
+ def ==(otherType)
163
+ return false unless otherType.is_a?(BareTypes::Union)
164
+ @intToType.each do |int, type|
165
+ return false unless type == otherType.intToType[int]
166
+ end
167
+ return true
168
+ end
169
+
170
+ def initialize(intToType)
171
+ intToType.keys.each do |i|
172
+ raise MinimumSizeError("Union's integer representations must be > 0, instead got: #{i}") if i < 0 or !i.is_a?(Integer)
173
+ end
174
+ raise MinimumSizeError("Union must have at least one type") if intToType.keys.size < 1
175
+ @intToType = intToType
176
+ end
177
+
178
+ def encode(msg)
179
+ type = msg[:type]
180
+ value = msg[:value]
181
+ unionTypeInt = nil
182
+ unionType = nil
183
+ @intToType.each do |int, typ|
184
+ if type.class == typ.class
185
+ unionTypeInt = int
186
+ unionType = typ
187
+ break
188
+ end
189
+ end
190
+ raise SchemaMismatch("Unable to find given type in union: #{@intToType.inspect}, type: #{type}") if unionType.nil? || unionTypeInt.nil?
191
+ bytes = Uint.new.encode(unionTypeInt)
192
+ encoded = unionType.encode(value)
193
+ bytes << encoded
194
+ end
195
+
196
+ def decode(msg)
197
+ unionTypeInt = Uint.new.decode(msg)
198
+ int = unionTypeInt[:value]
199
+ type = @intToType[int]
200
+ value = type.decode(unionTypeInt[:rest])
201
+ return {value: {value: value[:value], type: type}, rest: value[:rest]}
202
+ end
203
+ end
204
+
205
+ class DataFixedLen < BaseType
206
+ def ==(otherType)
207
+ return otherType.class == BareTypes::DataFixedLen && otherType.length == self.length
208
+ end
209
+
210
+ def length
211
+ return @length
212
+ end
213
+
214
+ def initialize(length)
215
+ raise MinimumSizeError("DataFixedLen must have a length greater than 0, got: #{length.inspect}") if length < 1
216
+ @length = length
217
+ end
218
+
219
+ def encode(msg)
220
+ return msg
221
+ end
222
+
223
+ def decode(msg)
224
+ return {value: msg[0..@length], rest: msg[@length..msg.size]}
225
+ end
226
+ end
227
+
228
+ class Data < BarePrimitive
229
+ def encode(msg)
230
+ bytes = Uint.new.encode(msg.size)
231
+ bytes << msg
232
+ return bytes
233
+ end
234
+
235
+ def decode(msg)
236
+ output = Uint.new.decode(msg)
237
+ rest = output[:rest]
238
+ dataSize = output[:value]
239
+ return {value: rest[0..dataSize], rest: rest[dataSize..rest.size]}
240
+ end
241
+ end
242
+
243
+ class Uint < BarePrimitive
244
+ def encode(msg)
245
+ bytes = "".b
246
+ _get_next_7_bits_as_byte(msg, 128) do |byte|
247
+ bytes << byte
248
+ end
249
+ (bytes.size - 1).downto(0) do |i|
250
+ if bytes.bytes[i] == 128 && bytes.size > 1
251
+ bytes = bytes.chop
252
+ else
253
+ break
254
+ end
255
+ end
256
+ bytes[bytes.size - 1] = [bytes.bytes[bytes.size - 1] & 127].pack('C')[0]
257
+ raise MaximumSizeError("Maximum u/int allowed is 64 bit precision") if bytes.size > 9
258
+ return bytes
259
+ end
260
+
261
+ def decode(msg)
262
+ ints = msg.unpack("CCCCCCCCC")
263
+ relevantInts = []
264
+ i = 0
265
+ while ints[i] & 0b10000000 == 128
266
+ relevantInts << ints[i] % 128
267
+ i += 1
268
+ end
269
+ relevantInts << ints[i]
270
+ sum = 0
271
+ relevantInts.each_with_index do |int, idx|
272
+ sum += int << (idx * 7)
273
+ end
274
+ return {value: sum, rest: msg[(i + 1)..msg.size]}
275
+ end
276
+ end
277
+
278
+ class U8 < BarePrimitive
279
+ def encode(msg)
280
+ return [msg].pack("C")
281
+ end
282
+
283
+ def decode(msg)
284
+ return {value: msg[0].unpack("C")[0], rest: msg[1..msg.size]}
285
+ end
286
+ end
287
+
288
+ class U16 < BarePrimitive
289
+ def encode(msg)
290
+ return [msg].pack("v")
291
+ end
292
+
293
+ def decode(msg)
294
+ return {value: msg.unpack("v")[0], rest: msg[2..msg.size]}
295
+ end
296
+ end
297
+
298
+ class U32 < BarePrimitive
299
+ def encode(msg)
300
+ return [msg].pack("V")
301
+ end
302
+
303
+ def decode(msg)
304
+ return {value: msg.unpack("V")[0], rest: msg[4..msg.size]}
305
+ end
306
+ end
307
+
308
+ class U64 < BarePrimitive
309
+ def encode(msg)
310
+ return [msg].pack("Q")
311
+ end
312
+
313
+ def decode(msg)
314
+ return {value: msg.unpack("Q")[0], rest: [8..msg.size]}
315
+ end
316
+ end
317
+
318
+ class I8 < BarePrimitive
319
+ def encode(msg)
320
+ return [msg].pack("c")
321
+ end
322
+
323
+ def decode(msg)
324
+ return {value: msg[0].unpack("c")[0], rest: msg[1..msg.size]}
325
+ end
326
+ end
327
+
328
+ class I16 < BarePrimitive
329
+ def encode(msg)
330
+ return [msg].pack("s<")
331
+ end
332
+
333
+ def decode(msg)
334
+ return {value: msg.unpack('s<')[0], rest: msg[2..msg.size]}
335
+ end
336
+ end
337
+
338
+ class I32 < BarePrimitive
339
+ def encode(msg)
340
+ return [msg].pack("l<")
341
+ end
342
+
343
+ def decode(msg)
344
+ return {value: msg.unpack('l<')[0], rest: msg[4..msg.size]}
345
+ end
346
+ end
347
+
348
+ class I64 < BarePrimitive
349
+ def encode(msg)
350
+ return [msg].pack("q<")
351
+ end
352
+
353
+ def decode(msg)
354
+ return {value: msg.unpack('q<')[0], rest: msg[8..msg.size]}
355
+ end
356
+ end
357
+
358
+ class Bool < BarePrimitive
359
+ def encode(msg)
360
+ return msg ? "\xFF\xFF".b : "\x00\x00".b
361
+ end
362
+
363
+ def decode(msg)
364
+ return {value: msg == "\x00\x00" ? false : true, rest: msg[1..msg.size]}
365
+ end
366
+ end
367
+
368
+ class Struct < BaseType
369
+ def ==(otherType)
370
+ return false unless otherType.class == BareTypes::Struct
371
+ @mapping.each do |k, v|
372
+ return false unless otherType.mapping[k] == v
373
+ end
374
+ return true
375
+ end
376
+
377
+ def mapping
378
+ @mapping
379
+ end
380
+
381
+ def initialize(symbolToType)
382
+ # Mapping from symbols to Bare types
383
+ symbolToType.keys.each do |k|
384
+ raise BareException("Struct keys must be symbols") unless k.is_a?(Symbol)
385
+ raise BareException("Struct values must be a BareTypes::TYPE\nInstead got: #{symbolToType[k].inspect}") unless symbolToType[k].class.ancestors.include?(BaseType)
386
+ raise VoidUsedOutsideTaggedSet("Void types may only be used as members of the set of types in a tagged union. Void type used as struct key") if symbolToType.class == BareTypes::Void
387
+ end
388
+ raise("Struct must have at least one field") if symbolToType.keys.size == 0
389
+ @mapping = symbolToType
390
+ end
391
+
392
+ def encode(msg)
393
+ bytes = "".b
394
+ @mapping.keys.each do |symbol|
395
+ raise SchemaMismatch("All struct fields must be specified, missing: #{symbol.inspect}") unless msg.keys.include?(symbol)
396
+ bytes << @mapping[symbol].encode(msg[symbol])
397
+ end
398
+ return bytes
399
+ end
400
+
401
+ def decode(msg)
402
+ hash = Hash.new
403
+ rest = msg
404
+ @mapping.keys.each do |symbol|
405
+ output = @mapping[symbol].decode(rest)
406
+ hash[symbol] = output[:value]
407
+ rest = output[:rest]
408
+ end
409
+ return {value: hash, rest: rest}
410
+ end
411
+ end
412
+
413
+ class Array < BaseType
414
+ def ==(otherType)
415
+ return otherType.class == BareTypes::Array && otherType.type == self.type
416
+ end
417
+
418
+ def type
419
+ return @type
420
+ end
421
+
422
+ def initialize(type)
423
+ raise VoidUsedOutsideTaggedSet("Void types may only be used as members of the set of types in a tagged union.") if type.class == BareTypes::Void
424
+ @type = type
425
+ end
426
+
427
+ def encode(msg)
428
+ bytes = Uint.new.encode(msg.size)
429
+ msg.each do |item|
430
+ bytes << @type.encode(item)
431
+ end
432
+ return bytes
433
+ end
434
+
435
+ def decode(msg)
436
+ output = Uint.new.decode(msg)
437
+ arr = []
438
+ arrayLen = output[:value]
439
+ lastSize = msg.size + 1 # Make sure msg size monotonically decreasing
440
+ (arrayLen - 1).downto(0) do
441
+ output = @type.decode(output[:rest])
442
+ arr << output[:value]
443
+ break if output[:rest].nil? || output[:rest].size == 0 || lastSize <= output[:rest].size
444
+ lastSize = output[:rest].size
445
+ end
446
+ return {value: arr, rest: output[:rest]}
447
+ end
448
+ end
449
+
450
+ class ArrayFixedLen < BaseType
451
+ def ==(otherType)
452
+ return otherType.class == BareTypes::ArrayFixedLen && otherType.type == @type && otherType.size == @size
453
+ end
454
+
455
+ def initialize(type, size)
456
+ @type = type
457
+ @size = size
458
+ raise VoidUsedOutsideTaggedSet("Void type may not be used as type of fixed length array.") if type.class == BareTypes::Void
459
+ raise MinimumSizeError("FixedLenArray size must be > 0") if size < 1
460
+ end
461
+
462
+ def type
463
+ @type
464
+ end
465
+
466
+ def size
467
+ @size
468
+ end
469
+
470
+ def encode(arr)
471
+ raise SchemaMismatch("This FixLenArray is of length #{@size.to_s} but you passed an array of length #{arr.size}") if arr.size != @size
472
+ bytes = ""
473
+ arr.each do |item|
474
+ bytes << @type.encode(item)
475
+ end
476
+ return bytes
477
+ end
478
+
479
+ def decode(msg)
480
+ array = []
481
+ rest = msg
482
+ @size.times do
483
+ output = @type.decode(rest)
484
+ rest = output[:rest]
485
+ array << output[:value]
486
+ end
487
+ return {value: array, rest: rest}
488
+ end
489
+ end
490
+
491
+ class Enum < BaseType
492
+ def ==(otherType)
493
+ return false unless otherType.class == BareTypes::Enum
494
+ @intToVal.each do |int, val|
495
+ return false unless otherType.intToVal[int] == val
496
+ end
497
+ return true
498
+ end
499
+
500
+ def intToVal
501
+ @intToVal
502
+ end
503
+
504
+ def initialize(source)
505
+ @intToVal = {}
506
+ @valToInt = {}
507
+ raise BareException("Enum must initialized with a hash from integers to anything") if !source.is_a?(Hash)
508
+ raise BareException("Enum must have unique positive integer assignments") if Set.new(source.keys).size != source.keys.size
509
+ raise BareException("Enum must have unique values") if source.values.to_set.size != source.values.size
510
+ source.each do |k, v|
511
+ raise("Enum keys must be positive integers") if k < 0
512
+ @intToVal[k.to_i] = v
513
+ @valToInt[v] = k.to_i
514
+ end
515
+ end
516
+
517
+ def encode(msg)
518
+ raise SchemaMismatch("#{msg.inspect} is not part of this enum: #{@intToVal}") if !@valToInt.keys.include?(msg)
519
+ integerRep = @valToInt[msg]
520
+ encoded = BareTypes::Uint.new.encode(integerRep)
521
+ return encoded
522
+ end
523
+
524
+ def decode(msg)
525
+ output = BareTypes::Uint.new.decode(msg)
526
+ value = output[:value]
527
+ rest = output[:rest]
528
+ return {value: @intToVal[value], rest: rest}
529
+ end
530
+ end
531
+ end
532
+
533
+ def _get_next_7_bits_as_byte(integer, base = 128)
534
+ # Base is the initial value of the byte before
535
+ # before |'ing it with 7bits from the integer
536
+ groups_of_7 = (integer.size * 8) / 7 + (integer.size % 7 == 0 ? 0 : 1)
537
+ 0.upto(groups_of_7 - 1) do |group_number|
538
+ byte = base
539
+ 0.upto(7).each do |bit_number|
540
+ byte = byte | (integer[group_number * 7 + bit_number] << bit_number)
541
+ end
542
+ yield(byte)
543
+ end
544
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bare-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nate Tracy-Amoroso
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-10-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Implements the bare message encoding and DSL in ruby
14
+ email: n8@u.northwestern.edu
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - "./lib/bare-rb.rb"
20
+ - "./lib/exceptions.rb"
21
+ - "./lib/types.rb"
22
+ homepage: https://none.none
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubygems_version: 3.1.2
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: Bare Message Encoding Implementation
45
+ test_files: []