cbor-packed 0.1.3 → 0.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ec7bd11d45d35c418a0fe98aa43c369b3384b2b43cb94486fd6e3463eae3232
4
- data.tar.gz: 88efc5decfeeedb1c049df0e5a9ba126c35bdf8a661218e29db3eb836c39f9c6
3
+ metadata.gz: 9306aa8a05d1747901cda5a72e3636199ce2dc403e09c8731c2fbd6c6be93623
4
+ data.tar.gz: 9ccd87f2b36818c1b71b7e946cdfe370c230f40ea9331fc407e925a0ff214824
5
5
  SHA512:
6
- metadata.gz: 1b6add7d575ff6ab3f239c9cf8f4f434ec8cc3c7d02e717c58b8c79a2007dd6d033ec10739c8bbac788e73d292f37501a002730303a8f58479a7131243fd04ac
7
- data.tar.gz: efd9b83c4da71f11354398048e6421770583b2fef0e22b4e3ac1f964ba2d5f4d2d69b2cb1e6441058062313940d6fee492f6dd7579dd0e25202734229a33058b
6
+ metadata.gz: b35a19072f26120f7aa4e67c9f4b3af64f3aea70eeaa0203b3d7d0c228d8ab20ea006cebd09e62d26233ea10c8fbacad5b9024e75f180f9f9462c81fe1e5632a
7
+ data.tar.gz: 57a9c79818180b0433230c3666fdc5742d3b8fa22e9fd1deda8941a8a75eef05b0f933c249c1131afb5f615b23920cce16b5d81fc09d30b07d04ac0d233dd8f9
data/cbor-packed.gemspec CHANGED
@@ -1,13 +1,12 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cbor-packed"
3
- s.version = "0.1.3"
3
+ s.version = "0.2.2"
4
4
  s.summary = "CBOR (Concise Binary Object Representation) packer"
5
5
  s.description = %q{cbor-packed implements packed encoding for CBOR, RFC 7049 Section 3.9}
6
6
  s.author = "Carsten Bormann"
7
7
  s.email = "cabo@tzi.org"
8
8
  s.license = "Apache-2.0"
9
9
  s.homepage = "http://cbor.io/"
10
- s.has_rdoc = false
11
10
  s.test_files = Dir['test/**/*.rb']
12
11
  s.files = Dir['lib/**/*.rb'] + %w(cbor-packed.gemspec) + Dir['bin/**/*.rb']
13
12
  s.executables = Dir['bin/**/*.rb'].map {|x| File.basename(x)}
data/lib/cbor-packed.rb CHANGED
@@ -1,56 +1,220 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require "cbor" unless defined? CBOR
4
- # require "pp"
3
+ require "cbor-pure" unless defined? CBOR
4
+ require "pp"
5
5
 
6
6
  $compression_hack = 0
7
7
 
8
+ # class String
9
+ # def xeh
10
+ # gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join
11
+ # end
12
+ # end
13
+
8
14
  module CBOR
9
- PACKED_TAG = 51
15
+ PACKED_TAG = 1113
16
+ MIXED_PACKED_TAG = 113
10
17
  REF_TAG = 6
18
+ STRAIGHT_11_SIZE = 32 # How many 1+1 straight tags do we have
19
+
20
+ HEX_UC_TAG = 23
21
+ HEX_LC_TAG = 108 # 'l'.ord, SQUATTING, fix this
22
+ RECORD_TAG = 114 # 'r'.ord
23
+ UNDEFINED = CBOR::Simple.new(23)
24
+
25
+ # Use with || 4
26
+ # simple 6(1+0) 6(1+1)
27
+ REF_SIZE = [*Array.new(16, 1), *Array.new(48, 2), *Array.new(512-48, 3)]
11
28
 
12
29
  class Packer
13
- def self.from_item(item)
30
+ def self.build_match_array(item)
31
+ # Hash with all data items as keys and the number of times they occur as values:
14
32
  count = Hash.new(0)
15
33
  item.cbor_visit do |o|
16
34
  (count[o] += 1) == 1
17
35
  # if the count gets > 1, we can stop visiting, so we return false in the block
18
36
  end
19
- # pp count
20
- # count is now a Hash with all data items as keys and the number of times they occur as values
21
37
 
22
38
  # choose those matches that are occurring > 1, make first rough estimate of saving
23
- good_count = count.select {|k, v| v > 1}.map {|k, v| [k, v, l = k.to_cbor.length,
24
- (v-1)*(l-1)]}
25
- # good_count is now an array of [k, v, length, savings] tuples
26
-
27
- # select those that potentially have savings (> 0) and sort by best saving first
28
- better_count = good_count.to_a.select {|a| a[3] > 0}.sort_by {|a| -a[3]}
29
- # pp better_count
39
+ good_count = count
40
+ .select {|k, v| v > 1}
41
+ .map {|k, v|
42
+ l = k.to_cbor.length
43
+ # v-1: We can only save space for the second etc.
44
+ # l-1: We'll need at least 1 byte for the reference
45
+ gain = (v-1)*(l-1)
46
+ [k, v, l] if gain > 0
47
+ }.compact
48
+ .sort_by {|a| -a[1]}
49
+ # good_count is now an array of [value, count, length] tuples that have potential gain,
50
+ # sorted by descending number of references we'll get
30
51
 
31
- # now: take the best out???; re-visit that reducing by n; re-sort and filter???
32
- # sort by descending number of references we'll get -- the higher reference counts go first
33
- match_array = better_count.sort_by {|a| -a[1]}.map {|a| a[0]}
52
+ match_array = []
53
+ good_count.each {|a|
54
+ match_array << a[0] if (REF_SIZE[match_array.size] || 4) < a[2]
55
+ }
34
56
  # pp match_array
57
+ [count, match_array]
58
+ end
59
+
60
+ def self.argref(n, casezero = 6)
61
+ case n
62
+ when 0
63
+ casezero
64
+ when 1..31
65
+ n + 224
66
+ when 32...0x1000
67
+ n + 0x7000
68
+ when 0x1000...0x10000000
69
+ n + 0x70000000
70
+ end
71
+ end
72
+
73
+ def self.sharedref(i)
74
+ if i < 16
75
+ CBOR::Simple.new(i)
76
+ else
77
+ i -= 16
78
+ CBOR::Tagged.new(REF_TAG, (i >> 1) ^ -(i & 1))
79
+ end
80
+ end
81
+
82
+ def self.build_bytes_from_hex(strings, count, match_array, item, translate, prefixes)
83
+ optimizable, others = strings.partition do |str|
84
+ if /(?:(?<lc>(?:[0-9a-f][0-9a-f]){4,})|(?<uc>(?:[0-9A-F][0-9A-F]){4,}))$/ =~ str
85
+ left = $`
86
+ # warn [:lc, left, lc, uc].inspect
87
+ bytes = [
88
+ if lc
89
+ tag = HEX_LC_TAG
90
+ fail if uc
91
+ lc
92
+ else
93
+ fail unless uc
94
+ tag = HEX_UC_TAG
95
+ uc
96
+ end].pack('H*').b
97
+ if outer = translate[str]
98
+ fail outer.inspect unless CBOR::Tagged === outer && left == outer.value # XXX
99
+ else
100
+ referto = bytes
101
+ if (count[bytes] += 1) == 2 # patch up match_array...
102
+ # translate[bytes] = sharedref(match_array.size) # happens later, too
103
+ match_array << bytes
104
+ referto = sharedref(match_array.size-1)
105
+ end
106
+ prefixes << left unless prefixes[-1] == left
107
+ translate[str] = CBOR::Tagged.new(argref(prefixes.size-1),
108
+ CBOR::Tagged.new(tag, referto)) unless count[bytes] > 2
109
+ # don't overwrite any entry made at == 2
110
+ end
111
+ true
112
+ end
113
+ end
114
+ others
115
+ end
116
+
117
+ def self.build_maps(count, match_array, translate, prefixes)
118
+ klcount = Hash.new(0)
119
+ mapvalues = Hash.new {|h, kl| # kl = key list
120
+ h.member?(kl) or h[kl] = Hash.new {|hv, kv|
121
+ hv.member?(kv) or hv[kv] = Hash.new(0)
122
+ }
123
+ }
124
+
125
+ count.select {|o, _n| Hash === o}.each do |o, _n|
126
+ kl = o.keys.sort
127
+ klcount[kl] += 1
128
+ v = o.values_at(*kl)
129
+ z = kl.zip(v)
130
+ z.each do |k1, v1|
131
+ mapvalues[kl][k1][v1] += 1
132
+ end
133
+ end
134
+ repkl = klcount.select {|kl, n| n > 1}.sort_by {|kl, n| -n} # sort not needed
135
+ # fudge here -- ignore single children even in parent selection
136
+
137
+ # parent selection
138
+
139
+ klparents = {}
140
+ repkl.map do |kl, n|
141
+ klparent = {}
142
+ mapvalues[kl].each do |k1, v1|
143
+ repv = v1.to_a.select{|k2, v2| v2 > (n+3)/2} # low-hanging ->
144
+ if repv != [] # -> should be zero or one of them
145
+ # pp [k1, repv]
146
+ # The k1 values are those that go into the head arrays
147
+ # first .first: low-hanging, second .first: repeated member value
148
+ klparent[k1] = repv.first.first
149
+ end
150
+ end
151
+ # pp klparent
152
+ if klparent != {}
153
+ ix = prefixes.size
154
+ prefixes << klparent
155
+ constant_keys = Set[*klparent.keys]
156
+ variable_keys = Set[*kl] - constant_keys
157
+ klparents[kl] = [
158
+ klparent, argref(ix), constant_keys, variable_keys.to_a
159
+ ]
160
+ end
161
+ end
162
+
163
+ klmatch = {}
164
+
165
+ count.select {|o, _n| Hash === o}.each do |o, _n|
166
+ kl = o.keys.sort
167
+ klp, ref, constant_keys, variable_kl = klparents[kl]
168
+ if klp # split off parent, return to common code then
169
+ oouter = o
170
+ o = o.reject {|k| constant_keys.include? k}
171
+ kl = variable_kl
172
+ end
173
+ if kl.size > 1 && (klp || klcount[kl] > 1) # TODO: tune
174
+ unless ix = klmatch[kl]
175
+ ix = prefixes.size
176
+ prefixes << CBOR::Tagged.new(RECORD_TAG, kl)
177
+ klmatch[kl] = ix
178
+ end
179
+ packed = translate[o] = CBOR::Tagged.new(argref(ix), o.values_at(*kl)) # needs packing
180
+ end
181
+ if packed && klp
182
+ translate[oouter] = CBOR::Tagged.new(ref, packed) # merge Hashes
183
+ end
184
+ end
185
+
186
+ end
35
187
 
36
- # XXX the below needs to be done with arrays and (hard!) maps as well
188
+ CBOR_PACKED_HEX = ENV["CBOR_PACKED_HEX"]
189
+ CBOR_PACKED_MAPS = ENV["CBOR_PACKED_MAPS"]
190
+
191
+ def self.from_item(item)
192
+ count, match_array = build_match_array(item)
193
+
194
+ # TODO: the below needs to be done with arrays and (hard!) maps as well
37
195
  # do this on the reverse to find common suffixes
196
+
197
+ translate = {} # map (sub)items to their arg-packed versions
198
+ prefixes = [] # provisional prefix table used in the above
199
+
38
200
  # select all strings (ignoring reference counts) and sort them
39
201
  strings = count.select {|k, v| String === k}.map(&:first).sort
40
202
  if strings != []
41
- string_common = strings[1..-1].zip(strings).map{ |y, x|
42
- l = x.chars.zip(y.chars).take_while{|a, b| a == b}.length # should be bytes
43
- [x, l]
44
- } << [strings[-1], 0]
203
+ strings = build_bytes_from_hex(strings, count, match_array, item, translate, prefixes) if CBOR_PACKED_HEX
204
+ if strings != []
205
+ string_common = strings[1..-1].zip(strings).map{ |y, x|
206
+ l = x.chars.zip(y.chars).take_while{|a, b| a == b}.length # should be bytes
207
+ [x, l]
208
+ } << [strings[-1], 0]
209
+ end
45
210
  # string_common: list of strings/counts of number of /bytes/ matching with next
46
211
  # pp string_common
47
212
  end
48
- translate = {}
49
- prefixes = []
50
213
  if string_common
214
+
51
215
  prefix_stack = [[0, false]] # sentinel
52
216
  pos = 0 # mirror prefix_stack[-1][0]
53
- tag_no = REF_TAG
217
+ tag_no = argref(prefixes.size) # REF_TAG
54
218
  string_common.each do |s, l|
55
219
  if l > pos + 2 + $compression_hack
56
220
  if t = prefix_stack[-1][1] # if we still have a prefix left
@@ -61,8 +225,9 @@ module CBOR
61
225
  prefix_stack << [l, tag_no]
62
226
  pos = l
63
227
  tag_no += 1
64
- tag_no = 225 if tag_no == REF_TAG+1
228
+ tag_no = 225 if tag_no == REF_TAG+1 # skip 224, as we always can use 6 here
65
229
  tag_no = 28704 if tag_no == 256
230
+ tag_no = 1879052288 if tag_no == 32768
66
231
  end
67
232
  if t = prefix_stack[-1][1] # if we still have a viable prefix left
68
233
  translate[s] = CBOR::Tagged.new(t, s[pos..-1])
@@ -77,8 +242,45 @@ module CBOR
77
242
  end
78
243
 
79
244
  end
80
- # pp translate
81
- # XXX test replacing match_array here
245
+ # pp [:TRANSLATE, translate]
246
+ # pp [:PREFIXES, prefixes]
247
+ # Note that we didn't compute a suffix table to mix in
248
+
249
+ build_maps(count, match_array, translate, prefixes) if CBOR_PACKED_MAPS
250
+
251
+ match_array_size = match_array.size
252
+ mixed = match_array_size + prefixes.size <= STRAIGHT_11_SIZE
253
+ if mixed && match_array_size != 0
254
+ # We need to shift by match_array_size
255
+ translate = Hash[translate.map {|k, v|
256
+ PP.pp v, STDERR
257
+ newtag = v.tag
258
+ if newtag == REF_TAG
259
+ newtag = 224
260
+ end
261
+ # this works only if <= STRAIGHT_11_SIZE (above)
262
+ newtag += match_array_size
263
+ [k, CBOR::Tagged.new(newtag, v.value)]
264
+ }]
265
+ prefixes = prefixes.map {|p|
266
+ if CBOR::Tagged === p
267
+ case p.tag
268
+ when REF_TAG
269
+ CBOR::Tagged.new(224 + match_array_size, p.value)
270
+ when 224..255
271
+ CBOR::Tagged.new(p.tag + match_array_size, p.value)
272
+ else # this works only if <= STRAIGHT_11_SIZE (above)
273
+ p
274
+ end
275
+ else
276
+ p
277
+ end
278
+ }
279
+ # pp translate
280
+ # pp prefixes
281
+ end
282
+
283
+ # Check match_array for direct entries of an arg-packed string
82
284
  match_array = match_array.map do |v|
83
285
  if r = translate[v]
84
286
  # puts "*** replacing #{v.inspect} by #{r.inspect}"
@@ -87,18 +289,18 @@ module CBOR
87
289
  v
88
290
  end
89
291
  end
90
- # pp [:PREFIXES, prefixes]
91
- # pp translate
92
- new(match_array, prefixes, [], translate)
292
+
293
+ new(match_array, prefixes, translate, mixed)
93
294
  end
94
- def initialize(match_array, prefix_array, suffix_array, translate)
295
+ def initialize(match_array, argument_array, translate, mixed)
296
+ @mixed = mixed
95
297
  @hit = translate
96
298
  # XXX: make sure we don't overwrite the existing prefix compression values!
97
299
  # (this should really be done downwards, ...) 16 x 1, 160 x 2, (512-48) x 3
98
300
  match_array[0...16].each_with_index do |o, i|
99
301
  @hit[o] = CBOR::Simple.new(i)
100
302
  end
101
- # if m = match_array[16...128]
303
+ # if m = match_array[16...128] # no allocation for simple(16...128)
102
304
  # m.each_with_index do |o, i|
103
305
  # @hit[o] = CBOR::Simple.new(i + 128)
104
306
  # end
@@ -114,45 +316,48 @@ module CBOR
114
316
  @hit[k] = r
115
317
  end
116
318
  end
117
- # p @hit
319
+ # warn [:HIT, @hit['688921AF59455E4D35D2FFE5FD21CA0598D42374'.xeh]].inspect
118
320
  @match_array = match_array
119
321
  # @prefix = {} -- do that later
120
- @prefix_array = prefix_array
121
- @suffix_array = suffix_array
322
+ @argument_array = argument_array
122
323
  end
123
324
  def has(o)
325
+ # warn [:HIT1].inspect if o == '688921AF59455E4D35D2FFE5FD21CA0598D42374'.xeh
124
326
  @hit[o]
125
327
  end
126
328
  def pack(pa)
127
- # Don't forget to pack the match_array!
128
- CBOR::Tagged.new(PACKED_TAG, [@match_array, @prefix_array, @suffix_array, pa])
329
+ pa = pa.to_packed_cbor1(self) # should also sort by frequency...
330
+ if @mixed
331
+ mixed_array = @match_array + @argument_array
332
+ CBOR::Tagged.new(MIXED_PACKED_TAG, [mixed_array, pa])
333
+ else
334
+ CBOR::Tagged.new(PACKED_TAG, [@match_array, @argument_array, pa])
335
+ end
129
336
  end
130
337
  end
131
338
 
132
339
  class Unpacker
133
- def initialize(match_array, prefix_array, suffix_array)
340
+ def initialize(match_array, argument_array)
134
341
  @simple_array = match_array[0...16]
135
342
  @tagged_array = match_array[16..-1]
136
343
  # index with 2i for i >= 0 or ~2i for i < 0
137
344
  # no map as we need to populate in progress
138
- # pp prefix_array
139
- @prefix_array = []
140
- prefix_array.each {|x| @prefix_array << x.to_unpacked_cbor1(self)}
141
- @suffix_array = []
142
- suffix_array.each {|x| @prefix_array << x.to_unpacked_cbor1(self)}
143
- # XXX order? -- must do lazily!
345
+ # pp argument_array
346
+ @argument_array = []
347
+ @raw_argument_array = argument_array
144
348
  end
145
349
  def unsimple(sv)
146
- @simple_array[sv]
350
+ @simple_array[sv].to_unpacked_cbor1(self)
147
351
  end
148
- def untag(tv)
149
- @tagged_array[(i << 1) ^ (i >> 63)]
352
+ def untag(i)
353
+ # @tagged_array[(i << 1) ^ (i >> 63)]
354
+ ix = (i << 1) ^ (i >> 63)
355
+ ret = @tagged_array[ix]
356
+ # warn "** UNTAG i=#{i} ix=#{ix} ret=#{ret}"
357
+ ret.to_unpacked_cbor1(self)
150
358
  end
151
359
  def unprefix(n)
152
- @prefix_array[n]
153
- end
154
- def unsuffix(n)
155
- @suffix_array[n]
360
+ @argument_array[n] ||= @raw_argument_array[n].to_unpacked_cbor1(self)
156
361
  end
157
362
  end
158
363
 
@@ -193,7 +398,26 @@ module CBOR
193
398
  module String_Packed_CBOR
194
399
  def packed_merge(other, unpacker)
195
400
  # add checks
196
- to_unpacked_cbor1(unpacker) + other.to_unpacked_cbor1(unpacker)
401
+ lhs = to_unpacked_cbor1(unpacker)
402
+ rhs = other.to_unpacked_cbor1(unpacker)
403
+ if CBOR::Tagged === rhs
404
+ case rhs.tag
405
+ when HEX_LC_TAG
406
+ rhs = rhs.value.unpack("H*").first
407
+ when HEX_UC_TAG
408
+ rhs = rhs.value.unpack("H*").first.upcase
409
+ else
410
+ fail ArgumentError, "*** String#packed_merge: unknown tag #{rhs.tag} on rhs"
411
+ end
412
+ end
413
+ begin
414
+ lhs + rhs
415
+ rescue => detail
416
+ warn "** lhs = #{lhs.inspect}"
417
+ warn "** rhs = #{rhs.inspect}"
418
+ warn "** error: #{detail}"
419
+ lhs + rhs.to_s
420
+ end
197
421
  end
198
422
  end
199
423
  String.send(:include, String_Packed_CBOR)
@@ -211,7 +435,7 @@ module CBOR
211
435
  end
212
436
  def to_packed_cbor1(packer = Packer.from_item(self))
213
437
  if c = packer.has(self)
214
- c.to_unpacked_cbor1(unpacker)
438
+ c.to_packed_cbor1(packer)
215
439
  else
216
440
  # TODO: Find useful prefixes
217
441
  map {|x| x.to_packed_cbor1(packer)}
@@ -238,15 +462,20 @@ module CBOR
238
462
  end
239
463
  def to_packed_cbor1(packer = Packer.from_item(self))
240
464
  if c = packer.has(self)
241
- c.to_unpacked_cbor1(unpacker)
465
+ c.to_packed_cbor1(packer)
242
466
  else
243
467
  # TODO: Find useful prefixes
244
468
  Hash[map {|k, v| [k.to_packed_cbor1(packer), v.to_packed_cbor1(packer)]}]
245
469
  end
246
470
  end
247
471
  def packed_merge(other, unpacker)
248
- # TODO: add checks
249
- to_unpacked_cbor1(unpacker).merge other.to_unpacked_cbor1(unpacker)
472
+ rhs = other
473
+ until Hash === rhs
474
+ rhso = rhs
475
+ rhs = rhs.to_unpacked_cbor1(unpacker)
476
+ fail ArgumentError, "*** In #{self.inspect}, cannot further unpack #{rhs.inspect}" unless rhso != rhs
477
+ end
478
+ to_unpacked_cbor1(unpacker).merge rhs
250
479
  end
251
480
  end
252
481
  Hash.send(:include, Hash_Packed_CBOR)
@@ -261,20 +490,30 @@ module CBOR
261
490
  if tag == PACKED_TAG
262
491
  # check that this really is an array
263
492
  # warn value.to_yaml
264
- ma, pa, sa, pv = value
265
- unpacker = Unpacker.new(ma, pa, sa)
493
+ ma, aa, pv = value
494
+ unpacker = Unpacker.new(ma, aa)
495
+ pv.to_unpacked_cbor1(unpacker)
496
+ elsif tag == MIXED_PACKED_TAG
497
+ # check that this really is an array
498
+ # warn value.to_yaml
499
+ args, pv = value
500
+ unpacker = Unpacker.new(args, args)
266
501
  pv.to_unpacked_cbor1(unpacker)
267
502
  else
268
- fail "error message here"
503
+ fail "*** Don't know how to unpack tag #{tag}"
269
504
  end
270
505
  end
271
506
  def to_unpacked_cbor1(unpacker)
272
507
  case tag
273
508
  when REF_TAG
274
- if Integer === value
275
- unpacker(untag(value))
509
+ # warn "** REF_TAG1 value #{value.class.inspect} #{value.inspect}"
510
+ unpacked_value = value.to_unpacked_cbor1(unpacker)
511
+ # warn "** REF_TAG2 value #{unpacked_value.class.inspect} #{unpacked_value.inspect}"
512
+ if Integer === unpacked_value
513
+ # warn "** I REF_TAG value #{value.class.inspect} #{value.inspect}"
514
+ unpacker.untag(unpacked_value)
276
515
  else
277
- unpacker.unprefix(0).packed_merge value, unpacker
516
+ unpacker.unprefix(0).packed_merge unpacked_value, unpacker
278
517
  end
279
518
  when 225...256
280
519
  unpacker.unprefix(tag-(256-32)).packed_merge value, unpacker
@@ -292,9 +531,30 @@ module CBOR
292
531
  CBOR::Tagged.new(tag, value.to_unpacked_cbor1(unpacker))
293
532
  end
294
533
  end
534
+ def packed_merge(other, unpacker)
535
+ # tag must be a function tag -- TODO add standin tag decoder for lhs (value)
536
+ case tag
537
+ when RECORD_TAG
538
+ rhs = other.to_unpacked_cbor1(unpacker)
539
+ raise ArgumentError, "*** need arrays for record tag (#{
540
+ value.inspect}, #{rhs.inspect})" unless Array === value && Array === rhs
541
+ n = value.size - rhs.size
542
+ if n > 0
543
+ rhs += [UNDEFINED] * n
544
+ elsif n < 0
545
+ raise ArgumentError, "*** too many values (#{rhs.inspect
546
+ }) for record tag keys (#{value.inspect
547
+ })" unless Array === value && Array === rhs
548
+ end
549
+ Hash[value.zip(rhs).select{|k, v| v != UNDEFINED}]
550
+ else
551
+ raise ArgumentError, "*** function tag #{tag} not implemented"
552
+ end
553
+ end
554
+
295
555
  def to_packed_cbor1(packer = Packer.from_item(self))
296
556
  if c = packer.has(self)
297
- c
557
+ c.to_packed_cbor1(packer)
298
558
  else
299
559
  CBOR::Tagged.new(tag, value.to_packed_cbor1(packer))
300
560
  end
data/test/exa2r1.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'cbor-pure'
2
+ require 'cbor-packed'
3
+ require 'treetop'
4
+ require 'cbor-diag-parser'
5
+ require 'cbor-diagnostic'
6
+ require 'json'
7
+ require 'yaml'
8
+
9
+ input = DATA.read
10
+
11
+
12
+ parser = CBOR_DIAGParser.new
13
+ if result = parser.parse(input)
14
+ o = result.to_rb.to_unpacked_cbor
15
+ puts o.cbor_diagnostic
16
+ else
17
+ puts "*** can't parse #{i}"
18
+ puts "*** #{parser.failure_reason}"
19
+ end
20
+
21
+ # ruby -I ../lib exa2r.rb | jq > .out1
22
+ # jq < ~/big/wot-thing-description/test-bed/data/plugfest/2017-05-osaka/MyLED_f.jsonld > .out2
23
+ # ruby .check
24
+ # => true
25
+
26
+
27
+ __END__
28
+
29
+ 1113([/shared/["name", "@type", "links", "href", "mediaType",
30
+ / 0 1 2 3 4 /
31
+ "application/json", "outputData", {"valueType": {"type":
32
+ / 5 6 7 /
33
+ "number"}}, ["Property"], "writable", "valueType", "type"],
34
+ / 8 9 10 11 /
35
+ /prefix/ ["http://192.168.1.10", 6("3:8445/wot/thing"),
36
+ / 6 225 /
37
+ 225("/MyLED/"), 226("rgbValue"), "rgbValue",
38
+ / 226 227 228 /
39
+ {simple(6): simple(7), simple(9): true, simple(1): simple(8)}],
40
+ / 229 /
41
+ /rump/ {simple(0): "MyLED",
42
+ "interactions": [
43
+ 229({simple(2): [{simple(3): 227("Red"), simple(4): simple(5)}],
44
+ simple(0): 228("Red")}),
45
+ 229({simple(2): [{simple(3): 227("Green"), simple(4): simple(5)}],
46
+ simple(0): 228("Green")}),
47
+ 229({simple(2): [{simple(3): 227("Blue"), simple(4): simple(5)}],
48
+ simple(0): 228("Blue")}),
49
+ 229({simple(2): [{simple(3): 227("White"), simple(4): simple(5)}],
50
+ simple(0): "rgbValueWhite"}),
51
+ {simple(2): [{simple(3): 226("ledOnOff"), simple(4): simple(5)}],
52
+ simple(6): {simple(10): {simple(11): "boolean"}}, simple(0):
53
+ "ledOnOff", simple(9): true, simple(1): simple(8)},
54
+ {simple(2): [{simple(3): 226("colorTemperatureChanged"),
55
+ simple(4): simple(5)}], simple(6): simple(7), simple(0):
56
+ "colorTemperatureChanged", simple(1): ["Event"]}],
57
+ simple(1): "Lamp", "id": "0", "base": 225(""),
58
+ "@context": 6("2:8444/wot/w3c-wot-td-context.jsonld")}])
data/test/test-packed.rb CHANGED
@@ -35,16 +35,18 @@ puts "CBOR packed: #{cbp.length}"
35
35
  File.write("/tmp/cbp", cbp)
36
36
  File.write("/tmp/cbu", cb)
37
37
  pad = CBOR.decode(cbp)
38
- # puts pad.to_yaml
38
+ puts "--- packed decoded ---"
39
+ puts pad.to_yaml
39
40
  up = pad.to_unpacked_cbor
40
- pp up == jo
41
+ pp [:EQUAL, up == jo]
41
42
  if up != jo
43
+ puts "--- not equal ---"
42
44
  puts up.to_yaml
43
45
  puts pad.to_yaml # get rid of unwanted sharing...
44
46
  end
45
47
 
46
48
  # puts "#{pa.value.map {|x| x.to_cbor.length}}"
47
- puts pa.value[1].to_json
49
+ puts "pa.value[1].to_json: ", pa.value[1].to_json
48
50
 
49
51
  exin = JSON.load(DATA)
50
52
  exout = exin.to_packed_cbor
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cbor-packed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carsten Bormann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-04 00:00:00.000000000 Z
11
+ date: 2025-01-03 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: cbor-packed implements packed encoding for CBOR, RFC 7049 Section 3.9
14
14
  email: cabo@tzi.org
@@ -20,6 +20,7 @@ files:
20
20
  - lib/cbor-packed.rb
21
21
  - test/exa2.rb
22
22
  - test/exa2r.rb
23
+ - test/exa2r1.rb
23
24
  - test/test-packed.rb
24
25
  homepage: http://cbor.io/
25
26
  licenses:
@@ -40,11 +41,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
40
41
  - !ruby/object:Gem::Version
41
42
  version: '0'
42
43
  requirements: []
43
- rubygems_version: 3.2.15
44
+ rubygems_version: 3.5.14
44
45
  signing_key:
45
46
  specification_version: 4
46
47
  summary: CBOR (Concise Binary Object Representation) packer
47
48
  test_files:
48
49
  - test/exa2.rb
49
50
  - test/exa2r.rb
51
+ - test/exa2r1.rb
50
52
  - test/test-packed.rb