├── 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 | --------------------------------------------------------------------------------