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.
- checksums.yaml +7 -0
- data/lib/bare-rb.rb +112 -0
- data/lib/exceptions.rb +40 -0
- data/lib/types.rb +544 -0
- metadata +45 -0
checksums.yaml
ADDED
@@ -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
|
data/lib/bare-rb.rb
ADDED
@@ -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
|
+
|
data/lib/exceptions.rb
ADDED
@@ -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
|
data/lib/types.rb
ADDED
@@ -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: []
|