slow_blink 0.0.7 → 0.0.8

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/ext/slow_blink/ext_schema_parser/extconf.rb +1 -0
  3. data/ext/slow_blink/ext_schema_parser/lexer.c +6 -8
  4. data/ext/slow_blink/ext_schema_parser/lexer.h +1 -1
  5. data/ext/slow_blink/ext_schema_parser/parser.c +3 -1
  6. data/ext/slow_blink/message/ext_compact_encoder/ext_compact_encoder.c +46 -24
  7. data/ext/slow_blink/message/ext_compact_encoder/extconf.rb +9 -2
  8. data/lib/slow_blink/enum.rb +4 -1
  9. data/lib/slow_blink/field.rb +1 -1
  10. data/lib/slow_blink/generate_c/model.rb +119 -12
  11. data/lib/slow_blink/message/binary.rb +9 -1
  12. data/lib/slow_blink/message/boolean.rb +12 -3
  13. data/lib/slow_blink/message/date.rb +9 -1
  14. data/lib/slow_blink/message/decimal.rb +9 -1
  15. data/lib/slow_blink/message/dynamic_group.rb +132 -0
  16. data/lib/slow_blink/message/enum.rb +6 -2
  17. data/lib/slow_blink/message/field.rb +67 -43
  18. data/lib/slow_blink/message/fixed.rb +5 -1
  19. data/lib/slow_blink/message/floating_point.rb +9 -1
  20. data/lib/slow_blink/message/group.rb +54 -197
  21. data/lib/slow_blink/message/integer.rb +16 -8
  22. data/lib/slow_blink/message/model.rb +20 -18
  23. data/lib/slow_blink/message/static_group.rb +63 -0
  24. data/lib/slow_blink/message/string.rb +9 -1
  25. data/lib/slow_blink/message/test_data.rb +126 -0
  26. data/lib/slow_blink/message/time.rb +10 -2
  27. data/lib/slow_blink/message/time_of_day.rb +18 -2
  28. data/lib/slow_blink/schema.rb +2 -2
  29. data/lib/slow_blink/type.rb +3 -3
  30. data/lib/slow_blink/version.rb +1 -1
  31. data/test/tc_inputs.rb +34 -25
  32. data/test/tc_model_enum.rb +47 -0
  33. data/test/tc_model_inputs.rb +47 -0
  34. metadata +9 -7
  35. data/ext/slow_blink/message/ext_compact_encoder/blink_compact.c +0 -642
  36. data/ext/slow_blink/message/ext_compact_encoder/blink_compact.h +0 -411
  37. data/ext/slow_blink/message/ext_compact_encoder/blink_debug.h +0 -46
  38. data/ext/slow_blink/message/ext_compact_encoder/blink_stream.c +0 -314
  39. data/ext/slow_blink/message/ext_compact_encoder/blink_stream.h +0 -185
@@ -0,0 +1,132 @@
1
+ # @license
2
+ #
3
+ # Copyright (c) 2016 Cameron Harper
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ # this software and associated documentation files (the "Software"), to deal in
7
+ # the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ # the Software, and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ module SlowBlink::Message
23
+
24
+ class DynamicGroup
25
+
26
+ def self.taggedGroups
27
+ @taggedGroups
28
+ end
29
+
30
+ def self.permittedID
31
+ @permittedID
32
+ end
33
+
34
+ def self.from_compact(input, depth)
35
+
36
+ group = nil
37
+
38
+ if depth > 0
39
+ depth = depth - 1
40
+ else
41
+ raise RecursionLimit
42
+ end
43
+
44
+ if input.kind_of? String
45
+ input = StringIO.new(input)
46
+ end
47
+
48
+ buf = input.getBinary
49
+
50
+ if buf.nil?
51
+
52
+ group = nil
53
+
54
+ elsif buf.size == 0
55
+
56
+ raise WeakError5.new "W5: Value cannot be null"
57
+
58
+ else
59
+
60
+ buf = StringIO.new(buf)
61
+ id = buf.getU64
62
+
63
+ if klass = @taggedGroups[id]
64
+
65
+ if @permittedID.include? id
66
+
67
+ group = klass.from_compact(buf, depth)
68
+
69
+ if !buf.eof?
70
+ size = buf.getU32
71
+ while group.extension.size < size do
72
+ group.extension << @anyTaggedGroup.from_compact(buf, depth).get
73
+ end
74
+ end
75
+
76
+ if !buf.eof?
77
+ raise ExtensionPadding
78
+ end
79
+
80
+ else
81
+ raise WeakError15.new "W15: Group is known but unexpected"
82
+ end
83
+ else
84
+ raise WeakError2.new "W2: Group id #{id} is unknown"
85
+ end
86
+
87
+ end
88
+
89
+ depth = depth + 1
90
+ self.new(group)
91
+
92
+ end
93
+
94
+ def set(value)
95
+ if value.kind_of? Group
96
+ if self.class.permittedID.include? value.class.id
97
+ @value = value
98
+ else
99
+ raise TypeError.new "incompatible group"
100
+ end
101
+ else
102
+ raise ArgumentError.new "argument must be kind_of Group"
103
+ end
104
+ end
105
+
106
+ # @return [Group] contained group
107
+ def get
108
+ @value.get
109
+ end
110
+
111
+ # @note calls {#set}(value)
112
+ def initialize(value)
113
+ set(value)
114
+ end
115
+
116
+ # @private
117
+ def to_compact(out)
118
+ @value.encode_compact(out)
119
+ end
120
+
121
+ def extension
122
+ @value.extension
123
+ end
124
+
125
+ def to_tag
126
+ "@#{@value.class.name}#{@value.to_tag_value}#{@value.to_tag_extension}"
127
+ end
128
+
129
+ end
130
+
131
+ end
132
+
@@ -29,7 +29,7 @@ module SlowBlink::Message
29
29
  end
30
30
 
31
31
  # @private
32
- def self.from_compact(input, stack)
32
+ def self.from_compact(input, depth)
33
33
  if value = input.getI32
34
34
  if symbol = type.symbol(value)
35
35
  self.new(symbol.name)
@@ -68,7 +68,11 @@ module SlowBlink::Message
68
68
 
69
69
  # @private
70
70
  def to_compact(out)
71
- out.putI32(self.class.type.symbol(@value).value)
71
+ out.putI32(self.class.type.symbol(@value).value)
72
+ end
73
+
74
+ def to_tag
75
+ @value
72
76
  end
73
77
 
74
78
  end
@@ -51,70 +51,84 @@ module SlowBlink::Message
51
51
 
52
52
  # @private
53
53
  # @param input [String] Blink compact form
54
- # @param stack [Array]
54
+ # @param depth [Array]
55
55
  # @return [Field] instance of anonymous subclass of Field
56
- def self.from_compact(input, stack)
57
- if sequence?
58
- if size = input.getU32
59
- value = []
60
- while value.size < size do
61
- value << @type.from_compact(input, stack).get
62
- end
63
- self.new(value)
64
- else
65
- nil
66
- end
67
- elsif optional? and (type.kind_of? StaticGroup or type.kind_of? FIXED)
68
- if input.get_present(input)
69
- self.new(@type.from_compact(input, stack).get)
70
- else
71
- nil
72
- end
73
- else
74
- self.new(@type.from_compact(input, stack).get)
75
- end
56
+ def self.from_compact(input, depth)
57
+ self.new(input, depth)
76
58
  end
77
59
 
78
- # @note calls {#set}(value)
79
- def initialize(value)
60
+ attr_reader :type
61
+
62
+ def sequence?
63
+ @sequence
64
+ end
65
+ def optional?
66
+ @optional
67
+ end
68
+
69
+ def initialize(input=nil, depth=nil)
80
70
 
81
71
  @optional = self.class.optional?
82
72
  @sequence = self.class.sequence?
83
73
  @type = self.class.type
84
-
85
- if value
86
- set(value)
87
- else
88
- @value = nil
74
+ @value = nil
75
+
76
+ if input and depth
77
+ if @sequence
78
+ if size = input.getU32
79
+ @value = []
80
+ size.times do
81
+ value = @type.from_compact(input, depth)
82
+ if value
83
+ @value << value
84
+ end
85
+ end
86
+ end
87
+ elsif @optional and (@type.kind_of? StaticGroup or @type.kind_of? FIXED)
88
+ if input.get_present
89
+ @value = @type.from_compact(input, depth)
90
+ end
91
+ else
92
+ @value = @type.from_compact(input, depth)
93
+ end
94
+
89
95
  end
90
-
96
+
91
97
  end
92
98
 
93
- def set(value)
94
- if value.nil?
95
- if optional?
99
+ def set(value)
100
+ if value.kind_of? Field
101
+ self.set(value.get)
102
+ elsif value.nil?
103
+ if @optional
96
104
  @value = nil
105
+ self
97
106
  else
98
- raise ArgumentError.new "field is not optional, value cannot be nil"
107
+ raise ArgumentError.new "field '#{self.class.name}'is not optional, value cannot be nil (sequence: #{@sequence})"
99
108
  end
100
- elsif self.class.sequence?
109
+ elsif @sequence
101
110
  if value.kind_of? Array
102
111
  @value = []
103
112
  value.each do |v|
113
+ if v.nil?
114
+ next
115
+ end
104
116
  @value << @type.new(v)
105
117
  end
118
+ self
106
119
  else
107
120
  raise ArgumentError.new "field value must be an array of type"
108
121
  end
109
122
  else
110
123
  @value = @type.new(value)
111
- end
124
+ self
125
+ end
112
126
  end
113
127
 
114
128
  # @return field value or nil
115
129
  def get
116
130
  if @value
117
- if self.class.sequence?
131
+ if @sequence
118
132
  @value.map{|v|v.get}
119
133
  else
120
134
  @value.get
@@ -129,7 +143,7 @@ module SlowBlink::Message
129
143
  # @return [StringIO]
130
144
  def to_compact(out)
131
145
  if @value
132
- if self.class.sequence?
146
+ if @sequence
133
147
  out.putU32(@value.size)
134
148
  @value.each do |v|
135
149
  v.to_compact(out)
@@ -143,16 +157,26 @@ module SlowBlink::Message
143
157
  elsif @optional
144
158
  out.putNull
145
159
  else
146
- raise IncompleteGroup.new "'#{self.name}' must not be null"
160
+ raise IncompleteGroup.new "'#{self.class.name}' must not be null"
147
161
  end
148
162
  end
149
163
 
150
- private
151
-
152
- def sequenceOfSameType(value)
153
- value.each do ||
154
- end
164
+ def to_tag
165
+ if @value
166
+ if @value.is_a? Array
167
+ @value.inject("|#{self.class.name}=[") do |out,v|
168
+ if @value.first != v
169
+ out << ";"
170
+ end
171
+ out << v.to_tag
172
+ end << "]"
173
+ else
174
+ "|#{self.class.name}=#{@value.to_tag}"
175
+ end
176
+ else
177
+ ""
155
178
  end
179
+ end
156
180
 
157
181
  end
158
182
 
@@ -24,8 +24,12 @@ module SlowBlink::Message
24
24
  # @abstract
25
25
  class FIXED
26
26
 
27
+ def self.type
28
+ @type
29
+ end
30
+
27
31
  # @private
28
- def self.from_compact(input, stack)
32
+ def self.from_compact(input, depth)
29
33
  if value = input.getFixed(size)
30
34
  self.new(value)
31
35
  else
@@ -24,8 +24,12 @@ module SlowBlink::Message
24
24
  # @abstract
25
25
  class FLOATING_POINT
26
26
 
27
+ def self.type
28
+ @type
29
+ end
30
+
27
31
  # @private
28
- def self.from_compact(input, stack)
32
+ def self.from_compact(input, depth)
29
33
  if value = input.getF64
30
34
  self.new(value)
31
35
  else
@@ -58,6 +62,10 @@ module SlowBlink::Message
58
62
  out.putF64(@value)
59
63
  end
60
64
 
65
+ def to_tag
66
+ @value.to_s
67
+ end
68
+
61
69
  end
62
70
 
63
71
  end
@@ -21,7 +21,9 @@
21
21
 
22
22
  module SlowBlink::Message
23
23
 
24
- class StaticGroup
24
+ class Group
25
+
26
+ attr_reader :extension
25
27
 
26
28
  # @private
27
29
  # @return [Array<Field>] group fields
@@ -41,30 +43,37 @@ module SlowBlink::Message
41
43
  @name
42
44
  end
43
45
 
46
+ def self.ancestorID
47
+ @ancestorID
48
+ end
49
+
44
50
  # @private
45
- # @param input [StringIO, String] Blink compact form
46
- # @param stack [Array] used to measure depth of recursion
47
- # @return [Group, nil]
48
- # @raise [Error] recursion depth limit
49
- def self.from_compact(input, stack)
51
+ def self.from_compact(input, depth)
50
52
 
51
- fields = {}
53
+ value = {}
52
54
 
53
- if stack.size < @maxRecursion
54
- stack << self
55
+ if depth > 0
56
+ depth = depth - 1
55
57
  else
56
58
  raise RecursionLimit
57
59
  end
58
60
 
59
61
  @fields.each do |f|
60
- fields[f.name] = f.from_compact(input, stack)
62
+ value[f.name] = f.from_compact(input, depth)
61
63
  end
62
64
 
63
- stack.pop
64
-
65
- self.new(fields)
65
+ depth = depth + 1
66
+
67
+ self.new(value)
66
68
 
67
69
  end
70
+
71
+ def initialize(fields={}, *extension)
72
+ @value = {}
73
+ self.class.fields.each{|f|@value[f.name] = f.new}
74
+ set(fields)
75
+ @extension = extension
76
+ end
68
77
 
69
78
  # @api user
70
79
  # Finds Field by name and calls {Field#set}(value)
@@ -74,7 +83,7 @@ module SlowBlink::Message
74
83
  # @raise [IndexError, TypeError]
75
84
  def []=(name, value)
76
85
  if field = @value[name]
77
- field.set(value)
86
+ @value[name] = field.set(value)
78
87
  else
79
88
  raise IndexError.new "field #{name} is not defined in this group"
80
89
  end
@@ -95,219 +104,67 @@ module SlowBlink::Message
95
104
  end
96
105
  end
97
106
 
98
- # Get this group
99
- # @return [self]
100
- def get
101
- self
102
- end
103
-
104
- # Set the contents of this group
105
- #
106
- # @overload set(value)
107
- # @param value [Hash{String=>Field,Numeric,String,Time,nil}] Hash of {Field} objects or literal values
108
- # @overload set(value)
109
- # @param value [Group] a Hash of {Field} objects will be extracted from a Group object by calling {Group#fields}
110
- # @return [self]
111
- # @raise [IndexError, TypeError]
112
107
  def set(value)
113
-
114
108
  if value.kind_of? Hash
115
109
  value.each do |fn, fv|
116
110
  if @value[fn]
117
- # replace entire field
118
- if fv.is_a? @value[fn].class
119
- @value[fn] = fv
120
- # set value of field
121
- else
122
- @value[fn].set(fv)
123
- end
111
+ @value[fn] = @value[fn].set(fv)
124
112
  else
125
113
  raise IndexError.new "field '#{fn}' is unknown"
126
114
  end
127
- end
128
- # replace @value with value from another instance of self.class
129
- elsif value.kind_of? self.class
130
- @value = value.fields.to_h
115
+ end
131
116
  else
132
- raise TypeError.new "expecting a Hash or a StaticGroup instance"
117
+ raise TypeError.new "expecting a Hash instance"
133
118
  end
134
119
  self
135
120
  end
136
121
 
137
- # @api user
138
- # Create a Group
139
- #
140
- # @note calls {#set}(fields)
141
- # @param fields [Hash]
142
- def initialize(fields={})
143
- @value = {}
144
- self.class.fields.each do |f|
145
- @value[f.name] = f.new(nil)
146
- end
147
- set(fields)
148
- end
149
-
150
- # @return [String] Blink Protocol compact form
151
- def encode_compact
152
- if self.class.id
153
- to_compact("")
154
- else
155
- raise UntaggedGroup.new "cannot encode a group without an ID"
156
- end
122
+ def get
123
+ self
157
124
  end
158
125
 
159
126
  # @private
160
127
  def to_compact(out)
161
- @value.each do |fn, fv|
162
- fv.to_compact(out)
163
- end
128
+ @value.values.each{|f|f.to_compact(out)}
164
129
  end
165
130
 
166
- protected
167
-
168
- # Get the value hash directly
169
- # @return [Hash{String => Field}]
170
- def fields
171
- @value
131
+ def encode_compact(out="".force_encoding("ASCII-8BIT"))
132
+ if self.class.id
133
+ groupOut = String.new.putU64(self.class.id)
134
+ to_compact(groupOut)
135
+ if @extension.size > 0
136
+ groupOut.putU32(@extension.size)
137
+ @extension.each{|e|e.encode_compact(groupOut)}
138
+ end
139
+
140
+ out.putU32(groupOut.size)
141
+ out << groupOut
142
+ else
143
+ raise UntaggedGroup.new "cannot encode a group without an ID"
172
144
  end
173
-
174
- end
175
-
176
- class Group < StaticGroup
177
-
178
- attr_reader :extension
179
-
180
- def self.ancestorID
181
- @ancestorID
182
145
  end
183
146
 
184
- def initialize(fields={}, *extension)
185
- super(fields)
186
- @extension = extension
147
+ def to_tag_value
148
+ @value.values.inject(""){|out,f|out << f.to_tag}
187
149
  end
188
150
 
189
- # @private
190
- def to_compact(out)
191
-
192
- groupOut = String.new.putU64(self.class.id)
193
- super(groupOut)
151
+ def to_tag_extension
194
152
  if @extension.size > 0
195
- groupOut.putU32(@extension.size)
153
+ out = "|["
196
154
  @extension.each do |e|
197
- e.to_compact(groupOut)
198
- end
199
- end
200
-
201
- out.putU32(groupOut.size)
202
- out << groupOut
203
-
204
- end
205
-
206
- end
207
-
208
- class DynamicGroup
209
-
210
- def self.taggedGroups
211
- @taggedGroups
212
- end
213
-
214
- def self.permittedID
215
- @permittedID
216
- end
217
-
218
- # @private
219
- # @param input [StringIO] Blink compact form
220
- # @param stack [Array] used to measure depth of recursion
221
- # @return [Group, nil]
222
- # @raise [Error] recursion depth limit
223
- def self.from_compact(input, stack)
224
-
225
- group = nil
226
-
227
- if stack.size < @maxRecursion
228
- stack << self
229
- else
230
- raise RecursionLimit
231
- end
232
-
233
- if input.kind_of? String
234
- input = StringIO.new(input)
235
- end
236
-
237
- buf = input.getBinary
238
-
239
- if buf.size > 0
240
-
241
- buf = StringIO.new(buf)
242
- id = buf.getU64
243
-
244
- if klass = @taggedGroups[id]
245
-
246
- if @permittedID.include? id
247
-
248
- group = klass.from_compact(buf, stack)
249
-
250
- if !buf.eof?
251
- size = buf.getU32
252
- while group.extension.size < size do
253
- group.extension << @anyTaggedGroup.from_compact(buf, stack)
254
- end
255
- end
256
-
257
- if !buf.eof?
258
- raise ExtensionPadding
259
- end
260
-
261
- else
262
- raise WeakError15.new "W15: Group is known but unexpected"
155
+ if e != @extension.first
156
+ out << ";"
263
157
  end
264
- else
265
- raise WeakError2.new "W2: Group id #{id} is unknown"
266
- end
267
-
268
- elsif stack.size == 1
269
- raise WeakError5.new "W??: top level cannot be null"
270
- else
271
- raise WeakError5.new "W5: Value cannot be null"
272
- end
273
-
274
- stack.pop
275
- group
276
-
277
- end
278
-
279
- def set(value)
280
- if value.kind_of? Group
281
- if self.class.permittedID.include? value.class.id
282
- @value = value
283
- else
284
- raise TypeError.new "incompatible group"
158
+ out << e.to_tag
285
159
  end
286
- # native values
287
- elsif value.kind_of? Hash
288
- @value.set(value)
160
+ out << "]"
289
161
  else
290
- raise
291
- end
292
- end
293
-
294
- # @return [Group] contained group
295
- def get
296
- @value.get
297
- end
298
-
299
- # @note calls {#set}(value)
300
- def initialize(value)
301
- set(value)
302
- end
303
-
304
- # @private
305
- def to_compact(out)
306
- @value.to_compact(out)
162
+ out = ""
163
+ end
307
164
  end
308
165
 
309
- def extension
310
- @value.extension
166
+ def to_tag
167
+ "@#{self.class.name}#{to_tag_value}#{to_tag_extension}"
311
168
  end
312
169
 
313
170
  end