├── CHANGES
├── LICENSE
├── Makefile
├── README
├── cbor.lua
├── cbor_c.c
├── cbor_s.lua
├── cbormisc.lua
├── dnf.c
├── dnf.h
├── org.conman.cbor-1.4.0-1.rockspec
├── test.lua
└── test_s.lua
/CHANGES:
--------------------------------------------------------------------------------
1 |
2 | 1.2.1 2016-10-05
3 |
4 | * Fix RAINS test.
5 |
6 | * Fix Makefile to calculate Lua version once.
7 |
8 | * Fix rockspec to use 'builtin' method, not the 'make' method
9 | to better work on Windows.
10 |
11 | 1.2.0 2016-10-04
12 |
13 | * Changed RAINS support to better reflect how it's supposed to be
14 | used.
15 |
16 | 1.1.2 2016-10-04
17 |
18 | * Update CHANGES file.
19 |
20 | 1.1.1 2016-10-04
21 |
22 | * Fix version in Makefile
23 |
24 | 1.1.0 2016-10-04
25 |
26 | * Added support for RAINS tagging
27 | (see https://britram.github.io/rains-prototype/)
28 |
29 | 1.0.1 2016-06-26
30 |
31 | * Bug fixes to Makefile.
32 |
33 | 1.0.0 2016-04-04
34 |
35 | * Initial version of CBOR.
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #########################################################################
2 | #
3 | # Copyright 2016 by Sean Conner. All Rights Reserved.
4 | #
5 | # This library is free software; you can redistribute it and/or modify it
6 | # under the terms of the GNU Lesser General Public License as published by
7 | # the Free Software Foundation; either version 3 of the License, or (at your
8 | # option) any later version.
9 | #
10 | # This library is distributed in the hope that it will be useful, but
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | # License for more details.
14 | #
15 | # You should have received a copy of the GNU Lesser General Public License
16 | # along with this library; if not, see .
17 | #
18 | # Comments, questions and criticisms can be sent to: sean@conman.org
19 | #
20 | ########################################################################
21 |
22 | UNAME := $(shell uname)
23 | VERSION := $(shell git describe --tag)
24 |
25 | ifeq ($(VERSION),)
26 | VERSION=1.4.0
27 | endif
28 |
29 | # ===================================================
30 |
31 | CC = gcc -std=c99 -Wall -Wextra -pedantic
32 | CFLAGS = -g -fPIC
33 |
34 | ifeq ($(UNAME),Linux)
35 | LDFLAGS = -g -shared
36 | endif
37 |
38 | ifeq ($(UNAME),Darwin)
39 | LDFLAGS = -g -bundle -undefined dynamic_lookup -all_load
40 | endif
41 |
42 | INSTALL = /usr/bin/install
43 | INSTALL_PROGRAM = $(INSTALL)
44 | INSTALL_DATA = $(INSTALL) -m 644
45 |
46 | prefix ?= /usr/local
47 | libdir ?= $(prefix)/lib
48 | datarootdir ?= $(prefix)/share
49 | dataroot ?= $(datarootdir)
50 |
51 | override CFLAGS += -DVERSION='"$(VERSION)"'
52 |
53 | # ===================================================
54 |
55 | LUA ?= lua
56 | LUA_VERSION := $(shell $(LUA) -e "io.stdout:write(_VERSION:match '^Lua (.*)','\n')")
57 | LIBDIR ?= $(libdir)/lua/$(LUA_VERSION)
58 | LUADIR ?= $(dataroot)/lua/$(LUA_VERSION)
59 |
60 | ifneq ($(LUA_INCDIR),)
61 | override CFLAGS += -I$(LUA_INCDIR)
62 | endif
63 |
64 | # ===================================================
65 |
66 | %.so :
67 | $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
68 |
69 | .PHONY: install uninstall clean check
70 |
71 | cbor_c.so : cbor_c.o dnf.o
72 | cbor_c.o : dnf.h
73 | dnf.o : dnf.h
74 |
75 | # ===================================================
76 |
77 | install: cbor_c.so
78 | $(INSTALL) -d $(DESTDIR)$(LIBDIR)/org/conman
79 | $(INSTALL) -d $(DESTDIR)$(LUADIR)/org/conman
80 | $(INSTALL_PROGRAM) cbor_c.so $(DESTDIR)$(LIBDIR)/org/conman/cbor_c.so
81 | $(INSTALL_DATA) cbor.lua $(DESTDIR)$(LUADIR)/org/conman/cbor.lua
82 | $(INSTALL_DATA) cbor_s.lua $(DESTDIR)$(LUADIR)/org/conman/cbor_s.lua
83 | $(INSTALL_DATA) cbormisc.lua $(DESTDIR)$(LUADIR)/org/conman/cbormisc.lua
84 |
85 | uninstall:
86 | $(RM) $(DESTDIR)$(LIBDIR)/org/conman/cbor_c.so
87 | $(RM) $(DESTDIR)$(LUADIR)/org/conman/cbor.lua
88 | $(RM) $(DESTDIR)$(LUADIR)/org/conman/cbor_s.lua
89 | $(RM) $(DESTDIR)$(LUADIR)/org/conman/cbormisc.lua
90 |
91 | check:
92 | luacheck cbor.lua test.lua cbor_s.lua test_s.lua cbormisc.lua
93 |
94 | clean:
95 | $(RM) *~ *.so *.o
96 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 |
2 | The Concise Binary Object Represenation Lua Modules
3 |
4 | The major module in this collection is 'org.conman.cbor', the most
5 | comprehensive CBOR module in the Lua universe, as it supports everything
6 | mentioned in RFC-8949 and the extensions published by the IANA. It's easy
7 | to use, but also allows enough control to get exactly what you want when
8 | encoding to CBOR. For example:
9 |
10 | cbor = require "org.conman.cbor"
11 |
12 | data =
13 | {
14 | name = "Sean Conner",
15 | userid = "spc476",
16 | login =
17 | {
18 | active = true,
19 | last_login = os.time(),
20 | }
21 | }
22 |
23 | enc = cbor.encode(data)
24 |
25 | It's that easy. To decode is just as simple:
26 |
27 | data = cbor.decode(enc)
28 |
29 | For a bit more control over the encoding though, say, to include some
30 | semantic tagging, requires a bit more work, but is still quite doable. For
31 | example, if we change things slghtly:
32 |
33 | mt =
34 | {
35 | __tocbor = function(self)
36 | return cbor.TAG._epoch(os.time(self))
37 | end
38 | }
39 |
40 | function date()
41 | local now = os.date("*t")
42 | setmetatable(now,mt)
43 | return now
44 | end
45 |
46 | data =
47 | {
48 | name = "Sean Conner",
49 | userid = "spc476",
50 | login =
51 | {
52 | active = true,
53 | last_login = date()
54 | }
55 | }
56 |
57 | enc = cbor.encode(data)
58 |
59 | You can supply the '__tocbor' metamethod to a metatable to have the item
60 | encode itself as this example shows. This is one approach to have userdata
61 | encoded into CBOR.
62 |
63 | Decoding tagged items is also quite easy:
64 |
65 | tagged_items =
66 | {
67 | _epoch = function(value)
68 | local t = os.date("*t",value)
69 | setmetatable(t,mt)
70 | return t
71 | end
72 | }
73 |
74 | -- first parameter is the data we're decoding
75 | -- second paramter is the offset to start with (optional)
76 | -- third parameter is a table of routines to process
77 | -- tagged data (optional)
78 |
79 | data = cbor.decode(enc,1,tagged_items)
80 |
81 | The _epoch tagged type is passed to our function where we return the value
82 | from os.date(). The resulting data looks like:
83 |
84 | data =
85 | {
86 | name = "Sean Conner",
87 | userid = "spc476",
88 | login =
89 | {
90 | active = true,
91 | last_login =
92 | {
93 | year = 2016,
94 | month = 4,
95 | day = 4,
96 | hour = 1,
97 | min = 42,
98 | sec = 53,
99 | wday = 2,
100 | yday = 95,
101 | isdst = true,
102 | } -- and we have a metatable that can encode this
103 | }
104 | }
105 |
106 | Of course, you could also do it like:
107 |
108 | enc = cbor.TYPE.MAP(3)
109 | .. cbor.encode "name" .. cbor.encode(data.name)
110 | .. cbor.encode "userid" .. cbor.encode(data.userid)
111 | .. cbor.encode "login" .. cbor.TYPE.MAP(2)
112 | .. cbor.encode "active"
113 | .. cbor.encode(data.login.active)
114 | .. cbor.encode "last_login"
115 | .. cbor.TYPE._epoch(os.time(data.login.last_login))
116 |
117 | if you really want to get down into the details of encoding CBOR.
118 |
119 | Tagged types are not the only thing that can be post-processed though. If
120 | we wanted to ensure that all the field names were lower case, we can do that
121 | as well:
122 |
123 | tagged_items =
124 | {
125 | TEXT = function(value,iskey)
126 | if iskey then
127 | value = value:lower()
128 | end
129 | return value
130 | end,
131 |
132 | _epoch = function(value)
133 | local t = os.date("*t",value)
134 | setmetatable(t,mt)
135 | return t
136 | end,
137 | }
138 |
139 | data = cbor.decode(enc,1,tagged_items)
140 |
141 | The second parameter indicates if the given value is a key in a MAP.
142 |
143 | This module can also deal with circular references with an additional
144 | parameter when encoding:
145 |
146 | x = {}
147 | x.x = x
148 |
149 | enc = cbor.encode(x,{})
150 |
151 | The addition of the empty table as the second parameter let's the module
152 | know that MAPs and ARRAYs might be referenced and to keep track as it's
153 | processing. The reason it's not done by default is that each MAP and ARRAY
154 | is tagged as "shareable", which adds some overhead to the output. If there
155 | are no circular references, such additions are just wasted. Just something
156 | to keep in mind.
157 |
158 | But the above does work and can be passed to cbor.decode() without any
159 | additional parameters (decoding will deal with such references
160 | automatically).
161 |
162 | Additionally, if the data you are sending might use repetative strings, say:
163 |
164 | data =
165 | {
166 | { filename = "cbor_c.so" , package = "org.conman" },
167 | { filename = "cbor.lua" , package = "org.conman" },
168 | { filename = "cbor_s.lua" , package = "org.conman" },
169 | { filename = "cbormisc.lua" , package = "org.conman" },
170 | }
171 |
172 | You can save some space by using string references:
173 |
174 | enc = cbor.encode(data,nil,{}) -- third parameter to use string refs
175 |
176 | Normally, this would take 160 bytes to encode, but with string references,
177 | it saves 54 bytes. Not much in this example, but it could add up
178 | significantly.
179 |
180 | And yes, you can request both shared references and string references in the
181 | same call.
182 |
183 | The CBOR values null and undefined map to Lua nil, and all the baggage that
184 | comes with that. If you really need to track null and/or undefined values,
185 | you can define the following with a unique value:
186 |
187 | cbor.null
188 | cbor.undefined
189 |
190 | And the value of those two fields will be checked for when encoding and the
191 | appropriate CBOR value used. Also, when decoding, the those values will be
192 | used when the CBOR null and undefined values are decoded. The safest value
193 | to use is an empty table for each one:
194 |
195 | cbor.null = {}
196 | cbor.undefined = {}
197 |
198 | That will ensure both have a unique value.
199 |
200 | If the 'org.conman.cbor' module is too heavy for your application, you can
201 | try the 'org.conman.cbor_s' module. It's similar to the full blown
202 | 'org.conman.cbor' module, but without the full support of the various tagged
203 | data items. And it too, supports custom null and undefined values---just
204 | set
205 |
206 | cbor_s.null
207 | cbor_s.undefined
208 |
209 | Our original example is the same:
210 |
211 | cbor_s = require "org.conman.cbor_s"
212 |
213 | data =
214 | {
215 | name = "Sean Conner",
216 | userid = "spc476",
217 | login =
218 | {
219 | active = true,
220 | last_login = os.time(),
221 | }
222 | }
223 |
224 | enc = cbor_s.encode(data)
225 | data2 = cbor_s.decode(enc)
226 |
227 | But the tagged version is slightly different:
228 |
229 | mt =
230 | {
231 | __tocbor = function(self)
232 |
233 | -- ---------------------------------------------------
234 | -- because of limited support, we need to specify the
235 | -- actual number for the _epoch tag.
236 | -- ---------------------------------------------------
237 |
238 | return cbor.encode(os.time(self),1)
239 | end
240 | }
241 |
242 | function date()
243 | local now = os.date("*t")
244 | setmetatable(now,mt)
245 | return now
246 | end
247 |
248 | data =
249 | {
250 | name = "Sean Conner",
251 | userid = "spc476",
252 | login =
253 | {
254 | active = true,
255 | last_login = date()
256 | }
257 | }
258 |
259 | enc = cbor.encode(data)
260 |
261 | tagged_items =
262 | {
263 | [1] = function(value)
264 | local t = os.date("*t",value)
265 | setmetatable(t,mt)
266 | return t
267 | end,
268 | }
269 |
270 | data2 = cbor.decode(enc,1,tagged_items)
271 |
272 | The first major difference is the lack of tag names (you have to use the
273 | actual values). The second difference is the lack of support for the
274 | reference tags (you can still catch them, but due to a lack of support in
275 | the underlying engine you can't really do anything with them at this time;
276 | you the full blown 'org.conman.cbor' module if you really need circular
277 | references) and string references (same issue).
278 |
279 | Dropping down yet another level is the 'org.conman.cbor_c' module. This is
280 | the basic core of the two previous modules and only supplies two functions,
281 | cbor_c.encode() and cbor_c.decode(). The modules 'org.conman.cbor_s' and
282 | 'org.conman.cbormisc' were developed to showcase the low level usage of the
283 | 'org.conman.cbor_c' module and can be the basis for a module for even more
284 | constrained devices.
285 |
286 | **************************************************************************
287 | *
288 | * CAVEATS
289 | *
290 | **************************************************************************
291 |
292 | Encoding a mixed Lua table (e.g. { 1 , 2 , 3 , foo = 1 , bar = 2 }) may not
293 | encode as expected. For best results, ensure that all Lua tables are either
294 | pure sequences (arrays, e.g. { 1 , 2 , 3 , 4 }) or are non-sequential in
295 | nature.
296 |
297 | NOTE: The following array is problematic:
298 |
299 | { foo = 1 , bar = 2 , [1] = 1 }
300 |
301 | because it is both a Lua sequence and contains non-sequence indecies, and
302 | will thus be encoded as an ARRAY. The following array:
303 |
304 | { foo = 1 , bar = 2 , [100] = 1 }
305 |
306 | is NOT considered a sequence and will be encoded as a MAP.
307 |
308 | **************************************************************************
309 | *
310 | * QUICK FUNCTION REFERENCE
311 | *
312 | * See http://cbor.io/spec.html for more information on CBOR
313 | *
314 | * org.conman.cbor
315 | *
316 | *************************************************************
317 |
318 | Full blown CBOR support. All of RFC-8949 as well as the currently defined
319 | IANA extensions are supported with this module. This module also includes
320 | enhanced error detection and type checking. As a result, it's quite a bit
321 | larger than the org.conman.cbor_s module. This module also contains a large
322 | number of functions.
323 |
324 | ==============================================================
325 |
326 | Usage: bool = cbor.isnumber(ctype)
327 | Desc: returns true of the given CBOR type is a number
328 | Input: type (enum/cbor) CBOR type
329 | Return: bool (boolean) true if number, false otherwise
330 |
331 | ==============================================================
332 |
333 | Usage: bool = cbor.isinteger(ctype)
334 | Desc: returns true if the given CBOR type is an integer
335 | Input: ctype (enum/cbor) CBOR type
336 | Return: bool (boolean) true if number, false othersise
337 |
338 | ==============================================================
339 |
340 | Usage: bool = cbor.isfloat(ctype)
341 | Desc: returns true if the given CBOR type is a float
342 | Input: ctype (enum/cbor) CBOR type
343 | Return: bool (boolean) true if number, false otherwise
344 |
345 | ==============================================================
346 |
347 | Usage: value,pos2,ctype = cbor.decode(packet[,pos][,conv][,ref][,iskey])
348 | Desc: Decode CBOR encoded data
349 | Input: packet (binary) CBOR binary blob
350 | pos (integer/optional) starting point for decoding
351 | conv (table/optional) table of conversion routines
352 | ref (table/optional) reference table (see notes)
353 | iskey (boolean/optional) is a key in a MAP (see notes)
354 | Return: value (any) the decoded CBOR data
355 | pos2 (integer) offset past decoded data
356 | ctype (enum/cbor) CBOR type of value
357 |
358 | Note: The conversion table should be constructed as:
359 | {
360 | UINT = function(v) return munge(v) end,
361 | _datetime = function(v) return munge(v) end,
362 | _url = function(v) return munge(v) end,,
363 | }
364 |
365 | The keys are CBOR types (listed above). These functions are
366 | expected to convert the decoded CBOR type into a more appropriate
367 | type for your code. For instance, an _epoch can be converted into a
368 | table.
369 |
370 | Users of this function *should not* pass a reference table into this
371 | routine---this is used internally to handle references. You need to
372 | know what you are doing to use this parameter. You have been
373 | warned.
374 |
375 | The iskey is true if the value is being used as a key in a map, and
376 | is passed to the conversion routine; this too, is an internal use
377 | only variable and you need to know what you are doing to use this.
378 | You have been warned.
379 |
380 | This function can throw an error. The returned error object MAY BE
381 | a table, in which case it has the format:
382 |
383 | {
384 | msg = "Error text",
385 | pos = 13 -- position in binary object of error
386 | }
387 |
388 | This function can throw errors
389 |
390 | ==============================================================
391 |
392 | Usage: value,pos2,ctype[,err] = cbor.pdecode(packet[,pos][,conv][,ref])
393 | Desc: Protected call to cbor.decode(), which will return an error
394 | Input: packet (binary) CBOR binary blob
395 | pos (integer/optional) starting point for decoding
396 | conv (table/optional) table of conversion routines (see cbor.decode())
397 | ref (table/optional) reference table (see cbor.decode())
398 | Return: value (any) the decoded CBOR data, nil on error
399 | pos2 (integer) offset past decoded data; if error, position of error
400 | ctype (enum/cbor) CBOR type
401 | err (string/optional) error message (if any)
402 |
403 | ==============================================================
404 |
405 | Usage: blob = cbor.encode(value[,sref][,stref])
406 | Desc: Encode a Lua type into a CBOR type
407 | Input: value (any)
408 | sref (table/optional) shared reference table
409 | stref (table/optional) shared string reference table
410 | Return: blob (binary) CBOR encoded value
411 |
412 | Note: This function can throw errors
413 |
414 | ==============================================================
415 |
416 | Usage: blob[,err] = cbor.pencode(value[,sref][,stref])
417 | Desc: Protected call to encode a CBOR type
418 | Input: value (any)
419 | sref (table/optional) shared reference table
420 | stref (table/optional) shared string reference table
421 | Return: blob (binary) CBOR encoded value, nil on error
422 | err (string/optional) error message
423 |
424 | ==============================================================
425 |
426 | cbor.__ENCODE_MAP
427 |
428 | A table of functions to map Lua values to CBOR encoded values. nil,
429 | boolean, number and string are handled directly (if a Lua string is valid
430 | UTF8, then it's encoded as a CBOR TEXT.
431 |
432 | For the other four types, only tables are directly supported without
433 | metatable support. If a metatable does exist, if the method '__tocbor' is
434 | defined, that function is called and the results returned. If '__len' is
435 | defined, then it is mapped as a CBOR ARRAY. For Lua 5.2, if '__ipairs' is
436 | defined, then it too, is mapped as a CBOR ARRAY. If Lua 5.2 or higher and
437 | '__pairs' is defined, then it's mapped as a CBOR MAP.
438 |
439 | Otherwise, an error is thrown.
440 |
441 | --------------------------------------------------------------
442 |
443 | Usage: blob = cbor.__ENCODE_MAP[luatype](value,sref,stref)
444 | Desc: Encode a Lua type into a CBOR type
445 | Input: value (any) a Lua value who's type matches luatype.
446 | sref (table/optional) shared reference table
447 | stref (table/optional) shared string reference table
448 | Return: blob (binary) CBOR encoded data
449 |
450 | ==============================================================
451 |
452 | cbor.TYPE
453 |
454 | Both encoding and decoding functions for CBOR base types are in this table.
455 | The functions 'UINT' (unsigned integer), 'NINT' (negative integer), 'BIN'
456 | (binary string), 'TEXT' (UTF-8 encoded text), 'ARRAY' (a Lua array or
457 | sequence) and 'MAP' are used to encode the given type. The numeric entries
458 | are for decoding CBOR data.
459 |
460 | --------------------------------------------------------------
461 |
462 | Usage: blob = cbor.TYPE['name'](n,sref,stref)
463 | Desc: Encode a CBOR base type
464 | Input: n (integer string table) Lua type (see notes)
465 | sref (table/optional) shared reference table
466 | stref (table/optional) shared string reference table
467 | Return: blob (binary) CBOR encoded value
468 |
469 | Note: UINT and NINT take an integer.
470 |
471 | BIN and TEXT take a string. TEXT will check to see if
472 | the text is well formed UTF8 and throw an error if the
473 | text is not valid UTF8.
474 |
475 | ARRAY and MAP take a table of an appropriate type. No
476 | checking is done of the passed in table, so a table
477 | of just name/value pairs passed in to ARRAY will return
478 | an empty CBOR encoded array.
479 |
480 | TAG and SIMPLE encoding are handled elsewhere.
481 |
482 | If the data you have includes tables with cycles, you will need to
483 | pass in an empty table for the sref parameter, otherwise, you run
484 | the risk of blowing out the stack.
485 |
486 | The parameter stref is used to pass strings as references in the
487 | resulting CBOR. This will make the resulting CBOR data smaller, but
488 | make sure the receiving end can deal with string references.
489 |
490 | ARRAY and MAP references are separate from string referenes. It is
491 | also safe to use both with this module.
492 |
493 | --------------------------------------------------------------
494 |
495 | Usage: value2,pos2,ctype = cbor.TYPE[n](packet,pos,info,value,conv,ref)
496 | Desc: Decode a CBOR base type
497 | Input: packet (binary) binary blob of CBOR data
498 | pos (integer) byte offset in packet to start parsing from
499 | info (integer) CBOR info (0 .. 31)
500 | value (integer) CBOR decoded value
501 | conv (table) conversion table (passed to decode())
502 | ref (table) used to generate references (TAG types only)
503 | Return: value2 (any) decoded CBOR value
504 | pos2 (integer) byte offset just past parsed data
505 | ctype (enum/cbor) CBOR deocded type
506 |
507 | Note: tag_* is returned for any non-supported TAG types. The
508 | actual format is 'tag_' ---for example,
509 | 'tag_1234567890'. Supported TAG types will return the
510 | appropriate type name.
511 |
512 | simple is returned for any non-supported SIMPLE types.
513 | Supported simple types will return the appropriate type
514 | name.
515 |
516 | The ref parameter is not marked optional here---it's used internally
517 | to handle ARRAY/MAP and string references. Make sure you always
518 | pass in the same table (it should be empty initally) here for
519 | consistent results.
520 |
521 | ==============================================================
522 |
523 | cbor.TAG
524 |
525 | Encoding and decoding of CBOR TAG types are here. Like cbor.TYPE, the named
526 | entries are for encoding and the numbered entries are for decoding. Named
527 | types are:
528 |
529 | * _datetime datetime (TEXT)
530 | * _epoch see cbor.isnumber()
531 | * _pbignum positive bignum (BIN)
532 | * _nbignum negative bignum (BIN)
533 | * _decimalfraction ARRAY(integer exp, integer mantissa)
534 | * _bigfloat ARRAY(float exp,integer mantissa)
535 | * _tobase64url should be base64url encoded (BIN)
536 | * _tobase64 should be base64 encoded (BIN)
537 | * _tobase16 should be base16 encoded (BIN)
538 | * _cbor CBOR encoded data (BIN)
539 | * _url URL (TEXT)
540 | * _base64url base64url encoded data (TEXT)
541 | * _base64 base64 encoded data (TEXT)
542 | * _regex regex (TEXT)
543 | * _mime MIME encoded messsage (TEXT)
544 | * _magic_cbor itself (no data, used to self-describe CBOR data)
545 |
546 | * _nthstring shared string
547 | * _perlobj Perl serialized object
548 | * _serialobj Generic serialized object
549 | * _shareable sharable resource (ARRAY or MAP)
550 | * _sharedref reference (UINT)
551 | * _rational Rational number
552 | * _uuid UUID value (BIN)
553 | * _language Language-tagged string
554 | * _id Identifier
555 | * _stringref string reference
556 | * _bmime Binary MIME message
557 | * _decimalfractionexp like _decimalfraction, non-int exponent
558 | * _bigfloatexp like _bigfloat, non-int exponent
559 | * _indirection Indirection
560 | * _rains RAINS based message
561 |
562 | --------------------------------------------------------------
563 |
564 | Usage: blob = cbor.TAG['name'](value,sref,stref)
565 | Desc: Encode a CBOR tagged value
566 | Input: value (any) any Lua type
567 | sref (table/optional) shared reference table
568 | stref (table/optional) shared string reference table
569 | Return: blob (binary) CBOR encoded tagged value
570 |
571 | Note: Some tags only support a subset of Lua types.
572 |
573 | --------------------------------------------------------------
574 |
575 | Usage: value,pos2,ctype = cbor.TAG[n](packet,pos,conv,ref)
576 | Desc: Decode a CBOR tagged value
577 | Input: packet (binary) binary blob of CBOR tagged data
578 | pos (integer) byte offset into packet
579 | conv (table) conversion routines (passed to decode())
580 | ref (table) reference table
581 | Return: value (any) decoded CBOR tagged value
582 | pos2 (integer) byte offset just past parsed data
583 | ctype (enum/cbor) CBOR type of value
584 |
585 | ==============================================================
586 |
587 | cbor.SIMPLE
588 |
589 | Encoding and decoding of CBOR simple types are here. These are:
590 |
591 | * false false value (Lua false)
592 | * true true value (Lua true)
593 | * null NULL value (Lua nil)
594 | * undefined undefined value (Lua nil)
595 | * half half precicion IEEE 754 float
596 | * single single precision IEEE 754 float
597 | * double double precision IEEE 754 float
598 | * __break SEE NOTES
599 |
600 | --------------------------------------------------------------
601 |
602 | Usage: blob = cbor.SIMPLE['name'](n)
603 | Desc: Encode a CBOR simple type
604 | Input: n (number/optional) floating point number to encode (see notes)
605 | Return: blob (binary) CBOR encoded simple type
606 |
607 | Note: Some functions ignore the passed in parameter.
608 |
609 | WARNING! The functions that do not ignore the parameter may
610 | throw an error if floating point precision will be lost
611 | during the encoding. Please be aware of what you are doing
612 | when calling SIMPLE.half(), SIMPLE.float() or
613 | SIMPLE.double().
614 |
615 | --------------------------------------------------------------
616 |
617 | Usage: value2,pos,ctype = cbor.SIMPLE[n](pos,value)
618 | Desc: Decode a CBOR simple type
619 | Input: pos (integer) byte offset in packet
620 | value (number/optional) floating point number
621 | Return: value2 (any) decoded value as Lua value
622 | pos (integer) original pos passed in (see notes)
623 | ctype (enum/cbor) CBOR type of value
624 |
625 | Note: The pos parameter is passed in to avoid special cases in
626 | the code and to conform to all other decoding routines.
627 |
628 | *************************************************************
629 | *
630 | * org.conman.cbor_s
631 | *
632 | *************************************************************
633 |
634 | This module provides a simple (or small, take your pick) implementation of
635 | CBOR, which should be fine for most uses, as long as such uses don't really
636 | require the need of TAGS (although there is some minimal support for such)
637 | and as long as you stick to simple Lua types like nil, booleans, numbers,
638 | strings and tables of only simple Lua types and that have no cycles. This
639 | function defines four functions. No type checking is done when encoding
640 | tagged values.
641 |
642 | ==============================================================
643 |
644 | Usage: blob = cbor.encode(value[,tag])
645 | Desc: Encode a Lua type into a CBOR type
646 | Input: value (any)
647 | tag (number/optional) CBOR tag value
648 | Return: blob (binary) CBOR encoded value
649 |
650 | Note: This function can throw errors
651 |
652 | ==============================================================
653 |
654 | Usage: blob[,err] = cbor_s.pencode(value[,tag])
655 | Desc: Protected call to encode into CBOR
656 | Input: value (any)
657 | tag (number/optional) CBOR tag value
658 | Return: blob (binary) CBOR encoded value
659 | err (string/optional) error message
660 |
661 | ==============================================================
662 |
663 | Usage: value,pos2,ctype = cbor.decode(packet[,pos][,conv])
664 | Desc: Decode CBOR encoded data
665 | Input: packet (binary) CBOR binary blob
666 | pos (integer/optional) starting point for decoding
667 | conv (table/optional) table of tagged conversion routines
668 | Return: value (any) the decoded CBOR data
669 | pos2 (integer) offset past decoded data
670 | ctype (enum/cbor) CBOR type of value
671 |
672 | Note: The conversion table should be constructed as:
673 |
674 | {
675 | [ 0] = function(v) return munge(v) end,
676 | [32] = function(v) return munge(v) end,,
677 | }
678 |
679 | The keys are CBOR types (as integers). These functions are
680 | expected to convert the decoded CBOR type into a more
681 | appropriate type for your code. For instance, [1] (epoch)
682 | can be converted into a table.
683 |
684 | This function can throw errors.
685 |
686 | ==============================================================
687 |
688 | Usage: value,pos2,ctype[,err] = cbor.pdecode(packet[,pos][,conv])
689 | Desc: Protected call to decode CBOR data
690 | Input: packet (binary) CBOR binary blob
691 | pos (integer/optional) starting point for decoding
692 | conv (table/optional) table of tagged conversion routines
693 | Return: value (any) the decoded CBOR data, nil on error
694 | pos2 (integer) offset past decoded data, 0 on error
695 | ctype (enum/cbor) CBOR type of value
696 | err (string/optional) error message, if any
697 |
698 | *************************************************************
699 | *
700 | * org.conman.cbor_c
701 | *
702 | *************************************************************
703 |
704 | This module provides the foundation of the CBOR modules and is written in C.
705 | This module deals with the lowest level details of encoding and decoding
706 | CBOR data. It will encode data with the minimal encoding size [1]. This
707 | module provides just two functions, and it helps to be familiar with
708 | RFC-8949 to use this module properly.
709 |
710 | NOTE: Both functions can throw an error.
711 |
712 | [1] Floating point values will by default be encoded with the minimal
713 | encoding size without losing precision. It is possible to use a
714 | larger encoding if wanted.
715 |
716 | ==============================================================
717 |
718 | Usage: blob = cbor_c.encode(type,value[,value2])
719 | Desc: Encode a CBOR value
720 | Input: type (integer) CBOR type
721 | value (number) value to encode (see note)
722 | value (number/optional) float to encode (see note)
723 | Return: blob (binary) CBOR encoded value
724 |
725 | Note: value is optional for type of 0xE0.
726 | value2 is optional for type of 0xE0; otherwise it's ignored.
727 |
728 | To encode a break value:
729 | blob = cbor_c.encode(0xE0)
730 |
731 | To encode a floating point value with a minimal encoding:
732 | blob = cbor_c.encode(0xE0,nil,1.23)
733 |
734 | To force a particular encoding of a float value:
735 | blob = cbor_c.encode(0xE0,27,math.huge)
736 |
737 | To encode an array of indeterminate length:
738 | blob = cbor_c.encode(0xA0)
739 | -- encode entries
740 | blob = blob .. cbor_c.encode(0xE0)
741 |
742 | ==============================================================
743 |
744 | Usage: ctype,info,value,pos2 = cbor_c.decode(blob,pos)
745 | Desc: Decode a CBOR-encoded value
746 | Input: blob (binary) binary CBOR sludge
747 | pos (integer) position to start decoding from
748 | Return: ctype (integer) CBOR major type
749 | info (integer) sub-major type information
750 | value (integer number) decoded value
751 | pos2 (integer) position past decoded data
752 |
753 | Note: Throws in invalid parameter
754 |
755 | *************************************************************
756 | *
757 | * org.conman.cbormisc
758 | *
759 | *************************************************************
760 |
761 | This module contains miscellaneous routines related to CBOR. Currently, two
762 | functions are defined, and these are not required for normal CBOR usage.
763 |
764 | ==============================================================
765 |
766 | Usage: diag = cbormisc.diagnostic(packet[,pos])
767 | Desc: Output CBOR encoded data in the CBOR diagnostic output format
768 | Input: packet (binary) CBOR encoded data
769 | pos (integer/optional) starting point for decoding
770 | Return: diag (string) CBOR data in CBOR diagnostic format
771 |
772 | Note: This function can throw errors
773 |
774 | ==============================================================
775 |
776 | Usage: diag[,err] = cbormisc.pdiagnostic(packet[,pos])
777 | Desc: Protected call to cbormisc.diagnostic
778 | Input: packet (binary) CBOR encoded data
779 | pos (integer/optional) starting point for decoding
780 | Return: diag (string) CBOR data in CBOR diagnostic format
781 | err (string/optional) error message if any
782 |
--------------------------------------------------------------------------------
/cbor.lua:
--------------------------------------------------------------------------------
1 | -- ***************************************************************
2 | --
3 | -- Copyright 2016 by Sean Conner. All Rights Reserved.
4 | --
5 | -- This library is free software; you can redistribute it and/or modify it
6 | -- under the terms of the GNU Lesser General Public License as published by
7 | -- the Free Software Foundation; either version 3 of the License, or (at your
8 | -- option) any later version.
9 | --
10 | -- This library is distributed in the hope that it will be useful, but
11 | -- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | -- License for more details.
14 | --
15 | -- You should have received a copy of the GNU Lesser General Public License
16 | -- along with this library; if not, see .
17 | --
18 | -- Comments, questions and criticisms can be sent to: sean@conman.org
19 | --
20 | -- ====================================================================
21 | --
22 | -- Module: cbor
23 | --
24 | -- Desc: Decodes CBOR data.
25 | --
26 | -- Types:
27 | -- cbor (enum)
28 | -- *** base types
29 | -- * UINT unsigned integer (Lua number)
30 | -- * NINT negative integer (Lua number)
31 | -- * BIN binary string (Lua string)
32 | -- * TEXT UTF-8 string (Lua string)
33 | -- * ARRAY value is item count (Lua number)
34 | -- * MAP value is item count (Lua number)
35 | -- *** simple types
36 | -- * SIMPLE SEE NOTES (Lua number)
37 | -- * false false value (Lua false)
38 | -- * true true value (Lua true)
39 | -- * null NULL value (Lua nil)
40 | -- * undefined undefined value (Lua nil)
41 | -- * half half precicion IEEE 754 float
42 | -- * single single precision IEEE 754 float
43 | -- * double double precision IEEE 754 float
44 | -- * __break SEE NOTES
45 | -- *** tagged types
46 | -- * TAG_* unsupported tag type (Lua number)
47 | -- * _datetime datetime (TEXT)
48 | -- * _epoch see cbor.isnumber()
49 | -- * _pbignum positive bignum (BIN)
50 | -- * _nbignum negative bignum (BIN)
51 | -- * _decimalfraction ARRAY(integer exp, integer mantissa)
52 | -- * _bigfloat ARRAY(float exp,integer mantissa)
53 | -- * _tobase64url should be base64url encoded (BIN)
54 | -- * _tobase64 should be base64 encoded (BIN)
55 | -- * _tobase16 should be base16 encoded (BIN)
56 | -- * _cbor CBOR encoded data (BIN)
57 | -- * _url URL (TEXT)
58 | -- * _base64url base64url encoded data (TEXT)
59 | -- * _base64 base64 encoded data (TEXT)
60 | -- * _regex regex (TEXT)
61 | -- * _mime MIME encoded messsage (TEXT)
62 | -- * _magic_cbor itself (no data, used to self-describe CBOR data)
63 | -- ** more tagged types, extensions
64 | -- * _nthstring shared string
65 | -- * _perlobj Perl serialized object
66 | -- * _serialobj Generic serialized object
67 | -- * _shareable sharable resource (ARRAY or MAP)
68 | -- * _sharedref reference (UINT)
69 | -- * _rational Rational number
70 | -- * _uuid UUID value (BIN)
71 | -- * _language Language-tagged string
72 | -- * _id Identifier
73 | -- * _stringref string reference
74 | -- * _bmime Binary MIME message
75 | -- * _decimalfractionexp like _decimalfraction, non-int exponent
76 | -- * _bigfloatexp like _bigfloat, non-int exponent
77 | -- * _indirection Indirection
78 | -- * _rains RAINS message
79 | -- * _ipaddress IP address (or MAC address)
80 | -- *** Lua CBOR library types
81 | -- * __error error parsing (TEXT)
82 | -- data (any) decoded CBOR data
83 | -- pos (integer) position parsing stopped
84 | --
85 | -- NOTES: The simple type is returned for non-defined simple types.
86 | --
87 | -- The __break type is used to indicate the end of an
88 | -- indefinite array or map.
89 | --
90 | -- luacheck: globals isnumber isinteger isfloat decode encode pdecode pencode
91 | -- luacheck: globals TYPE TAG SIMPLE _VERSION __ENCODE_MAP _ENV
92 | -- luacheck: globals null undefined
93 | -- luacheck: ignore 611
94 | -- ********************************************************************
95 |
96 | local math = require "math"
97 | local string = require "string"
98 | local table = require "table"
99 | local lpeg = require "lpeg"
100 | local cbor_c = require "org.conman.cbor_c"
101 |
102 | local LUA_VERSION = _VERSION
103 | local error = error
104 | local pcall = pcall
105 | local assert = assert
106 | local getmetatable = getmetatable
107 | local setmetatable = setmetatable
108 | local pairs = pairs
109 | local ipairs = ipairs
110 | local type = type
111 | local tonumber = tonumber
112 |
113 | if LUA_VERSION < "Lua 5.3" then
114 | function math.type(n)
115 | return n >= -9007199254740992
116 | and n <= 9007199254740992
117 | and n % 1 == 0
118 | and 'integer'
119 | or 'float'
120 | end
121 | end
122 |
123 | if LUA_VERSION == "Lua 5.1" then
124 | module "org.conman.cbor" -- luacheck: ignore
125 | else
126 | _ENV = {} -- luacheck: ignore
127 | end
128 |
129 | _VERSION = cbor_c._VERSION
130 |
131 | -- ***********************************************************************
132 | -- UTF-8 defintion from RFC-3629. There's a deviation from the RFC
133 | -- specification in that I only allow certain codes from the US-ASCII C0
134 | -- range (control codes) that are in common use.
135 | -- ***********************************************************************
136 |
137 | local UTF8 = (
138 | lpeg.R("\7\13"," ~")
139 | + lpeg.R("\194\223") * lpeg.R("\128\191")
140 | + lpeg.P("\224") * lpeg.R("\160\191") * lpeg.R("\128\191")
141 | + lpeg.R("\225\236") * lpeg.R("\128\191") * lpeg.R("\128\191")
142 | + lpeg.P("\237") * lpeg.R("\128\159") * lpeg.R("\128\191")
143 | + lpeg.R("\238\239") * lpeg.R("\128\191") * lpeg.R("\128\191")
144 | + lpeg.P("\240") * lpeg.R("\144\191") * lpeg.R("\128\191") * lpeg.R("\128\191")
145 | + lpeg.R("\241\243") * lpeg.R("\128\191") * lpeg.R("\128\191") * lpeg.R("\128\191")
146 | + lpeg.P("\224") * lpeg.R("\128\142") * lpeg.R("\128\191") * lpeg.R("\128\191")
147 | )^0
148 |
149 | -- ***********************************************************************
150 |
151 | local function throw(pos,...)
152 | error( { pos = pos , msg = string.format(...) } , 2)
153 | end
154 |
155 | -- ***********************************************************************
156 | -- Usage: bool = cbor.isnumber(ctype)
157 | -- Desc: returns true of the given CBOR type is a number
158 | -- Input: type (enum/cbor) CBOR type
159 | -- Return: bool (boolean) true if number, false otherwise
160 | -- ***********************************************************************
161 |
162 | function isnumber(ctype)
163 | return ctype == 'UINT'
164 | or ctype == 'NINT'
165 | or ctype == 'half'
166 | or ctype == 'single'
167 | or ctype == 'double'
168 | end
169 |
170 | -- ***********************************************************************
171 | -- Usage: bool = cbor.isinteger(ctype)
172 | -- Desc: returns true if the given CBOR type is an integer
173 | -- Input: ctype (enum/cbor) CBOR type
174 | -- Return: bool (boolean) true if number, false othersise
175 | -- ***********************************************************************
176 |
177 | function isinteger(ctype)
178 | return ctype == 'UINT'
179 | or ctype == 'NINT'
180 | end
181 |
182 | -- ***********************************************************************
183 | -- Usage: bool = cbor.isfloat(ctype)
184 | -- Desc: returns true if the given CBOR type is a float
185 | -- Input: ctype (enum/cbor) CBOR type
186 | -- Return: bool (boolean) true if number, false otherwise
187 | -- ***********************************************************************
188 |
189 | function isfloat(ctype)
190 | return ctype == 'half'
191 | or ctype == 'single'
192 | or ctype == 'double'
193 | end
194 |
195 | -- ***********************************************************************
196 | -- usage: len = mstrlen(ref)
197 | -- desc: This function returns the minimum length a string should
198 | -- have to find its reference (see notes)
199 | -- input: ref (table) reference table
200 | -- return: len (integer) minimum string length for reference
201 | --
202 | -- note: via http://cbor.schmorp.de/stringref
203 | -- ***********************************************************************
204 |
205 | local function mstrlen(ref)
206 | if #ref < 24 then
207 | return 3
208 | elseif #ref < 256 then
209 | return 4
210 | elseif #ref < 65536 then
211 | return 5
212 | elseif #ref < 4294967296 then
213 | return 7
214 | else
215 | return 11
216 | end
217 | end
218 |
219 | -- ***********************************************************************
220 | -- usage: value2,pos2,ctype2 = decbintext(packet,pos,info,value,conv,ref,ctype)
221 | -- desc: Decode a CBOR BIN or CBOR TEXT into a Lua string
222 | -- input: packet (binary) binary blob
223 | -- pos (integer) byte position in packet
224 | -- info (integer) CBOR info value (0..31)
225 | -- value (integer) string length
226 | -- conv (table) conversion routines (passed to decode())
227 | -- ref (table) reference table
228 | -- ctype (enum/cbor) 'BIN' or 'TEXT'
229 | -- return: value2 (string) string from packet
230 | -- pos2 (integer) position past string just extracted
231 | -- ctype2 (enum/cbor) 'BIN' or 'TEXT'
232 | -- ***********************************************************************
233 |
234 | local function decbintext(packet,pos,info,value,conv,ref,ctype)
235 |
236 | -- ----------------------------------------------------------------------
237 | -- Support for _stringref and _nthstring tags [1]. Strings shorter than
238 | -- the reference mark will NOT be encoded, so these strings will not have
239 | -- a reference upon decoding.
240 | --
241 | -- [1] http://cbor.schmorp.de/stringref
242 | -- ----------------------------------------------------------------------
243 |
244 | if info < 31 then
245 | local data = packet:sub(pos,pos + value - 1)
246 |
247 | -- --------------------------------------------------
248 | -- make sure the string is long enough to reference
249 | -- --------------------------------------------------
250 |
251 | if not ref._stringref[data] then
252 | if #data >= mstrlen(ref._stringref) then
253 | table.insert(ref._stringref,{ ctype = ctype , value = data })
254 | ref._stringref[data] = true
255 | end
256 | end
257 | return data,pos + value,ctype
258 |
259 | else
260 | local acc = {}
261 | local t,nvalue
262 |
263 | while true do
264 | nvalue,pos,t = decode(packet,pos,conv,ref)
265 | if t == '__break' then
266 | break;
267 | end
268 | if t ~= ctype then
269 | throw(pos,"%s: expecting %s, got %s",ctype,ctype,t)
270 | end
271 | table.insert(acc,nvalue)
272 | end
273 |
274 | return table.concat(acc),pos,ctype
275 | end
276 | end
277 |
278 |
279 | -- ***********************************************************************
280 | -- usage: blob = encbintext(value,sref,stref,ctype)
281 | -- desc: Encode a string into a CBOR BIN or TYPE
282 | -- input: value (string) Lua string to encode
283 | -- sref (table) shared references
284 | -- stref (table) string references
285 | -- ctype (integer) either 0x40 (BIN) or 0x60 (TEXT)
286 | -- return: blob (binary) encoded string
287 | -- ***********************************************************************
288 |
289 | local function encbintext(value,sref,stref,ctype)
290 | if stref then
291 | if not stref[value] then
292 | if #value >= mstrlen(stref) then
293 | table.insert(stref,value)
294 | stref[value] = #stref - 1
295 | end
296 | else
297 | return TAG._nthstring(stref[value],sref,stref)
298 | end
299 | end
300 |
301 | return cbor_c.encode(ctype,#value) .. value
302 | end
303 |
304 | -- ***********************************************************************
305 | --
306 | -- CBOR base TYPES
307 | --
308 | -- Both encoding and decoding functions for CBOR base types are here.
309 | --
310 | -- Usage: blob = cbor.TYPE['name'](n,sref,stref)
311 | -- Desc: Encode a CBOR base type
312 | -- Input: n (integer string table) Lua type (see notes)
313 | -- sref (table/optional) shared reference table
314 | -- stref (table/optional) shared string reference table
315 | -- Return: blob (binary) CBOR encoded value
316 | --
317 | -- Note: UINT and NINT take an integer.
318 | --
319 | -- BIN and TEXT take a string. TEXT will check to see if
320 | -- the text is well formed UTF8 and throw an error if the
321 | -- text is not valid UTF8.
322 | --
323 | -- ARRAY and MAP take a table of an appropriate type. No
324 | -- checking is done of the passed in table, so a table
325 | -- of just name/value pairs passed in to ARRAY will return
326 | -- an empty CBOR encoded array.
327 | --
328 | -- TAG and SIMPLE encoding are handled elsewhere.
329 | --
330 | -- Usage: value2,pos2,ctype = cbor.TYPE[n](packet,pos,info,value,conv,ref)
331 | -- Desc: Decode a CBOR base type
332 | -- Input: packet (binary) binary blob of CBOR data
333 | -- pos (integer) byte offset in packet to start parsing from
334 | -- info (integer) CBOR info (0 .. 31)
335 | -- value (integer) CBOR decoded value
336 | -- conv (table) conversion table (passed to decode())
337 | -- ref (table) used to generate references (TAG types only)
338 | -- Return: value2 (any) decoded CBOR value
339 | -- pos2 (integer) byte offset just past parsed data
340 | -- ctype (enum/cbor) CBOR deocded type
341 | --
342 | -- Note: tag_* is returned for any non-supported TAG types. The
343 | -- actual format is 'tag_' ---for example,
344 | -- 'tag_1234567890'. Supported TAG types will return the
345 | -- appropriate type name.
346 | --
347 | -- simple is returned for any non-supported SIMPLE types.
348 | -- Supported simple types will return the appropriate type
349 | -- name.
350 | --
351 | -- ***********************************************************************
352 |
353 | TYPE =
354 | {
355 | UINT = function(n)
356 | return cbor_c.encode(0x00,n)
357 | end,
358 |
359 | [0x00] = function(_,pos,info,value)
360 | if info == 31 then throw(pos,"invalid data") end
361 | return value,pos,'UINT'
362 | end,
363 |
364 | -- =====================================================================
365 |
366 | NINT = function(n)
367 | return cbor_c.encode(0x20,-1 - n)
368 | end,
369 |
370 | [0x20] = function(_,pos,info,value)
371 | if info == 31 then throw(pos,"invalid data") end
372 | return -1 - value,pos,'NINT'
373 | end,
374 |
375 | -- =====================================================================
376 |
377 | BIN = function(b,sref,stref)
378 | if not b then
379 | return "\95"
380 | else
381 | return encbintext(b,sref,stref,0x40)
382 | end
383 | end,
384 |
385 | [0x40] = function(packet,pos,info,value,conv,ref)
386 | return decbintext(packet,pos,info,value,conv,ref,'BIN')
387 | end,
388 |
389 | -- =====================================================================
390 |
391 | TEXT = function(s,sref,stref)
392 | if not s then
393 | return "\127"
394 | else
395 | assert(UTF8:match(s) > #s,"TEXT: not UTF-8 text")
396 | return encbintext(s,sref,stref,0x60)
397 | end
398 | end,
399 |
400 | [0x60] = function(packet,pos,info,value,conv,ref)
401 | return decbintext(packet,pos,info,value,conv,ref,'TEXT')
402 | end,
403 |
404 | -- =====================================================================
405 |
406 | ARRAY = function(array,sref,stref)
407 | if not array then
408 | return "\159"
409 | elseif type(array) == 'number' then
410 | return cbor_c.encode(0x80,array)
411 | end
412 |
413 | local res = ""
414 |
415 | if sref then
416 | if sref[array] then
417 | return TAG._sharedref(sref[array],sref,stref)
418 | end
419 |
420 | res = TAG._shareable(array)
421 | table.insert(sref,array)
422 | sref[array] = #sref - 1
423 | end
424 |
425 | res = res .. cbor_c.encode(0x80,#array)
426 | for _,item in ipairs(array) do
427 | res = res .. encode(item,sref,stref)
428 | end
429 | return res
430 | end,
431 |
432 | [0x80] = function(packet,pos,_,value,conv,ref)
433 |
434 | -- ---------------------------------------------------------------------
435 | -- Per [1], shared references need to exist before the decoding process.
436 | -- ref._sharedref.REF will be such a reference. If it doesn't exist,
437 | -- then just create a table.
438 | --
439 | -- [1] http://cbor.schmorp.de/value-sharing
440 | -- ---------------------------------------------------------------------
441 |
442 | local acc = ref._sharedref.REF or {}
443 |
444 | for i = 1 , value do
445 | local avalue,npos,ctype = decode(packet,pos,conv,ref)
446 | if ctype == '__break' then return acc,npos,'ARRAY' end
447 | acc[i] = avalue
448 | pos = npos
449 | end
450 | return acc,pos,'ARRAY'
451 | end,
452 |
453 | -- =====================================================================
454 |
455 | MAP = function(map,sref,stref)
456 | if not map then
457 | return "\191"
458 | elseif type(map) == 'number' then
459 | return cbor_c.encode(0xA0,map)
460 | end
461 |
462 | local ref = ""
463 |
464 | if sref then
465 | if sref[map] then
466 | return TAG._sharedref(sref[map],sref,stref)
467 | end
468 |
469 | ref = TAG._shareable(map)
470 | table.insert(sref,map)
471 | sref[map] = #sref - 1
472 | end
473 |
474 | local res = ""
475 | local cnt = 0
476 |
477 | for key,value in pairs(map) do
478 | res = res .. encode(key,sref,stref)
479 | res = res .. encode(value,sref,stref)
480 | cnt = cnt + 1
481 | end
482 |
483 | return ref .. cbor_c.encode(0xA0,cnt) .. res
484 | end,
485 |
486 | [0xA0] = function(packet,pos,_,value,conv,ref)
487 | local acc = ref._sharedref.REF or {} -- see comment above
488 | for _ = 1 , value do
489 | local nvalue,npos,nctype = decode(packet,pos,conv,ref,true)
490 | if nctype == '__break' then return acc,npos,'MAP' end
491 | local vvalue,npos2 = decode(packet,npos,conv,ref)
492 | acc[nvalue] = vvalue
493 | pos = npos2
494 | end
495 | return acc,pos,'MAP'
496 | end,
497 |
498 | -- =====================================================================
499 |
500 | [0xC0] = function(packet,pos,info,value,conv,ref)
501 | if info == 31 then throw(pos,"invalid data") end
502 | return TAG[value](packet,pos,conv,ref)
503 | end,
504 |
505 | -- =====================================================================
506 |
507 | [0xE0] = function(_,pos,info,value)
508 | return SIMPLE[info](pos,value)
509 | end,
510 | }
511 |
512 | -- ***********************************************************************
513 | --
514 | -- CBOR TAG values
515 | --
516 | -- Encoding and decoding of CBOR TAG types are here.
517 | --
518 | -- Usage: blob = cbor.TAG['name'](value,sref,stref)
519 | -- Desc: Encode a CBOR tagged value
520 | -- Input: value (any) any Lua type
521 | -- sref (table/optional) shared reference table
522 | -- stref (table/optional) shared string reference table
523 | -- Return: blob (binary) CBOR encoded tagged value
524 | --
525 | -- Note: Some tags only support a subset of Lua types.
526 | --
527 | -- Usage: value,pos2,ctype = cbor.TAG[n](packet,pos,conv,ref)
528 | -- Desc: Decode a CBOR tagged value
529 | -- Input: packet (binary) binary blob of CBOR tagged data
530 | -- pos (integer) byte offset into packet
531 | -- conv (table) conversion routines (passed to decode())
532 | -- ref (table) reference table
533 | -- Return: value (any) decoded CBOR tagged value
534 | -- pos2 (integer) byte offset just past parsed data
535 | -- ctype (enum/cbor) CBOR type of value
536 | --
537 | -- ***********************************************************************
538 |
539 | TAG = setmetatable(
540 | {
541 | _datetime = function(value)
542 | return cbor_c.encode(0xC0,0) .. TYPE.TEXT(value)
543 | end,
544 |
545 | [0] = function(packet,pos,conv,ref)
546 | local value,npos,ctype = decode(packet,pos,conv,ref)
547 | if ctype == 'TEXT' then
548 | return value,npos,'_datetime'
549 | else
550 | throw(pos,"_datetime: wanted TEXT, got %s",ctype)
551 | end
552 | end,
553 |
554 | -- =====================================================================
555 |
556 | _epoch = function(value,sref,stref)
557 | assert(type(value) == 'number',"_epoch expects a number")
558 | return cbor_c.encode(0xC0,1) .. encode(value,sref,stref)
559 | end,
560 |
561 | [1] = function(packet,pos,conv,ref)
562 | local value,npos,ctype = decode(packet,pos,conv,ref)
563 | if isnumber(ctype) then
564 | return value,npos,'_epoch'
565 | else
566 | throw(pos,"_epoch: wanted number, got %s",ctype)
567 | end
568 | end,
569 |
570 | -- =====================================================================
571 |
572 | _pbignum = function(value,sref,stref)
573 | return cbor_c.encode(0xC0,2) .. TYPE.BIN(value,sref,stref)
574 | end,
575 |
576 | [2] = function(packet,pos,conv,ref)
577 | local value,npos,ctype = decode(packet,pos,conv,ref)
578 | if ctype == 'BIN' then
579 | return value,npos,'_pbignum'
580 | else
581 | throw(pos,"_pbignum: wanted BIN, got %s",ctype)
582 | end
583 | end,
584 |
585 | -- =====================================================================
586 |
587 | _nbignum = function(value,sref,stref)
588 | return cbor_c.encode(0xC0,3) .. TYPE.BIN(value,sref,stref)
589 | end,
590 |
591 | [3] = function(packet,pos,conv,ref)
592 | local value,npos,ctype = decode(packet,pos,conv,ref)
593 | if ctype == 'BIN' then
594 | return value,npos,'_nbignum'
595 | else
596 | throw(pos,"_nbignum: wanted BIN, got %s",ctype)
597 | end
598 | end,
599 |
600 | -- =====================================================================
601 |
602 | _decimalfraction = function(value,sref,stref)
603 | assert(type(value) == 'table', "_decimalfractoin expects an array")
604 | assert(#value == 2, "_decimalfraction expects a two item array")
605 | assert(math.type(value[1]) == 'integer',"_decimalfraction expects integer as first element")
606 | assert(math.type(value[2]) == 'integer',"_decimalfraction expects integer as second element")
607 | return cbor_c.encode(0xC0,4) .. TYPE.ARRAY(value,sref,stref)
608 | end,
609 |
610 | [4] = function(packet,pos,conv,ref)
611 | local value,npos,ctype = decode(packet,pos,conv,ref)
612 |
613 | if ctype ~= 'ARRAY' then
614 | throw(pos,"_decimalfraction: wanted ARRAY, got %s",ctype)
615 | end
616 |
617 | if #value ~= 2 then
618 | throw(pos,"_decimalfraction: wanted ARRAY[2], got ARRAY[%d]",#value)
619 | end
620 |
621 | if math.type(value[1]) ~= 'integer' then
622 | throw(pos,"_decimalfraction: wanted integer for exp, got %s",type(value[1]))
623 | end
624 |
625 | if math.type(value[2]) ~= 'integer' then
626 | throw(pos,"_decimalfraction: wanted integer for mantissa, got %s",type(value[2]))
627 | end
628 |
629 | return value,npos,'_decimalfraction'
630 | end,
631 |
632 | -- =====================================================================
633 |
634 | _bigfloat = function(value,sref,stref)
635 | assert(type(value) == 'table', "_bigfloat expects an array")
636 | assert(#value == 2, "_bigfloat expects a two item array")
637 | assert(math.type(value[1]) == 'integer',"_bigfloat expects an integer as first element")
638 | assert(math.type(value[2]) == 'integer',"_bigfloat expects an integer as second element")
639 | return cbor_c.encode(0xC0,5) .. TYPE.ARRAY(value,sref,stref)
640 | end,
641 |
642 | [5] = function(packet,pos,conv,ref)
643 | local value,npos,ctype = decode(packet,pos,conv,ref)
644 |
645 | if ctype ~= 'ARRAY' then
646 | throw(pos,"_bigfloat: wanted ARRAY, got %s",ctype)
647 | end
648 |
649 | if #value ~= 2 then
650 | throw(pos,"_bigfloat: wanted ARRAY[2], got ARRAY[%s]",value)
651 | end
652 |
653 | if type(value[1]) ~= 'number' then
654 | throw(pos,"_bigfloat: wanted number for exp, got %s",ctype)
655 | end
656 |
657 | if math.type(value[2]) ~= 'integer' then
658 | throw(pos,"_bigfloat: wanted integer for mantissa, got %s",ctype)
659 | end
660 |
661 | return value,npos,'_bigfloat'
662 | end,
663 |
664 | -- =====================================================================
665 |
666 | _tobase64url = function(value,sref,stref)
667 | return cbor_c.encode(0xC0,21) .. encode(value,sref,stref)
668 | end,
669 |
670 | [21] = function(packet,pos,conv,ref)
671 | local value,npos = decode(packet,pos,conv,ref)
672 | return value,npos,'_tobase64url'
673 | end,
674 |
675 | -- =====================================================================
676 |
677 | _tobase64 = function(value,sref,stref)
678 | return cbor_c.encode(0xC0,22) .. encode(value,sref,stref)
679 | end,
680 |
681 | [22] = function(packet,pos,conv,ref)
682 | local value,npos = decode(packet,pos,conv,ref)
683 | return value,npos,'_tobase64'
684 | end,
685 |
686 | -- =====================================================================
687 |
688 | _tobase16 = function(value,sref,stref)
689 | return cbor_c.encode(0xC0,23) .. encode(value,sref,stref)
690 | end,
691 |
692 | [23] = function(packet,pos,conv,ref)
693 | local value,npos = decode(packet,pos,conv,ref)
694 | return value,npos,'_tobase16'
695 | end,
696 |
697 | -- =====================================================================
698 |
699 | _cbor = function(value,sref,stref)
700 | return cbor_c.encode(0xC0,24) .. TYPE.BIN(value,sref,stref)
701 | end,
702 |
703 | [24] = function(packet,pos,conv,ref)
704 | local value,npos,ctype = decode(packet,pos,conv,ref)
705 | if ctype == 'BIN' then
706 | return value,npos,'_cbor'
707 | else
708 | throw(pos,"_cbor: wanted BIN, got %s",ctype)
709 | end
710 | end,
711 |
712 | -- =====================================================================
713 |
714 | _url = function(value,sref,stref)
715 | return cbor_c.encode(0xC0,32) .. TYPE.TEXT(value,sref,stref)
716 | end,
717 |
718 | [32] = function(packet,pos,conv,ref)
719 | local value,npos,ctype = decode(packet,pos,conv,ref)
720 | if ctype == 'TEXT' then
721 | return value,npos,'_url'
722 | else
723 | throw(pos,"_url: wanted TEXT, got %s",ctype)
724 | end
725 | end,
726 |
727 | -- =====================================================================
728 |
729 | _base64url = function(value,sref,stref)
730 | return cbor_c.encode(0xC0,33) .. TYPE.TEXT(value,sref,stref)
731 | end,
732 |
733 | [33] = function(packet,pos,conv,ref)
734 | local value,npos,ctype = decode(packet,pos,conv,ref)
735 | if ctype == 'TEXT' then
736 | return value,npos,'_base64url'
737 | else
738 | throw(pos,"_base64url: wanted TEXT, got %s",ctype)
739 | end
740 | end,
741 |
742 | -- =====================================================================
743 |
744 | _base64 = function(value,sref,stref)
745 | return cbor_c.encode(0xC0,34) .. TYPE.TEXT(value,sref,stref)
746 | end,
747 |
748 | [34] = function(packet,pos,conv,ref)
749 | local value,npos,ctype = decode(packet,pos,conv,ref)
750 | if ctype == 'TEXT' then
751 | return value,npos,'_base64'
752 | else
753 | throw(pos,"_base64: wanted TEXT, got %s",ctype)
754 | end
755 | end,
756 |
757 | -- =====================================================================
758 |
759 | _regex = function(value,sref,stref)
760 | return cbor_c.encode(0xC0,35) .. TYPE.TEXT(value,sref,stref)
761 | end,
762 |
763 | [35] = function(packet,pos,conv,ref)
764 | local value,npos,ctype = decode(packet,pos,conv,ref)
765 | if ctype == 'TEXT' then
766 | return value,npos,'_regex'
767 | else
768 | throw(pos,"_regex: wanted TEXT, got %s",ctype)
769 | end
770 | end,
771 |
772 | -- =====================================================================
773 |
774 | _mime = function(value,sref,stref)
775 | return cbor_c.encode(0xC0,36) .. TYPE.TEXT(value,sref,stref)
776 | end,
777 |
778 | [36] = function(packet,pos,conv,ref)
779 | local value,npos,ctype = decode(packet,pos,conv,ref)
780 | if ctype == 'TEXT' then
781 | return value,npos,'_mime'
782 | else
783 | throw(pos,"_mime: wanted TEXT, got %s",ctype)
784 | end
785 | end,
786 |
787 | -- =====================================================================
788 |
789 | _magic_cbor = function()
790 | return cbor_c.encode(0xC0,55799)
791 | end,
792 |
793 | [55799] = function(_,pos)
794 | return '_magic_cbor',pos,'_magic_cbor'
795 | end,
796 |
797 | -- **********************************************************
798 | -- Following defined by IANA
799 | -- http://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
800 | -- **********************************************************
801 |
802 | _nthstring = function(value,sref,stref)
803 | return cbor_c.encode(0xC0,25) .. encode(value,sref,stref)
804 | end,
805 |
806 | [25] = function(packet,pos,conv,ref)
807 | local value,npos,ctype = decode(packet,pos,conv,ref)
808 | if ctype == 'UINT' then
809 | value = value + 1
810 | if not ref._stringref[value] then
811 | throw(pos,"_nthstring: invalid index %d",value - 1)
812 | end
813 | return ref._stringref[value].value,npos,ref._stringref[value].ctype
814 | else
815 | throw(pos,"_nthstring: wanted UINT, got %s",ctype)
816 | end
817 | end,
818 |
819 | -- =====================================================================
820 |
821 | _perlobj = function(value,sref,stref)
822 | return cbor_c.encode(0xC0,26) .. TYPE.ARRAY(value,sref,stref)
823 | end,
824 |
825 | [26] = function(packet,pos,conv,ref)
826 | local value,npos,ctype = decode(packet,pos,conv,ref)
827 | if ctype == 'ARRAY' then
828 | return value,npos,'_perlobj'
829 | else
830 | throw(pos,"_perlobj: wanted ARRAY, got %s",ctype)
831 | end
832 | end,
833 |
834 | -- =====================================================================
835 |
836 | _serialobj = function(value,sref,stref)
837 | return cbor_c.encode(0xC0,27) .. TYPE.ARRAY(value,sref,stref)
838 | end,
839 |
840 | [27] = function(packet,pos,conv,ref)
841 | local value,npos,ctype = decode(packet,pos,conv,ref)
842 | if ctype == 'ARRAY' then
843 | return value,npos,'_serialobj'
844 | else
845 | throw(pos,"_serialobj: wanted ARRAY, got %s",ctype)
846 | end
847 | end,
848 |
849 | -- =====================================================================
850 | -- To cut down on the silliness, not all types are shareable, only
851 | -- ARRAYs and MAPs will be supported. TEXT and BIN have their own
852 | -- reference system; sharing UINT, NINT or SIMPLE just doesn't make
853 | -- sense, and TAGs aren't shareable either. So ARRAY and MAP it is!
854 | -- =====================================================================
855 |
856 | _shareable = function(value)
857 | assert(type(value) == 'table',"_shareable: expects a table")
858 | return cbor_c.encode(0xC0,28)
859 | end,
860 |
861 | [28] = function(packet,pos,conv,ref)
862 | ref._sharedref.REF = {}
863 | table.insert(ref._sharedref,{ value = ref._sharedref.REF })
864 | local value,npos,ctype = decode(packet,pos,conv,ref)
865 | if ctype == 'ARRAY' or ctype == 'MAP' then
866 | ref._sharedref[#ref._sharedref].ctype = ctype
867 | return value,npos,ctype
868 | else
869 | throw(pos,"_shareable: wanted ARRAY or MAP, got %s",ctype)
870 | end
871 | end,
872 |
873 | -- =====================================================================
874 |
875 | _sharedref = function(value,sref,stref)
876 | return cbor_c.encode(0xC0,29) .. encode(value,sref,stref)
877 | end,
878 |
879 | [29] = function(packet,pos,conv,ref)
880 | local value,npos,ctype = decode(packet,pos,conv,ref)
881 | if ctype == 'UINT' then
882 | value = value + 1
883 | if not ref._sharedref[value] then
884 | throw(pos,"_sharedref: invalid index %d",value - 1)
885 | end
886 | return ref._sharedref[value].value,npos,ref._sharedref[value].ctype
887 | else
888 | throw(pos,"_sharedref: wanted ARRAY or MAP, got %s",ctype)
889 | end
890 | end,
891 |
892 | -- =====================================================================
893 |
894 | _rational = function(value,sref,stref)
895 | -- -----------------------------------------------------------------
896 | -- Per spec [1], the first value must be an integer (positive or
897 | -- negative) and the second value must be a positive integer greater
898 | -- than 0. Since I'm don't know the format for bignums, there's
899 | -- little error checking if those are in use. That's the way things
900 | -- go.
901 | --
902 | -- The encoding phase is done by hand for this. Here we go ...
903 | -- -----------------------------------------------------------------
904 |
905 | assert(type(value) == 'table',"_rational: expecting a table")
906 | assert(#value == 2,"_rational: expecting a table of two values")
907 | assert(math.type(value[1]) == 'integer' or type(value[1] == 'string'),"_rational: bad numerator")
908 | assert(
909 | math.type(value[2]) == 'integer' and value[2] > 0
910 | or type(value[2]) == 'string',
911 | "_rational: bad denominator"
912 | )
913 |
914 | local res = cbor_c.encode(0xC0,30) .. cbor_c.encode(0x80,2)
915 |
916 | if math.type(value[1]) == 'integer' then
917 | res = res .. __ENCODE_MAP.number(value[1],sref,stref)
918 | else
919 | res = res .. TYPE.BIN(value[1],sref,stref)
920 | end
921 |
922 | if math.type(value[2]) == 'integer' then
923 | res = res .. TYPE.UINT(value[2],sref,stref)
924 | else
925 | res = res .. TYPE.BIN(value[2],sref,stref)
926 | end
927 |
928 | return res
929 | end,
930 |
931 | [30] = function(packet,pos,conv,ref)
932 | local value,npos,ctype = decode(packet,pos,conv,ref)
933 |
934 | if ctype ~= 'ARRAY' then
935 | throw(pos,"_rational wanted ARRAY, got %s",ctype)
936 | end
937 |
938 | if #value ~= 2 then
939 | throw(pos,"_rational: wanted ARRAY[2], got ARRAY[%d]",#value)
940 | end
941 |
942 | if math.type(value[1]) ~= 'integer' and type(value[1]) ~= 'string' then
943 | throw(pos,"_rationa;: wanted integer or bignum for numerator, got %s",type(value[1]))
944 | end
945 |
946 | if math.type(value[2]) ~= 'integer' and type(value[2]) ~= 'string' then
947 | throw(pos,"_rational: wanted integer or bignum for demoninator, got %s",type(value[2]))
948 | end
949 |
950 | if math.type(value[2]) == 'integer' and value[2] < 1 then
951 | throw(pos,"_rational: wanted >1 for demoninator")
952 | end
953 |
954 | return value,npos,'_rational'
955 | end,
956 |
957 | -- =====================================================================
958 |
959 | _uuid = function(value,sref,stref)
960 | assert(type(value) == 'string',"_uuid: expecting a string")
961 | assert(#value == 16,"_uuid: expecting a binary string of 16 bytes")
962 | return cbor_c.encode(0xC0,37) .. TYPE.BIN(value,sref,stref)
963 | end,
964 |
965 | [37] = function(packet,pos,conv,ref)
966 | local value,npos,ctype = decode(packet,pos,conv,ref)
967 | if ctype == 'BIN' then
968 | if #value ~= 16 then
969 | throw(pos,"_uuid: invalid data for UUID")
970 | end
971 | return value,npos,'_uuid'
972 | else
973 | throw(pos,"_uuid: wanted BIN, got %s",ctype)
974 | end
975 | end,
976 |
977 | -- =====================================================================
978 |
979 | _language = function(value,sref,stref)
980 | assert(type(value) == 'table',"_language: expecting a table")
981 | assert(#value == 2,"_language: expecting a table of two values")
982 | assert(type(value[1]) == 'string',"_language: expeting a string")
983 | assert(type(value[2]) == 'string',"_language: expeting a string")
984 |
985 | return cbor_c.encode(0xC0,38) .. TYPE.ARRAY(value,sref,stref)
986 | end,
987 |
988 | [38] = function(packet,pos,conv,ref)
989 | local value,npos,ctype = decode(packet,pos,conv,ref)
990 | if ctype ~= 'ARRAY' then
991 | throw(pos,"_language: wanted ARRAY, got %s",ctype)
992 | end
993 |
994 | if #value ~= 2 then
995 | throw(pos,"_language: wanted ARRAY(2), got ARRAY(%d)",#value)
996 | end
997 |
998 | if type(value[1]) ~= 'string' then
999 | throw(pos,"_langauge: wanted TEXT for language specifier")
1000 | end
1001 |
1002 | if type(value[2]) ~= 'string' then
1003 | throw(pos,"_language: wanted TEXT for text");
1004 | end
1005 |
1006 | return value,npos,'_language'
1007 | end,
1008 |
1009 | -- =====================================================================
1010 |
1011 | _id = function(value,sref,stref)
1012 | return cbor_c.encode(0xC0,39) .. encode(value,sref,stref)
1013 | end,
1014 |
1015 | [39] = function(packet,pos,conv,ref)
1016 | local value,npos = decode(packet,pos,conv,ref)
1017 | return value,npos,'_id'
1018 | end,
1019 |
1020 | -- =====================================================================
1021 | -- _stringref is like _magic_cbor, it stands for itself and just
1022 | -- indicates that we're using string references for the next object.
1023 | -- I'm doing this because this also have to interact with _sharedref.
1024 | -- =====================================================================
1025 |
1026 | _stringref = function(_,_,stref)
1027 | stref.SEEN = true
1028 | return cbor_c.encode(0xC0,256)
1029 | end,
1030 |
1031 | [256] = function(packet,pos,conv,ref)
1032 | local prev = ref._stringref
1033 | ref._stringref = {}
1034 | local value,npos,ctype = decode(packet,pos,conv,ref)
1035 | ref._stringref = prev
1036 | return value,npos,ctype
1037 | end,
1038 |
1039 | -- =====================================================================
1040 |
1041 | _bmime = function(value,sref,stref)
1042 | return cbor_c.encode(0xC0,257) .. TYPE.BIN(value,sref,stref)
1043 | end,
1044 |
1045 | [257] = function(packet,pos,conv,ref)
1046 | local value,npos = decode(packet,pos,conv,ref)
1047 | return value,npos,'_bmime'
1048 | end,
1049 |
1050 | -- =====================================================================
1051 |
1052 | _ipaddress = function(value,sref,stref)
1053 | assert(type(value) == 'string',"_ipaddress expects a string")
1054 | assert(#value == 4 or #value == 16 or #value == 6,"_ipaddress wrong length")
1055 | return cbor_c.encode(0xC0,260) .. TYPE.BIN(value,sref,stref)
1056 | end,
1057 |
1058 | [260] = function(packet,pos,conv,ref)
1059 | local value,npos,ctype = decode(packet,pos,conv,ref)
1060 |
1061 | if ctype ~= 'BIN' then
1062 | throw(pos,"_ipaddress: wanted BIN, got %s",ctype)
1063 | end
1064 |
1065 | if #value ~= 4 and #value ~= 16 and #value ~= 6 then
1066 | throw(pos,"_ipaddress: wrong size address: %d",#value)
1067 | end
1068 |
1069 | return value,npos,'_ipaddress'
1070 | end,
1071 |
1072 | -- =====================================================================
1073 |
1074 | _decimalfractionexp = function(value,sref,stref)
1075 | assert(type(value) == 'table',"__decimalfractionexp expects an array")
1076 | assert(#value == 2,"_decimalfractionexp expects a two item array")
1077 | assert(type(value[1]) == 'string' or math.type(value[1]) == 'integer')
1078 | assert(math.type(value[2]) == 'integer')
1079 | return cbor_c.encode(0xC0,264) .. TYPE.ARRAY(value,sref,stref)
1080 | end,
1081 |
1082 | [264] = function(packet,pos,conv,ref)
1083 | local value,npos,ctype = decode(packet,pos,conv,ref)
1084 |
1085 | if ctype ~= 'ARRAY' then
1086 | throw(pos,"_decimalfractionexp: wanted ARRAY, got %s",ctype)
1087 | end
1088 |
1089 | if #value ~= 2 then
1090 | throw(pos,"_decimalfractionexp: wanted ARRAY(2), got ARRAY(%d)",#value)
1091 | end
1092 |
1093 | if type(value[1]) ~= 'string' and math.type(value[1]) ~= 'integer' then
1094 | throw(pos,"_decimalfractionexp: wanted integer or bignum for exp, got %s",type(value))
1095 | end
1096 |
1097 | if math.type(value[2]) ~= 'integer' then
1098 | throw(pos,"_decimalfractionexp: wanted integer or mantissa, got %s",type(value))
1099 | end
1100 |
1101 | return value,npos,'_decimalfractionexp'
1102 | end,
1103 |
1104 | -- =====================================================================
1105 |
1106 | _bigfloatexp = function(value,sref,stref)
1107 | assert(type(value) == 'table',"__bigfloatexp expects an array")
1108 | assert(#value == 2,"_bigfloatexp expects a two item array")
1109 | assert(type(value[1]) == 'string' or math.type(value[1]) == 'integer')
1110 | assert(math.type(value[2]) == 'integer')
1111 | return cbor_c.encode(0xC0,265) .. TYPE.ARRAY(value,sref,stref)
1112 |
1113 | end,
1114 |
1115 | [265] = function(packet,pos,conv,ref)
1116 | local value,npos,ctype = decode(packet,pos,conv,ref)
1117 |
1118 | if ctype ~= 'ARRAY' then
1119 | throw(pos,"_bigfloatexp: wanted ARRAY, got %s",ctype)
1120 | end
1121 |
1122 | if #value ~= 2 then
1123 | throw(pos,"_bigfloatexp: wanted ARRAY(2), got ARRAY(%d)",#value)
1124 | end
1125 |
1126 | if type(value[1]) ~= 'string' and math.type(value[1]) ~= 'integer' then
1127 | throw(pos,"_bigfloatexp: wanted integer or bignum for exp, got %s",type(value))
1128 | end
1129 |
1130 | if math.type(value[2]) ~= 'integer' then
1131 | throw(pos,"_bigfloatexp: wanted integer or mantissa, got %s",type(value))
1132 | end
1133 |
1134 | return value,npos,'_bigfloatexp'
1135 | end,
1136 |
1137 | -- =====================================================================
1138 |
1139 | _indirection = function(value,sref,stref)
1140 | return cbor_c.encode(0xC0,22098) .. encode(value,sref,stref)
1141 | end,
1142 |
1143 | [22098] = function(packet,pos,conv,ref)
1144 | local value,npos = decode(packet,pos,conv,ref)
1145 | return value,npos,'_indirection'
1146 | end,
1147 |
1148 | -- =====================================================================
1149 |
1150 | _rains = function(value,sref,stref)
1151 | return cbor_c.encode(0xC0,15309736) .. TYPE.MAP(value,sref,stref)
1152 | end,
1153 |
1154 | [15309736] = function(packet,pos,conv,ref)
1155 | local value,npos = decode(packet,pos,conv,ref)
1156 | return value,npos,'_rains'
1157 | end,
1158 | },
1159 | {
1160 | __index = function(_,key)
1161 | if type(key) == 'number' then
1162 | return function(packet,pos,conv,ref)
1163 | local value,npos = decode(packet,pos,conv,ref)
1164 | return value,npos,string.format('TAG_%d',key)
1165 | end
1166 |
1167 | elseif type(key) == 'string' then
1168 | return function(value)
1169 | return cbor_c.encode(0xC0,tonumber(key)) .. encode(value)
1170 | end
1171 | end
1172 | end
1173 | }
1174 | )
1175 |
1176 | -- ***********************************************************************
1177 | --
1178 | -- CBOR SIMPLE data types
1179 | --
1180 | -- Encoding and decoding of CBOR simple types are here.
1181 | --
1182 | -- Usage: blob = cbor.SIMPLE['name'](n)
1183 | -- Desc: Encode a CBOR simple type
1184 | -- Input: n (number/optional) floating point number to encode (see notes)
1185 | -- Return: blob (binary) CBOR encoded simple type
1186 | --
1187 | -- Note: Some functions ignore the passed in parameter.
1188 | --
1189 | -- WARNING! The functions that do not ignore the parameter may
1190 | -- throw an error if floating point precision will be lost
1191 | -- during the encoding. Please be aware of what you are doing
1192 | -- when calling SIMPLE.half(), SIMPLE.float() or
1193 | -- SIMPLE.double().
1194 | --
1195 | -- Usage: value2,pos,ctype = cbor.SIMPLE[n](pos,value)
1196 | -- Desc: Decode a CBOR simple type
1197 | -- Input: pos (integer) byte offset in packet
1198 | -- value (number/optional) floating point number
1199 | -- Return: value2 (any) decoded value as Lua value
1200 | -- pos (integer) original pos passed in (see notes)
1201 | -- ctype (enum/cbor) CBOR type of value
1202 | --
1203 | -- Note: The pos parameter is passed in to avoid special cases in
1204 | -- the code and to conform to all other decoding routines.
1205 | --
1206 | -- ***********************************************************************
1207 |
1208 | SIMPLE = setmetatable(
1209 | {
1210 | [20] = function(pos) return false ,pos,'false' end,
1211 | [21] = function(pos) return true ,pos,'true' end,
1212 | [22] = function(pos) return null ,pos,'null' end,
1213 | [23] = function(pos) return undefined,pos,'undefined' end,
1214 | [25] = function(pos,value) return value ,pos,'half' end,
1215 | [26] = function(pos,value) return value ,pos,'single' end,
1216 | [27] = function(pos,value) return value ,pos,'double' end,
1217 | [31] = function(pos) return false ,pos,'__break' end,
1218 |
1219 | ['false'] = function() return "\244" end,
1220 | ['true'] = function() return "\245" end,
1221 | null = function() return "\246" end,
1222 | undefined = function() return "\247" end,
1223 | half = function(h) return cbor_c.encode(0xE0,25,h) end,
1224 | single = function(s) return cbor_c.encode(0xE0,26,s) end,
1225 | double = function(d) return cbor_c.encode(0xE0,27,d) end,
1226 | __break = function() return "\255" end,
1227 | },
1228 | {
1229 | __index = function(_,key)
1230 | if type(key) == 'number' then
1231 | return function(pos,value)
1232 | return value,pos,'SIMPLE'
1233 | end
1234 |
1235 | elseif type(key) == 'string' then
1236 | return function()
1237 | return cbor_c.encode(0xE0,tonumber(key))
1238 | end
1239 | end
1240 | end
1241 | }
1242 | )
1243 |
1244 | -- ***********************************************************************
1245 | -- Usage: value,pos2,ctype = cbor.decode(packet[,pos][,conv][,ref][,iskey])
1246 | -- Desc: Decode CBOR encoded data
1247 | -- Input: packet (binary) CBOR binary blob
1248 | -- pos (integer/optional) starting point for decoding
1249 | -- conv (table/optional) table of conversion routines
1250 | -- ref (table/optional) reference table (see notes)
1251 | -- iskey (boolean/optional) is a key in a MAP (see notes)
1252 | -- Return: value (any) the decoded CBOR data
1253 | -- pos2 (integer) offset past decoded data
1254 | -- ctype (enum/cbor) CBOR type of value
1255 | --
1256 | -- Note: The conversion table should be constructed as:
1257 | --
1258 | -- {
1259 | -- UINT = function(v) return munge(v) end,
1260 | -- _datetime = function(v) return munge(v) end,
1261 | -- _url = function(v) return munge(v) end,,
1262 | -- }
1263 | --
1264 | -- The keys are CBOR types (listed above). These functions are
1265 | -- expected to convert the decoded CBOR type into a more
1266 | -- appropriate type for your code. For instance, an _epoch can
1267 | -- be converted into a table.
1268 | --
1269 | -- Users of this function *should not* pass a reference table
1270 | -- into this routine---this is used internally to handle
1271 | -- references. You need to know what you are doing to use this
1272 | -- parameter. You have been warned.
1273 | --
1274 | -- The iskey is true if the value is being used as a key in a
1275 | -- map, and is passed to the conversion routine; this too,
1276 | -- is an internal use only variable and you need to know what
1277 | -- you are doing to use this. You have been warned.
1278 | --
1279 | -- This function can throw an error. The returned error object
1280 | -- MAY BE a table, in which case it has the format:
1281 | --
1282 | -- {
1283 | -- msg = "Error text",
1284 | -- pos = 13 -- position in binary object of error
1285 | -- }
1286 | --
1287 | -- ***********************************************************************
1288 |
1289 | function decode(packet,pos,conv,ref,iskey)
1290 | pos = pos or 1
1291 | conv = conv or {}
1292 | ref = ref or { _stringref = {} , _sharedref = {} }
1293 |
1294 | local ctype,info,value,npos = cbor_c.decode(packet,pos)
1295 | local value2,npos2,ctype2 = TYPE[ctype](packet,npos,info,value,conv,ref)
1296 |
1297 | if conv[ctype2] then
1298 | value2 = conv[ctype2](value2,iskey)
1299 | end
1300 |
1301 | return value2,npos2,ctype2
1302 | end
1303 |
1304 | -- ***********************************************************************
1305 | -- Usage: value,pos2,ctype[,err] = cbor.pdecode(packet[,pos][,conv][,ref])
1306 | -- Desc: Protected call to cbor.decode(), which will return an error
1307 | -- Input: packet (binary) CBOR binary blob
1308 | -- pos (integer/optional) starting point for decoding
1309 | -- conv (table/optional) table of conversion routines (see cbor.decode())
1310 | -- ref (table/optional) reference table (see cbor.decode())
1311 | -- Return: value (any) the decoded CBOR data, nil on error
1312 | -- pos2 (integer) offset past decoded data; if error, position of error
1313 | -- ctype (enum/cbor) CBOR type
1314 | -- err (string/optional) error message (if any)
1315 | -- ***********************************************************************
1316 |
1317 | function pdecode(packet,pos,conv,ref)
1318 | local okay,value,npos,ctype = pcall(decode,packet,pos,conv,ref)
1319 | if okay then
1320 | return value,npos,ctype
1321 | else
1322 | return nil,value.pos,'__error',value.msg
1323 | end
1324 | end
1325 |
1326 | -- ***********************************************************************
1327 |
1328 | local function generic(value,sref,stref)
1329 | local mt = getmetatable(value)
1330 | if not mt then
1331 | if type(value) == 'table' then
1332 | if #value > 0 then
1333 | return TYPE.ARRAY(value,sref,stref)
1334 | else
1335 | return TYPE.MAP(value,sref,stref)
1336 | end
1337 | else
1338 | error(string.format("Cannot encode %s",type(value)))
1339 | end
1340 | end
1341 |
1342 | if mt.__tocbor then
1343 | return mt.__tocbor(value,sref,stref)
1344 |
1345 | elseif mt.__len then
1346 | return TYPE.ARRAY(value,sref,stref)
1347 |
1348 | elseif LUA_VERSION == "Lua 5.2" and mt.__ipairs then
1349 | return TYPE.ARRAY(value,sref,stref)
1350 |
1351 | elseif LUA_VERSION >= "Lua 5.2" and mt.__pairs then
1352 | return TYPE.MAP(value,sref,stref)
1353 |
1354 | else
1355 | error(string.format("Cannot encode %s",type(value)))
1356 | end
1357 | end
1358 |
1359 | -- ***********************************************************************
1360 | --
1361 | -- __ENCODE_MAP
1362 | --
1363 | -- A table of functions to map Lua values to CBOR encoded values. nil,
1364 | -- boolean, number and string are handled directly (if a Lua string is valid
1365 | -- UTF8, then it's encoded as a CBOR TEXT.
1366 | --
1367 | -- For the other four types, only tables are directly supported without
1368 | -- metatable support. If a metatable does exist, if the method '__tocbor'
1369 | -- is defined, that function is called and the results returned. If '__len'
1370 | -- is defined, then it is mapped as a CBOR ARRAY. For Lua 5.2, if
1371 | -- '__ipairs' is defined, then it too, is mapped as a CBOR ARRAY. If Lua
1372 | -- 5.2 or higher and '__pairs' is defined, then it's mapped as a CBOR MAP.
1373 | --
1374 | -- Otherwise, an error is thrown.
1375 | --
1376 | -- Usage: blob = cbor.__ENCODE_MAP[luatype](value,sref,stref)
1377 | -- Desc: Encode a Lua type into a CBOR type
1378 | -- Input: value (any) a Lua value who's type matches luatype.
1379 | -- sref (table/optional) shared reference table
1380 | -- stref (table/optional) shared string reference table
1381 | -- Return: blob (binary) CBOR encoded data
1382 | --
1383 | -- ***********************************************************************
1384 |
1385 | __ENCODE_MAP =
1386 | {
1387 | ['nil'] = SIMPLE.null,
1388 |
1389 | ['boolean'] = function(b)
1390 | if b then
1391 | return SIMPLE['true']()
1392 | else
1393 | return SIMPLE['false']()
1394 | end
1395 | end,
1396 |
1397 | ['number'] = function(value)
1398 | if math.type(value) == 'integer' then
1399 | if value < 0 then
1400 | return TYPE.NINT(value)
1401 | else
1402 | return TYPE.UINT(value)
1403 | end
1404 | else
1405 | return cbor_c.encode(0xE0,nil,value)
1406 | end
1407 | end,
1408 |
1409 | ['string'] = function(value,sref,stref)
1410 | if UTF8:match(value) > #value then
1411 | return TYPE.TEXT(value,sref,stref)
1412 | else
1413 | return TYPE.BIN(value,sref,stref)
1414 | end
1415 | end,
1416 |
1417 | ['table'] = generic,
1418 | ['function'] = generic,
1419 | ['userdata'] = generic,
1420 | ['thread'] = generic,
1421 | }
1422 |
1423 | -- ***********************************************************************
1424 | -- Usage: blob = cbor.encode(value[,sref][,stref])
1425 | -- Desc: Encode a Lua type into a CBOR type
1426 | -- Input: value (any)
1427 | -- sref (table/optional) shared reference table
1428 | -- stref (table/optional) shared string reference table
1429 | -- Return: blob (binary) CBOR encoded value
1430 | -- ***********************************************************************
1431 |
1432 | function encode(value,sref,stref)
1433 | if value == null then
1434 | return SIMPLE.null()
1435 | elseif value == undefined then
1436 | return SIMPLE.undefined()
1437 | end
1438 |
1439 | local res = ""
1440 |
1441 | if stref and not stref.SEEN then
1442 | res = TAG._stringref(nil,nil,stref)
1443 | end
1444 |
1445 | return res .. __ENCODE_MAP[type(value)](value,sref,stref)
1446 | end
1447 |
1448 | -- ***********************************************************************
1449 | -- Usage: blob[,err] = cbor.pencode(value[,sref][,stref])
1450 | -- Desc: Protected call to encode a CBOR type
1451 | -- Input: value (any)
1452 | -- sref (table/optional) shared reference table
1453 | -- stref (table/optional) shared string reference table
1454 | -- Return: blob (binary) CBOR encoded value, nil on error
1455 | -- err (string/optional) error message
1456 | -- ***********************************************************************
1457 |
1458 | function pencode(value,sref,stref)
1459 | local okay,value2 = pcall(encode,value,sref,stref)
1460 | if okay then
1461 | return value2
1462 | else
1463 | return nil,value2
1464 | end
1465 | end
1466 |
1467 | -- ***********************************************************************
1468 |
1469 | if LUA_VERSION >= "Lua 5.2" then
1470 | return _ENV
1471 | end
1472 |
--------------------------------------------------------------------------------
/cbor_c.c:
--------------------------------------------------------------------------------
1 | /***************************************************************************
2 | *
3 | * Copyright 2016 by Sean Conner.
4 | *
5 | * This library is free software; you can redistribute it and/or modify it
6 | * under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation; either version 3 of the License, or (at your
8 | * option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful, but
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | * License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this library; if not, see .
17 | *
18 | * Comments, questions and criticisms can be sent to: sean@conman.org
19 | *
20 | *************************************************************************/
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | #include
29 | #include
30 |
31 | #include "dnf.h"
32 |
33 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501
34 | # error You need to compile against Lua 5.1 or higher
35 | #endif
36 |
37 | /**************************************************************************/
38 |
39 | typedef union
40 | {
41 | uint32_t i;
42 | float f;
43 | } float__u;
44 |
45 | typedef union
46 | {
47 | uint64_t i;
48 | double d;
49 | } double__u;
50 |
51 | typedef union
52 | {
53 | uint8_t b[9];
54 | char c[9];
55 | } buffer__u;
56 |
57 | /***************************************************************************
58 | * Push a CBOR encoded float value onto the stack (passed in as an integer so
59 | * we can push things like +-inf or any nubmer of NaNs). The number of bytes
60 | * to use is passed in (since floats are encoded in 2, 4 or 8 bytes
61 | * respectively).
62 | ****************************************************************************/
63 |
64 | static void cbor_cL_pushvalueN(
65 | lua_State *L,
66 | int typeinfo,
67 | unsigned long long int value,
68 | size_t len
69 | )
70 | {
71 | buffer__u result;
72 | size_t idx = sizeof(buffer__u);
73 |
74 | assert(L != NULL);
75 | assert(
76 | ((len == 1) && ((typeinfo & 0x1F) == 24))
77 | || ((len == 2) && ((typeinfo & 0x1F) == 25))
78 | || ((len == 4) && ((typeinfo & 0x1F) == 26))
79 | || ((len == 8) && ((typeinfo & 0x1F) == 27))
80 | );
81 |
82 | for (uint8_t b = (uint8_t)value ; len > 0 ; b = (uint8_t)(value >>= 8) , len--)
83 | result.b[--idx] = b;
84 |
85 | result.b[--idx] = typeinfo;
86 | lua_pushlstring(L,&result.c[idx],sizeof(buffer__u) - idx);
87 | }
88 |
89 | /*************************************************************************
90 | * Push a CBOR encoded value onto the Lua stack. This will use the minimal
91 | * encoding for a value.
92 | **************************************************************************/
93 |
94 | static void cbor_cL_pushvalue(
95 | lua_State *L,
96 | int type,
97 | unsigned long long int value
98 | )
99 | {
100 | assert(L != NULL);
101 | assert((type & 0x1F) == 0);
102 |
103 | /*-----------------------------------------------
104 | ; Values below 24 are encoded in the type byte.
105 | ;------------------------------------------------*/
106 |
107 | if (value < 24)
108 | {
109 | char t = (char)type | (char)value;
110 | lua_pushlstring(L,&t,1);
111 | }
112 |
113 | /*----------------------------------------------------------------------
114 | ; larger values will take 1 additional byte (info of 24), 2 bytes (25),
115 | ; four bytes (26) or eight bytes (27), stored in network-byte order (MSB
116 | ; first). We do this by filling in the character array backwards, filling
117 | ; it with the next 8 bits in network-byte-order. When we hit a zero byte,
118 | ; we stop with the loop since there's no more to do.
119 | ;------------------------------------------------------------------------*/
120 |
121 | else
122 | {
123 | if (value < 256uLL)
124 | cbor_cL_pushvalueN(L,type | 24,value,1);
125 | else if (value < 65536uLL)
126 | cbor_cL_pushvalueN(L,type | 25,value,2);
127 | else if (value < 4294967296uLL)
128 | cbor_cL_pushvalueN(L,type | 26,value,4);
129 | else
130 | cbor_cL_pushvalueN(L,type | 27,value,8);
131 | }
132 | }
133 |
134 | /******************************************************************
135 | * usage: blob = cbor_c.encode02C(type,value)
136 | * desc: Encode a CBOR integer
137 | * input: type (integer) 0x00, 0x20, 0xC0
138 | * value (number) value to encode
139 | * return: blog (binary) CBOR encoded value
140 | *
141 | * note: This is expected to be called to encode CBOR types
142 | * UINT (0x00), NINT (0x20) or a TAG (0xC0).
143 | *
144 | * note: Throws on invalid parameters
145 | *******************************************************************/
146 |
147 | static int cbor_clua_encode02C(lua_State *L)
148 | {
149 | assert(L != NULL);
150 |
151 | #ifndef NDEBUG
152 | int type = luaL_checkinteger(L,1);
153 | assert((type == 0x00) || (type == 0x20) || (type == 0xC0));
154 | #endif
155 |
156 | cbor_cL_pushvalue(L,luaL_checkinteger(L,1),luaL_checknumber(L,2));
157 | return 1;
158 | }
159 |
160 | /******************************************************************
161 | * usage: blob = cbor_c.encode468A(type[,value])
162 | * desc: Encode a CBOR integer
163 | * input: type (integer) 0x40, 0x60, 0x80, 0xA0
164 | * value (number/optional) value to encode
165 | * return: blob (binary) CBOR encoded value
166 | *
167 | * note: This is expected to be called to encode CBOR types BIN
168 | * (0x40), TEXT (0x60), ARRAY (0x80) or MAP (0xA0). The value
169 | * is optional, if if not present (or nil), a size of
170 | * indefinite (info of 31) is used.
171 | *
172 | * note: Throws on invalid parameters
173 | *******************************************************************/
174 |
175 | static int cbor_clua_encode468A(lua_State *L)
176 | {
177 | assert(L != NULL);
178 |
179 | #ifndef NDEBUG
180 | int type = luaL_checkinteger(L,1);
181 | assert((type == 0x40) || (type == 0x60) || (type == 0x80) || (type == 0xA0));
182 | #endif
183 |
184 | if (lua_isnoneornil(L,2))
185 | {
186 | char t = (char)(luaL_checkinteger(L,1) | 31);
187 | lua_pushlstring(L,&t,1);
188 | }
189 | else
190 | cbor_cL_pushvalue(L,luaL_checkinteger(L,1),luaL_checknumber(L,2));
191 |
192 | return 1;
193 | }
194 |
195 | /******************************************************************
196 | * usage: blob = cbor_c.encodeE(type[,value][,value2])
197 | * desc: Encode a CBOR integer or float
198 | * input: type (integer) 0xE0
199 | * value (number/optional) possible integer to encode
200 | * value2 (number/optional) possible float to encode
201 | * return: blob (binary) CBOR encoded value
202 | *
203 | * note: This is expected to be called to encode a CBOR simple type
204 | * (0xE0). If value and value2 are nil, then the __break
205 | * simple type is encoded; if value is nil and value2 exists,
206 | * then the floating point value2 is encoded using the best
207 | * size fo the encoding. If value is 25 (half), 26 (single) or
208 | * 27 (double) then value2 is encoded.
209 | *
210 | * note: Throws on invalid parameters or if float encoding will lose
211 | * precision.
212 | *******************************************************************/
213 |
214 | static int cbor_clua_encodeE(lua_State *L)
215 | {
216 | unsigned short h;
217 | double__u d;
218 | float__u f;
219 | dnf__s cv;
220 | int type;
221 |
222 | type = luaL_checkinteger(L,1);
223 |
224 | assert(L != NULL);
225 |
226 | if (lua_isnoneornil(L,2))
227 | {
228 | /*--------------------------------
229 | ; encoding a __break value?
230 | ;---------------------------------*/
231 |
232 | if (lua_isnoneornil(L,3))
233 | {
234 | char t = (char)(luaL_checkinteger(L,1) | 31);
235 | lua_pushlstring(L,&t,1);
236 | }
237 |
238 | /*---------------------------------------------------------------------
239 | ; nope, encoding a floating point value in the smallest encoding we can
240 | ; muster.
241 | ;----------------------------------------------------------------------*/
242 |
243 | else
244 | {
245 | d.d = luaL_checknumber(L,3);
246 | dnf_fromdouble(&cv,d.d);
247 | if (dnf_tohalf(&h,cv) == 0)
248 | cbor_cL_pushvalueN(L,type | 25,(unsigned long long int)h,2);
249 | else if (dnf_tosingle(&f.f,cv) == 0)
250 | cbor_cL_pushvalueN(L,type | 26,(unsigned long long int)f.i,4);
251 | else
252 | cbor_cL_pushvalueN(L,type | 27,d.i,8);
253 | }
254 | }
255 | else
256 | {
257 | /*-------------------------------------------------------------------
258 | ; if we're encoding infos 25, 26 or 27, then we have a floating point
259 | ; number to encode, and we'll try to encode it to the specified size.
260 | ; If not, boom! If so, woot! If it's not infos 25, 26 or 27, then
261 | ; proceed normally.
262 | ;--------------------------------------------------------------------*/
263 |
264 | unsigned long long int value = luaL_checknumber(L,2);
265 | if (value == 25)
266 | {
267 | d.d = luaL_checknumber(L,3);
268 | dnf_fromdouble(&cv,d.d);
269 | if (dnf_tohalf(&h,cv) != 0)
270 | return luaL_error(L,"cannot convert to half-precision");
271 | cbor_cL_pushvalueN(L,type | 25,(unsigned long long int)h,2);
272 | }
273 | else if (value == 26)
274 | {
275 | d.d = luaL_checknumber(L,3);
276 | dnf_fromdouble(&cv,d.d);
277 | if (dnf_tosingle(&f.f,cv) != 0)
278 | return luaL_error(L,"cannot convert to single-preccision");
279 | cbor_cL_pushvalueN(L,type | 26,(unsigned long long int)f.i,4);
280 | }
281 | else if (value == 27)
282 | {
283 | d.d = luaL_checknumber(L,3);
284 | cbor_cL_pushvalueN(L,type | 27,d.i,8);
285 | }
286 | else
287 | cbor_cL_pushvalue(L,luaL_checkinteger(L,1),value);
288 | }
289 |
290 | return 1;
291 | }
292 |
293 | /******************************************************************
294 | * Usage: blob = cbor_c.encode(type,value[,value2])
295 | * Desc: Encode a CBOR value
296 | * Input: type (integer) CBOR type
297 | * value (number) value to encode (see note)
298 | * value (number/optional) float to encode (see note)
299 | * Return: blob (binary) CBOR encoded value
300 | *
301 | * Note: value is optional for type of 0xE0.
302 | * value2 is optional for type of 0xE0; otherwise it's ignored.
303 | *******************************************************************/
304 |
305 | static int cbor_clua_encode(lua_State *L)
306 | {
307 | assert(L != NULL);
308 |
309 | switch(luaL_checkinteger(L,1))
310 | {
311 | case 0x00:
312 | case 0x20:
313 | case 0xC0: return cbor_clua_encode02C(L);
314 |
315 | case 0x40:
316 | case 0x60:
317 | case 0x80:
318 | case 0xA0: return cbor_clua_encode468A(L);
319 |
320 | case 0xE0: return cbor_clua_encodeE(L);
321 | default: break;
322 | }
323 |
324 | return luaL_error(L,"invalid type %d",lua_tointeger(L,1));
325 | }
326 |
327 | /******************************************************************
328 | * Usage: ctype,info,value,pos2 = cbor_c.decode(blob,pos)
329 | * Desc: Decode a CBOR-encoded value
330 | * Input: blob (binary) binary CBOR sludge
331 | * pos (integer) position to start decoding from
332 | * Return: ctype (integer) CBOR major type
333 | * info (integer) sub-major type information
334 | * value (integer number) decoded value
335 | * pos2 (integer) position past decoded data
336 | *
337 | * Note: Throws in invalid parameter
338 | *******************************************************************/
339 |
340 | static int cbor_clua_decode(lua_State *L)
341 | {
342 | size_t packlen;
343 | const char *packet = luaL_checklstring(L,1,&packlen);
344 | size_t pos = luaL_checkinteger(L,2);
345 | int type;
346 | int info;
347 | unsigned long long int value;
348 | size_t i;
349 | size_t len;
350 |
351 | assert(L != NULL);
352 |
353 | if (pos > packlen)
354 | return luaL_error(L,"no input");
355 |
356 | pos--;
357 | lua_pushinteger(L,type = packet[pos] & 0xE0);
358 | lua_pushinteger(L,info = packet[pos] & 0x1F);
359 |
360 | /*----------------------------------------------------------------------
361 | ; Info values less than 24 and 31 are inherent---the data is just there.
362 | ; So we handle these directly here---the value is either the info value,
363 | ; or a HUGE_VAL (in the case of info=31). Info values 24 to 27 have
364 | ; extention bytes (1, 2, 4 or 8). Get the length for these and carry on.
365 | ;-----------------------------------------------------------------------*/
366 |
367 | if (info < 24)
368 | {
369 | lua_pushinteger(L,info);
370 | lua_pushinteger(L,pos + 2);
371 | return 4;
372 | }
373 | else if (info == 24)
374 | len = 1;
375 | else if (info == 25)
376 | len = 2;
377 | else if (info == 26)
378 | len = 4;
379 | else if (info == 27)
380 | len = 8;
381 | else if (info == 31)
382 | {
383 | lua_pushnumber(L,HUGE_VAL);
384 | lua_pushinteger(L,pos + 2);
385 | return 4;
386 | }
387 | else
388 | return luaL_error(L,"invalid data");
389 |
390 | /*---------------------
391 | ; Sanity checking
392 | ;--------------------*/
393 |
394 | if (pos + len + 1 > packlen)
395 | return luaL_error(L,"no more input");
396 |
397 | /*----------------------------------------------
398 | ; Read len bytes of a network-byte-order value.
399 | ;-----------------------------------------------*/
400 |
401 | for (value = 0 , i = 0 ; i < len ; i++)
402 | value = (value << 8) | (unsigned long long)((unsigned char)packet[++pos]);
403 |
404 | /*----------------------------------------------------------------------
405 | ; The 0xE0 type encodes actual floating point values. If we've just read
406 | ; in one of these, convert to a double.
407 | ;-----------------------------------------------------------------------*/
408 |
409 | if ((type == 0xE0) && (len > 1))
410 | {
411 | double__u d;
412 | float__u f;
413 | dnf__s cv;
414 |
415 | if (len == 2)
416 | {
417 | dnf_fromhalf(&cv,value);
418 | dnf_todouble(&d.d,cv);
419 | }
420 | else if (len == 4)
421 | {
422 | f.i = value;
423 | dnf_fromsingle(&cv,f.f);
424 | dnf_todouble(&d.d,cv);
425 | }
426 | else if (len == 8)
427 | d.i = value;
428 |
429 | lua_pushnumber(L,d.d);
430 | lua_pushinteger(L,pos + 2);
431 | return 4;
432 | }
433 |
434 | # if LUA_VERSION_NUM < 503
435 | lua_pushnumber(L,value);
436 | # else
437 | lua_pushinteger(L,value);
438 | # endif
439 |
440 | lua_pushinteger(L,pos + 2);
441 | return 4;
442 | }
443 |
444 | /**************************************************************************/
445 |
446 | static const luaL_Reg cbor_c_reg[] =
447 | {
448 | { "encode" , cbor_clua_encode } ,
449 | { "decode" , cbor_clua_decode } ,
450 | { NULL , NULL }
451 | };
452 |
453 | int luaopen_org_conman_cbor_c(lua_State *L)
454 | {
455 | #if LUA_VERSION_NUM == 501
456 | luaL_register(L,"org.conman.cbor_c",cbor_c_reg);
457 | #else
458 | luaL_newlib(L,cbor_c_reg);
459 | #endif
460 |
461 | #ifdef VERSION
462 | lua_pushliteral(L,VERSION);
463 | lua_setfield(L,-2,"_VERSION");
464 | #endif
465 |
466 | return 1;
467 | }
468 |
469 | /**************************************************************************/
470 |
--------------------------------------------------------------------------------
/cbor_s.lua:
--------------------------------------------------------------------------------
1 | -- ***************************************************************
2 | --
3 | -- Copyright 2016 by Sean Conner. All Rights Reserved.
4 | --
5 | -- This library is free software; you can redistribute it and/or modify it
6 | -- under the terms of the GNU Lesser General Public License as published by
7 | -- the Free Software Foundation; either version 3 of the License, or (at your
8 | -- option) any later version.
9 | --
10 | -- This library is distributed in the hope that it will be useful, but
11 | -- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | -- License for more details.
14 | --
15 | -- You should have received a copy of the GNU Lesser General Public License
16 | -- along with this library; if not, see .
17 | --
18 | -- Comments, questions and criticisms can be sent to: sean@conman.org
19 | --
20 | -- ====================================================================
21 | --
22 | -- A simpler CBOR encoding/decoding module
23 | --
24 | -- luacheck: globals _ENV _VERSION decode encode pdecode pencode
25 | -- luacheck: ignore 611
26 | -- ***************************************************************
27 |
28 | local math = require "math"
29 | local table = require "table"
30 | local lpeg = require "lpeg"
31 | local cbor_c = require "org.conman.cbor_c"
32 |
33 | local LUA_VERSION = _VERSION
34 | local getmetatable = getmetatable
35 | local setmetatable = setmetatable
36 | local ipairs = ipairs
37 | local pairs = pairs
38 | local type = type
39 | local pcall = pcall
40 |
41 | if LUA_VERSION < "Lua 5.3" then
42 | function math.type(n)
43 | return n >= -9007199254740992
44 | and n <= 9007199254740992
45 | and n % 1 == 0
46 | and 'integer'
47 | or 'float'
48 | end
49 | end
50 |
51 | if LUA_VERSION == "Lua 5.1" then
52 | module "org.conman.cbor_s" -- luacheck: ignore
53 | else
54 | _ENV = {} -- luacheck: ignore
55 | end
56 |
57 | _VERSION = cbor_c._VERSION
58 |
59 | -- ***************************************************************
60 | -- UTF-8 defintion from RFC-3629. There's a deviation from the RFC
61 | -- specification in that I only allow certain codes from the US-ASCII C0
62 | -- range (control codes) that are in common use.
63 | -- ***********************************************************************
64 |
65 | local UTF8 = (
66 | lpeg.R("\7\13"," ~")
67 | + lpeg.R("\194\223") * lpeg.R("\128\191")
68 | + lpeg.P("\224") * lpeg.R("\160\191") * lpeg.R("\128\191")
69 | + lpeg.R("\225\236") * lpeg.R("\128\191") * lpeg.R("\128\191")
70 | + lpeg.P("\237") * lpeg.R("\128\159") * lpeg.R("\128\191")
71 | + lpeg.R("\238\239") * lpeg.R("\128\191") * lpeg.R("\128\191")
72 | + lpeg.P("\240") * lpeg.R("\144\191") * lpeg.R("\128\191") * lpeg.R("\128\191")
73 | + lpeg.R("\241\243") * lpeg.R("\128\191") * lpeg.R("\128\191") * lpeg.R("\128\191")
74 | + lpeg.P("\224") * lpeg.R("\128\142") * lpeg.R("\128\191") * lpeg.R("\128\191")
75 | )^0
76 |
77 | -- ***********************************************************************
78 | -- usage: value2,pos2,ctype2 = bintext(packet,pos,info,value,ctype)
79 | -- desc: Decode a CBOR BIN or CBOR TEXT into a Lua string
80 | -- input: packet (binary) binary blob
81 | -- pos (integer) byte position in packet
82 | -- info (integer) CBOR info value (0..31)
83 | -- value (integer) string length
84 | -- ctype (enum/cbor) 'BIN' or 'TEXT'
85 | -- return: value2 (string) string from packet
86 | -- pos2 (integer) position past string just extracted
87 | -- ctype2 (enum/cbor) 'BIN' or 'TEXT'
88 | -- ***********************************************************************
89 |
90 | local function bintext(packet,pos,info,value,ctype)
91 | if info == 31 then
92 | local res = ""
93 | while true do
94 | local nvalue,npos,ntype = decode(packet,pos)
95 | if ntype == '__break' then
96 | return res,npos,ctype
97 | end
98 | res = res .. nvalue
99 | pos = npos
100 | end
101 | end
102 |
103 | local bt = packet:sub(pos,pos + value - 1)
104 | return bt,pos + value,ctype
105 | end
106 |
107 | -- ***************************************************************
108 | --
109 | -- CBOR SIMPLE data types
110 | --
111 | -- Dencoding of CBOR simple types are here.
112 | --
113 | -- Usage: value2,pos,ctype = cbor.SIMPLE[n](pos,value)
114 | -- Desc: Decode a CBOR simple type
115 | -- Input: pos (integer) byte offset in packet
116 | -- value (number/optional) floating point number
117 | -- Return: value2 (any) decoded value as Lua value
118 | -- pos (integer) original pos passed in (see notes)
119 | -- ctype (enum/cbor) CBOR type of value
120 | --
121 | -- Note: The pos parameter is passed in to avoid special cases in
122 | -- the code and to conform to all other decoding routines.
123 | --
124 | -- ***********************************************************************
125 |
126 | local SIMPLE = setmetatable(
127 | {
128 | [20] = function(pos) return false ,pos,'false' end,
129 | [21] = function(pos) return true ,pos,'true' end,
130 | [22] = function(pos) return null ,pos,'null' end,
131 | [23] = function(pos) return undefined,pos,'undefined' end,
132 | [25] = function(pos,value) return value ,pos,'half' end,
133 | [26] = function(pos,value) return value ,pos,'single' end,
134 | [27] = function(pos,value) return value ,pos,'double' end,
135 | [31] = function(pos) return false ,pos,'__break' end,
136 | },
137 | {
138 | __index = function()
139 | return function(pos,value) return value,pos,'SIMPLE' end
140 | end
141 | }
142 | )
143 |
144 | -- ***************************************************************
145 | --
146 | -- CBOR base TYPES
147 | --
148 | -- Dencoding functions for CBOR base types are here.
149 | --
150 | -- Usage: value2,pos2,ctype = cbor.TYPE[n](packet,pos,info,value,conv)
151 | -- Desc: Decode a CBOR base type
152 | -- Input: packet (binary) binary blob of CBOR data
153 | -- pos (integer) byte offset in packet to start parsing from
154 | -- info (integer) CBOR info (0 .. 31)
155 | -- value (integer) CBOR decoded value
156 | -- conv (table) conversion table (passed to decode())
157 | -- Return: value2 (any) decoded CBOR value
158 | -- pos2 (integer) byte offset just past parsed data
159 | -- ctype (enum/cbor) CBOR deocded type
160 | --
161 | -- Note: simple is returned for any non-supported SIMPLE types.
162 | -- Supported simple types will return the appropriate type
163 | -- name.
164 | --
165 | -- ***********************************************************************
166 |
167 | local TYPE =
168 | {
169 | [0x00] = function(_,pos,_,value)
170 | return value,pos,'UINT'
171 | end,
172 |
173 | [0x20] = function(_,pos,_,value)
174 | return -1 - value,pos,'NINT'
175 | end,
176 |
177 | [0x40] = function(packet,pos,info,value)
178 | return bintext(packet,pos,info,value,'BIN')
179 | end,
180 |
181 | [0x60] = function(packet,pos,info,value)
182 | return bintext(packet,pos,info,value,'TEXT')
183 | end,
184 |
185 | [0x80] = function(packet,pos,_,value,conv)
186 | local array = {}
187 | for _ = 1 , value do
188 | local val,npos,ctype = decode(packet,pos,conv)
189 | if ctype == '__break' then break end
190 | table.insert(array,val)
191 | pos = npos
192 | end
193 | return array,pos,'ARRAY'
194 | end,
195 |
196 | [0xA0] = function(packet,pos,_,value,conv)
197 | local map = {}
198 | for _ = 1 , value do
199 | local name,npos,ctype = decode(packet,pos,conv)
200 | if ctype == '__break' then break end
201 | local val,npos2 = decode(packet,npos,conv)
202 | map[name] = val;
203 | pos = npos2
204 | end
205 | return map,pos,'MAP'
206 | end,
207 |
208 | [0xC0] = function(packet,pos,_,value,conv)
209 | local val,npos,ctype = decode(packet,pos,conv)
210 | if conv and conv[value] then
211 | val = conv[value](val)
212 | end
213 | return val,npos,ctype
214 | end,
215 |
216 | [0xE0] = function(_,pos,info,value)
217 | return SIMPLE[info](pos,value)
218 | end,
219 | }
220 |
221 | -- ***************************************************************
222 | -- Usage: value,pos2,ctype = cbor.decode(packet[,pos][,conv])
223 | -- Desc: Decode CBOR encoded data
224 | -- Input: packet (binary) CBOR binary blob
225 | -- pos (integer/optional) starting point for decoding
226 | -- conv (table/optional) table of tagged conversion routines
227 | -- Return: value (any) the decoded CBOR data
228 | -- pos2 (integer) offset past decoded data
229 | -- ctype (enum/cbor) CBOR type of value
230 | --
231 | -- Note: The conversion table should be constructed as:
232 | --
233 | -- {
234 | -- [ 0] = function(v) return munge(v) end,
235 | -- [32] = function(v) return munge(v) end,,
236 | -- }
237 | --
238 | -- The keys are CBOR types (as integers). These functions are
239 | -- expected to convert the decoded CBOR type into a more
240 | -- appropriate type for your code. For instance, [1] (epoch)
241 | -- can be converted into a table.
242 | --
243 | -- ***********************************************************************
244 |
245 | function decode(packet,pos,conv)
246 | pos = pos or 1
247 | local ctype,info,value,npos = cbor_c.decode(packet,pos)
248 | return TYPE[ctype](packet,npos,info,value,conv)
249 | end
250 |
251 | -- ***************************************************************
252 | -- Usage: value,pos2,ctype[,err] = cbor.pdecode(packet[,pos][,conv])
253 | -- Desc: Protected call to decode CBOR data
254 | -- Input: packet (binary) CBOR binary blob
255 | -- pos (integer/optional) starting point for decoding
256 | -- conv (table/optional) table of tagged conversion routines
257 | -- Return: value (any) the decoded CBOR data, nil on error
258 | -- pos2 (integer) offset past decoded data, 0 on error
259 | -- ctype (enum/cbor) CBOR type of value
260 | -- err (string/optional) error message, if any
261 | -- ***********************************************************************
262 |
263 | function pdecode(packet,pos,conv)
264 | local okay,value,npos,ctype = pcall(decode,packet,pos,conv)
265 | if okay then
266 | return value,npos,ctype
267 | else
268 | return nil,0,'__error',value
269 | end
270 | end
271 |
272 | -- ***************************************************************
273 | --
274 | -- __ENCODE_MAP
275 | --
276 | -- A table of functions to map Lua values to CBOR encoded values. nil,
277 | -- boolean, number, string and tables are handled directly (if a Lua string
278 | -- is valid UTF8, then it's encoded as a CBOR TEXT.
279 | --
280 | -- For tables, if the __tocbor method exists, it will be called; otherwise,
281 | -- if the table has a length greater than 0, it's encoded as an ARRAY;
282 | -- otherwise it's encoded as a MAP (so empty tables will end up as a MAP by
283 | -- default).
284 | --
285 | -- Other Lua types are not supported.
286 | --
287 | -- Usage: blob = cbor.__ENCODE_MAP[luatype](value[,tag])
288 | -- Desc: Encode a Lua type into a CBOR type
289 | -- Input: value (any) a Lua value who's type matches luatype.
290 | -- tag (number/optional) CBOR tag type
291 | -- Return: blob (binary) CBOR encoded data
292 | --
293 | -- ***********************************************************************
294 |
295 | local ENCODE_MAP =
296 | {
297 |
298 | ['nil'] = function()
299 | return "\246"
300 | end,
301 |
302 | ['boolean'] = function(b)
303 | if b then
304 | return "\245"
305 | else
306 | return "\244"
307 | end
308 | end,
309 |
310 | ['number'] = function(value)
311 | if math.type(value) == 'integer' then
312 | if value < 0 then
313 | return cbor_c.encode(0x20,-1 - value)
314 | else
315 | return cbor_c.encode(0x00,value)
316 | end
317 | else
318 | return cbor_c.encode(0xE0,nil,value)
319 | end
320 | end,
321 |
322 | ['string'] = function(value)
323 | if UTF8:match(value) > #value then
324 | return cbor_c.encode(0x60,#value) .. value
325 | else
326 | return cbor_c.encode(0x40,#value) .. value
327 | end
328 | end,
329 |
330 | ['table'] = function(value)
331 | local mt = getmetatable(value)
332 | if mt and mt.__tocbor then
333 | return mt.__tocbor(value)
334 | else
335 | if #value > 0 then
336 | local res = cbor_c.encode(0x80,#value)
337 | for _,item in ipairs(value) do
338 | res = res .. encode(item)
339 | end
340 | return res
341 | else
342 | local res = ""
343 | local cnt = 0
344 |
345 | for key,item in pairs(value) do
346 | res = res .. encode(key)
347 | res = res .. encode(item)
348 | cnt = cnt + 1
349 | end
350 | return cbor_c.encode(0xA0,cnt) .. res
351 | end
352 | end
353 | end,
354 |
355 | ['function'] = function()
356 | error("function not supported")
357 | end,
358 |
359 | ['userdata'] = function()
360 | error("userdata not supported")
361 | end,
362 |
363 | ['thread'] = function()
364 | error("thread not supported")
365 | end,
366 | }
367 |
368 | -- ***************************************************************
369 | -- Usage: blob = cbor.encode(value[,tag])
370 | -- Desc: Encode a Lua type into a CBOR type
371 | -- Input: value (any)
372 | -- tag (number/optional) CBOR tag value
373 | -- Return: blob (binary) CBOR encoded value
374 | -- ***********************************************************************
375 |
376 | function encode(value,tag)
377 | local blob do
378 | if value == null then
379 | blob = "\246"
380 | elseif value == undefined then
381 | blob = "\247"
382 | else
383 | blob = ENCODE_MAP[type(value)](value)
384 | end
385 | end
386 |
387 | if tag then
388 | return cbor_c.encode(0xC0,tag) .. blob
389 | else
390 | return blob
391 | end
392 | end
393 |
394 | -- ***************************************************************
395 | -- Usage: blob[,err] = cbor_s.pencode(value[,tag])
396 | -- Desc: Protected call to encode into CBOR
397 | -- Input: value (any)
398 | -- tag (number/optional) CBOR tag value
399 | -- Return: blob (binary) CBOR encoded value
400 | -- err (string/optional) error message
401 | -- ***************************************************************
402 |
403 | function pencode(value,tag)
404 | local okay,value2 = pcall(encode,value,tag)
405 | if okay then
406 | return value2
407 | else
408 | return nil,value2
409 | end
410 | end
411 |
412 | -- ***************************************************************
413 |
414 | if LUA_VERSION >= "Lua 5.2" then
415 | return _ENV
416 | end
417 |
--------------------------------------------------------------------------------
/cbormisc.lua:
--------------------------------------------------------------------------------
1 | -- ***************************************************************
2 | --
3 | -- Copyright 2016 by Sean Conner. All Rights Reserved.
4 | --
5 | -- This library is free software; you can redistribute it and/or modify it
6 | -- under the terms of the GNU Lesser General Public License as published by
7 | -- the Free Software Foundation; either version 3 of the License, or (at your
8 | -- option) any later version.
9 | --
10 | -- This library is distributed in the hope that it will be useful, but
11 | -- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | -- License for more details.
14 | --
15 | -- You should have received a copy of the GNU Lesser General Public License
16 | -- along with this library; if not, see .
17 | --
18 | -- Comments, questions and criticisms can be sent to: sean@conman.org
19 | --
20 | -- ====================================================================
21 | --
22 | -- Output in the CBOR dianostic format
23 | --
24 | -- luacheck: globals _ENV TYPE TAG SIMPLE diagnostic pdiagnostic
25 | -- luacheck: ignore 611
26 | -- ***************************************************************
27 |
28 | local string = require "string"
29 | local math = require "math"
30 | local cbor_c = require "org.conman.cbor_c"
31 |
32 | local _VERSION = _VERSION
33 | local setmetatable = setmetatable
34 | local tostring = tostring
35 | local pcall = pcall
36 | local type = type
37 |
38 | if _VERSION == "Lua 5.1" then
39 | module "org.conman.cbormisc" -- luacheck: ignore
40 | else
41 | _ENV = {} -- luacheck: ignore
42 | end
43 |
44 | -- ***************************************************************
45 |
46 | local char_trans =
47 | {
48 | ['\a'] = '\\a',
49 | ['\b'] = '\\b',
50 | ['\t'] = '\\t',
51 | ['\n'] = '\\n',
52 | ['\v'] = '\\v',
53 | ['\f'] = '\\f',
54 | ['\r'] = '\\r',
55 | ['"'] = '\\"',
56 | ['\\'] = '\\\\',
57 | }
58 |
59 | local function safestring(v)
60 | if type(v) == 'string' then
61 | return '"' .. v:gsub(".",function(c)
62 | if char_trans[c] then
63 | return char_trans[c]
64 | end
65 |
66 | local b = c:byte()
67 |
68 | if b < 32 or b > 126 then
69 | return string.format("\\%03d",b)
70 | else
71 | return c
72 | end
73 | end) .. '"'
74 | else
75 | return tostring(v)
76 | end
77 | end
78 |
79 | -- *************************************************************
80 |
81 | local function bintext(packet,pos,info,value,ctype,f)
82 | if info == 31 then
83 | local res = "(_ "
84 | local comma = ""
85 |
86 | while true do
87 | local ltype,nvalue,npos = diagnostic(packet,pos)
88 | if ltype == '__break' then
89 | return ctype,res .. ")",npos
90 | end
91 | res = res .. comma .. nvalue
92 | comma = ", "
93 | pos = npos
94 | end
95 | end
96 |
97 | local bt = packet:sub(pos,pos + value - 1)
98 | return ctype,f(bt),pos + value
99 | end
100 |
101 | -- ***************************************************************
102 |
103 | TYPE =
104 | {
105 | [0x00] = function(_,pos,_,value)
106 | return 'UINT',tostring(value),pos
107 | end,
108 |
109 | [0x20] = function(_,pos,_,value)
110 | return 'NINT',tostring(-1 - value),pos
111 | end,
112 |
113 | [0x40] = function(packet,pos,info,value)
114 | return bintext(packet,pos,info,value,'BIN',function(c)
115 | c = c:gsub(".",function(ch)
116 | return string.format("%02X",ch:byte())
117 | end)
118 | return string.format("h'%s'",c)
119 | end)
120 | end,
121 |
122 | [0x60] = function(packet,pos,info,value)
123 | return bintext(packet,pos,info,value,'TEXT',safestring)
124 | end,
125 |
126 | [0x80] = function(packet,pos,info,value)
127 | local res = "["
128 | if info == 31 then
129 | res = res .. "_ "
130 | end
131 |
132 | for i = 1 , value do
133 | local ctype,s,npos = diagnostic(packet,pos)
134 | if ctype == '__break' then break end
135 | res = res .. s
136 | if i < value then res = res .. ", " end
137 | pos = npos
138 | end
139 | return 'ARRAY',res .. "]",pos
140 | end,
141 |
142 | [0xA0] = function(packet,pos,info,value)
143 | local res = "{"
144 | if info == 31 then
145 | res = res .. "_ "
146 | end
147 |
148 | for i = 1 , value do
149 | local ctypen,name,npos = diagnostic(packet,pos)
150 | if ctypen == '__break' then break end
151 | local _,val,npos2 = diagnostic(packet,npos)
152 | res = res .. string.format("%s: %s",name,val)
153 | if i < value then res = res .. ", " end
154 | pos = npos2
155 | end
156 | return 'MAP',res .. "}",pos
157 | end,
158 |
159 | [0xC0] = function(packet,pos,_,value)
160 | local _,s,npos = diagnostic(packet,pos)
161 | local ctype = TAG[value]
162 | return ctype,string.format("%s(%s)",ctype,s),npos
163 | end,
164 |
165 | [0xE0] = function(_,pos,info,value)
166 | if info >= 25 and info <= 27 then
167 | return '__float',SIMPLE[info](value),pos
168 | elseif info == 31 then
169 | return '__break',math.huge,pos
170 | else
171 | return SIMPLE[value],SIMPLE[value],pos
172 | end
173 | end,
174 | }
175 |
176 | -- ***************************************************************
177 |
178 | TAG = setmetatable(
179 | {
180 | [ 0] = "_datetime",
181 | [ 1] = "_epoch",
182 | [ 2] = "_pbignum",
183 | [ 3] = "_nbignum",
184 | [ 4] = "_decimalfraction",
185 | [ 5] = "_bigfloat",
186 | [ 21] = "_tobase64url",
187 | [ 22] = "_tobase64",
188 | [ 23] = "_tobase16",
189 | [ 24] = "_cbor",
190 | [ 32] = "_url",
191 | [ 33] = "_base64url",
192 | [ 34] = "_base64",
193 | [ 35] = "_regex",
194 | [ 36] = "_mime",
195 | [55799] = "_magic_cbor",
196 |
197 | [ 25] = "_nthstring",
198 | [ 26] = "_perlobj",
199 | [ 27] = "_serialobj",
200 | [ 28] = "_shareable",
201 | [ 29] = "_sharedref",
202 | [ 30] = "_rational",
203 | [ 37] = "_uuid",
204 | [ 38] = "_language",
205 | [ 39] = "_id",
206 | [ 256] = "_stringref",
207 | [ 257] = "_bmime",
208 | [ 264] = "_decimalfractionexp",
209 | [ 265] = "_bigfloatexp",
210 | [22098] = "_indirection",
211 | },
212 | {
213 | __index = function(_,key)
214 | return tostring(key)
215 | end
216 | }
217 | )
218 |
219 | -- ***************************************************************
220 |
221 | local function simple(value)
222 | if value ~= value then
223 | return 'NaN'
224 | elseif value == math.huge then
225 | return 'Infinity'
226 | elseif value == -math.huge then
227 | return '-Infinity'
228 | else
229 | return string.format('%f',value)
230 | end
231 | end
232 |
233 | -- ***************************************************************
234 |
235 | SIMPLE = setmetatable(
236 | {
237 | [20] = 'false',
238 | [21] = 'true',
239 | [22] = 'null',
240 | [23] = 'undefined',
241 | [25] = simple,
242 | [26] = simple,
243 | [27] = simple,
244 | [31] = '__break',
245 | },
246 | {
247 | __index = function(_,key)
248 | return string.format("simple(%d)",key)
249 | end
250 | }
251 | )
252 |
253 | -- ***************************************************************
254 |
255 | function diagnostic(packet,pos)
256 | pos = pos or 1
257 |
258 | local ctype,info,value,npos = cbor_c.decode(packet,pos)
259 | return TYPE[ctype](packet,npos,info,value)
260 | end
261 |
262 | -- ***************************************************************
263 |
264 | function pdiagnostic(packet,pos)
265 | local okay,result = pcall(diagnostic,packet,pos)
266 | if okay then
267 | return result
268 | else
269 | return nil,result
270 | end
271 | end
272 |
273 | -- ***************************************************************
274 |
275 | if _VERSION > "Lua 5.1" then
276 | return _ENV
277 | end
278 |
--------------------------------------------------------------------------------
/dnf.c:
--------------------------------------------------------------------------------
1 | /***************************************************************************
2 | *
3 | * Copyright 2016 by Sean Conner.
4 | *
5 | * This library is free software; you can redistribute it and/or modify it
6 | * under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation; either version 3 of the License, or (at your
8 | * option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful, but
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | * License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this library; if not, see .
17 | *
18 | * Comments, questions and criticisms can be sent to: sean@conman.org
19 | *
20 | * =======================================================================
21 | *
22 | * There are a lot of "magic numbers" in this file. This is intentional. I
23 | * don't expect IEEE-754 formats to go away any time soon, so the numbers
24 | * *are* defined per the spec. I find it easier to understand, say, the 15
25 | * in dnf_fromhalf()/dnf_tohalf() as being the maximum exponent than to have
26 | * to parse IEEE_754_HALF_MAX_EXP or some silliness like that. Your milage
27 | * may vary. You have been warned.
28 | *
29 | * Since the routines are all very similar, comments only appear in the first
30 | * routine of a set (dnf_fromhalf() and dnf_tohalf()). The magic numbers
31 | * change, but not the algorithm itself.
32 | *
33 | *************************************************************************/
34 |
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 |
41 | #include "dnf.h"
42 |
43 | typedef union
44 | {
45 | float f;
46 | uint32_t i;
47 | } float__u;
48 |
49 | typedef union
50 | {
51 | double d;
52 | uint64_t i;
53 | } double__u;
54 |
55 | /**************************************************************************
56 | * Normalize a subnormal floating point number---rotate the fractional
57 | * portion until the MSBit is set. As this is done, the exponent is adjust
58 | * accordingly.
59 | ***************************************************************************/
60 |
61 | static void dnfi_normalize(dnf__s *const pv)
62 | {
63 | assert(pv != NULL);
64 |
65 | for (
66 | size_t i = 0 ;
67 | (i < 64) && ((pv->frac & 0x8000000000000000uLL) == 0uLL) ;
68 | i++
69 | )
70 | {
71 | pv->frac *= 2;
72 | pv->exp--;
73 | }
74 | }
75 |
76 | /**************************************************************************
77 | * Denormalize a number to a subnormal floating point number. We do this to
78 | * the prescribed limit.
79 | ***************************************************************************/
80 |
81 | static void dnfi_denormalize(dnf__s *const pv,int maxexp)
82 | {
83 | while(pv->exp < maxexp)
84 | {
85 | pv->frac /= 2;
86 | pv->exp++;
87 | }
88 |
89 | assert(pv->frac != 0uLL); /* we should have at least one bit left */
90 | }
91 |
92 | /**************************************************************************
93 | * Conversion FROM half/single/double
94 | ***************************************************************************/
95 |
96 | int dnf_fromhalf(dnf__s *const pv,unsigned short int h)
97 | {
98 | assert(pv != NULL);
99 |
100 | /*-------------------------------------------------------
101 | ; Isolate the sign bit, the exponent and the fraction.
102 | ;--------------------------------------------------------*/
103 |
104 | pv->sign = (h >> 15) != 0;
105 | pv->exp = (h >> 10) & 0x1F;
106 | pv->frac = (unsigned long long)(h & 0x3FFu) << 53;
107 |
108 | /*----------------------------------------------------------------------
109 | ; Maximum exponent encodes +-inf and NaNs. The only difference between
110 | ; the two---the fraction is 0 for +-inf, otherwise, it's a NaN.
111 | ;----------------------------------------------------------------------*/
112 |
113 | if (pv->exp == 0x1F)
114 | pv->exp = INT_MAX;
115 |
116 | /*--------------------------------------------------------------------
117 | ; Exponent of 0 is either +-0 (with a fractional portion of 0) or a sub-
118 | ; normal (a non-zero fractional portion). If a subnormal, renornalize the
119 | ; number (that is, make sure the leading one bit is 1 and adjust the
120 | ; exponent accordingly).
121 | ;---------------------------------------------------------------------*/
122 |
123 | else if (pv->exp == 0)
124 | {
125 | if (pv->frac != 0uLL)
126 | {
127 | pv->exp = -14;
128 | dnfi_normalize(pv);
129 | }
130 | }
131 |
132 | /*---------------------------------------------------
133 | ; Otherwise, it's a normal floating point number.
134 | ;----------------------------------------------------*/
135 |
136 | else
137 | {
138 | pv->exp = pv->exp - 15;
139 | pv->frac |= 0x8000000000000000uLL;
140 | }
141 |
142 | return 0;
143 | }
144 |
145 | /**************************************************************************/
146 |
147 | int dnf_fromsingle(dnf__s *const pv,float f)
148 | {
149 | float__u x = { .f = f };
150 |
151 | assert(pv != NULL);
152 |
153 | pv->sign = (x.i >> 31) != 0;
154 | pv->exp = (int)((x.i >> 23) & 0xFFuL);
155 | pv->frac = (unsigned long long)(x.i & 0x007FFFFFuL) << 40;
156 |
157 | if (pv->exp == 0xFF)
158 | pv->exp = INT_MAX;
159 | else if (pv->exp == 0)
160 | {
161 | if (pv->frac != 0uLL)
162 | {
163 | pv->exp = - 126;
164 | dnfi_normalize(pv);
165 | }
166 | }
167 | else
168 | {
169 | pv->exp = pv->exp - 127;
170 | pv->frac |= 0x8000000000000000uLL;
171 | }
172 |
173 | return 0;
174 | }
175 |
176 | /**************************************************************************/
177 |
178 | int dnf_fromdouble(dnf__s *const pv,double d)
179 | {
180 | double__u x = { .d = d };
181 |
182 | assert(pv != NULL);
183 |
184 | pv->sign = (x.i >> 63) != 0;
185 | pv->exp = (int)((x.i >> 52) & 0x7FFuLL);
186 | pv->frac = (unsigned long long)(x.i & 0x000FFFFFFFFFFFFFuLL) << 11;
187 |
188 | if (pv->exp == 0x7FF)
189 | pv->exp = INT_MAX;
190 | else if (pv->exp == 0)
191 | {
192 | if (pv->frac != 0uLL)
193 | {
194 | pv->exp = -1022;
195 | dnfi_normalize(pv);
196 | }
197 | }
198 | else
199 | {
200 | pv->exp = pv->exp - 1023;
201 | pv->frac |= 0x8000000000000000uLL;
202 | }
203 |
204 | return 0;
205 | }
206 |
207 | /**************************************************************************
208 | * Conversion TO half/single/double
209 | ***************************************************************************/
210 |
211 | int dnf_tohalf(unsigned short int *const ph,dnf__s v)
212 | {
213 | unsigned short h;
214 |
215 | assert(ph != NULL);
216 |
217 | /*-------------------------------------------------------
218 | ; Maximum exponent designates either +-inf or a NaN.
219 | ;--------------------------------------------------------*/
220 |
221 | if (v.exp == INT_MAX)
222 | h = 0x7C00;
223 |
224 | /*-----------------------------------------------------------------------
225 | ; Normally a half-precision float can only handle exponents down to -14,
226 | ; but with subnormals, we can go as low as -24. We check the extreme low
227 | ; end with the normal high end. If we exceed either of those, we signal
228 | ; an error.
229 | ;------------------------------------------------------------------------*/
230 |
231 | else if ((v.exp < -24) || (v.exp > 15))
232 | return ERANGE;
233 |
234 | /*----------------------------------------
235 | ; Check for 0---this is a special case.
236 | ;-----------------------------------------*/
237 |
238 | else if ((v.exp == 0) && (v.frac == 0))
239 | h = 0;
240 |
241 | /*------------------------------------------------------------------------
242 | ; We have a subnormal. Adjust the fraction; the exponent is then set to 0
243 | ; to indicate a subnormal.
244 | ;-------------------------------------------------------------------------*/
245 |
246 | else if (v.exp < -14)
247 | {
248 | dnfi_denormalize(&v,-14);
249 | h = 0;
250 | }
251 |
252 | /*-----------------------------------
253 | ; It's a normal exponent.
254 | ;------------------------------------*/
255 |
256 | else
257 | h = (unsigned short)((unsigned)((v.exp + 15) & 0x1F) << 10);
258 |
259 | /*--------------------------------------------------------------------
260 | ; Check the precision and indicate an error if we exceed the number of
261 | ; bits we have for the fractional portion.
262 | ;---------------------------------------------------------------------*/
263 |
264 | if ((v.frac & 0x001FFFFFFFFFFFFFuLL) != 0uLL)
265 | return EDOM;
266 |
267 | h |= (unsigned short)(v.frac >> 53) & 0x03FFuLL;
268 | h |= v.sign ? 0x8000 : 0x0000;
269 | *ph = h;
270 | return 0;
271 | }
272 |
273 | /**************************************************************************/
274 |
275 | int dnf_tosingle(float *const pf,dnf__s v)
276 | {
277 | float__u f;
278 |
279 | assert(pf != NULL);
280 |
281 | if (v.exp == INT_MAX)
282 | f.i = (uint32_t)0x7F800000uL;
283 | else if ((v.exp < -149) || (v.exp > 127))
284 | return ERANGE;
285 | else if ((v.exp == 0) && (v.frac == 0))
286 | f.i = 0;
287 | else if (v.exp < -126)
288 | {
289 | dnfi_denormalize(&v,-126);
290 | f.i = 0;
291 | }
292 | else
293 | f.i = (uint32_t)((v.exp + 127) & 0xFFuL) << 23;
294 |
295 | if ((v.frac & 0x000000FFFFFFFFFFuLL) != 0uLL)
296 | return EDOM;
297 |
298 | f.i |= (uint32_t)(v.frac >> 40) & 0x007FFFFFuL;
299 | f.i |= v.sign ? 0x80000000uL : 0x00000000uL;
300 | *pf = f.f;
301 | return 0;
302 | }
303 |
304 | /**************************************************************************/
305 |
306 | int dnf_todouble(double *const pd,dnf__s v)
307 | {
308 | double__u d;
309 |
310 | assert(pd != NULL);
311 |
312 | if (v.exp == INT_MAX)
313 | d.i = 0x7FF0000000000000uLL;
314 | else if ((v.exp < -1074) || (v.exp > 1023))
315 | return ERANGE;
316 | else if ((v.exp == 0) && (v.frac == 0))
317 | d.i = 0;
318 | else if (v.exp < -1022)
319 | {
320 | dnfi_denormalize(&v,-1022);
321 | d.i = 0;
322 | }
323 | else
324 | d.i = (uint64_t)((v.exp + 1023) & 0x7FFuLL) << 52;
325 |
326 | if ((v.frac & 0x0000000000000FFFuLL) != 0uLL)
327 | return EDOM;
328 |
329 | d.i |= (uint64_t)((v.frac >> 11) & 0x000FFFFFFFFFFFFFuLL);
330 | d.i |= v.sign ? 0x8000000000000000uLL : 0x0000000000000000uLL;
331 | *pd = d.d;
332 | return 0;
333 | }
334 |
335 | /**************************************************************************/
336 |
--------------------------------------------------------------------------------
/dnf.h:
--------------------------------------------------------------------------------
1 | /***************************************************************************
2 | *
3 | * Copyright 2016 by Sean Conner.
4 | *
5 | * This library is free software; you can redistribute it and/or modify it
6 | * under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation; either version 3 of the License, or (at your
8 | * option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful, but
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | * License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this library; if not, see .
17 | *
18 | * Comments, questions and criticisms can be sent to: sean@conman.org
19 | *
20 | * =======================================================================
21 | *
22 | * DNF---the floating point conversion routines. These routines allow you to
23 | * safely convert halfs (16b IEEE-754), singles (32b IEEE-754) or doubles
24 | * (64b IEEE-764) values to halfs, singles or doubles. This is done in two
25 | * steps, a conversion *from* one of the formats, and a conversion *to* one
26 | * of the formats.
27 | *
28 | * dnf_fromhalf()
29 | * Convert from IEEE-754 16b format to internal format used
30 | * for conversion.
31 | *
32 | * dnf_fromsingle()
33 | * Convert from IEEE-754 32b format to internal format used
34 | * for conversion.
35 | *
36 | * dnf_fromdouble()
37 | * Convert from IEEE-754 62b format to internal format used
38 | * for conversion.
39 | *
40 | * dnf_tohalf()
41 | * Convert internal format to IEEE-754 16b format.
42 | *
43 | * dnf_tosingle()
44 | * Convert internal format to IEEE-754 32b format.
45 | *
46 | * dnf_todouble()
47 | * Convert internal format to IEEE_754 64b format.
48 | *
49 | * All routines will return an error code:
50 | *
51 | * 0 conversion succeeded
52 | * EDOM fraction contains too many bits to safely convert
53 | * ERANGE exponent exceeds allowable range of format
54 | *
55 | *************************************************************************/
56 |
57 | #ifndef I_C49BBFEF_CB06_5427_B636_83754812AC51
58 | #define I_C49BBFEF_CB06_5427_B636_83754812AC51
59 |
60 | #include
61 |
62 | typedef struct
63 | {
64 | bool sign;
65 | int exp;
66 | unsigned long long frac;
67 | } dnf__s;
68 |
69 | extern int dnf_fromhalf (dnf__s *const,unsigned short int);
70 | extern int dnf_fromsingle(dnf__s *const,float);
71 | extern int dnf_fromdouble(dnf__s *const,double);
72 |
73 | extern int dnf_tohalf (unsigned short int *const,dnf__s);
74 | extern int dnf_tosingle (float *const,dnf__s);
75 | extern int dnf_todouble (double *const,dnf__s);
76 |
77 | #endif
78 |
--------------------------------------------------------------------------------
/org.conman.cbor-1.4.0-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "org.conman.cbor"
2 | version = "1.4.0-1"
3 |
4 | source =
5 | {
6 | url = "git+https://github.com/spc476/CBOR.git",
7 | tag = version:match "[^-]+"
8 | }
9 |
10 | description =
11 | {
12 | homepage = "http://github.com/spc476/CBOR.git",
13 | maintainer = "Sean Conner ",
14 | license = "LGPL3+",
15 | summary = "The most comprehensive CBOR module in the Lua universe.",
16 | detailed = [[
17 | A complete implementation of CBOR (Concise Binary Object
18 | Representation) with all the currently defined bells and whistles,
19 | including string, array and map references (if so desired). A
20 | simpler, small implementation of CBOR is also provided for less
21 | intensive or simpler uses.
22 | ]]
23 | }
24 |
25 | dependencies =
26 | {
27 | "lua >= 5.1, <= 5.4",
28 | "lpeg ~= 1.0",
29 | }
30 |
31 | build =
32 | {
33 | platforms =
34 | {
35 | linux = { build_variables = { CC = "gcc -std=c99" } },
36 | solaris = { build_varaibles = { CC = "c99" } },
37 | windows =
38 | {
39 | type = "builtin",
40 | modules =
41 | {
42 | ['org.conman.cbor_c'] =
43 | {
44 | sources = { 'cbor_c.c', 'dnf.c' },
45 | defines = { 'VERSION="' .. source.tag .. '"' },
46 | },
47 |
48 | ['org.conman.cbor'] = 'cbor.lua',
49 | ['org.conman.cbor_s'] = 'cbor_s.lua',
50 | ['org.conman.cbormisc'] = 'cbormisc.lua',
51 | }
52 | }
53 | },
54 |
55 | type = "make",
56 | build_variables =
57 | {
58 | CC = "$(CC)",
59 | CFLAGS = "$(CFLAGS) -DNDEBUG -I$(LUA_INCDIR)",
60 | LDFLAGS = "$(LIBFLAG)",
61 | LUA = "$(LUA)",
62 | },
63 |
64 | install_variables =
65 | {
66 | LIBDIR = "$(LIBDIR)",
67 | LUADIR = "$(LUADIR)",
68 | LUA = "$(LUA)",
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/test.lua:
--------------------------------------------------------------------------------
1 | -- ***************************************************************
2 | --
3 | -- Copyright 2016 by Sean Conner. All Rights Reserved.
4 | --
5 | -- This library is free software; you can redistribute it and/or modify it
6 | -- under the terms of the GNU Lesser General Public License as published by
7 | -- the Free Software Foundation; either version 3 of the License, or (at your
8 | -- option) any later version.
9 | --
10 | -- This library is distributed in the hope that it will be useful, but
11 | -- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | -- License for more details.
14 | --
15 | -- You should have received a copy of the GNU Lesser General Public License
16 | -- along with this library; if not, see .
17 | --
18 | -- Comments, questions and criticisms can be sent to: sean@conman.org
19 | --
20 | -- luacheck: globals cbor cbor_c test
21 | -- luacheck: ignore 611
22 | -- ***************************************************************
23 |
24 | cbor_c = require "org.conman.cbor_c"
25 | cbor = require "org.conman.cbor"
26 |
27 | -- ***********************************************************************
28 |
29 | local function assertf(cond,...)
30 | local msg = string.format(...)
31 | assert(cond,msg)
32 | end
33 |
34 | -- ***********************************************************************
35 |
36 | local function hextobin(hbin)
37 | local bin = ""
38 | for pair in hbin:gmatch "(%x%x)" do
39 | bin = bin .. string.char(tonumber(pair,16))
40 | end
41 | return bin
42 | end
43 |
44 | -- ***********************************************************************
45 |
46 | local function bintohex(bin)
47 | local hbin = ""
48 | for c in bin:gmatch(".") do
49 | hbin = hbin .. string.format("%02X ",string.byte(c))
50 | end
51 | return hbin
52 | end
53 |
54 | -- ***********************************************************************
55 |
56 | local function compare(a,b)
57 | if type(a) ~= type(b) then
58 | return false
59 | end
60 |
61 | if type(a) == 'table' then
62 | for name,value in pairs(a) do
63 | if not compare(value,b[name]) then
64 | return false
65 | end
66 | end
67 | for name,value in pairs(b) do
68 | if not compare(value,a[name]) then
69 | return false
70 | end
71 | end
72 | return true
73 | else
74 | if a ~= a and b ~= b then -- handle NaNs
75 | return true
76 | else
77 | return a == b
78 | end
79 | end
80 | end
81 |
82 | assert(compare({a=1,b=2},{b=2,a=1}))
83 | assert(compare({1,2,3},{1,2,3}))
84 |
85 | -- ***********************************************************************
86 |
87 | local function testx(ctype,hbinary,src,srcf,destf)
88 | local bin = hextobin(hbinary)
89 | local encoded
90 |
91 | io.stdout:write("\tTesting ",ctype," ...") io.stdout:flush()
92 | if srcf ~= 'SKIP' then
93 | if srcf then
94 | encoded = srcf()
95 | else
96 | encoded = cbor.encode(src)
97 | end
98 |
99 | assertf(encoded == bin,"encoding for %s failed:\n%s\n%s",ctype,bintohex(bin),bintohex(encoded))
100 | else
101 | print("SKIPPED encoding",ctype)
102 | encoded = bin
103 | end
104 |
105 | local decoded,_,rctype = cbor.decode(encoded)
106 |
107 | assertf(rctype == ctype,"decoding type failed: wanted %s got %s",ctype,rctype)
108 |
109 | if type(destf) == 'function' then
110 | assertf(destf(src,decoded),"decoding for %s is different",ctype)
111 | else
112 | assertf(compare(src,decoded),"decoding for %s is different",ctype)
113 | end
114 |
115 | io.stdout:write(" GO\n")
116 | return true
117 | end
118 |
119 | function test(...)
120 | local okay,ret = pcall(testx,...)
121 | if okay then return ret end
122 | if type(ret) == 'string' then
123 | print(string.format(" FAILED %s",ret))
124 | else
125 | print(string.format(" FAILED %s",ret.msg))
126 | end
127 | os.exit(1)
128 | end
129 |
130 | -- ***********************************************************************
131 |
132 | local function rtst(ctype,src,f,sref,stref)
133 | local encode
134 |
135 | io.stdout:write("\tTesting ",ctype," ...") io.stdout:flush()
136 | if f then
137 | encode = f(src,sref,stref)
138 | else
139 | encode = cbor.encode(src,sref,stref)
140 | end
141 |
142 | local decode,_,rctype = cbor.decode(encode)
143 | assertf(rctype == ctype,"decoding type failed: wanted %s got %s",ctype,rctype)
144 | assertf(compare(src,decode),"decoding for %s is different",ctype)
145 | io.stdout:write("GO!\n")
146 | end
147 |
148 | -- ***********************************************************************
149 | -- values from RFC-7049
150 | -- ***********************************************************************
151 |
152 | test('UINT',"00",0)
153 | test('UINT',"01",1)
154 | test('UINT',"0A",10)
155 | test('UINT',"17",23)
156 | test('UINT',"1818",24)
157 | test('UINT',"1819",25)
158 | test('UINT',"1864",100)
159 | test('UINT',"1903e8",1000)
160 | test('UINT',"1a000f4240",1000000)
161 | test('UINT',"1b000000e8d4a51000",1000000000000)
162 | test('NINT',"20",-1)
163 | test('NINT',"29",-10)
164 | test('NINT',"3863",-100)
165 | test('NINT',"3903E7",-1000)
166 | test('half',"F90000",0.0, function() return cbor.SIMPLE.half(0.0) end)
167 | test('half',"F98000",-0.0, function() return cbor.SIMPLE.half(-0.0) end)
168 | test('half',"F93C00",1.0, function() return cbor.SIMPLE.half(1.0) end)
169 | test('half',"F93E00",1.5)
170 | test('half',"F97BFF",65504.0,function() return cbor.SIMPLE.half(65504.0) end)
171 | test('single',"fa47c35000",100000.0, function() return cbor.SIMPLE.single(100000.0) end)
172 | test('single',"fa7f7fffff",3.4028234663852886e+38,
173 | function()
174 | return cbor.SIMPLE.single(3.40282346638528859811704183484516925440e+38)
175 | end)
176 | test('double',"fb7e37e43c8800759c",1.0e+300,
177 | function()
178 | return cbor.SIMPLE.double(1.0e+300)
179 | end)
180 | test('half',"f90001",5.960464477539063e-8)
181 | test('half',"f90400",0.00006103515625)
182 | test('half',"f9c400",-4.0,function() return cbor.SIMPLE.half(-4) end)
183 | test('double',"fbc010666666666666",-4.1)
184 | test('half',"f97c00",math.huge)
185 | test('half',"f9fe00",0/0) -- can't code a positive NaN here
186 | test('half',"f9fc00",-math.huge)
187 | test('single',"fa7f800000",math.huge,
188 | function() return cbor.SIMPLE.single(math.huge) end)
189 | test('single',"faffc00000",0/0, -- can't code positive NaN here
190 | function() return cbor.SIMPLE.single(0.0/0.0) end)
191 | test('single',"faff800000",-math.huge,
192 | function() return cbor.SIMPLE.single(-math.huge) end)
193 | test('double',"fb7ff0000000000000",math.huge,
194 | function() return cbor.SIMPLE.double(math.huge) end)
195 | test('double',"fbfff8000000000000",0/0, -- can't code positive NaN here
196 | function() return cbor.SIMPLE.double(0/0) end)
197 | test('double',"fbfff0000000000000",-math.huge,
198 | function() return cbor.SIMPLE.double(-math.huge) end)
199 | test('false',"F4",false)
200 | test('true',"F5",true)
201 | test('null',"F6",nil)
202 | test('undefined',"F7",nil,function() return cbor.SIMPLE.undefined() end)
203 | test('SIMPLE',"F0",16, function() return cbor.SIMPLE['16']() end)
204 | test('SIMPLE',"f818",24, function() return cbor.SIMPLE['24']() end)
205 | test('SIMPLE',"F8FF",255,function() return cbor.SIMPLE['255']() end)
206 | test('_epoch',"c11a514b67b0",1363896240,
207 | function() return cbor.TAG._epoch(1363896240) end)
208 | test('_epoch',"c1fb41d452d9ec200000",1363896240.5,
209 | function() return cbor.TAG._epoch(1363896240.5) end)
210 | test('_tobase16',"d74401020304","\1\2\3\4", -- RFC wrong here
211 | function() return cbor.TAG._tobase16 "\1\2\3\4" end)
212 | test('_cbor',"d818456449455446","dIETF",
213 | function() return cbor.TAG._cbor(cbor.TYPE.TEXT("IETF")) end)
214 | test('_url',"d82076687474703a2f2f7777772e6578616d706c652e636f6d",
215 | "http://www.example.com",
216 | function() return cbor.TAG._url "http://www.example.com" end)
217 | test('BIN',"40","",function() return cbor.TYPE.BIN("") end)
218 | test('BIN',"4401020304","\1\2\3\4")
219 | test('TEXT',"60","")
220 | test('TEXT',"6161","a")
221 | test('TEXT',"6449455446","IETF")
222 | test('TEXT',"62225c",[["\]])
223 | test('TEXT',"62c3bc","\195\188")
224 | test('TEXT',"63e6b0b4","\230\176\180")
225 | test('TEXT',"64f0908591","\240\144\133\145")
226 | test('ARRAY',"80",{},function() return cbor.TYPE.ARRAY {} end)
227 | test('ARRAY',"83010203",{1,2,3})
228 | test('ARRAY',"8301820203820405",{ 1 , { 2 , 3 } , { 4 , 5 }})
229 | test('ARRAY',"98190102030405060708090a0b0c0d0e0f101112131415161718181819",
230 | { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 })
231 | test('MAP',"A0",{})
232 | test('MAP',"a201020304",{ [1] = 2 , [3] = 4},
233 | function()
234 | return cbor.TYPE.MAP(2)
235 | .. cbor.encode(1) .. cbor.encode(2)
236 | .. cbor.encode(3) .. cbor.encode(4)
237 | end)
238 | rtst('MAP',{ [1] = 2 , [3] = 4 },cbor.TYPE.MAP)
239 | test('MAP',"a26161016162820203",{ a = 1 , b = { 2, 3 } },
240 | function()
241 | return cbor.TYPE.MAP(2)
242 | .. cbor.encode "a" .. cbor.encode(1)
243 | .. cbor.encode "b" .. cbor.encode { 2 , 3 }
244 | end)
245 | rtst('MAP',{ a = 1 , b = { 2 , 3 }} )
246 | test('ARRAY',"826161a161626163",{ "a" , { b = "c" }})
247 | test('MAP',"a56161614161626142616361436164614461656145",
248 | { a = 'A' , b = 'B' , c = 'C' , d = 'D' , e = 'E' },
249 | function()
250 | return cbor.TYPE.MAP(5)
251 | .. cbor.encode "a" .. cbor.encode "A"
252 | .. cbor.encode "b" .. cbor.encode "B"
253 | .. cbor.encode "c" .. cbor.encode "C"
254 | .. cbor.encode "d" .. cbor.encode "D"
255 | .. cbor.encode "e" .. cbor.encode "E"
256 | end
257 | )
258 | rtst('MAP',{ a = "A" , b = 'B' , c = 'C' , d = "D" , e = [[E]] })
259 | test('BIN',"5f42010243030405ff","\1\2\3\4\5",
260 | function()
261 | return cbor_c.encode(0x40)
262 | .. cbor.TYPE.BIN "\1\2"
263 | .. cbor.TYPE.BIN "\3\4\5"
264 | .. cbor.SIMPLE.__break()
265 | end)
266 | test('TEXT',"7f657374726561646d696e67ff","streaming",
267 | function()
268 | return cbor_c.encode(0x60)
269 | .. cbor.TYPE.TEXT("strea")
270 | .. cbor.TYPE.TEXT("ming")
271 | .. cbor.SIMPLE.__break()
272 | end)
273 | test('ARRAY',"9fff",{},
274 | function()
275 | return cbor.TYPE.ARRAY()
276 | .. cbor.SIMPLE.__break()
277 | end)
278 | test('ARRAY',"9f018202039f0405ffff",{ 1 , { 2 , 3 } , { 4 , 5 }},
279 | function()
280 | return cbor.TYPE.ARRAY()
281 | .. cbor.encode(1)
282 | .. cbor.encode { 2 , 3 }
283 | .. cbor.TYPE.ARRAY()
284 | .. cbor.encode(4)
285 | .. cbor.encode(5)
286 | .. cbor.SIMPLE.__break()
287 | .. cbor.SIMPLE.__break()
288 | end)
289 | test('ARRAY',"9f01820203820405ff",{ 1 , { 2 ,3 } , { 4 , 5 }},
290 | function()
291 | return cbor.TYPE.ARRAY()
292 | .. cbor.encode(1)
293 | .. cbor.encode{ 2 , 3 }
294 | .. cbor.encode{ 4 , 5 }
295 | .. cbor.SIMPLE.__break()
296 | end)
297 | test('ARRAY',"83018202039f0405ff",{ 1 , { 2 ,3 } , { 4 , 5 }},
298 | function()
299 | return cbor.TYPE.ARRAY(3)
300 | .. cbor.encode(1)
301 | .. cbor.encode { 2, 3 }
302 | .. cbor.TYPE.ARRAY()
303 | .. cbor.encode(4)
304 | .. cbor.encode(5)
305 | .. cbor.SIMPLE.__break()
306 | end)
307 | test('ARRAY',"83019F0203FF820405",{ 1 , { 2 ,3 } , { 4 , 5 }},
308 | function()
309 | return cbor.TYPE.ARRAY(3)
310 | .. cbor.encode(1)
311 | .. cbor.TYPE.ARRAY()
312 | .. cbor.encode(2)
313 | .. cbor.encode(3)
314 | .. cbor.SIMPLE.__break()
315 | .. cbor.encode { 4 , 5 }
316 | end)
317 | test('ARRAY',"9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff",
318 | { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 },
319 | function()
320 | local res = cbor.TYPE.ARRAY()
321 | for i = 1 , 25 do
322 | res = res .. cbor.encode(i)
323 | end
324 | return res .. cbor.SIMPLE.__break()
325 | end)
326 | test('MAP',"bf61610161629f0203ffff",{ a = 1 , b = { 2, 3 } },
327 | function()
328 | return cbor.TYPE.MAP()
329 | .. cbor.encode "a" .. cbor.encode(1)
330 | .. cbor.encode "b" .. cbor.TYPE.ARRAY()
331 | .. cbor.encode(2)
332 | .. cbor.encode(3)
333 | .. cbor.SIMPLE.__break()
334 | .. cbor.SIMPLE.__break()
335 | end)
336 | test('ARRAY',"826161bf61626163ff",{ "a" , { b = "c" }},
337 | function()
338 | return cbor.TYPE.ARRAY(2)
339 | .. cbor.encode "a"
340 | .. cbor.TYPE.MAP()
341 | .. cbor.encode "b" .. cbor.encode "c"
342 | .. cbor.SIMPLE.__break()
343 | end)
344 | test('MAP',"bf6346756ef563416d7421ff",{ Fun = true , Amt = -2 },
345 | function()
346 | return cbor.TYPE.MAP()
347 | .. cbor.encode "Fun" .. cbor.SIMPLE['true']()
348 | .. cbor.encode "Amt" .. cbor.encode(-2)
349 | .. cbor.SIMPLE.__break()
350 | end)
351 |
352 | -- ***********************************************************************
353 | -- other tests
354 | -- ***********************************************************************
355 |
356 | test('TAG_1234567890',"DA499602D200",0,
357 | function() return cbor.TAG['1234567890'](0) end)
358 | test('_datetime',"C073323031362D30332D30315431343A31343A3333","2016-03-01T14:14:33",
359 | function() return cbor.TAG._datetime "2016-03-01T14:14:33" end)
360 | test('_pbignum',"C24A0102030405060708090A","\1\2\3\4\5\6\7\8\9\10",
361 | function() return cbor.TAG._pbignum "\1\2\3\4\5\6\7\8\9\10" end)
362 | test('_nbignum',"C34A8002030405060708090A","\128\2\3\4\5\6\7\8\9\10",
363 | function() return cbor.TAG._nbignum "\128\2\3\4\5\6\7\8\9\10" end)
364 | test('_decimalfraction',"C4820103",{ 1 , 3 },
365 | function() return cbor.TAG._decimalfraction { 1 , 3 } end)
366 | test('_bigfloat',"C5822003",{ -1 , 3 },
367 | function() return cbor.TAG._bigfloat { -1 , 3 } end)
368 | test('_tobase64url',"D54401020304","\1\2\3\4",
369 | function() return cbor.TAG._tobase64url "\1\2\3\4" end)
370 | test('_tobase64',"D64401020304","\1\2\3\4",
371 | function() return cbor.TAG._tobase64 "\1\2\3\4" end)
372 | test('_base64url',"D821684142434461626364","ABCDabcd",
373 | function() return cbor.TAG._base64url "ABCDabcd" end)
374 | test('_base64',"D822684142434461626364","ABCDabcd",
375 | function() return cbor.TAG._base64 "ABCDabcd" end)
376 | test('_regex',"D823712F5B52725D5B45655D5B47675D65783F2F","/[Rr][Ee][Gg]ex?/",
377 | function() return cbor.TAG._regex "/[Rr][Ee][Gg]ex?/" end)
378 | test('_mime',
379 | "D824781E436F6E74656E742D547970653A206170706C69636174696F6E2F63626F72",
380 | "Content-Type: application/cbor",
381 | function()
382 | return cbor.TAG._mime "Content-Type: application/cbor"
383 | end)
384 | test('_magic_cbor',"D9D9F7","_magic_cbor",
385 | function() return cbor.TAG._magic_cbor() end)
386 |
387 | -- _stringref and _nthstring tests
388 | -- http://cbor.schmorp.de/stringref
389 | -- This is annoying to test.
390 |
391 | test('ARRAY',"d9010083a34472616e6b0445636f756e741901a1446e616d6548436f636b7461696ca3d819024442617468d81901190138d8190004a3d8190244466f6f64d819011902b3d8190004", -- luacheck: ignore
392 | {
393 | {
394 | name = 'Cocktail',
395 | count = 417,
396 | rank = 4,
397 | },
398 | {
399 | rank = 4,
400 | count = 312,
401 | name = "Bath",
402 | },
403 | {
404 | count = 691,
405 | name = "Food",
406 | rank = 4
407 | },
408 | },
409 | function()
410 | local stref = {}
411 | return cbor.TAG._stringref(nil,nil,stref)
412 | .. cbor.TYPE.ARRAY(3)
413 | .. cbor.TYPE.MAP(3)
414 | .. cbor.TYPE.BIN("rank",nil,stref)
415 | .. cbor.encode(4,nil,stref)
416 | .. cbor.TYPE.BIN("count",nil,stref)
417 | .. cbor.encode(417,nil,stref)
418 | .. cbor.TYPE.BIN("name",nil,stref)
419 | .. cbor.TYPE.BIN("Cocktail",nil,stref)
420 |
421 | .. cbor.TYPE.MAP(3)
422 | .. cbor.TYPE.BIN("name",nil,stref)
423 | .. cbor.TYPE.BIN("Bath",nil,stref)
424 | .. cbor.TYPE.BIN("count",nil,stref)
425 | .. cbor.encode(312,nil,stref)
426 | .. cbor.TYPE.BIN("rank",nil,stref)
427 | .. cbor.encode(4,nil,stref)
428 |
429 | .. cbor.TYPE.MAP(3)
430 | .. cbor.TYPE.BIN("name",nil,stref)
431 | .. cbor.TYPE.BIN("Food",nil,stref)
432 | .. cbor.TYPE.BIN("count",nil,stref)
433 | .. cbor.encode(691,nil,stref)
434 | .. cbor.TYPE.BIN("rank",nil,stref)
435 | .. cbor.encode(4,nil,stref)
436 | end)
437 |
438 | rtst('ARRAY', {
439 | {
440 | name = 'Cocktail',
441 | count = 417,
442 | rank = 4,
443 | },
444 | {
445 | rank = 4,
446 | count = 312,
447 | name = "Bath",
448 | },
449 | {
450 | count = 691,
451 | name = "Food",
452 | rank = 4
453 | },
454 | },nil,nil,{})
455 |
456 | -- NOTE: in the 2nd example, the JSON array and the binary CBOR
457 | -- representation don't match. The JSON array here is fixed to match the
458 | -- actual binary presented.
459 |
460 | test('ARRAY',"d9010098204131433232324333333341344335353543363636433737374338383843393939436161614362626243636363436464644365656543666666436767674368686843696969436a6a6a436b6b6b436c6c6c436d6d6d436e6e6e436f6f6f4370707043717171437272724473737373d81901d8191743727272d8191818", -- luacheck: ignore
461 | {
462 | "1", "222", "333", "4", "555", "666", "777", "888", "999",
463 | "aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii",
464 | "jjj", "kkk", "lll", "mmm", "nnn", "ooo", "ppp", "qqq", "rrr",
465 | "ssss" , "333", "qqq", "rrr", "ssss"
466 | },
467 | function()
468 | local data =
469 | {
470 | "1", "222", "333", "4", "555", "666", "777", "888", "999",
471 | "aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii",
472 | "jjj", "kkk", "lll", "mmm", "nnn", "ooo", "ppp", "qqq", "rrr",
473 | "ssss" , "333", "qqq", "rrr", "ssss"
474 | }
475 |
476 | local stref = {}
477 | local res = cbor.TAG._stringref(nil,nil,stref) .. cbor.TYPE.ARRAY(#data,nil,stref)
478 | for _,s in ipairs(data) do
479 | res = res .. cbor.TYPE.BIN(s,nil,stref)
480 | end
481 | return res
482 | end)
483 |
484 | rtst('ARRAY', {
485 | "1", "222", "333", "4", "555", "666", "777", "888", "999",
486 | "aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii",
487 | "jjj", "kkk", "lll", "mmm", "nnn", "ooo", "ppp", "qqq", "rrr",
488 | "ssss" , "333", "qqq", "rrr", "ssss"
489 | },nil,nil,{})
490 |
491 | test('ARRAY',"d901008563616161d81900d90100836362626263616161d81901d901008263636363d81900d81900",
492 | {
493 | "aaa" , "aaa" ,
494 | { "bbb" , "aaa" , "aaa" } ,
495 | { "ccc" , "ccc" } ,
496 | "aaa"
497 | },
498 | function()
499 | local stref1 = {}
500 | local stref2 = {}
501 | local stref3 = {}
502 |
503 | return cbor.TAG._stringref(nil,nil,stref1)
504 | .. cbor.TYPE.ARRAY(5)
505 | .. cbor.encode("aaa",nil,stref1)
506 | .. cbor.encode("aaa",nil,stref1)
507 | .. cbor.TAG._stringref(nil,nil,stref2)
508 | .. cbor.TYPE.ARRAY(3)
509 | .. cbor.encode("bbb",nil,stref2)
510 | .. cbor.encode("aaa",nil,stref2)
511 | .. cbor.encode("aaa",nil,stref2)
512 | .. cbor.TAG._stringref(nil,nil,stref3)
513 | .. cbor.TYPE.ARRAY(2)
514 | .. cbor.encode("ccc",nil,stref3)
515 | .. cbor.encode("ccc",nil,stref3)
516 | .. cbor.encode("aaa",nil,stref1)
517 | end)
518 |
519 | -- _perlobj
520 |
521 | test('_perlobj',"d81a826c4d793a3a4461746554696d651a00bc614e",
522 | { "My::DateTime" , 12345678 },
523 | function() return cbor.TAG._perlobj { 'My::DateTime' , 12345678 } end)
524 |
525 | -- _shareable and __sharedref
526 | -- http://cbor.schmorp.de/value-sharing
527 |
528 | test('ARRAY',"83d81c80d81d0080",{{},{},{}},
529 | function()
530 | local x = {}
531 | local ref = {}
532 | return cbor.TYPE.ARRAY(3)
533 | .. cbor.TYPE.ARRAY(x,ref)
534 | .. cbor.TYPE.ARRAY(x,ref)
535 | .. cbor.TYPE.ARRAY({})
536 | end,
537 | function(_,v)
538 | assertf(type(v) == 'table',"_sharedref: wanted table got %s",type(v))
539 | assertf(#v == 3,"_sharedref: wanted a table of three entries")
540 | assertf(type(v[1]) == 'table',"_sharedref: wanted v[1] as table")
541 | assertf(type(v[2]) == 'table',"_sharedref: wanged v[2] as table")
542 | assertf(type(v[3]) == 'table','_sharedref: wanted v[3] as table')
543 | assertf(v[1] == v[2],"_sharedref: wanted first two tables equal")
544 |
545 | return true
546 | end)
547 |
548 | local ref1 = {} ref1[1] = ref1
549 | test('ARRAY',"d81c81d81d00",ref1,
550 | function()
551 | return cbor.encode(ref1,{})
552 | end,
553 |
554 | function(_,v)
555 | assertf(type(v) == 'table',"_sharedref: wanted table got %s",type(v))
556 | assertf(#v == 1,"_sharedref: length bad %d",#v)
557 | assertf(v[1] == v,"_sharedref: not a reference")
558 | return true
559 | end)
560 |
561 | local ref2 = { 1 , 2 , 3 }
562 | rtst('ARRAY',{ ref2 , ref2 },
563 | function(v)
564 | return cbor.encode(v,{})
565 | end)
566 |
567 | -- _rational
568 | -- http://peteroupc.github.io/CBOR/rational.html
569 |
570 | test('_rational',"d81e820103",{ 1 , 3 },
571 | function() return cbor.TAG._rational { 1 , 3 } end)
572 |
573 | -- _uuid
574 | -- https://github.com/lucas-clemente/cbor-specs/blob/master/uuid.md
575 |
576 | test('_uuid',"D825506BA7B8119DAD11D180B400C04FD430C8",
577 | "k\167\184\17\157\173\17\209\128\180\0\192O\2120\200",
578 | function()
579 | return cbor.TAG._uuid "k\167\184\17\157\173\17\209\128\180\0\192O\2120\200"
580 | end)
581 |
582 | -- _language
583 | -- http://peteroupc.github.io/CBOR/langtags.html
584 |
585 | test('_language',"d8268262656E6548656C6C6F",{ "en" , "Hello"},
586 | function() return cbor.TAG._language { "en" , "Hello" } end)
587 |
588 | test('_language',"d8268262667267426F6E6A6F7572",{ "fr" , "Bonjour" },
589 | function() return cbor.TAG._language { "fr" , "Bonjour" } end)
590 |
591 | -- _id
592 | -- https://github.com/lucas-clemente/cbor-specs/blob/master/id.md
593 |
594 | test('_id',"D82768696F2E737464696E","io.stdin",
595 | function() return cbor.TAG._id "io.stdin" end)
596 |
597 | -- _bmime
598 | -- http://peteroupc.github.io/CBOR/binarymime.html
599 |
600 | test('_bmime',"D901014401020304","\1\2\3\4",
601 | function() return cbor.TAG._bmime "\1\2\3\4" end)
602 |
603 | -- _decimalfractionexp and _bigfloatexp
604 | -- http://peteroupc.github.io/CBOR/bigfrac.html
605 |
606 | test('_decimalfractionexp',
607 | "D90108824A0102030405060708090A03",
608 | { "\1\2\3\4\5\6\7\8\9\10" , 3 },
609 | function()
610 | return cbor.TAG._decimalfractionexp { "\1\2\3\4\5\6\7\8\9\10" , 3 }
611 | end)
612 |
613 | test('_bigfloatexp',
614 | "D90109824A0102030405060708090A03",
615 | { "\1\2\3\4\5\6\7\8\9\10" , 3 },
616 | function()
617 | return cbor.TAG._bigfloatexp { "\1\2\3\4\5\6\7\8\9\10" , 3 }
618 | end)
619 |
620 | -- _indirection
621 | -- http://cbor.schmorp.de/indirection
622 |
623 | test('_indirection',"D95652820102" , { 1 , 2 },
624 | function() return cbor.TAG._indirection { 1 , 2 } end)
625 |
626 | -- _ipaddress
627 | -- http://www.employees.org/~ravir/cbor-network.txt
628 | -- IPv6 encoding example wrong in spec
629 |
630 | test('_ipaddress','D9010444C00A0A01',"\192\10\10\1",
631 | function() return cbor.TAG._ipaddress "\192\10\10\1" end)
632 |
633 | test('_ipaddress',"D90104460123456789AB","\01\35\69\103\137\171",
634 | function() return cbor.TAG._ipaddress "\01\35\69\103\137\171" end)
635 |
636 | test('_ipaddress',"D901045020010db885a3000000008a2e03707334",hextobin("20010db885a3000000008a2e03707334"),
637 | function() return cbor.TAG._ipaddress(hextobin("20010db885a3000000008a2e03707334")) end)
638 |
639 | -- ***********************************************************************
640 | -- And now, test *both* types of references in the same structure. In order
641 | -- to ensure a consistent check, we use arrays only for this test. The
642 | -- second test using a MAP.
643 | -- ***********************************************************************
644 |
645 | local hoade = { 'first' , "Sean" , 'last' , "Hoade" , 'occupation' , "writer" }
646 | local conner = { 'first' , "Sean" , 'last' , "Conner" , 'occupation' , "programmer" }
647 | local array = { hoade , hoade , hoade , conner , conner , conner }
648 | test('ARRAY',
649 | "D90100D81C86D81C86656669727374645365616E646C61737465486F6164656A6F636375706174696F6E66777269746572D81D01D81D01D81C86D81900D81901D8190266436F6E6E6572D819046A70726F6772616D6D6572D81D02D81D02", -- luacheck: ignore
650 | array,
651 | function()
652 | return cbor.encode(array,{},{})
653 | end)
654 |
655 | local hoade2 = { first = "Sean" , last = "Hoade" , occupation = "writer" }
656 | local conner2 = { first = "Sean" , last = "Conner" , occupation = "programmer" }
657 | local array2 = { hoade2 , hoade2 , hoade2 , conner2 , conner2 , conner2 }
658 | rtst('ARRAY',array2,nil,{},{})
659 |
660 | -- *********************************************************************
661 | -- Read https://britram.github.io/rains-prototype/#cbor-object for
662 | -- context for this test.
663 | -- *********************************************************************
664 |
665 | local q =
666 | {
667 | [0] = -- content key
668 | {
669 | {
670 | 4 , -- query type
671 |
672 | -- ------------------------------------------------------------------
673 | -- This is a sparse array, and because of that, it's stored in the
674 | -- hash portion of the table, which means the order of fields is
675 | -- unpredictable. In order to fix this, we'll set a __tocbor method
676 | -- on this subtable to make sure we have this in order.
677 | -- ------------------------------------------------------------------
678 |
679 | setmetatable(
680 | {
681 | [ 5] = "www.conman.org.", -- query name
682 | [13] = { "." }, -- query context
683 | [14] = { 1 , 2 , 3 } , -- query types
684 | },
685 | {
686 | __tocbor = function(self)
687 | return cbor.TYPE.MAP(3)
688 | .. cbor.encode( 5) .. cbor.encode(self[ 5])
689 | .. cbor.encode(13) .. cbor.encode(self[13])
690 | .. cbor.encode(14) .. cbor.encode(self[14])
691 | end
692 | })
693 | }
694 | }
695 | }
696 |
697 | test('_rains',"DA00E99BA8A100818204A3056F7777772E636F6E6D616E2E6F72672E0D81612E0E83010203"
698 | ,q,function() return cbor.TAG._rains(q) end)
699 |
700 | -- *********************************************************************
701 | -- Test for a custom null and undefined values. By default, Lua treats
702 | -- null and undefined as nil when decoding, and any nil value becomes null
703 | -- when encoding. If you want special sentinel values, define cbor.null
704 | -- and cbor.undefined to some unique value. NaN will break, because it is
705 | -- not equal even unto itself. An empty table is good enough.
706 | -- *********************************************************************
707 |
708 | cbor.null = {}
709 | cbor.undefined = {}
710 |
711 | test('null',"F6",cbor.null)
712 | test('undefined',"F7",cbor.undefined)
713 | test('ARRAY',"82F6F7",{ cbor.null , cbor.undefined })
714 |
--------------------------------------------------------------------------------
/test_s.lua:
--------------------------------------------------------------------------------
1 | -- ***************************************************************
2 | --
3 | -- Copyright 2016 by Sean Conner. All Rights Reserved.
4 | --
5 | -- This library is free software; you can redistribute it and/or modify it
6 | -- under the terms of the GNU Lesser General Public License as published by
7 | -- the Free Software Foundation; either version 3 of the License, or (at your
8 | -- option) any later version.
9 | --
10 | -- This library is distributed in the hope that it will be useful, but
11 | -- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | -- License for more details.
14 | --
15 | -- You should have received a copy of the GNU Lesser General Public License
16 | -- along with this library; if not, see .
17 | --
18 | -- Comments, questions and criticisms can be sent to: sean@conman.org
19 | --
20 | -- luacheck: globals cbor
21 | -- luacheck: ignore 611
22 | -- ***************************************************************
23 |
24 | local cbor = require "org.conman.cbor_s"
25 |
26 | -- ***********************************************************************
27 |
28 | local function assertf(cond,...)
29 | local msg = string.format(...)
30 | assert(cond,msg)
31 | end
32 |
33 | -- ***********************************************************************
34 |
35 | local function hextobin(hbin)
36 | local bin = ""
37 | for pair in hbin:gmatch "(%x%x)" do
38 | bin = bin .. string.char(tonumber(pair,16))
39 | end
40 | return bin
41 | end
42 |
43 | -- ***********************************************************************
44 |
45 | local function bintohex(bin)
46 | local hbin = ""
47 | for c in bin:gmatch(".") do
48 | hbin = hbin .. string.format("%02X ",string.byte(c))
49 | end
50 | return hbin
51 | end
52 |
53 | -- ***********************************************************************
54 |
55 | local function compare(a,b)
56 | if type(a) ~= type(b) then
57 | return false
58 | end
59 |
60 | if type(a) == 'table' then
61 | for name,value in pairs(a) do
62 | if not compare(value,b[name]) then
63 | return false
64 | end
65 | end
66 | for name,value in pairs(b) do
67 | if not compare(value,a[name]) then
68 | return false
69 | end
70 | end
71 | return true
72 | else
73 | if a ~= a and b ~= b then -- handle NaNs
74 | return true
75 | else
76 | return a == b
77 | end
78 | end
79 | end
80 |
81 | assert(compare({a=1,b=2},{b=2,a=1}))
82 | assert(compare({1,2,3},{1,2,3}))
83 |
84 | -- ***********************************************************************
85 |
86 | local function test(ctype,hbinary,src,srcf,destf)
87 | local bin = hextobin(hbinary)
88 | local encoded
89 |
90 | io.stdout:write("\tTesting ",ctype," ...") io.stdout:flush()
91 |
92 | if srcf ~= 'SKIP' then
93 | if srcf then
94 | encoded = srcf()
95 | else
96 | encoded = cbor.encode(src)
97 | end
98 |
99 | assertf(encoded == bin,"encoding for %s failed:\n%s\n%s",ctype,bintohex(bin),bintohex(encoded))
100 | else
101 | print("SKIPPED encoding",ctype)
102 | encoded = bin
103 | end
104 |
105 | local decoded,_,rctype = cbor.decode(encoded)
106 |
107 | assertf(rctype == ctype,"decoding type failed: wanted %s got %s",ctype,rctype)
108 |
109 | if type(destf) == 'function' then
110 | assertf(destf(src,decoded),"decoding for %s is different",ctype)
111 | else
112 | assertf(compare(src,decoded),"xdecoding for %s is different",ctype)
113 | end
114 |
115 | io.stdout:write("GO\n")
116 | return true
117 | end
118 |
119 | -- ***********************************************************************
120 |
121 | local function rtst(ctype,src,f)
122 | local encode
123 |
124 | io.stdout:write("\tTesting ",ctype," ...") io.stdout:flush()
125 | if f then
126 | encode = f(src)
127 | else
128 | encode = cbor.encode(src)
129 | end
130 |
131 | local decode,_,rctype = cbor.decode(encode)
132 | assertf(rctype == ctype,"decoding type failed: wanted %s got %s",ctype,rctype)
133 | assertf(compare(src,decode),"decoding for %s is different",ctype)
134 | io.stdout:write("GO!\n")
135 | end
136 |
137 | -- ***********************************************************************
138 | -- values from RFC-7049
139 | -- ***********************************************************************
140 |
141 | test('UINT',"00",0)
142 | test('UINT',"01",1)
143 | test('UINT',"0A",10)
144 | test('UINT',"17",23)
145 | test('UINT',"1818",24)
146 | test('UINT',"1819",25)
147 | test('UINT',"1864",100)
148 | test('UINT',"1903e8",1000)
149 | test('UINT',"1a000f4240",1000000)
150 | test('UINT',"1b000000e8d4a51000",1000000000000)
151 | test('NINT',"20",-1)
152 | test('NINT',"29",-10)
153 | test('NINT',"3863",-100)
154 | test('NINT',"3903E7",-1000)
155 | test('half',"F93E00",1.5)
156 | test('single',"fa7f7fffff",3.4028234663852886e+38,
157 | function()
158 | return cbor.encode(3.40282346638528859811704183484516925440e+38)
159 | end)
160 | test('double',"fb7e37e43c8800759c",1.0e+300,
161 | function()
162 | return cbor.encode(1.0e+300)
163 | end)
164 | test('half',"f90001",5.960464477539063e-8)
165 | test('half',"f90400",0.00006103515625)
166 | test('double',"fbc010666666666666",-4.1)
167 | test('half',"f97c00",math.huge)
168 | test('half',"f9fe00",0/0) -- can't code a positive NaN here
169 | test('half',"f9fc00",-math.huge)
170 | test('false',"F4",false)
171 | test('true',"F5",true)
172 | test('null',"F6",nil)
173 |
174 | test('UINT',"c11a514b67b0",1363896240,
175 | function() return cbor.encode(1363896240,1) end)
176 | test('double',"c1fb41d452d9ec200000",1363896240.5,
177 | function() return cbor.encode(1363896240.5,1) end)
178 | test('BIN',"d74401020304","\1\2\3\4", -- RFC wrong here
179 | function() return cbor.encode("\1\2\3\4",23) end)
180 | test('TEXT',"d818656449455446","dIETF", -- modified slightly from RFC
181 | function() return cbor.encode(cbor.encode("IETF"),24) end)
182 | test('TEXT',"d82076687474703a2f2f7777772e6578616d706c652e636f6d",
183 | "http://www.example.com",
184 | function() return cbor.encode("http://www.example.com",32) end)
185 | test('BIN',"4401020304","\1\2\3\4")
186 | test('TEXT',"60","")
187 | test('TEXT',"6161","a")
188 | test('TEXT',"6449455446","IETF")
189 | test('TEXT',"62225c",[["\]])
190 | test('TEXT',"62c3bc","\195\188")
191 | test('TEXT',"63e6b0b4","\230\176\180")
192 | test('TEXT',"64f0908591","\240\144\133\145")
193 | test('ARRAY',"83010203",{1,2,3})
194 | test('ARRAY',"8301820203820405",{ 1 , { 2 , 3 } , { 4 , 5 }})
195 | test('ARRAY',"98190102030405060708090a0b0c0d0e0f101112131415161718181819",
196 | { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 })
197 | rtst('MAP',{ a = 1 , b = { 2 , 3 }} )
198 | test('ARRAY',"826161a161626163",{ "a" , { b = "c" }})
199 | rtst('MAP',{ a = "A" , b = 'B' , c = 'C' , d = "D" , e = [[E]] })
200 |
201 | -- *********************************************************************
202 | -- Test for a custom null and undefined values. By default, Lua treats
203 | -- null and undefined as nil when decoding, and any nil value becomes null
204 | -- when encoding. If you want special sentinel values, define cbor.null
205 | -- and cbor.undefined to some unique value. NaN will break, because it is
206 | -- not equal even unto itself. An empty table is good enough.
207 | -- *********************************************************************
208 |
209 | cbor.null = {}
210 | cbor.undefined = {}
211 |
212 | test('null',"F6",cbor.null)
213 | test('undefined',"F7",cbor.undefined)
214 | test('ARRAY',"82F6F7",{ cbor.null , cbor.undefined })
215 |
--------------------------------------------------------------------------------