gps_pvt 0.9.4 → 0.10.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.
@@ -0,0 +1,888 @@
1
+ module GPS_PVT
2
+ end
3
+
4
+ resolve_tree = proc{|root|
5
+ assigned_type = {}
6
+ assigned_const = {}
7
+ resolved = nil
8
+ expand_ref = proc{|child_v, path, parent|
9
+ child_k = (path ||= [])[-1]
10
+ case child_v
11
+ when Hash
12
+ keys = child_v.keys
13
+ assigned_k = [path[0], child_k] if child_k.kind_of?(Symbol)
14
+ if child_v[:typeref] then
15
+ keys -= [:typeref, :type]
16
+ if child_v[:typeref].kind_of?(Array) then # imported type
17
+ src_k = child_v[:typeref].collect!{|v| v.to_sym}
18
+ if !assigned_type[assigned_k] && assigned_type[src_k] then
19
+ assigned_type[assigned_k] = assigned_type[src_k]
20
+ assigned_const[assigned_k] = assigned_const[src_k] if assigned_const[src_k]
21
+ resolved += 1
22
+ end
23
+ elsif !child_v[:type] then
24
+ child_v[:typeref] = child_v[:typeref].to_sym
25
+ if (child_v[:type] = assigned_type[[path[0], child_v[:typeref]]]) then
26
+ assigned_type[assigned_k] = child_v[:type] if assigned_k
27
+ resolved += 1
28
+ end
29
+ elsif !child_v[:type][0] then # referenced type with additional constraints
30
+ child_v[:typeref] = child_v[:typeref].to_sym
31
+ type, opts = assigned_type[[path[0], child_v[:typeref]]]
32
+ if type then
33
+ child_v.delete(:typeref) # because this type is not same as the referenced type
34
+ child_v[:type] = [type, opts.merge(child_v[:type][1])]
35
+ assigned_type[assigned_k] = child_v[:type] if assigned_k
36
+ resolved += 1
37
+ end
38
+ end
39
+ elsif child_v[:type] then
40
+ type, *other = child_v[:type]
41
+ if type.kind_of?(String) then
42
+ child_v[:type] = [type.to_sym] + (other.empty? ? [{}] : other)
43
+ resolved += 1
44
+ if assigned_k then
45
+ assigned_type[assigned_k] = child_v[:type]
46
+ if child_v.keys.include?(:value) then
47
+ assigned_const[assigned_k] = child_v[:value]
48
+ else
49
+ child_v[:type][1][:typename] = child_k
50
+ end
51
+ end
52
+ end
53
+ end
54
+ keys.each{|k|
55
+ expand_ref.call(child_v[k], path + [k], child_v)
56
+ }
57
+ when Array
58
+ child_v.each.with_index{|v, i|
59
+ expand_ref.call(v, path + [i], child_v)
60
+ }
61
+ when String
62
+ if [:value, :size].any?{|k| path.include?(k)}
63
+ src_k = [path[0], child_v.to_sym]
64
+ if assigned_const[src_k] then
65
+ parent[child_k] = assigned_const[src_k]
66
+ resolved += 1
67
+ end
68
+ end
69
+ end
70
+ }
71
+ while true
72
+ resolved_previous = resolved
73
+ resolved = 0
74
+ expand_ref.call(root)
75
+ break if (resolved_previous == 0) && (resolved == 0)
76
+ end
77
+
78
+ reduce_constraint = proc{|props|
79
+ case props
80
+ when Array
81
+ props.collect!{|prop| reduce_constraint.call(prop)}
82
+ when Hash
83
+ props.keys.each{|k|
84
+ props[k] = reduce_constraint.call(props[k])
85
+ }
86
+ if props[:and] then
87
+ down_to, up_to, less_than = [[], [], []]
88
+ prop = props[:and].reject{|v|
89
+ case v
90
+ when Range
91
+ down_to << v.first
92
+ v.exclude_end? ? (up_to << v.last) : (less_then << v.last)
93
+ when Array
94
+ case v[0]
95
+ when ">="; down_to << v[1]
96
+ when "<="; up_to << v[1]
97
+ when "<"; less_then << v[1]
98
+ end
99
+ end
100
+ }
101
+ if (first = down_to.max) && (last = [up_to, less_than].flatten.min) then
102
+ prop << Range::new(first, last, last == less_than.min)
103
+ if prop.size > 1 then
104
+ props[:and] = prop
105
+ elsif props.keys.size == 1 then
106
+ next prop[0]
107
+ else
108
+ props[:and] = prop[0] # Rare case?
109
+ end
110
+ end
111
+ end
112
+ props
113
+ else
114
+ props
115
+ end
116
+ }
117
+ find_range = proc{|type_opts, k|
118
+ type_opts[k] = reduce_constraint.call(type_opts[k]) if type_opts[k]
119
+ res = {:root => []} # [min, max]
120
+ res.define_singleton_method(:belong_to){|v|
121
+ min, max = self[:root]
122
+ if (!min || (min <= v)) && (!max || (max >= v)) then
123
+ self[:additional] ? :root : true
124
+ else
125
+ self[:additional] ? :additional : raise
126
+ end
127
+ }
128
+ iter_proc = proc{|props, cat, cnd_or|
129
+ switch_min = proc{|min_new, more_than|
130
+ min_new = (more_than && min_new.kind_of?(Integer)) \
131
+ ? (min_new + 1) : min_new.ceil
132
+ res[cat][0] = [res[cat][0], min_new].compact.send(
133
+ cnd_or ? :min : :max)
134
+ }
135
+ switch_max = proc{|max_new, less_than|
136
+ max_new = (less_than && max_new.kind_of?(Integer)) \
137
+ ? (max_new - 1) : max_new.floor
138
+ res[cat][1] = [res[cat][1], max_new].compact.send(
139
+ cnd_or ? :max : :min)
140
+ }
141
+ case props
142
+ when Range
143
+ switch_min.call(props.first)
144
+ switch_max.call(props.last, props.exclude_end?)
145
+ when Array
146
+ case prop[0]
147
+ when '>=', '>'; switch_min.call(prop[1], prop[1] == '>')
148
+ when '<=', '>'; switch_max.call(prop[1], prop[1] == '<')
149
+ end
150
+ when Hash
151
+ if props[:root] then
152
+ res[:additional] = []
153
+ iter_proc.call(props[:root], cat, cnd_or)
154
+ iter_proc.call(props[:additional], :additional, cnd_or) if props[:additional]
155
+ elsif props[k] then
156
+ [props[k]].flatten(1).each{|prop|
157
+ iter_proc.call(prop, cat, true)
158
+ }
159
+ elsif (cnd = ([:and, :or, :not] & props.keys)[0]) then
160
+ res_bkup = res
161
+ res2 = res = {cat => []}
162
+ cnd_or2 = (cnd != :and)
163
+ props[cnd].each{|prop| iter_proc.call(prop, cat, cnd_or2)}
164
+ res = res_bkup
165
+ res2.each{|cat2, (min, max)|
166
+ cat = cat2
167
+ if cnd == :not then
168
+ switch_min.call(max, true)
169
+ switch_max.call(min, true)
170
+ else
171
+ switch_min.call(min)
172
+ switch_max.call(max)
173
+ end
174
+ }
175
+ end
176
+ else
177
+ switch_min.call(props)
178
+ switch_max.call(props)
179
+ end
180
+ }
181
+ iter_proc.call(type_opts, :root)
182
+ res
183
+ }
184
+ find_element = proc{|type_opts, k, elm_default|
185
+ type_opts[k] = reduce_constraint.call(type_opts[k]) if type_opts[k]
186
+ elm_default ||= []
187
+ res = {:root => nil}
188
+ iter_proc = proc{|props, cat, cnd_or|
189
+ add = proc{|item|
190
+ res[cat] = unless res[cat] then
191
+ item
192
+ else
193
+ a, b = [res[cat], item].collect{|v|
194
+ case v
195
+ when String
196
+ next v.each_char.to_a
197
+ when Range
198
+ next Range::new("#{v.first}", "#{v.last}").to_a
199
+ end if k == :from
200
+ v.to_a rescue [v]
201
+ }
202
+ a.send(cnd_or ? :| : :&, b).sort
203
+ end
204
+ }
205
+ case props
206
+ when Hash
207
+ if props[:root] then
208
+ res[:additional] = nil
209
+ iter_proc.call(props[:root], cat, cnd_or)
210
+ iter_proc.call(props[:additional], :additional, cnd_or)
211
+ elsif props[k] then
212
+ [props[k]].flatten(1).each{|prop|
213
+ iter_proc.call(prop, cat, true)
214
+ }
215
+ elsif (cnd = ([:and, :or, :not] & props.keys)[0]) then
216
+ res_bkup = res
217
+ res2 = res = {cat => nil}
218
+ cnd_or2 = (cnd != :and)
219
+ props[cnd].each{|prop| iter_proc.call(prop, cat, cnd_or2)}
220
+ res = res_bkup
221
+ res2.each{|cat2, item|
222
+ cat = cat2
223
+ if (cnd == :not) then
224
+ a, b = [res[cat] || elm_default, item].collect{|v| v.to_a rescue [v]}
225
+ res[cat] = a - b
226
+ else
227
+ add.call(item)
228
+ end
229
+ }
230
+ end
231
+ else
232
+ add.call(props)
233
+ end
234
+ }
235
+ iter_proc.call(type_opts, :root)
236
+ [:root, :addtional].each{|cat|
237
+ next if !res.keys.include?(cat) || res[cat]
238
+ res[cat] = case elm_default
239
+ when Range; ([nil] * elm_default.first.bytes[0]) + elm_default.to_a # zero padding
240
+ when Array; elm_default
241
+ else; raise
242
+ end
243
+ }
244
+ to_hash = proc{|src|
245
+ Hash[*(src.to_a.collect.with_index.to_a.select{|v, i| v}.flatten(1))]
246
+ }
247
+ tbl_root = to_hash.call(res[:root])
248
+ unless res[:additional] then
249
+ res.define_singleton_method(:index){|v|
250
+ raise unless idx = tbl_root[v]
251
+ [idx]
252
+ }
253
+ else
254
+ tbl_additional = to_hash.call(res[:additional])
255
+ res.define_singleton_method(:index){|v|
256
+ indices = [tbl_root[v], tbl_additional[v]]
257
+ raise if indices.any?
258
+ indices
259
+ }
260
+ end
261
+ res
262
+ }
263
+
264
+ # This proc is useless because assumed automatic tagging
265
+ # does not require to re-order based on manual tags or class numbers
266
+ # @see https://stackoverflow.com/a/31450137
267
+ get_universal_class_number = proc{|type|
268
+ next get_universal_class_number.call(type[1][:root][0][:type]) if type[0] == :CHOICE
269
+ { # @see Table.1 unless any comment
270
+ :BOOLEAN => 1,
271
+ :INTEGER => 2,
272
+ :BIT_STRING => 3,
273
+ :OCTET_STRING => 4,
274
+ :NULL => 5,
275
+ :ENUMERATED => 10,
276
+ :SEQUENCE => 16,
277
+ :NumericString => 18, # @see Table.6
278
+ :PrintableString => 19, # @see Table.6
279
+ :IA5String => 22, # @see Table.6
280
+ :VisibleString => 26, # @see Table.6
281
+ :UTCTime => 23, # @see 43.3
282
+ }[type[0]]
283
+ }
284
+
285
+ prepare_coding = proc{|tree|
286
+ next tree.each{|k, v|
287
+ prepare_coding.call(v) unless [:typeref].include?(k) # skip alias
288
+ } unless tree.include?(:type)
289
+ next unless tree[:type] # skip undefined type
290
+ next if tree[:typeref]
291
+
292
+ type, opts = tree[:type]
293
+
294
+ case type
295
+ when :BOOLEAN
296
+ when :INTEGER
297
+ opts[:value_range] = find_range.call(opts, :value)
298
+ when :ENUMERATED
299
+ opts[:encoded] = {}
300
+ [:root, :additional].each{|k|
301
+ next unless opts[k]
302
+ opts[:encoded][k] = opts[k].to_a.sort{|(k_a, v_a), (k_b, v_b)|
303
+ v_a <=> v_b
304
+ }.collect{|k, v| k}
305
+ }
306
+ opts[:encoded].define_singleton_method(:belong_to){|k|
307
+ if (i = self[:root].find_index(k)) then
308
+ next [self[:additional] ? :root : true, i]
309
+ elsif (i = (self[:additional] || []).find_index(k)) then
310
+ next [:additional, i]
311
+ end
312
+ []
313
+ }
314
+ when :BIT_STRING, :OCTET_STRING
315
+ opts[:size_range] = find_range.call(opts, :size)
316
+ when :SEQUENCE
317
+ (opts[:root] + (opts[:extension] || [])).each.with_index{|v, i|
318
+ v[:name] = v[:name] ? v[:name].to_sym : i
319
+ v[:type][1][:typename] ||= v[:name] if v[:name].kind_of?(Symbol) && v[:type] # for debugger
320
+ v[:type] = [:SEQUENCE, {:root => v[:group]}] if v[:group]
321
+ prepare_coding.call(v)
322
+ v[:names] = v[:group].collect{|v2|
323
+ # if name is not Symbol, it will be changed to [index_in_sequence, index_in_group]
324
+ v2[:name] = [v[:name], v2[:name]] unless v2[:name].kind_of?(Symbol)
325
+ v2[:name]
326
+ } if v[:group]
327
+ }
328
+ when :SEQUENCE_OF
329
+ opts[:size_range] = find_range.call(opts, :size)
330
+ prepare_coding.call(opts)
331
+ when :CHOICE
332
+ # Skip reordering based on automatic tagging assumption
333
+ opts[:extension] = opts[:extension].collect{|v|
334
+ v[:group] || [v] # 22. Note says "Version brackets have no effect"
335
+ }.flatten(1) if opts[:extension]
336
+ (opts[:root] + (opts[:extension] || [])).each.with_index{|v, i|
337
+ v[:name] = v[:name] ? v[:name].to_sym : i
338
+ v[:type][1][:typename] ||= v[:name] if v[:name].kind_of?(Symbol) && v[:type] # for debugger
339
+ prepare_coding.call(v)
340
+ }
341
+ when :IA5String, :VisibleString, :NumericString, :PrintableString
342
+ opts[:size_range] = find_range.call(opts, :size)
343
+ opts[:character_table] = find_element.call(
344
+ opts, :from, {
345
+ :IA5String => ("\x0".."\x7F"),
346
+ :VisibleString => ("\x20".."\x7E"),
347
+ :NumericString => ([' '] + ('0'..'9').to_a),
348
+ :PrintableString => ("\x20".."\x7A"),
349
+ }[type])
350
+ when :UTCTime
351
+ props = [:VisibleString, opts]
352
+ prepare_coding.call({:type => props})
353
+ opts.merge!(props[1])
354
+ when :NULL
355
+ else
356
+ raise
357
+ end
358
+ }
359
+ prepare_coding.call(root)
360
+ }
361
+
362
+ generate_skeleton = proc{|tree, data|
363
+ if tree.include?(:type) then
364
+ type, opts = tree[:type]
365
+ case type
366
+ when :BOOLEAN
367
+ (data == nil) ? true : data
368
+ when :INTEGER
369
+ data || opts[:value_range][:root].first || 0
370
+ when :ENUMERATED
371
+ data || opts[:encoded][:root][0]
372
+ when :BIT_STRING, :OCTET_STRING
373
+ data || ({:BIT_STRING => [0], :OCTET_STRING => [0xFF]}[type] \
374
+ * (opts[:size_range][:root].first rescue 0))
375
+ when :SEQUENCE
376
+ data ||= {}
377
+ Hash[*((opts[:root] + (opts[:extension] || [])).collect{|v|
378
+ if v[:group] then
379
+ generate_skeleton.call(v, data).to_a
380
+ else
381
+ k = v[:name]
382
+ if data[k] then
383
+ [[k, generate_skeleton.call(v, data[k])]]
384
+ elsif !(v[:optional] || v[:default]) then
385
+ [[k, generate_skeleton.call(v)]]
386
+ end
387
+ end
388
+ }.compact.flatten(2))]
389
+ when :SEQUENCE_OF
390
+ next data.collect{|v| generate_skeleton.call(opts, v)} if data
391
+ v = Marshal::dump(generate_skeleton.call(opts))
392
+ (opts[:size_range][:root].first rescue 0).times.collect{
393
+ Marshal::load(v)
394
+ }
395
+ when :CHOICE
396
+ (data && (opts[:root] + (opts[:extension] || [])).any?{|v|
397
+ k = v[:name]
398
+ next unless data[k]
399
+ break {k => generate_skeleton.call(v, data[k])}
400
+ }) || Hash[*((opts[:root] + (opts[:extension] || [])).collect{|v|
401
+ [v[:name], generate_skeleton.call(v)]
402
+ }.flatten(1))]
403
+ when :IA5String, :VisibleString, :NumericString, :PrintableString
404
+ data || (opts[:character_table][:root].first * (opts[:size_range][:root].first || 0))
405
+ when :UTCTime
406
+ data || Time::now #.utc.strftime("%y%m%d%H%MZ")
407
+ when :NULL
408
+ nil
409
+ else
410
+ p tree
411
+ raise
412
+ end
413
+ else
414
+ data ||= {}
415
+ Hash[*(tree.collect{|k, v|
416
+ [k, generate_skeleton.call(v, data[k])]
417
+ }.flatten(1))]
418
+ end
419
+ }
420
+
421
+ require_relative 'per'
422
+
423
+ encoder = GPS_PVT::PER::Basic_Unaligned::Encoder
424
+ encode_opentype = proc{|bits| # 10.2
425
+ len_oct, r = bits.length.divmod(8)
426
+ if r != 0 then
427
+ bits += "0" * (8 - r)
428
+ len_oct += 1
429
+ end
430
+ res, len_oct_remain = encoder.length_otherwise(len_oct) # 10.2.2 unconstrained length
431
+ while len_oct_remain # fragmentation
432
+ res += bits.slice!(0, (len_oct - len_oct_remain) * 8)
433
+ len_oct = len_oct_remain
434
+ len_enc, len_oct_remain = encoder.length_otherwise(len_oct) # 10.2.2 unconstrained length
435
+ res += len_enc
436
+ end
437
+ res + bits
438
+ }
439
+ encode = proc{|tree, data|
440
+ if tree.include?(:type) then
441
+ type, opts = tree[:type]
442
+ data = opts[:hook_encode].call(data) if opts[:hook_encode]
443
+ case type
444
+ when :BOOLEAN
445
+ data ? "1" : "0"
446
+ when :INTEGER
447
+ mark, (min, max) = case (cat = opts[:value_range].belong_to(data))
448
+ when :additional # 12.1
449
+ ["1"]
450
+ else
451
+ [cat == :root ? "0" : "", opts[:value_range][:root]]
452
+ end
453
+ bits = if min then
454
+ if max then
455
+ (min == max) ? "" : encoder.constrainted_whole_number2(data, min, max)
456
+ else
457
+ encoder.semi_constrained_whole_number(data, min)
458
+ end
459
+ else
460
+ encoder.unconstrained_whole_number(data)
461
+ end
462
+ "#{mark}#{bits}"
463
+ when :ENUMERATED
464
+ cat, idx = opts[:encoded].belong_to(data)
465
+ if cat == :additional then
466
+ "1#{encoder.normally_small_non_negative_whole_number(idx)}"
467
+ else
468
+ "#{'0' if cat == :root}#{encoder.constrainted_whole_number2(idx, 0, opts[:encoded][:root].size-1)}"
469
+ end
470
+ when :BIT_STRING, :OCTET_STRING
471
+ res, (lb, ub) = case (cat = opts[:size_range].belong_to(data.size))
472
+ when :additional # 15.6, 16.3
473
+ ['1']
474
+ else
475
+ [cat == :root ? '0' : '', opts[:size_range][:root]]
476
+ end
477
+ lb ||= 0
478
+ bits = {:BIT_STRING => 1, :OCTET_STRING => 8}[type]
479
+ if ub == 0 then # 15.8, 16.5
480
+ data = []
481
+ elsif (lb == ub) && (ub < (1 << 16)) then # 15.9-10, 16.6-7
482
+ data += ([0] * (ub - data.size))
483
+ else # 15.11, 16.8
484
+ if ub && (ub < (1 << 16)) then
485
+ res += encoder.constrainted_whole_number2(data.size, lb, ub)
486
+ else
487
+ res += encoder.semi_constrained_whole_number(data.size, lb)
488
+ end
489
+ end
490
+ res += data.collect{|v| "%0#{bits}b"%[v]}.join
491
+ when :SEQUENCE
492
+ opt_def_flags, root_encoded = opts[:root].collect{|v| # 18.2
493
+ has_elm = data.include?(v[:name])
494
+ elm = data[v[:name]]
495
+ if v[:default] then # 18.2
496
+ (has_elm && (v[:default] != elm)) ? ["1", encode.call(v, elm)] : ["0", nil]
497
+ elsif v[:optional] then
498
+ has_elm ? ["1", encode.call(v, elm)] : ["0", nil]
499
+ else
500
+ raise unless has_elm
501
+ [nil, encode.call(v, elm)]
502
+ end
503
+ }.transpose.each{|ary| ary.compact}
504
+ raise if opt_def_flags.size > (1 << 16) # 18.3
505
+
506
+ ext_bit, ext_encoded = if opts[:extension] then
507
+ flags, args_list = opts[:extension].collect{|v|
508
+ elm = if v[:group] then
509
+ data_in_group = data.select{|k2, v2| v[:names].include?(k2)}
510
+ data_in_group.empty? ? nil : data_in_group
511
+ else
512
+ data[v[:name]]
513
+ end
514
+ elm ? ['1', [v, elm]] : ['0', nil]
515
+ }.transpose
516
+ # If any extension element is absent, '0' extension bit should be selected.
517
+ # '1' extension bit with "000..." bit-fields is wrong.
518
+ (args_list ||= []).compact!
519
+ unless args_list.empty? then # 18.1
520
+ ['1', "#{ # 18.8 -> 10.9
521
+ encoder.with_length(flags.size, :length_normally_small_length).collect{|len_str, range|
522
+ len_str + flags[range].join
523
+ }.join # 18.7
524
+ }#{
525
+ args_list.collect{|args|
526
+ encode_opentype.call(encode.call(*args)) # 18.9
527
+ }.join
528
+ }"]
529
+ else
530
+ '0'
531
+ end
532
+ end
533
+
534
+ "#{ext_bit}#{opt_def_flags.join}#{root_encoded.join}#{ext_encoded}"
535
+ when :SEQUENCE_OF
536
+ ext_bit, len_enc = case (cat = opts[:size_range].belong_to(data.size))
537
+ when :additional
538
+ # 19.4 -> 10.9.4.2(semi_constrained_whole_number)
539
+ ['1', :length_otherwise]
540
+ else
541
+ lb, ub = opts[:size_range][:root]
542
+ lb ||= 0 # 19.2
543
+ [
544
+ cat == :root ? '0' : '',
545
+ if (lb != ub) || (ub >= (1 << 16)) then
546
+ # 19.6 -> 10.9.4.1(constrained_whole_number) or 10.9.4.2(semi_constrained_whole_number)
547
+ ub \
548
+ ? [:length_constrained_whole_number, lb..ub] \
549
+ : :length_otherwise
550
+ else
551
+ ''
552
+ end
553
+ ]
554
+ end
555
+ ext_bit + encoder.with_length(data.size, *len_enc).collect{|len_str, range|
556
+ len_str + data[range].collect{|v| encode.call(opts, v)}.join
557
+ }.join
558
+ when :CHOICE
559
+ res = ""
560
+ root_i_lt = opts[:root].size
561
+ opts[:root].each.with_index.any?{|v, i|
562
+ next false unless data.include?(k = v[:name])
563
+ res += "0" if opts[:extension] # 22.5
564
+ if root_i_lt > 1 then
565
+ res += encoder.constrainted_whole_number2(i, 0, root_i_lt - 1) # 22.6
566
+ end
567
+ res += encode.call(v, data[k])
568
+ } || (opts[:extension] || []).each.with_index.any?{|v, i|
569
+ next false unless data.include?(k = v[:name])
570
+ res += "1" # 22.5
571
+ res += encoder.normally_small_non_negative_whole_number(i) # 22.8
572
+ res += encode_opentype.call(encode.call(v, data[k]))
573
+ } || raise
574
+ res
575
+ when :IA5String, :VisibleString, :NumericString, :PrintableString
576
+ tbl_all = opts[:character_table]
577
+ idx_root, idx_additional = data.each_char.collect{|char|
578
+ tbl_all.index(char)
579
+ }.transpose
580
+ idx_root ||= []
581
+ ext_bit, (alb, aub) = case (cat_size =
582
+ opts[:size_range].belong_to(data.size))
583
+ when :additional
584
+ ["1", opts[:size_range][:additional]]
585
+ else
586
+ [((cat_size == :root) || idx_additional) \
587
+ ? (idx_root.all? ? "0" : "1") : "",
588
+ opts[:size_range][:root]]
589
+ end
590
+ idx, tbl = if (ext_bit == "1") && idx_additional then
591
+ [idx_additional, tbl_all[:additional]]
592
+ else
593
+ [idx_root, tbl_all[:root]]
594
+ end
595
+ b = Math::log2(tbl.to_a.size).ceil # 27.5.2
596
+
597
+ alb ||= 0 # 27.3
598
+ # 27.5.6(=10.9.4.1) & 27.5.7 -> 10.9.4
599
+ len_enc = aub \
600
+ ? [:length_constrained_whole_number, alb..aub] \
601
+ : :length_otherwise
602
+ ext_bit + encoder.with_length(idx.size, *len_enc).collect{|len_str, range|
603
+ len_str + idx[range].collect{|i|
604
+ encoder.non_negative_binary_integer2(i, b) # 27.5.4
605
+ }.join
606
+ }.join
607
+ when :UTCTime
608
+ encode.call(
609
+ {:type => [:VisibleString, opts]},
610
+ data.getutc.strftime("%y%m%d%H%M%SZ"))
611
+ when :NULL
612
+ ''
613
+ else
614
+ raise
615
+ end
616
+ else
617
+ tree.collect{|k, v|
618
+ encode.call(v, data[k])
619
+ }.join
620
+ end
621
+ }
622
+
623
+ decoder = GPS_PVT::PER::Basic_Unaligned::Decoder
624
+ decode_opentype = eval(<<-__SRC__)
625
+ proc{|str, &b| # 10.2
626
+ len_oct, cnt = decoder.length_otherwise(str) # 10.2.2 unconstrained length
627
+ if cnt then # fragmentation
628
+ str_buf = str.slice!(0, len_oct * 8)
629
+ loop{
630
+ len_oct, cnt = decoder.length_otherwise(str)
631
+ str_buf += str.slice!(0, len_oct * 8)
632
+ break unless cnt
633
+ }
634
+ b.call(str_buf)
635
+ else
636
+ len_before = str.size
637
+ res = b.call(str)
638
+ str.slice!(0, [(len_oct * 8) - (len_before - str.size), 0].max) # erase padding
639
+ res
640
+ end
641
+ }
642
+ __SRC__
643
+ decode = proc{|tree, str|
644
+ if tree.include?(:type) then
645
+ type, opts = tree[:type]
646
+ res = case type
647
+ when :BOOLEAN
648
+ str.slice!(0) == "1"
649
+ when :INTEGER
650
+ min, max = if opts[:value_range][:additional] && (str.slice!(0) == "1") then
651
+ opts[:value_range][:additional]
652
+ else
653
+ opts[:value_range][:root]
654
+ end
655
+ if min then
656
+ if max then
657
+ (min == max) ? min : decoder.constrainted_whole_number2(str, min, max)
658
+ else
659
+ decoder.semi_constrained_whole_number(str, min)
660
+ end
661
+ else
662
+ decoder.unconstrained_whole_number(str)
663
+ end
664
+ when :ENUMERATED
665
+ tbl_additional = opts[:encoded][:additional]
666
+ if tbl_additional && (str.slice!(0) == "1") then
667
+ tbl_additional[
668
+ decoder.normally_small_non_negative_whole_number(str)]
669
+ else
670
+ tbl_root = opts[:encoded][:root]
671
+ tbl_root[
672
+ decoder.constrainted_whole_number2(str, 0, tbl_root.size-1)]
673
+ end
674
+ when :BIT_STRING, :OCTET_STRING
675
+ lb, ub = if opts[:size_range][:additional] && (str.slice!(0) == "1") then
676
+ [] # 15.6, 16.3
677
+ else
678
+ opts[:size_range][:root]
679
+ end
680
+ lb ||= 0
681
+ bits = {:BIT_STRING => 1, :OCTET_STRING => 8}[type]
682
+ len = if ub == 0 then # 15.8, 16.5
683
+ 0
684
+ elsif (lb == ub) && (ub < (1 << 16)) then # 15.9-10, 16.6-7
685
+ ub
686
+ else # 15.11, 16.8
687
+ if ub && (ub < (1 << 16)) then
688
+ decoder.constrainted_whole_number2(str, lb, ub)
689
+ else
690
+ decoder.semi_constrained_whole_number(str, lb)
691
+ end
692
+ end
693
+ str.slice!(0, bits * len).scan(/.{#{bits}}/).collect{|chunk| chunk.to_i(2)}
694
+ when :SEQUENCE
695
+ has_extension = (opts[:extension] && (str.slice!(0) == '1'))
696
+ data = Hash[*(
697
+ opts[:root].collect{|v| [v[:name], v[:default]] if v[:default]}.compact.flatten(1)
698
+ )].merge(Hash[*(opts[:root].select{|v| # 18.2
699
+ (v[:default] || v[:optional]) ? (str.slice!(0) == '1') : true
700
+ }.collect{|v|
701
+ [v[:name], decode.call(v, str)]
702
+ }.flatten(1))])
703
+ data.merge!(Hash[*(
704
+ decoder.with_length(str, :length_normally_small_length).collect{|len|
705
+ len.times.collect{str.slice!(0) == '1'}
706
+ }.flatten(1).zip(opts[:extension]).collect{|has_elm, v|
707
+ next unless has_elm
708
+ decoded = decode_opentype.call(str){|str2| decode.call(v, str2)}
709
+ v[:group] ? decoded.to_a : [[v[:name], decoded]]
710
+ }.compact.flatten(2))]) if has_extension
711
+ data
712
+ when :SEQUENCE_OF
713
+ len_dec = if opts[:size_range][:additional] && (str.slice!(0) == '1') then
714
+ # 19.4 -> 10.9.4.2(semi_constrained_whole_number)
715
+ :length_otherwise
716
+ else
717
+ lb, ub = opts[:size_range][:root]
718
+ lb ||= 0
719
+ if (lb != ub) || (ub >= (1 << 16)) then
720
+ # 19.6 -> 10.9.4.1(constrained_whole_number) or 10.9.4.2(semi_constrained_whole_number)
721
+ ub \
722
+ ? [:length_constrained_whole_number, lb..ub] \
723
+ : :length_otherwise
724
+ else
725
+ lb
726
+ end
727
+ end
728
+ decoder.with_length(str, *len_dec).collect{|len|
729
+ len.times.collect{decode.call(opts, str)}
730
+ }.flatten(1)
731
+ when :CHOICE
732
+ if opts[:extension] && (str.slice!(0) == '1') then
733
+ i = decoder.normally_small_non_negative_whole_number(str) # 22.8
734
+ v = opts[:extension][i]
735
+ {v[:name] => decode_opentype.call(str){|str2| decode.call(v, str2)}}
736
+ else
737
+ root_i_lt = opts[:root].size
738
+ i = if root_i_lt > 1 then
739
+ decoder.constrainted_whole_number2(str, 0, root_i_lt - 1) # 22.6
740
+ else
741
+ 0
742
+ end
743
+ v = opts[:root][i]
744
+ {v[:name] => decode.call(v, str)}
745
+ end
746
+ when :IA5String, :VisibleString, :NumericString, :PrintableString
747
+ tbl = opts[:character_table][:additional]
748
+ alb, aub = if (tbl || opts[:size_range][:additional]) \
749
+ && (str.slice!(0) == '1') then
750
+ tbl ||= opts[:character_table][:root]
751
+ opts[:size_range][:additional]
752
+ else
753
+ tbl = opts[:character_table][:root]
754
+ opts[:size_range][:root]
755
+ end
756
+ alb ||= 0 # 27.3
757
+ tbl = tbl.to_a
758
+ b = Math::log2(tbl.size).ceil # 27.5.2
759
+
760
+ # 27.5.6(=10.9.4.1) & 27.5.7 -> 10.9.4
761
+ len_dec = aub \
762
+ ? [:length_constrained_whole_number, alb..aub] \
763
+ : :length_otherwise
764
+ decoder.with_length(str, *len_dec).collect{|len|
765
+ len.times.collect{
766
+ tbl[decoder.non_negative_binary_integer(str, b)] # 27.5.4
767
+ }
768
+ }.join
769
+ when :UTCTime
770
+ raise unless /^(\d{10})(\d{2})?(?:Z|([\+\-]\d{4}))$/ =~ decode.call(
771
+ {:type => [:VisibleString, opts]},
772
+ str)
773
+ args = 5.times.collect{|i| $1[i * 2, 2].to_i}
774
+ args[0] += 2000
775
+ args << $2.to_i if $2
776
+ data = Time::gm(*args)
777
+ data += ($3[0, 3].to_i * 60 + $3[3, 2].to_i) if $3
778
+ data
779
+ when :NULL
780
+ else
781
+ raise
782
+ end
783
+ res = opts[:hook_decode].call(res) if opts[:hook_decode]
784
+ res
785
+ else
786
+ Hash[*(tree.collect{|k, v|
787
+ [k, decode.call(v, str)]
788
+ }.flatten(1))]
789
+ end
790
+ }
791
+ debug_decode = proc{ # debugger
792
+ decode_orig = decode
793
+ check_str = proc{|str|
794
+ history = str.history
795
+ idx = history[:orig].size - str.size
796
+ idx_previous = history[:index][-1]
797
+ if idx > idx_previous then
798
+ history[:index] << idx
799
+ history[:orig].slice(idx_previous, idx - idx_previous)
800
+ end
801
+ }
802
+ print_str = proc{|str_used, history, value|
803
+ next unless str_used
804
+ type, opts = (history[:parent][-1][:type] rescue nil)
805
+ $stderr.puts [
806
+ (" " * history[:parent].size) + str_used,
807
+ "#{type}#{"(#{opts[:typename]})" if opts[:typename]}",
808
+ case value
809
+ when NilClass; nil
810
+ when Array; "\"#{value.inspect}\""
811
+ else; value.inspect
812
+ end
813
+ ].compact.join(',')
814
+ }
815
+ decode = proc{|tree, str|
816
+ if !str.respond_to?(:history) then
817
+ history = {
818
+ :orig => str.dup,
819
+ :index => [0],
820
+ :parent => [tree],
821
+ }
822
+ str.define_singleton_method(:history){history}
823
+ else
824
+ history = str.history
825
+ print_str.call(check_str.call(str), history)
826
+ history[:parent] << tree
827
+ end
828
+ begin
829
+ res = decode_orig.call(tree, str)
830
+ print_str.call(check_str.call(str), history, res)
831
+ res
832
+ rescue
833
+ type, opts = [tree.kind_of?(Hash) ? tree[:type] : nil, [nil, {}]].compact[0]
834
+ $stderr.puts [
835
+ "#{" " * (history[:parent].size - 1)}[error]",
836
+ ("#{type}#{"(#{opts[:typename]})" if opts[:typename]}" if type)
837
+ ].compact.join(',')
838
+ raise
839
+ ensure
840
+ history[:parent].pop
841
+ end
842
+ }
843
+ }
844
+
845
+ dig = proc{|tree, *keys|
846
+ if tree[:type] then
847
+ type, opts = tree[:type]
848
+ case type
849
+ when :SEQUENCE, :CHOICE
850
+ k = keys.shift
851
+ elm = (opts[:root] + (opts[:extension] || [])).find{|v| v[:name] == k}
852
+ keys.empty? ? elm : dig.call(elm, *keys)
853
+ else
854
+ raise
855
+ end
856
+ else
857
+ elm = tree[keys.shift]
858
+ keys.empty? ? elm : dig.call(elm, *keys)
859
+ end
860
+ }
861
+
862
+ GPS_PVT::ASN1 = Module::new{
863
+ define_method(:resolve_tree, &resolve_tree)
864
+ define_method(:generate_skeleton){|tree, *data|
865
+ generate_skeleton.call(tree, *data)
866
+ }
867
+ define_method(:encode_per, &encode)
868
+ define_method(:decode_per){|*args| decode.call(args)}
869
+ define_method(:dig, &dig)
870
+ define_method(:read_json){|*src_list|
871
+ require 'json'
872
+ resolve_tree(src_list.inject({}){|res, src|
873
+ res.merge(JSON::parse(
874
+ (src.respond_to?(:read) ? src : open(src)).read,
875
+ {:symbolize_names => true}))
876
+ })
877
+ }
878
+ decode_orig = decode
879
+ define_method(:debug=){|bool|
880
+ if bool then
881
+ debug_decode.call
882
+ else
883
+ debug = decode_orig
884
+ end
885
+ bool
886
+ }
887
+ module_function(*instance_methods(false))
888
+ }