slow_blink 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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