cddl 0.10.3 → 0.10.7
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/cddl.gemspec +5 -3
- data/data/cddl.abnf +3 -1
- data/lib/cddl.rb +101 -25
- data/test-data/prim.cddl +7 -0
- data/test-data/printf.cddl +2 -0
- data/test-data/printf0.cddl +1 -0
- data/test-data/printf1.cddl +2 -0
- data/test-data/printf2.cddl +2 -0
- data/test-data/printf3.cddl +2 -0
- metadata +42 -9
- data/lib/base45_lite.rb +0 -71
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd34ee2aa2fc9a5d0d504d0dc48222f4e94722b4e19086d4932b8bcc82980e92
|
|
4
|
+
data.tar.gz: b397db50185a9f899c6b3d4bc9ce63946a1f57c6989cb7cf55215eb1d6da9868
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 96be7107033f0e3ef5da1ee747738bcd712eeb5af8230e11f128d64d0649920c8d15f5f4a13c5ccfb0ececad19dac7da1a145337f6e1a322ffe3573d7af4c689
|
|
7
|
+
data.tar.gz: d3edd46ef0698fde296cb18509101f98700568c942652e77e6d36abdd7a9a79e27d2fb920a3a5efc46160466feff0ecaa0ae0fda45411a94d234ed4ace037deb
|
data/cddl.gemspec
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
spec = Gem::Specification.new do |s|
|
|
2
2
|
s.name = 'cddl'
|
|
3
|
-
s.version = '0.10.
|
|
3
|
+
s.version = '0.10.7'
|
|
4
4
|
s.summary = "CDDL generator and validator."
|
|
5
5
|
s.description = %{A parser, generator, and validator for CDDL}
|
|
6
6
|
s.add_dependency('cbor-diag')
|
|
7
|
-
s.add_dependency('abnc')
|
|
7
|
+
s.add_dependency('abnc', '~> 0.1.1')
|
|
8
8
|
s.add_dependency('json_pure')
|
|
9
9
|
s.add_dependency('abnftt')
|
|
10
10
|
s.add_dependency('regexp-examples') # , '1.1.0')
|
|
11
11
|
s.add_dependency('colorize')
|
|
12
12
|
s.add_dependency('base32', '~> 0.3')
|
|
13
|
+
s.add_dependency('base45_lite', '~> 1.0')
|
|
14
|
+
s.add_dependency('scanf', '~> 1.0')
|
|
13
15
|
s.files = `git ls-files`.split("\n").grep(/^[a-z]/)
|
|
14
16
|
s.files = Dir['lib/**/*.rb'] + %w(cddl.gemspec) + Dir['data/**/*.abnf'] + Dir['data/**/*.cddl'] + Dir['test-data/**/*.cddl'] + Dir['test/**/*.rb']
|
|
15
17
|
s.require_path = 'lib'
|
|
16
18
|
s.executables = ['cddl']
|
|
17
19
|
s.default_executable = 'cddl'
|
|
18
|
-
s.required_ruby_version = '>= 2.
|
|
20
|
+
s.required_ruby_version = '>= 2.3'
|
|
19
21
|
s.author = "Carsten Bormann"
|
|
20
22
|
s.email = "cabo@tzi.org"
|
|
21
23
|
s.homepage = "http://github.com/cabo/cddl"
|
data/data/cddl.abnf
CHANGED
|
@@ -13,7 +13,7 @@ genericarg = "<" S type1 S *("," S type1 S ) ">"
|
|
|
13
13
|
type = type1 S *("/" S type1 S)
|
|
14
14
|
|
|
15
15
|
type1 = type2 [S (rangeop / annotator) S type2]
|
|
16
|
-
/ "#" "6" ["."
|
|
16
|
+
/ "#" "6" ["." tagnumber] "(" S type S ")" ; note no space!
|
|
17
17
|
/ "#" DIGIT ["." uint] ; major/ai
|
|
18
18
|
/ "#" ; any
|
|
19
19
|
/ "~" S typename [genericarg]
|
|
@@ -22,6 +22,8 @@ type1 = type2 [S (rangeop / annotator) S type2]
|
|
|
22
22
|
/ "&" S "(" S group S ")"
|
|
23
23
|
/ "&" S groupname [genericarg]
|
|
24
24
|
|
|
25
|
+
tagnumber = uint / ("<" type ">")
|
|
26
|
+
|
|
25
27
|
type2 = value
|
|
26
28
|
/ typename [genericarg]
|
|
27
29
|
/ "(" type ")"
|
data/lib/cddl.rb
CHANGED
|
@@ -9,6 +9,7 @@ require 'colorize'
|
|
|
9
9
|
require 'base64'
|
|
10
10
|
require 'base32'
|
|
11
11
|
require 'base45_lite'
|
|
12
|
+
require 'scanf'
|
|
12
13
|
|
|
13
14
|
module CDDL
|
|
14
15
|
|
|
@@ -422,7 +423,11 @@ module CDDL
|
|
|
422
423
|
when 3
|
|
423
424
|
gen_word
|
|
424
425
|
when 6
|
|
425
|
-
|
|
426
|
+
tn = Integer === where[2] ? where[2] : generate1(where[2])
|
|
427
|
+
unless Integer === tn && tn >= 0
|
|
428
|
+
fail "Can't generate a tag number out of #{where[2]}"
|
|
429
|
+
end
|
|
430
|
+
CBOR::Tagged.new(tn, generate1(where[3]))
|
|
426
431
|
when 7
|
|
427
432
|
case where[2]
|
|
428
433
|
when nil
|
|
@@ -433,8 +438,8 @@ module CDDL
|
|
|
433
438
|
true
|
|
434
439
|
when 22
|
|
435
440
|
nil
|
|
436
|
-
when 23
|
|
437
|
-
|
|
441
|
+
when 0..19, 23, 32..255
|
|
442
|
+
CBOR::Simple.new(where[2])
|
|
438
443
|
when 25, 26, 27
|
|
439
444
|
rand()
|
|
440
445
|
end
|
|
@@ -551,8 +556,7 @@ module CDDL
|
|
|
551
556
|
warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil?
|
|
552
557
|
end
|
|
553
558
|
end
|
|
554
|
-
|
|
555
|
-
content = generate1(target)
|
|
559
|
+
try_generate(target) do |content|
|
|
556
560
|
if validate1(content, where)
|
|
557
561
|
return content
|
|
558
562
|
end
|
|
@@ -605,22 +609,22 @@ module CDDL
|
|
|
605
609
|
content = Integer(generate1(control)).to_s
|
|
606
610
|
content
|
|
607
611
|
when :join
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
612
|
+
try_generate(control) do |content|
|
|
613
|
+
if Array === content &&
|
|
614
|
+
content.all? {|x| String === x} &&
|
|
615
|
+
Set[content.map {|x| x.encoding}].size == 1
|
|
616
|
+
content = content.join
|
|
617
|
+
if validate1(content, target)
|
|
618
|
+
return content
|
|
619
|
+
end
|
|
620
|
+
end
|
|
614
621
|
end
|
|
615
|
-
|
|
616
|
-
content
|
|
622
|
+
fail "Don't know yet how to generate #{where}"
|
|
617
623
|
when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
|
|
618
624
|
:b45, :b32, :h32, :hex, :hexlc, :hexuc
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
end
|
|
623
|
-
content = case conop
|
|
625
|
+
try_generate(control) do |content|
|
|
626
|
+
if String === content
|
|
627
|
+
content = case conop
|
|
624
628
|
when :b64u, :"b64u-sloppy"
|
|
625
629
|
Base64.urlsafe_encode64(content, padding: false)
|
|
626
630
|
when :b64c, :"b64c-sloppy"
|
|
@@ -639,10 +643,32 @@ module CDDL
|
|
|
639
643
|
content.unpack("H*")[0]
|
|
640
644
|
else fail "Cannot happen"
|
|
641
645
|
end
|
|
642
|
-
|
|
646
|
+
if validate1(content, target)
|
|
647
|
+
return content
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
fail "Not smart enough to generate #{where}"
|
|
652
|
+
when :printf
|
|
653
|
+
try_generate(control) do |content|
|
|
654
|
+
if Array === content && content.size >= 1
|
|
655
|
+
fmt, *data = content
|
|
656
|
+
if String === fmt
|
|
657
|
+
begin
|
|
658
|
+
content = fmt % data
|
|
659
|
+
if validate1(content, target)
|
|
660
|
+
return content
|
|
661
|
+
end
|
|
662
|
+
rescue ArgumentError => e
|
|
663
|
+
# be verbose about mismatches here
|
|
664
|
+
@last_message << "\n** #{fmt.inspect} ArgumentError #{e}"
|
|
665
|
+
end
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
fail "Not smart enough to generate #{where}#{@last_message}"
|
|
643
670
|
when :within, :and
|
|
644
|
-
|
|
645
|
-
content = generate1(target)
|
|
671
|
+
try_generate(target) do |content|
|
|
646
672
|
if validate1(content, control)
|
|
647
673
|
return content
|
|
648
674
|
elsif conop == :within
|
|
@@ -670,12 +696,24 @@ module CDDL
|
|
|
670
696
|
end
|
|
671
697
|
end
|
|
672
698
|
|
|
699
|
+
def try_generate(target)
|
|
700
|
+
32.times do
|
|
701
|
+
content = generate1(target)
|
|
702
|
+
yield content # should return if success
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
|
|
673
706
|
VALUE_TYPE = {text: String, bytes: String, int: Integer, float: Float}
|
|
674
707
|
SIMPLE_VALUE = {
|
|
675
708
|
[:prim, 7, 20] => [true, false, :bool],
|
|
676
709
|
[:prim, 7, 21] => [true, true, :bool],
|
|
677
710
|
[:prim, 7, 22] => [true, nil, :nil],
|
|
678
|
-
|
|
711
|
+
}
|
|
712
|
+
SIMPLE_VALUE_SIMPLE = Set[23] + (0..19) + (32..255)
|
|
713
|
+
SIMPLE_VALUE.default_proc = proc { |hash, t|
|
|
714
|
+
if t[0] == :prim && t[1] == 7 && SIMPLE_VALUE_SIMPLE.include?(t[2])
|
|
715
|
+
[true, CBOR::Simple.new(t[2]), :simple]
|
|
716
|
+
end
|
|
679
717
|
}
|
|
680
718
|
|
|
681
719
|
def extract_value(t) # []
|
|
@@ -717,6 +755,19 @@ module CDDL
|
|
|
717
755
|
}]
|
|
718
756
|
end
|
|
719
757
|
|
|
758
|
+
|
|
759
|
+
def extract_arg0(t)
|
|
760
|
+
return [false] unless t[0] == :array
|
|
761
|
+
[true,
|
|
762
|
+
(el = t[1]
|
|
763
|
+
return [false] unless el[0..3] == [:member, 1, 1, nil]
|
|
764
|
+
ok, v, vt = extract_value(el[4])
|
|
765
|
+
return [false] unless ok
|
|
766
|
+
[v, vt]
|
|
767
|
+
),
|
|
768
|
+
*t[2..-1]]
|
|
769
|
+
end
|
|
770
|
+
|
|
720
771
|
def extract_feature(control, d)
|
|
721
772
|
ok, v, vt = extract_value(control)
|
|
722
773
|
if ok
|
|
@@ -1126,6 +1177,21 @@ module CDDL
|
|
|
1126
1177
|
end
|
|
1127
1178
|
) && validate1(decoded.b, control)
|
|
1128
1179
|
)
|
|
1180
|
+
when :printf
|
|
1181
|
+
ann if String === d && (
|
|
1182
|
+
ok, fmt, *v = extract_arg0(control)
|
|
1183
|
+
if ok && String == fmt[1]
|
|
1184
|
+
fmt = fmt[0]
|
|
1185
|
+
# warn "** ok #{ok.inspect} fmt #{fmt.inspect} v #{v.inspect}"
|
|
1186
|
+
decoded = d.scanf(fmt) # this is a bit too lenient, so let's do:
|
|
1187
|
+
encode_again = fmt % decoded
|
|
1188
|
+
if encode_again != d
|
|
1189
|
+
warn "** fmt #{fmt.inspect} d #{d.inspect} decoded #{decoded.inspect} encode_again #{encode_again.inspect}"
|
|
1190
|
+
else
|
|
1191
|
+
validate1(decoded, [:array, *v])
|
|
1192
|
+
end
|
|
1193
|
+
end
|
|
1194
|
+
)
|
|
1129
1195
|
when :within
|
|
1130
1196
|
if validate1(d, control)
|
|
1131
1197
|
ann
|
|
@@ -1161,7 +1227,9 @@ module CDDL
|
|
|
1161
1227
|
end
|
|
1162
1228
|
d = CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*"))
|
|
1163
1229
|
end
|
|
1164
|
-
CBOR::Tagged === d &&
|
|
1230
|
+
CBOR::Tagged === d && (
|
|
1231
|
+
Integer === where[2] ? d.tag == where[2] : validate1a(d.tag, where[2])
|
|
1232
|
+
) && validate1a(d.data, where[3])
|
|
1165
1233
|
when 7
|
|
1166
1234
|
t, v = extract_value(where)
|
|
1167
1235
|
if t
|
|
@@ -1170,11 +1238,11 @@ module CDDL
|
|
|
1170
1238
|
case where[2]
|
|
1171
1239
|
when nil
|
|
1172
1240
|
# XXX
|
|
1173
|
-
fail
|
|
1241
|
+
fail [:valnil, d, where].inspect
|
|
1174
1242
|
when 25, 26, 27
|
|
1175
1243
|
Float === d
|
|
1176
1244
|
else
|
|
1177
|
-
fail
|
|
1245
|
+
fail [:val7, d, where].inspect
|
|
1178
1246
|
end
|
|
1179
1247
|
end
|
|
1180
1248
|
else
|
|
@@ -1488,6 +1556,7 @@ module CDDL
|
|
|
1488
1556
|
:json, :decimal, :join,
|
|
1489
1557
|
:b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
|
|
1490
1558
|
:b45, :b32, :h32, :hex, :hexlc, :hexuc,
|
|
1559
|
+
:printf,
|
|
1491
1560
|
]
|
|
1492
1561
|
|
|
1493
1562
|
def type1(n, canbegroup = false)
|
|
@@ -1520,6 +1589,13 @@ module CDDL
|
|
|
1520
1589
|
when /\A#(\d+)/
|
|
1521
1590
|
maj = $1.to_i
|
|
1522
1591
|
s = [:prim, maj, *n.children(:uint).map(&:to_s).map(&:to_i)]
|
|
1592
|
+
if tn = n.tagnumber
|
|
1593
|
+
if ui = tn.uint
|
|
1594
|
+
s << ui.to_s.to_i
|
|
1595
|
+
elsif tt = tn.type
|
|
1596
|
+
s << type(tt)
|
|
1597
|
+
end
|
|
1598
|
+
end
|
|
1523
1599
|
if tagged_type = n.type
|
|
1524
1600
|
s << type(tagged_type)
|
|
1525
1601
|
end
|
data/test-data/prim.cddl
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
my_label = text .printf (["0x%x: %a", 4711, 81.5])
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cddl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.10.
|
|
4
|
+
version: 0.10.7
|
|
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: 2024-02-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cbor-diag
|
|
@@ -28,16 +28,16 @@ dependencies:
|
|
|
28
28
|
name: abnc
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 0.1.1
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 0.1.1
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: json_pure
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,6 +108,34 @@ dependencies:
|
|
|
108
108
|
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0.3'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: base45_lite
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '1.0'
|
|
118
|
+
type: :runtime
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '1.0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: scanf
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '1.0'
|
|
132
|
+
type: :runtime
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '1.0'
|
|
111
139
|
description: A parser, generator, and validator for CDDL
|
|
112
140
|
email: cabo@tzi.org
|
|
113
141
|
executables:
|
|
@@ -119,7 +147,6 @@ files:
|
|
|
119
147
|
- cddl.gemspec
|
|
120
148
|
- data/cddl.abnf
|
|
121
149
|
- data/prelude.cddl
|
|
122
|
-
- lib/base45_lite.rb
|
|
123
150
|
- lib/cbor-pp-play.rb
|
|
124
151
|
- lib/cbor-pp.rb
|
|
125
152
|
- lib/cddl.rb
|
|
@@ -208,6 +235,12 @@ files:
|
|
|
208
235
|
- test-data/oidbat.cddl
|
|
209
236
|
- test-data/patch1.cddl
|
|
210
237
|
- test-data/plus.cddl
|
|
238
|
+
- test-data/prim.cddl
|
|
239
|
+
- test-data/printf.cddl
|
|
240
|
+
- test-data/printf0.cddl
|
|
241
|
+
- test-data/printf1.cddl
|
|
242
|
+
- test-data/printf2.cddl
|
|
243
|
+
- test-data/printf3.cddl
|
|
211
244
|
- test-data/reused_named_group.cddl
|
|
212
245
|
- test-data/sasl.cddl
|
|
213
246
|
- test-data/sequence.cddl
|
|
@@ -240,14 +273,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
240
273
|
requirements:
|
|
241
274
|
- - ">="
|
|
242
275
|
- !ruby/object:Gem::Version
|
|
243
|
-
version: '2.
|
|
276
|
+
version: '2.3'
|
|
244
277
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
278
|
requirements:
|
|
246
279
|
- - ">="
|
|
247
280
|
- !ruby/object:Gem::Version
|
|
248
281
|
version: '0'
|
|
249
282
|
requirements: []
|
|
250
|
-
rubygems_version: 3.4.
|
|
283
|
+
rubygems_version: 3.4.10
|
|
251
284
|
signing_key:
|
|
252
285
|
specification_version: 4
|
|
253
286
|
summary: CDDL generator and validator.
|
data/lib/base45_lite.rb
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Copyright (c) 2022 Weihang Jian <https://tonytonyjan.net>
|
|
4
|
-
#
|
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
# furnished to do so, subject to the following conditions:
|
|
11
|
-
#
|
|
12
|
-
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
# copies or substantial portions of the Software.
|
|
14
|
-
#
|
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
# SOFTWARE.
|
|
22
|
-
|
|
23
|
-
# An implementation of draft-faltstrom-base45-10, see
|
|
24
|
-
# https://datatracker.ietf.org/doc/draft-faltstrom-base45/
|
|
25
|
-
module Base45Lite
|
|
26
|
-
class Error < ::StandardError; end
|
|
27
|
-
class OverflowError < Error; end
|
|
28
|
-
class InvalidCharacterError < Error; end
|
|
29
|
-
class ForbiddenLengthError < Error; end
|
|
30
|
-
|
|
31
|
-
MAX_UINT18 = 2**16 - 1
|
|
32
|
-
SQUARED_45 = 45**2
|
|
33
|
-
MAPPING = [
|
|
34
|
-
*'0'..'9',
|
|
35
|
-
*'A'..'Z',
|
|
36
|
-
' ', '$', '%', '*', '+', '-', '.', '/', ':'
|
|
37
|
-
].map!.with_index { |x, i| [i, x] }.to_h.freeze
|
|
38
|
-
REVERSE_MAPPING = MAPPING.invert.freeze
|
|
39
|
-
|
|
40
|
-
def self.encode(input)
|
|
41
|
-
sequence = []
|
|
42
|
-
input.unpack('n*').map! do |uint16|
|
|
43
|
-
i, c = uint16.divmod(45)
|
|
44
|
-
i, d = i.divmod(45)
|
|
45
|
-
_, e = i.divmod(45)
|
|
46
|
-
sequence.push(c, d, e)
|
|
47
|
-
end
|
|
48
|
-
if input.bytesize.odd?
|
|
49
|
-
i, c = input.getbyte(-1).divmod(45)
|
|
50
|
-
_, d = i.divmod(45)
|
|
51
|
-
sequence.push(c, d)
|
|
52
|
-
end
|
|
53
|
-
sequence.map!{ |n| MAPPING[n] }.join
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def self.decode(input)
|
|
57
|
-
input
|
|
58
|
-
.chars.map! { |c| REVERSE_MAPPING[c] || raise(InvalidCharacterError) }
|
|
59
|
-
.each_slice(3).map do |slice|
|
|
60
|
-
c, d, e = slice
|
|
61
|
-
raise ForbiddenLengthError if d.nil?
|
|
62
|
-
|
|
63
|
-
sum = c + d * 45
|
|
64
|
-
sum += e * SQUARED_45 if e
|
|
65
|
-
raise OverflowError if sum > MAX_UINT18
|
|
66
|
-
|
|
67
|
-
sum
|
|
68
|
-
end
|
|
69
|
-
.pack((input.length % 3).zero? ? 'n*' : "n#{input.length / 3}C")
|
|
70
|
-
end
|
|
71
|
-
end
|