bare-rb 0.1.2 → 0.2.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.
data/lib/types.rb CHANGED
@@ -3,201 +3,214 @@ require_relative './exceptions'
3
3
  class BareTypes
4
4
 
5
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)
6
+ def initialize
7
+ @finalized = false
8
+ super
24
9
  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] }
10
+ def cycle_search(seen)
30
11
  end
31
12
  end
32
13
 
33
- class Void < BarePrimitive
34
- def encode(msg)
35
- return "".b
14
+ class Schema
15
+ attr_accessor :types
16
+
17
+ def initialize(types)
18
+ @types = types.map { |k, v| [k.to_sym, v] }.to_h
19
+ @types.each do |k, v|
20
+ unless k.is_a?(Symbol)
21
+ raise("Keys to a schema must be symbols")
22
+ end
23
+ if v.nil?
24
+ raise("Schema values cannot be nil")
25
+ end
26
+ end
27
+
28
+ # Resolve references in schema
29
+ # type A u8
30
+ # type B A
31
+ # type C B
32
+ # first loop would find B and make it a reference to A
33
+ # second loop would find C and make it a reference to B
34
+ progress = true
35
+ remaining = @types.keys.to_a
36
+ while progress
37
+ progress = false
38
+ remaining.each do |key|
39
+ val = @types[key]
40
+ if val.is_a?(Symbol) && !@types[val].is_a?(Symbol)
41
+ @types[key.to_sym] = BareTypes::Reference.new(key, @types[val])
42
+ progress = true
43
+ else
44
+ end
45
+ end
46
+ end
47
+
48
+ @types.each do |key, val|
49
+ if val.is_a?(Symbol)
50
+ raise ReferenceException.new("Your types contain an unresolved reference '#{val}'.")
51
+ end
52
+ end
53
+
54
+ @types.values.each do |val|
55
+ val.finalize_references(@types)
56
+ end
57
+
58
+ @types.each do |key, val|
59
+ val.cycle_search(SeenList.new)
60
+ end
36
61
  end
37
- def decode(msg)
38
- return {value: nil, rest: msg}
62
+
63
+ def ==(otherSchema)
64
+ return false unless otherSchema.is_a?(BareTypes::Schema)
65
+ @types == otherSchema.types
39
66
  end
40
- end
41
67
 
42
- class F32 < BarePrimitive
43
- def encode(msg)
44
- return [msg].pack("e")
68
+ def to_s
69
+ buffer = ""
70
+ @types.each do |name, type|
71
+ if type.is_a?(BareTypes::Enum)
72
+ buffer << "enum #{name} "
73
+ type.to_schema(buffer)
74
+ buffer << "\n"
75
+ else
76
+ buffer << "type #{name} "
77
+ type.to_schema(buffer)
78
+ buffer << "\n"
79
+ end
80
+ end
81
+ buffer
45
82
  end
46
- def decode(msg)
47
- return {value: msg.unpack("e")[0], rest: msg[4..msg.size]}
83
+
84
+ def [](key)
85
+ return @types[key]
48
86
  end
49
87
  end
50
88
 
51
- class F64 < BarePrimitive
52
- def encode(msg)
53
- return [msg].pack("E")
89
+ # Used to represent a Type reference in a schema.
90
+ # eg. test8.schema's address field on Customer contains 'Address'
91
+ # a reference to the Address type defined earlier.
92
+ class Reference < BaseType
93
+ attr_accessor :name
94
+ attr_accessor :ref
95
+
96
+ def cycle_search(seen)
97
+ seen.add(self)
98
+ @ref.cycle_search(seen)
99
+ seen.pop
54
100
  end
55
- def decode(msg)
56
- return {value: msg.unpack("E")[0], rest: msg[8..msg.size]}
101
+
102
+ def ==(other)
103
+ other.is_a?(Reference) && @name == other.name && @ref == other.ref
57
104
  end
58
- end
59
105
 
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"
106
+ def initialize(name, reference)
107
+ @name = name
108
+ @ref = reference
109
+ @finalized = false
110
+ unless reference.is_a?(BareTypes::BaseType)
111
+ raise ReferenceException.new("Reference must be to bare types")
67
112
  end
68
- bytes = Uint.new.encode(encodedString.size)
69
- bytes << encodedString
70
- return bytes
71
113
  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] }
114
+
115
+ def finalize_references(schema)
116
+ return if @finalized
117
+ @finalized = true
118
+ self.ref.finalize_references(schema)
77
119
  end
78
- end
79
120
 
80
- class Optional < BaseType
81
- def ==(otherType)
82
- return otherType.class == BareTypes::Optional && otherType.optionalType == @optionalType
121
+ def encode(msg, buffer)
122
+ @ref.encode(msg, buffer)
83
123
  end
84
124
 
85
- def optionalType
86
- @optionalType
125
+ def decode(msg)
126
+ @ref.decode(msg)
87
127
  end
88
128
 
89
- def initialize(optionalType)
90
- raise VoidUsedOutsideTaggedSet() if optionalType.class == BareTypes::Void
91
- @optionalType = optionalType
129
+ def to_schema(buffer)
130
+ buffer << @name.to_s
92
131
  end
132
+ end
93
133
 
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
134
+ class BarePrimitive < BaseType
135
+
136
+ # Types which are always equivalent to another instantiation of themselves
137
+ # Eg. Uint.new == Uint.new
138
+ # But Union.new(types1) != Union.new(types2)
139
+ # since unions could have different sets of types
140
+
141
+ def ==(other)
142
+ self.class == other.class
102
143
  end
103
144
 
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
145
+ def finalize_references(schema) end
146
+
147
+ def to_schema(buffer)
148
+ buffer << self.class.name.split('::').last.downcase
110
149
  end
150
+
111
151
  end
112
152
 
113
- class Map < BaseType
114
- def ==(otherType)
115
- return otherType.class == BareTypes::Map && otherType.from == @from && otherType.to == @to
153
+ #region Primitives
154
+ class Int < BarePrimitive
155
+ # https://developers.google.com/protocol-buffers/docs/encoding
156
+ # Easy to just convert to signed and re-use uint code
157
+ def encode(msg, buffer)
158
+ mappedInteger = msg < 0 ? -2 * msg - 1 : msg * 2
159
+ Uint.new.encode(mappedInteger, buffer)
116
160
  end
117
161
 
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
162
+ def decode(msg)
163
+ value, rest = Uint.new.decode(msg)
164
+ value = value.odd? ? (value + 1) / -2 : value / 2
165
+ return value, rest
123
166
  end
167
+ end
124
168
 
125
- def from
126
- @from
169
+ class Void < BarePrimitive
170
+ def encode(msg, buffer)
171
+ buffer << "".b
127
172
  end
128
173
 
129
- def to
130
- @to
174
+ def decode(msg)
175
+ return nil, msg
131
176
  end
177
+ end
132
178
 
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
179
+ class F32 < BarePrimitive
180
+ def encode(msg, buffer)
181
+ buffer << [msg].pack("e")
140
182
  end
141
183
 
142
184
  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
- end
152
- return {value: hash, rest: output[:rest]}
185
+ return msg.unpack("e")[0], msg[4..msg.size]
153
186
  end
154
187
  end
155
188
 
156
- class Union < BarePrimitive
157
- def intToType
158
- @intToType
159
- end
160
-
161
- def ==(otherType)
162
- return false unless otherType.is_a?(BareTypes::Union)
163
- @intToType.each do |int, type|
164
- return false unless type == otherType.intToType[int]
165
- end
166
- return true
189
+ class F64 < BarePrimitive
190
+ def encode(msg, buffer)
191
+ buffer << [msg].pack("E")
167
192
  end
168
193
 
169
- def initialize(intToType)
170
- intToType.keys.each do |i|
171
- raise MinimumSizeError("Union's integer representations must be > 0, instead got: #{i}") if i < 0 or !i.is_a?(Integer)
172
- end
173
- raise MinimumSizeError("Union must have at least one type") if intToType.keys.size < 1
174
- @intToType = intToType
194
+ def decode(msg)
195
+ return msg.unpack("E")[0], msg[8..msg.size]
175
196
  end
197
+ end
176
198
 
177
- def encode(msg)
178
- type = msg[:type]
179
- value = msg[:value]
180
- unionTypeInt = nil
181
- unionType = nil
182
- @intToType.each do |int, typ|
183
- if type.class == typ.class
184
- unionTypeInt = int
185
- unionType = typ
186
- break
187
- end
199
+ class String < BarePrimitive
200
+ def encode(msg, buffer)
201
+ begin
202
+ encodedString = msg.encode("UTF-8").b
203
+ rescue Encoding::UndefinedConversionError => error
204
+ 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"
188
205
  end
189
- raise SchemaMismatch("Unable to find given type in union: #{@intToType.inspect}, type: #{type}") if unionType.nil? || unionTypeInt.nil?
190
- bytes = Uint.new.encode(unionTypeInt)
191
- encoded = unionType.encode(value)
192
- bytes << encoded
206
+ Uint.new.encode(encodedString.size, buffer)
207
+ buffer << encodedString
193
208
  end
194
209
 
195
210
  def decode(msg)
196
- unionTypeInt = Uint.new.decode(msg)
197
- int = unionTypeInt[:value]
198
- type = @intToType[int]
199
- value = type.decode(unionTypeInt[:rest])
200
- return {value: {value: value[:value], type: type}, rest: value[:rest]}
211
+ strLen, rest = Uint.new.decode(msg)
212
+ string = rest[0..strLen - 1]
213
+ return string.force_encoding("utf-8"), rest[strLen..rest.size]
201
214
  end
202
215
  end
203
216
 
@@ -206,41 +219,53 @@ class BareTypes
206
219
  return otherType.class == BareTypes::DataFixedLen && otherType.length == self.length
207
220
  end
208
221
 
222
+ def to_schema(buffer)
223
+ buffer << "data<#{@length}>"
224
+ end
225
+
209
226
  def length
210
- return @length
227
+ @length
211
228
  end
212
229
 
230
+ def finalize_references(schema) end
231
+
213
232
  def initialize(length)
214
- raise MinimumSizeError("DataFixedLen must have a length greater than 0, got: #{length.inspect}") if length < 1
233
+ raise MinimumSizeError.new("DataFixedLen must have a length greater than 0, got: #{length.inspect}") if length < 1
215
234
  @length = length
216
235
  end
217
236
 
218
- def encode(msg)
219
- return msg
237
+ def encode(msg, buffer)
238
+ if msg.size != @length
239
+ raise FixedDataSizeWrong.new("Message is not proper sized for DataFixedLen should have been #{@length} but was #{msg.size}")
240
+ end
241
+ buffer << msg
220
242
  end
221
243
 
222
244
  def decode(msg)
223
- return {value: msg[0..@length], rest: msg[@length..msg.size]}
245
+ return msg[0..@length-1], msg[@length..msg.size]
224
246
  end
225
247
  end
226
248
 
227
249
  class Data < BarePrimitive
228
- def encode(msg)
229
- bytes = Uint.new.encode(msg.size)
230
- bytes << msg
231
- return bytes
250
+
251
+ def finalize_references(schema) end
252
+
253
+ def encode(msg, buffer)
254
+ Uint.new.encode(msg.size, buffer)
255
+ buffer << msg
232
256
  end
233
257
 
234
258
  def decode(msg)
235
- output = Uint.new.decode(msg)
236
- rest = output[:rest]
237
- dataSize = output[:value]
238
- return {value: rest[0..dataSize-1], rest: rest[dataSize..]}
259
+ dataSize, rest = Uint.new.decode(msg)
260
+ return rest[0..dataSize - 1], rest[dataSize..rest.size]
239
261
  end
240
262
  end
241
263
 
242
264
  class Uint < BarePrimitive
243
- def encode(msg)
265
+
266
+ def finalize_references(schema) end
267
+
268
+ def encode(msg, buffer)
244
269
  bytes = "".b
245
270
  _get_next_7_bits_as_byte(msg, 128) do |byte|
246
271
  bytes << byte
@@ -253,12 +278,12 @@ class BareTypes
253
278
  end
254
279
  end
255
280
  bytes[bytes.size - 1] = [bytes.bytes[bytes.size - 1] & 127].pack('C')[0]
256
- raise MaximumSizeError("Maximum u/int allowed is 64 bit precision") if bytes.size > 9
257
- return bytes
281
+ raise MaximumSizeError.new("Maximum u/int allowed is 64 bit precision") if bytes.size > 9
282
+ buffer << bytes
258
283
  end
259
284
 
260
285
  def decode(msg)
261
- ints = msg.unpack("CCCCCCCCC")
286
+ ints = msg.unpack("CCCCCCCC")
262
287
  relevantInts = []
263
288
  i = 0
264
289
  while ints[i] & 0b10000000 == 128
@@ -270,220 +295,478 @@ class BareTypes
270
295
  relevantInts.each_with_index do |int, idx|
271
296
  sum += int << (idx * 7)
272
297
  end
273
- return {value: sum, rest: msg[(i + 1)..msg.size]}
298
+ return sum, msg[(i + 1)..msg.size]
274
299
  end
275
300
  end
276
301
 
277
302
  class U8 < BarePrimitive
278
- def encode(msg)
279
- return [msg].pack("C")
303
+ def encode(msg, buffer)
304
+ buffer << [msg].pack("C")
280
305
  end
281
306
 
282
307
  def decode(msg)
283
- return {value: msg[0].unpack("C")[0], rest: msg[1..msg.size]}
308
+ return msg[0].unpack("C")[0], msg[1..msg.size]
284
309
  end
285
310
  end
286
311
 
287
312
  class U16 < BarePrimitive
288
- def encode(msg)
289
- return [msg].pack("v")
313
+ def encode(msg, buffer)
314
+ buffer << [msg].pack("v")
290
315
  end
291
316
 
292
317
  def decode(msg)
293
- return {value: msg.unpack("v")[0], rest: msg[2..msg.size]}
318
+ return msg.unpack("v")[0], msg[2..msg.size]
294
319
  end
295
320
  end
296
321
 
297
322
  class U32 < BarePrimitive
298
- def encode(msg)
299
- return [msg].pack("V")
323
+ def encode(msg, buffer)
324
+ buffer << [msg].pack("V")
300
325
  end
301
326
 
302
327
  def decode(msg)
303
- return {value: msg.unpack("V")[0], rest: msg[4..msg.size]}
328
+ return msg.unpack("V")[0], msg[4..msg.size]
304
329
  end
305
330
  end
306
331
 
307
332
  class U64 < BarePrimitive
308
- def encode(msg)
309
- return [msg].pack("Q")
333
+ def encode(msg, buffer)
334
+ buffer << [msg].pack("Q")
310
335
  end
311
336
 
312
337
  def decode(msg)
313
- return {value: msg.unpack("Q")[0], rest: [8..msg.size]}
338
+ return msg.unpack("Q")[0], msg[8..msg.size]
314
339
  end
315
340
  end
316
341
 
317
342
  class I8 < BarePrimitive
318
- def encode(msg)
319
- return [msg].pack("c")
343
+ def encode(msg, buffer)
344
+ buffer << [msg].pack("c")
320
345
  end
321
346
 
322
347
  def decode(msg)
323
- return {value: msg[0].unpack("c")[0], rest: msg[1..msg.size]}
348
+ return msg[0].unpack("c")[0], msg[1..msg.size]
324
349
  end
325
350
  end
326
351
 
327
352
  class I16 < BarePrimitive
328
- def encode(msg)
329
- return [msg].pack("s<")
353
+ def encode(msg, buffer)
354
+ buffer << [msg].pack("s<")
330
355
  end
331
356
 
332
357
  def decode(msg)
333
- return {value: msg.unpack('s<')[0], rest: msg[2..msg.size]}
358
+ return msg.unpack('s<')[0], msg[2..msg.size]
334
359
  end
335
360
  end
336
361
 
337
362
  class I32 < BarePrimitive
338
- def encode(msg)
339
- return [msg].pack("l<")
363
+ def encode(msg, buffer)
364
+ buffer << [msg].pack("l<")
340
365
  end
341
366
 
342
367
  def decode(msg)
343
- return {value: msg.unpack('l<')[0], rest: msg[4..msg.size]}
368
+ return msg.unpack('l<')[0], msg[4..msg.size]
344
369
  end
345
370
  end
346
371
 
347
372
  class I64 < BarePrimitive
348
- def encode(msg)
349
- return [msg].pack("q<")
373
+ def encode(msg, buffer)
374
+ buffer << [msg].pack("q<")
350
375
  end
351
376
 
352
377
  def decode(msg)
353
- return {value: msg.unpack('q<')[0], rest: msg[8..msg.size]}
378
+ return msg.unpack('q<')[0], msg[8..msg.size]
354
379
  end
355
380
  end
356
381
 
357
382
  class Bool < BarePrimitive
358
- def encode(msg)
359
- return msg ? "\xFF\xFF".b : "\x00\x00".b
383
+ def encode(msg, buffer)
384
+ buffer << (msg ? "\x00".b : "\x01".b)
385
+ end
386
+
387
+ def decode(msg)
388
+ if msg != "\x00" && msg != "\x01"
389
+ raise InvalidBool.new("Expected a bool but found #{msg.inspect}. Standard requires bool to be 0x00 or 0x01")
390
+ end
391
+ return (msg == "\x00" ? true : false), msg[1..msg.size]
392
+ end
393
+ end
394
+ #endregion
395
+
396
+ class Optional < BaseType
397
+ def ==(otherType)
398
+ return otherType.class == BareTypes::Optional && otherType.optionalType == @optionalType
399
+ end
400
+
401
+ def to_schema(buffer)
402
+ buffer << "optional<"
403
+ @optionalType.to_schema(buffer)
404
+ buffer << ">"
405
+ end
406
+
407
+ def finalize_references(schema)
408
+ return if @finalized
409
+ @finalized = true
410
+ if @optionalType.is_a?(Symbol)
411
+ @optionalType = Reference.new(@optionalType, schema[@optionalType])
412
+ else
413
+ @optionalType.finalize_references(schema)
414
+ end
415
+ end
416
+
417
+ def optionalType
418
+ @optionalType
419
+ end
420
+
421
+ def initialize(optionalType)
422
+ raise VoidUsedOutsideTaggedSet() if optionalType.class == BareTypes::Void
423
+ @optionalType = optionalType
424
+ end
425
+
426
+ def encode(msg, buffer)
427
+ if msg.nil?
428
+ buffer << "\x00".b
429
+ else
430
+ buffer << "\x01".b
431
+ @optionalType.encode(msg, buffer)
432
+ end
433
+ end
434
+
435
+ def decode(msg)
436
+ if msg.unpack("C")[0] == 0
437
+ return nil, msg[1..msg.size]
438
+ else
439
+ return @optionalType.decode(msg[1..msg.size])
440
+ end
441
+ end
442
+ end
443
+
444
+ class Map < BaseType
445
+ def ==(otherType)
446
+ return otherType.class == BareTypes::Map && otherType.from == @from && otherType.to == @to
447
+ end
448
+
449
+ def to_schema(buffer)
450
+ buffer << "map["
451
+ @from.to_schema(buffer)
452
+ buffer << "]"
453
+ @to.to_schema(buffer)
454
+ end
455
+
456
+ def finalize_references(schema)
457
+ return if @finalized
458
+ @finalized = true
459
+ if @from.is_a?(Symbol)
460
+ @to = Reference.new(@from, schema[@to])
461
+ else
462
+ @to.finalize_references(schema)
463
+ end
464
+ end
465
+
466
+ def initialize(fromType, toType)
467
+ raise VoidUsedOutsideTaggedSet if fromType.class == BareTypes::Void or toType.class == BareTypes::Void
468
+ if !fromType.class.ancestors.include?(BarePrimitive) ||
469
+ fromType.is_a?(BareTypes::Data) ||
470
+ fromType.is_a?(BareTypes::DataFixedLen)
471
+ raise MapKeyError("Map keys must use a primitive type which is not data or data<length>.")
472
+ end
473
+ @from = fromType
474
+ @to = toType
475
+ end
476
+
477
+ def from
478
+ @from
479
+ end
480
+
481
+ def to
482
+ @to
483
+ end
484
+
485
+ def encode(msg, buffer)
486
+ Uint.new.encode(msg.size, buffer)
487
+ msg.each do |from, to|
488
+ @from.encode(from, buffer)
489
+ @to.encode(to, buffer)
490
+ end
360
491
  end
361
492
 
362
493
  def decode(msg)
363
- return {value: msg == "\x00\x00" ? false : true, rest: msg[1..msg.size]}
494
+ hash = Hash.new
495
+ mapSize, rest = Uint.new.decode(msg)
496
+ # (0..mapSize).each do
497
+ (mapSize - 1).to_i.downto(0) do
498
+ key, rest = @from.decode(rest)
499
+ value, rest = @to.decode(rest)
500
+ hash[key] = value
501
+ end
502
+ return hash, rest
503
+ end
504
+ end
505
+
506
+ class Union < BaseType
507
+ attr_accessor :intToType
508
+
509
+ def cycle_search(seen)
510
+ if @intToType.size == 1
511
+ seen.add(self)
512
+ @intToType.values.each do |type|
513
+ type.cycle_search(seen)
514
+ end
515
+ seen.pop
516
+ end
517
+ end
518
+
519
+ def finalize_references(schema)
520
+ return if @finalized
521
+ @finalized = true
522
+ @intToType.keys.each do |key|
523
+ if @intToType[key].is_a?(Symbol)
524
+ @intToType[key] = Reference.new(@intToType[key], schema[@intToType[key]])
525
+ else
526
+ @intToType[key].finalize_references(schema)
527
+ end
528
+ end
529
+ end
530
+
531
+ def ==(otherType)
532
+ return false unless otherType.is_a?(BareTypes::Union)
533
+ @intToType.each do |int, type|
534
+ return false unless type == otherType.intToType[int]
535
+ end
536
+ true
537
+ end
538
+
539
+ def initialize(intToType)
540
+ intToType.keys.each do |i|
541
+ raise MinimumSizeError("Union's integer representations must be > 0, instead got: #{i}") if i < 0 or !i.is_a?(Integer)
542
+ end
543
+ raise MinimumSizeError("Union must have at least one type") if intToType.keys.size < 1
544
+ @intToType = intToType
545
+ end
546
+
547
+ def to_schema(buffer)
548
+ buffer << "("
549
+ strs = []
550
+ @intToType.size.times do
551
+ strs << ""
552
+ end
553
+ @intToType.values.each_with_index.map { |type, i| type.to_schema(strs[i]) }
554
+ buffer << strs.join(" | ")
555
+ buffer << ")"
556
+ end
557
+
558
+ def encode(msg, buffer)
559
+ type = msg[:type]
560
+ value = msg[:value]
561
+ unionTypeInt = nil
562
+ unionType = nil
563
+ @intToType.each do |int, typ|
564
+ if type.class == typ.class
565
+ unionTypeInt = int
566
+ unionType = typ
567
+ break
568
+ end
569
+ end
570
+ raise SchemaMismatch("Unable to find given type in union: #{@intToType.inspect}, type: #{type}") if unionType.nil? || unionTypeInt.nil?
571
+ Uint.new.encode(unionTypeInt, buffer)
572
+ unionType.encode(value, buffer)
573
+ end
574
+
575
+ def decode(msg)
576
+ int, rest = Uint.new.decode(msg)
577
+ type = @intToType[int]
578
+ value, rest = type.decode(rest)
579
+ return { value: value, type: type }, rest
364
580
  end
365
581
  end
366
582
 
367
583
  class Struct < BaseType
584
+ attr_accessor :mapping
585
+
586
+ def cycle_search(seen)
587
+ seen.add(self)
588
+ @mapping.values.each do |val|
589
+ val.cycle_search(seen)
590
+ end
591
+ seen.pop
592
+ end
593
+
594
+ def [](key)
595
+ @mapping[key]
596
+ end
597
+
368
598
  def ==(otherType)
369
599
  return false unless otherType.class == BareTypes::Struct
370
600
  @mapping.each do |k, v|
371
601
  return false unless otherType.mapping[k] == v
372
602
  end
373
- return true
374
- end
375
-
376
- def mapping
377
- @mapping
603
+ true
604
+ end
605
+
606
+ def finalize_references(schema)
607
+ return if @finalized
608
+ @finalized = true
609
+ @mapping.each do |key, val|
610
+ if val.is_a?(Symbol)
611
+ @mapping[key] = Reference.new(val, schema[val])
612
+ begin
613
+ @mapping[key].ref.finalize_references(schema)
614
+ rescue NoMethodError => e
615
+ puts "err"
616
+ end
617
+ else
618
+ val.finalize_references(schema)
619
+ end
620
+ end
378
621
  end
379
622
 
380
623
  def initialize(symbolToType)
381
- # Mapping from symbols to Bare types
624
+ # Mapping from symbols to Bare types (or possibly symbols before finalizing)
382
625
  symbolToType.keys.each do |k|
383
- raise BareException("Struct keys must be symbols") unless k.is_a?(Symbol)
384
- raise BareException("Struct values must be a BareTypes::TYPE\nInstead got: #{symbolToType[k].inspect}") unless symbolToType[k].class.ancestors.include?(BaseType)
385
- 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
626
+ raise BareException.new("Struct keys must be symbols") unless k.is_a?(Symbol)
627
+ if (!symbolToType[k].class.ancestors.include?(BaseType) && !symbolToType[k].is_a?(Symbol))
628
+ raise BareException.new("Struct values must be a BareTypes::TYPE or a symbol with the same
629
+ name as a user defined type\nInstead got: #{symbolToType[k].inspect}")
630
+ end
631
+ raise VoidUsedOutsideTaggedSet.new("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
386
632
  end
387
633
  raise("Struct must have at least one field") if symbolToType.keys.size == 0
388
634
  @mapping = symbolToType
389
635
  end
390
636
 
391
- def encode(msg)
392
- bytes = "".b
637
+ def encode(msg, buffer)
393
638
  @mapping.keys.each do |symbol|
394
- raise SchemaMismatch("All struct fields must be specified, missing: #{symbol.inspect}") unless msg.keys.include?(symbol)
395
- bytes << @mapping[symbol].encode(msg[symbol])
639
+ raise SchemaMismatch.new("All struct fields must be specified, missing: #{symbol.inspect}") unless msg.keys.include?(symbol)
640
+ @mapping[symbol].encode(msg[symbol], buffer)
396
641
  end
397
- return bytes
398
642
  end
399
643
 
400
644
  def decode(msg)
401
645
  hash = Hash.new
402
646
  rest = msg
403
647
  @mapping.keys.each do |symbol|
404
- output = @mapping[symbol].decode(rest)
405
- hash[symbol] = output[:value]
406
- rest = output[:rest]
648
+ value, rest = @mapping[symbol].decode(rest)
649
+ hash[symbol] = value
407
650
  end
408
- return {value: hash, rest: rest}
651
+ return hash, rest
652
+ end
653
+
654
+ def to_schema(buffer)
655
+ buffer << "{"
656
+ @mapping.each do |symbol, type|
657
+ buffer << " #{symbol}: "
658
+ type.to_schema(buffer)
659
+ buffer << "\n"
660
+ end
661
+ buffer << "}"
409
662
  end
410
663
  end
411
664
 
412
665
  class Array < BaseType
413
666
  def ==(otherType)
414
- return otherType.class == BareTypes::Array && otherType.type == self.type
667
+ otherType.class == BareTypes::Array && otherType.type == self.type
668
+ end
669
+
670
+ def cycle_search(seen)
671
+ seen.add(self)
672
+ @type.cycle_search(seen)
673
+ seen.pop
415
674
  end
416
675
 
417
676
  def type
418
- return @type
677
+ @type
678
+ end
679
+
680
+ def to_schema(buffer)
681
+ buffer << "[]"
682
+ @type.to_schema(buffer)
683
+ end
684
+
685
+ def finalize_references(schema)
686
+ return if @finalized
687
+ @finalized = true
688
+ if @type.is_a?(Symbol)
689
+ @type = Reference.new(@type, schema[@type])
690
+ else
691
+ @type.finalize_references(schema)
692
+ end
419
693
  end
420
694
 
421
695
  def initialize(type)
422
- raise VoidUsedOutsideTaggedSet("Void types may only be used as members of the set of types in a tagged union.") if type.class == BareTypes::Void
696
+ raise VoidUsedOutsideTaggedSet.new("Void types may only be used as members of the set of types in a tagged union.") if type.class == BareTypes::Void
423
697
  @type = type
424
698
  end
425
699
 
426
- def encode(msg)
427
- bytes = Uint.new.encode(msg.size)
700
+ def encode(msg, buffer)
701
+ Uint.new.encode(msg.size, buffer)
428
702
  msg.each do |item|
429
- bytes << @type.encode(item)
703
+ @type.encode(item, buffer)
430
704
  end
431
- return bytes
432
705
  end
433
706
 
434
707
  def decode(msg)
435
- output = Uint.new.decode(msg)
436
708
  arr = []
437
- arrayLen = output[:value]
709
+ arrayLen, rest = Uint.new.decode(msg)
438
710
  lastSize = msg.size + 1 # Make sure msg size monotonically decreasing
439
711
  (arrayLen - 1).downto(0) do
440
- output = @type.decode(output[:rest])
441
- arr << output[:value]
442
- break if output[:rest].nil? || output[:rest].size == 0 || lastSize <= output[:rest].size
443
- lastSize = output[:rest].size
712
+ arrVal, rest = @type.decode(rest)
713
+ arr << arrVal
714
+ break if rest.nil? || rest.size == 0 || lastSize <= rest.size
715
+ lastSize = rest.size
444
716
  end
445
- return {value: arr, rest: output[:rest]}
717
+ return arr, rest
446
718
  end
447
719
  end
448
720
 
449
721
  class ArrayFixedLen < BaseType
722
+ attr_accessor :type, :size
723
+
724
+ def cycle_search(seen)
725
+ seen.add(self)
726
+ @type.cycle_search(seen)
727
+ seen.pop
728
+ end
729
+
450
730
  def ==(otherType)
451
731
  return otherType.class == BareTypes::ArrayFixedLen && otherType.type == @type && otherType.size == @size
452
732
  end
453
733
 
734
+ def finalize_references(schema)
735
+ return if @finalized
736
+ @finalized = true
737
+ if @type.is_a?(Symbol)
738
+ @type = Reference.new(@type, schema[@type])
739
+ else
740
+ @type.finalize_references(schema)
741
+ end
742
+ end
743
+
454
744
  def initialize(type, size)
455
745
  @type = type
456
746
  @size = size
457
- raise VoidUsedOutsideTaggedSet("Void type may not be used as type of fixed length array.") if type.class == BareTypes::Void
458
- raise MinimumSizeError("FixedLenArray size must be > 0") if size < 1
459
- end
460
-
461
- def type
462
- @type
463
- end
464
-
465
- def size
466
- @size
747
+ raise VoidUsedOutsideTaggedSet.new("Void type may not be used as type of fixed length array.") if type.class == BareTypes::Void
748
+ raise MinimumSizeError.new("ArrayFixedLen size must be > 0") if size < 1
467
749
  end
468
750
 
469
- def encode(arr)
470
- raise SchemaMismatch("This FixLenArray is of length #{@size.to_s} but you passed an array of length #{arr.size}") if arr.size != @size
471
- bytes = ""
751
+ def encode(arr, buffer)
752
+ raise SchemaMismatch.new("This ArrayFixedLen is of length #{@size.to_s} but you passed an array of length #{arr.size}") if arr.size != @size
472
753
  arr.each do |item|
473
- bytes << @type.encode(item)
754
+ @type.encode(item, buffer)
474
755
  end
475
- return bytes
476
756
  end
477
757
 
478
- def decode(msg)
758
+ def decode(rest)
479
759
  array = []
480
- rest = msg
481
760
  @size.times do
482
- output = @type.decode(rest)
483
- rest = output[:rest]
484
- array << output[:value]
761
+ arrVal, rest = @type.decode(rest)
762
+ array << arrVal
485
763
  end
486
- return {value: array, rest: rest}
764
+ return array, rest
765
+ end
766
+
767
+ def to_schema(buffer)
768
+ buffer << "[#{@size}]"
769
+ @type.to_schema(buffer)
487
770
  end
488
771
  end
489
772
 
@@ -496,6 +779,16 @@ class BareTypes
496
779
  return true
497
780
  end
498
781
 
782
+ def to_schema(buffer)
783
+ buffer << "{\n"
784
+ @valToInt.each do |name, repr|
785
+ buffer << " #{name} = #{repr}\n"
786
+ end
787
+ buffer << "}"
788
+ end
789
+
790
+ def finalize_references(schema) end
791
+
499
792
  def intToVal
500
793
  @intToVal
501
794
  end
@@ -503,9 +796,9 @@ class BareTypes
503
796
  def initialize(source)
504
797
  @intToVal = {}
505
798
  @valToInt = {}
506
- raise BareException("Enum must initialized with a hash from integers to anything") if !source.is_a?(Hash)
507
- raise BareException("Enum must have unique positive integer assignments") if Set.new(source.keys).size != source.keys.size
508
- raise EnumValueError("Enum must have unique values") if source.values.to_set.size != source.values.size
799
+ raise BareException.new("Enum must initialized with a hash from integers to anything") if !source.is_a?(Hash)
800
+ raise BareException.new("Enum must have unique positive integer assignments") if Set.new(source.keys).size != source.keys.size
801
+ raise EnumValueError.new("Enum must have unique values") if source.values.to_set.size != source.values.size
509
802
  source.each do |k, v|
510
803
  raise("Enum keys must be positive integers") if k < 0
511
804
  @intToVal[k.to_i] = v
@@ -513,18 +806,15 @@ class BareTypes
513
806
  end
514
807
  end
515
808
 
516
- def encode(msg)
517
- raise SchemaMismatch("#{msg.inspect} is not part of this enum: #{@intToVal}") if !@valToInt.keys.include?(msg)
809
+ def encode(msg, buffer)
810
+ raise SchemaMismatch.new("#{msg.inspect} is not part of this enum: #{@intToVal}") if !@valToInt.keys.include?(msg)
518
811
  integerRep = @valToInt[msg]
519
- encoded = BareTypes::Uint.new.encode(integerRep)
520
- return encoded
812
+ BareTypes::Uint.new.encode(integerRep, buffer)
521
813
  end
522
814
 
523
815
  def decode(msg)
524
- output = BareTypes::Uint.new.decode(msg)
525
- value = output[:value]
526
- rest = output[:rest]
527
- return {value: @intToVal[value], rest: rest}
816
+ value, rest = BareTypes::Uint.new.decode(msg)
817
+ return @intToVal[value], rest
528
818
  end
529
819
  end
530
820
  end
@@ -540,4 +830,4 @@ def _get_next_7_bits_as_byte(integer, base = 128)
540
830
  end
541
831
  yield(byte)
542
832
  end
543
- end
833
+ end