Skip to content

Commit ea184bc

Browse files
committed
add tests, indexing and bugfixed
1 parent 8d4df82 commit ea184bc

File tree

3 files changed

+209
-42
lines changed

3 files changed

+209
-42
lines changed

ber.lua

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,21 @@ local Types = {
1919
NULL = 5,
2020
SEQUENCE = 16,
2121
SET = 17,
22+
IA5String = 22,
23+
DATE = 31,
24+
TIME_OF_DAY = 32,
25+
DATE_TIME = 33,
26+
DURATION = 34,
27+
OID_IRI = 35,
28+
RELATIVE_OID_IRI = 36,
2229
}
2330

2431
local constructedOnly = {
25-
["8"] = true,
26-
["11"] = true,
27-
["16"] = true,
28-
["17"] = true,
29-
["29"] = true,
32+
[8] = true,
33+
[11] = true,
34+
[16] = true,
35+
[17] = true,
36+
[29] = true,
3037
}
3138

3239

@@ -72,25 +79,32 @@ end
7279

7380

7481

75-
local function encode(value)
82+
local function encode(value, forceIdentifier)
83+
local ident = forceIdentifier and function(o)
84+
local t = {}
85+
for k, v in pairs(o) do t[k] = v end
86+
for k, v in pairs(forceIdentifier) do t[k] = v end
87+
return identifier(t)
88+
end or identifier
89+
7690
local mt = getmetatable(value)
7791
local tober = mt and mt.__tober
7892
if tober then
7993
if type(tober) == "function" then
80-
return encode(tober(value))
94+
return encode(tober(value), forceIdentifier)
8195
else
82-
return encode(tober)
96+
return encode(tober, forceIdentifier)
8397
end
8498
end
8599

86100
local t = type(value)
87101

88102
if t == "nil" then
89-
return identifier{type = Types.NULL} .. length(0)
103+
return ident{type = Types.NULL} .. length(0)
90104
elseif t == "number" then
91105
if math.floor(value) == value then
92106
local len = bytesRequired(value)
93-
local res = identifier{type = Types.INTEGER} .. length(len)
107+
local res = ident{type = Types.INTEGER} .. length(len)
94108
if len > 0 then
95109
res = res .. string.pack(">i"..len, value)
96110
end
@@ -99,36 +113,49 @@ local function encode(value)
99113
error("Not implemented")
100114
end
101115
elseif t == "string" then
102-
return identifier{type = Types.OCTET_STRING} .. length(#value) .. value
116+
return ident{type = Types.OCTET_STRING} .. length(#value) .. value
103117
elseif t == "boolean" then
104-
return identifier{type = Types.BOOLEAN} .. length(1) .. string.char(value and 1 or 0)
118+
return ident{type = Types.BOOLEAN} .. length(1) .. string.char(value and 0xff or 0)
105119
elseif t == "table" then
106120
if value[1] then
107-
local res = {}
121+
local children = {}
108122
for i, v in ipairs(value) do
109-
res[i] = encode(v)
123+
children[i] = v
124+
value[i] = nil
110125
end
111-
res = table.concat(res, "")
112-
return identifier{type = Types.SEQUENCE} .. length(#res) .. res
113-
else
114-
if value.constructed == nil and constructedOnly[value.type] then
115-
value.constructed = true
116-
end
117-
if value.constructed and value.children then
118-
value.data = encode(value.children)
119-
value.length = #value.data
126+
value.children = children
127+
if not value.type then
128+
value.type = Types.SEQUENCE
120129
end
130+
end
121131

122-
if not value.length then
123-
if not value.data then
124-
value.data = ""
125-
value.length = 0
126-
else
127-
value.length = #value.data
132+
if value.constructed == nil and constructedOnly[value.type] then
133+
value.constructed = true
134+
end
135+
if value.constructed and value.children then
136+
local res = {}
137+
if value.index then
138+
for i, v in ipairs(value.children) do
139+
res[i] = encode(v, {class = 2, type = i - 1})
140+
end
141+
else
142+
for i, v in ipairs(value.children) do
143+
res[i] = encode(v)
128144
end
129145
end
130-
return identifier(value) .. length(value.length) .. value.data
146+
value.data = table.concat(res, "")
147+
value.length = #value.data
148+
end
149+
150+
if not value.length then
151+
if not value.data then
152+
value.data = ""
153+
value.length = 0
154+
else
155+
value.length = #value.data
156+
end
131157
end
158+
return ident(value) .. length(value.length) .. value.data
132159
else
133160
error("Type not supported: "..t)
134161
end
@@ -138,7 +165,7 @@ end
138165

139166
local function decode(value, cursor, maxDepth)
140167
local i = cursor or 1 -- Cursor
141-
depth = (depth or math.maxinteger) - 1
168+
maxDepth = (maxDepth or math.maxinteger) - 1
142169

143170
-- Identifier octets
144171

@@ -219,4 +246,5 @@ return {
219246
decode = decode,
220247
identifier = identifier,
221248
length = length,
249+
Types = Types,
222250
}

readme.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ assert(ber.encode {"hello", 42} == ber.encode {
7171
})
7272
```
7373

74+
Tables with numbered indices may also contain BerObject properties:
75+
```lua
76+
assert(ber.encode {
77+
type = ber.Types.SET,
78+
1, 2, 3
79+
} == ber.encode {
80+
type = ber.Types.SET,
81+
children = {1, 2, 3}
82+
})
83+
```
84+
7485
If `constructed` is true and `children` is set, `encode` will first encode children and use the result as the data.
7586
```lua
7687
ber.encode {
@@ -84,6 +95,25 @@ ber.encode {
8495
}
8596
```
8697

98+
Constructed types may set `index` to `true` to auto index children:
99+
```lua
100+
assert(ber.encode {
101+
index = true,
102+
1, 2
103+
} == ber.encode {
104+
{
105+
class = 2,
106+
type = 0,
107+
data = string.byte(1)
108+
},
109+
{
110+
class = 2,
111+
type = 1,
112+
data = string.byte(2)
113+
}
114+
})
115+
```
116+
87117
The metatable index `__tober` can be used to customize encoding, by providing an encodable value
88118
or a function returning an encodable value.
89119
```lua

test.lua

Lines changed: 120 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
local ber = require "ber"
22

33

4+
-- Utils
5+
46

57
local function toHex(str)
68
return str:gsub(".", function (s) return string.format("%02x ", string.byte(s)) end)
@@ -12,25 +14,132 @@ local function pprint(t)
1214
end
1315
end
1416

17+
function recursive_compare(t1,t2)
18+
-- Use usual comparison first.
19+
if t1==t2 then return true end
20+
-- We only support non-default behavior for tables
21+
if (type(t1)~="table") then return false end
22+
-- They better have the same metatables
23+
local mt1 = getmetatable(t1)
24+
local mt2 = getmetatable(t2)
25+
if( not recursive_compare(mt1,mt2) ) then return false end
26+
27+
-- Check each key-value pair
28+
-- We have to do this both ways in case we miss some.
29+
-- TODO: Could probably be smarter and not check those we've
30+
-- already checked though!
31+
for k1,v1 in pairs(t1) do
32+
local v2 = t2[k1]
33+
if( not recursive_compare(v1,v2) ) then
34+
print("Diff", k1, v1, v2)
35+
return false
36+
end
37+
end
38+
for k2,v2 in pairs(t2) do
39+
local v1 = t1[k2]
40+
if( not recursive_compare(v1,v2) ) then
41+
print("Diff", k2, v1, v2)
42+
return false
43+
end
44+
end
1545

16-
17-
print "Testing..."
46+
return true
47+
end
1848

1949

50+
-- Tests
2051

21-
local str = "Hello world!"
2252

23-
assert(ber.decode(ber.encode(str)).data == str)
53+
print "Testing..."
2454

25-
print(toHex(ber.encode(str)))
26-
print(ber.encode(str))
27-
pprint(ber.decode(ber.encode(str)))
2855

29-
print(toHex(ber.encode(0x1234)))
30-
pprint(ber.decode(ber.encode(0x40)))
31-
print(ber.decode(ber.encode(0x40)).data)
3256

33-
print(toHex(ber.encode{"hello", 4660, false}))
57+
-- Examples
58+
59+
60+
-- "Maggie", 4, true
61+
local result = "\x31\x0e\x16\x06\x4d\x61\x67\x67\x69\x65\x02\x01\x04\x01\x01\xFF"
62+
assert(result == ber.encode {
63+
type = ber.Types.SET,
64+
{
65+
type = ber.Types.IA5String,
66+
data = "Maggie"
67+
}, 4, true
68+
})
69+
70+
-- Tag long form
71+
result = "\x1F\x22\x05\x31\x30\x30\x30\x59"
72+
assert(result == ber.encode {
73+
type = ber.Types.DURATION,
74+
data = "1000Y"
75+
})
76+
77+
-- Sequence indexing
78+
result = "\x30\x10\x80\x08\x62\x69\x67\x20\x68\x65\x61\x64\x81\x01\x02\x82\x01\x1A"
79+
assert(result == ber.encode {
80+
index = true,
81+
"\x62\x69\x67\x20\x68\x65\x61\x64",
82+
2,
83+
26,
84+
})
85+
86+
87+
88+
-- Internal tests
89+
90+
91+
-- string
92+
assert(recursive_compare(
93+
ber.decode(ber.encode "test"),
94+
{
95+
class = 0,
96+
constructed = false,
97+
type = 4,
98+
length = 4,
99+
data = "test",
100+
}
101+
))
102+
103+
-- Sequence
104+
local tmp = ber.encode "a" .. ber.encode "b"
105+
assert(recursive_compare(
106+
ber.decode(ber.encode {"a", "b"}),
107+
{
108+
class = 0,
109+
constructed = true,
110+
type = ber.Types.SEQUENCE,
111+
length = #tmp,
112+
data = tmp,
113+
children = {{
114+
class = 0,
115+
constructed = false,
116+
type = 4,
117+
length = 1,
118+
data = "a",
119+
}, {
120+
class = 0,
121+
constructed = false,
122+
type = 4,
123+
length = 1,
124+
data = "b",
125+
}}
126+
}
127+
))
128+
129+
-- metamethod
130+
tmp = setmetatable({name = "steve"}, {
131+
__tober = function (this) return string.format("Hello %s!", this.name) end
132+
})
133+
assert(recursive_compare(
134+
ber.decode(ber.encode(tmp)),
135+
{
136+
class = 0,
137+
constructed = false,
138+
type = 4,
139+
length = 12,
140+
data = "Hello steve!"
141+
}
142+
))
34143

35144

36145

0 commit comments

Comments
 (0)