gps_pvt 0.9.4 → 0.10.0

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