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 +4 -4
- data/cbor-packed.gemspec +1 -2
- data/lib/cbor-packed.rb +323 -63
- data/test/exa2r1.rb +58 -0
- data/test/test-packed.rb +5 -3
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9306aa8a05d1747901cda5a72e3636199ce2dc403e09c8731c2fbd6c6be93623
|
4
|
+
data.tar.gz: 9ccd87f2b36818c1b71b7e946cdfe370c230f40ea9331fc407e925a0ff214824
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
-
|
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 =
|
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.
|
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
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
-
|
42
|
-
|
43
|
-
[
|
44
|
-
|
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
|
-
#
|
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
|
-
|
91
|
-
|
92
|
-
new(match_array, prefixes, [], translate)
|
292
|
+
|
293
|
+
new(match_array, prefixes, translate, mixed)
|
93
294
|
end
|
94
|
-
def initialize(match_array,
|
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
|
-
#
|
319
|
+
# warn [:HIT, @hit['688921AF59455E4D35D2FFE5FD21CA0598D42374'.xeh]].inspect
|
118
320
|
@match_array = match_array
|
119
321
|
# @prefix = {} -- do that later
|
120
|
-
@
|
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
|
-
#
|
128
|
-
|
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,
|
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
|
139
|
-
@
|
140
|
-
|
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(
|
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
|
-
@
|
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
|
-
|
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.
|
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.
|
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
|
-
|
249
|
-
|
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,
|
265
|
-
unpacker = Unpacker.new(ma,
|
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 "
|
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
|
-
|
275
|
-
|
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
|
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
|
-
|
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.
|
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:
|
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.
|
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
|