├── .gitignore ├── .travis.yml ├── CHANGES ├── COPYRIGHT ├── Makefile ├── README.md ├── rockspec.in ├── rockspec ├── lua-messagepack-0.1.0-1.rockspec ├── lua-messagepack-0.2.0-1.rockspec ├── lua-messagepack-0.2.1-1.rockspec ├── lua-messagepack-0.2.2-1.rockspec └── lua-messagepack-0.3.0-1.rockspec ├── src └── MessagePack.lua └── test ├── 00-require.t ├── 01-spectest.t ├── 02-except.t ├── 03-edge.t ├── 04-cover.t ├── 11-userdata-bc.t ├── 12-metatable.t ├── 13-coat.t ├── 14-function.t └── 15-ffi-cdata.t /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /MANIFEST 3 | /README.html 4 | /*.tar 5 | /*.bak 6 | /*.tar.gz 7 | 8 | *.out 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Lua is not officially supported, but an erlang environment will do. 2 | language: erlang 3 | 4 | env: 5 | matrix: 6 | - LUA=lua5.1 LUAVER=5.1 7 | - LUA=lua5.2 LUAVER=5.2 8 | 9 | # Tool setup. 10 | install: 11 | - sudo apt-get install lua5.1 12 | - sudo apt-get install lua5.2 13 | - git clone git://github.com/fperrad/lua-TestMore.git 14 | - sudo make -C lua-TestMore install LUAVER="$LUAVER" 15 | 16 | # Build and test. 17 | script: 18 | - make LUA="$LUA" test 19 | 20 | branches: 21 | only: 22 | - master 23 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | Revision history for lua-MessagePack 2 | 3 | 0.3.0 Sun Sep 1 15:30:00 2013 4 | compliant with specifications v5 5 | 6 | 0.2.2 Sun Aug 25 09:30:00 2013 7 | fix packer when downcast double->float 8 | 9 | 0.2.1 Thu Nov 29 19:30:00 2012 10 | add set_array 11 | 12 | 0.2.0 Sat Nov 17 15:30:00 2012 13 | INCOMPATIBILITY : now, unpacker accepts a ltn12.source instead of a file 14 | 15 | 0.1.0 Sun Nov 11 09:30:00 2012 16 | First release 17 | 18 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | lua-MessagePack License 2 | -------------------------- 3 | 4 | lua-MessagePack is licensed under the terms of the MIT/X11 license reproduced below. 5 | 6 | =============================================================================== 7 | 8 | Copyright (C) 2012-2013 Francois Perrad. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | 28 | =============================================================================== 29 | 30 | (end of COPYRIGHT) 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | LUA := lua 3 | VERSION := $(shell cd src && $(LUA) -e "m = require [[MessagePack]]; print(m._VERSION)") 4 | TARBALL := lua-messagepack-$(VERSION).tar.gz 5 | REV := 1 6 | 7 | LUAVER := 5.1 8 | PREFIX := /usr/local 9 | DPREFIX := $(DESTDIR)$(PREFIX) 10 | LIBDIR := $(DPREFIX)/share/lua/$(LUAVER) 11 | INSTALL := install 12 | 13 | all: 14 | @echo "Nothing to build here, you can just make install" 15 | 16 | install: 17 | $(INSTALL) -m 644 -D src/MessagePack.lua $(LIBDIR)/MessagePack.lua 18 | 19 | uninstall: 20 | rm -f $(LIBDIR)/MessagePack.lua 21 | 22 | manifest_pl := \ 23 | use strict; \ 24 | use warnings; \ 25 | my @files = qw{MANIFEST}; \ 26 | while (<>) { \ 27 | chomp; \ 28 | next if m{^\.}; \ 29 | next if m{^doc/\.}; \ 30 | next if m{^doc/google}; \ 31 | next if m{^rockspec/}; \ 32 | push @files, $$_; \ 33 | } \ 34 | print join qq{\n}, sort @files; 35 | 36 | rockspec_pl := \ 37 | use strict; \ 38 | use warnings; \ 39 | use Digest::MD5; \ 40 | open my $$FH, q{<}, q{$(TARBALL)} \ 41 | or die qq{Cannot open $(TARBALL) ($$!)}; \ 42 | binmode $$FH; \ 43 | my %config = ( \ 44 | version => q{$(VERSION)}, \ 45 | rev => q{$(REV)}, \ 46 | md5 => Digest::MD5->new->addfile($$FH)->hexdigest(), \ 47 | ); \ 48 | close $$FH; \ 49 | while (<>) { \ 50 | s{@(\w+)@}{$$config{$$1}}g; \ 51 | print; \ 52 | } 53 | 54 | version: 55 | @echo $(VERSION) 56 | 57 | CHANGES: 58 | perl -i.bak -pe "s{^$(VERSION).*}{q{$(VERSION) }.localtime()}e" CHANGES 59 | 60 | tag: 61 | git tag -a -m 'tag release $(VERSION)' $(VERSION) 62 | 63 | doc: 64 | git read-tree --prefix=doc/ -u remotes/origin/gh-pages 65 | 66 | MANIFEST: doc 67 | git ls-files | perl -e '$(manifest_pl)' > MANIFEST 68 | 69 | $(TARBALL): MANIFEST 70 | [ -d lua-MessagePack-$(VERSION) ] || ln -s . lua-MessagePack-$(VERSION) 71 | perl -ne 'print qq{lua-MessagePack-$(VERSION)/$$_};' MANIFEST | \ 72 | tar -zc -T - -f $(TARBALL) 73 | rm lua-MessagePack-$(VERSION) 74 | rm -rf doc 75 | git rm doc/* 76 | 77 | dist: $(TARBALL) 78 | 79 | rockspec: $(TARBALL) 80 | perl -e '$(rockspec_pl)' rockspec.in > rockspec/lua-messagepack-$(VERSION)-$(REV).rockspec 81 | 82 | check: test 83 | 84 | test: 85 | cd src && prove --exec=$(LUA) ../test/*.t 86 | 87 | coverage: 88 | rm -f src/luacov.stats.out src/luacov.report.out 89 | cd src && prove --exec="$(LUA) -lluacov" ../test/*.t 90 | cd src && luacov 91 | 92 | README.html: README.md 93 | Markdown.pl README.md > README.html 94 | 95 | clean: 96 | rm -rf doc 97 | rm -f MANIFEST *.bak src/luacov.*.out README.html 98 | 99 | realclean: clean 100 | 101 | .PHONY: test rockspec CHANGES 102 | 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lua-MessagePack : a pure Lua implementation (spec v5) 2 | ===================================================== 3 | 4 | [![Build Status](https://travis-ci.org/fperrad/lua-MessagePack.png)](https://travis-ci.org/fperrad/lua-MessagePack) 5 | 6 | Introduction 7 | ------------ 8 | 9 | [MessagePack](http://msgpack.org/) is an efficient binary serialization format. 10 | 11 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 12 | 13 | It's a pure Lua implementation, without dependency. 14 | 15 | And it's really fast with [LuaJIT](http://luajit.org). 16 | 17 | Links 18 | ----- 19 | 20 | The homepage is at [http://fperrad.github.io/lua-MessagePack/](http://fperrad.github.io/lua-MessagePack/), 21 | and the sources are hosted at [http://github.com/fperrad/lua-MessagePack/](http://github.com/fperrad/lua-MessagePack/). 22 | 23 | Copyright and License 24 | --------------------- 25 | 26 | Copyright (c) 2012-2013 Francois Perrad 27 | 28 | This library is licensed under the terms of the MIT/X11 license, like Lua itself. 29 | 30 | -------------------------------------------------------------------------------- /rockspec.in: -------------------------------------------------------------------------------- 1 | package = 'lua-MessagePack' 2 | version = '@version@-@rev@' 3 | source = { 4 | url = 'http://sites.google.com/site/fperrad/lua-messagepack-@version@.tar.gz', 5 | md5 = '@md5@', 6 | dir = 'lua-MessagePack-@version@', 7 | } 8 | description = { 9 | summary = "a pure Lua implementation of the MessagePack serialization format", 10 | detailed = [[ 11 | MessagePack is an efficient binary serialization format. 12 | 13 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 14 | ]], 15 | homepage = 'http://fperrad.github.io/lua-MessagePack/', 16 | maintainer = 'Francois Perrad', 17 | license = 'MIT/X11' 18 | } 19 | dependencies = { 20 | 'lua >= 5.1', 21 | } 22 | build = { 23 | type = 'builtin', 24 | modules = { 25 | ['MessagePack'] = 'src/MessagePack.lua', 26 | }, 27 | copy_directories = { 'doc', 'test' }, 28 | } 29 | -------------------------------------------------------------------------------- /rockspec/lua-messagepack-0.1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-MessagePack' 2 | version = '0.1.0-1' 3 | source = { 4 | url = 'http://cloud.github.com/downloads/fperrad/lua-MessagePack/lua-messagepack-0.1.0.tar.gz', 5 | md5 = '3fb1625aa2b277d7913ee090fd770d87', 6 | dir = 'lua-MessagePack-0.1.0', 7 | } 8 | description = { 9 | summary = "a pure Lua implementation", 10 | detailed = [[ 11 | MessagePack is an efficient binary serialization format. 12 | 13 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 14 | ]], 15 | homepage = 'http://fperrad.github.com/lua-MessagePack/', 16 | maintainer = 'Francois Perrad', 17 | license = 'MIT/X11' 18 | } 19 | dependencies = { 20 | 'lua >= 5.1', 21 | } 22 | build = { 23 | type = 'builtin', 24 | modules = { 25 | ['MessagePack'] = 'src/MessagePack.lua', 26 | }, 27 | copy_directories = { 'doc', 'test' }, 28 | } 29 | -------------------------------------------------------------------------------- /rockspec/lua-messagepack-0.2.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-MessagePack' 2 | version = '0.2.0-1' 3 | source = { 4 | url = 'http://cloud.github.com/downloads/fperrad/lua-MessagePack/lua-messagepack-0.2.0.tar.gz', 5 | md5 = 'bf08ae0e81c83fa512be9041a45de44e', 6 | dir = 'lua-MessagePack-0.2.0', 7 | } 8 | description = { 9 | summary = "a pure Lua implementation", 10 | detailed = [[ 11 | MessagePack is an efficient binary serialization format. 12 | 13 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 14 | ]], 15 | homepage = 'http://fperrad.github.com/lua-MessagePack/', 16 | maintainer = 'Francois Perrad', 17 | license = 'MIT/X11' 18 | } 19 | dependencies = { 20 | 'lua >= 5.1', 21 | } 22 | build = { 23 | type = 'builtin', 24 | modules = { 25 | ['MessagePack'] = 'src/MessagePack.lua', 26 | }, 27 | copy_directories = { 'doc', 'test' }, 28 | } 29 | -------------------------------------------------------------------------------- /rockspec/lua-messagepack-0.2.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-MessagePack' 2 | version = '0.2.1-1' 3 | source = { 4 | url = 'http://cloud.github.com/downloads/fperrad/lua-MessagePack/lua-messagepack-0.2.1.tar.gz', 5 | md5 = '867da634cbe61e83e6d052435afa8eaa', 6 | dir = 'lua-MessagePack-0.2.1', 7 | } 8 | description = { 9 | summary = "a pure Lua implementation", 10 | detailed = [[ 11 | MessagePack is an efficient binary serialization format. 12 | 13 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 14 | ]], 15 | homepage = 'http://fperrad.github.com/lua-MessagePack/', 16 | maintainer = 'Francois Perrad', 17 | license = 'MIT/X11' 18 | } 19 | dependencies = { 20 | 'lua >= 5.1', 21 | } 22 | build = { 23 | type = 'builtin', 24 | modules = { 25 | ['MessagePack'] = 'src/MessagePack.lua', 26 | }, 27 | copy_directories = { 'doc', 'test' }, 28 | } 29 | -------------------------------------------------------------------------------- /rockspec/lua-messagepack-0.2.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-MessagePack' 2 | version = '0.2.2-1' 3 | source = { 4 | url = 'http://sites.google.com/site/fperrad/lua-messagepack-0.2.2.tar.gz', 5 | md5 = '6b0e08e6b877d2bb9ada27fd6b20fd95', 6 | dir = 'lua-MessagePack-0.2.2', 7 | } 8 | description = { 9 | summary = "a pure Lua implementation of the MessagePack serialization format", 10 | detailed = [[ 11 | MessagePack is an efficient binary serialization format. 12 | 13 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 14 | ]], 15 | homepage = 'http://fperrad.github.io/lua-MessagePack/', 16 | maintainer = 'Francois Perrad', 17 | license = 'MIT/X11' 18 | } 19 | dependencies = { 20 | 'lua >= 5.1', 21 | } 22 | build = { 23 | type = 'builtin', 24 | modules = { 25 | ['MessagePack'] = 'src/MessagePack.lua', 26 | }, 27 | copy_directories = { 'doc', 'test' }, 28 | } 29 | -------------------------------------------------------------------------------- /rockspec/lua-messagepack-0.3.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-MessagePack' 2 | version = '0.3.0-1' 3 | source = { 4 | url = 'http://sites.google.com/site/fperrad/lua-messagepack-0.3.0.tar.gz', 5 | md5 = 'ef645a32b779d1a80f2b4f1477111fd7', 6 | dir = 'lua-MessagePack-0.3.0', 7 | } 8 | description = { 9 | summary = "a pure Lua implementation of the MessagePack serialization format", 10 | detailed = [[ 11 | MessagePack is an efficient binary serialization format. 12 | 13 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 14 | ]], 15 | homepage = 'http://fperrad.github.io/lua-MessagePack/', 16 | maintainer = 'Francois Perrad', 17 | license = 'MIT/X11' 18 | } 19 | dependencies = { 20 | 'lua >= 5.1', 21 | } 22 | build = { 23 | type = 'builtin', 24 | modules = { 25 | ['MessagePack'] = 'src/MessagePack.lua', 26 | }, 27 | copy_directories = { 'doc', 'test' }, 28 | } 29 | -------------------------------------------------------------------------------- /src/MessagePack.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- lua-MessagePack : 3 | -- 4 | 5 | local r, jit = pcall(require, 'jit') 6 | if not r then 7 | jit = nil 8 | end 9 | 10 | local SIZEOF_NUMBER = 8 11 | local NUMBER_INTEGRAL = false 12 | if not jit then 13 | -- Lua 5.1 & 5.2 14 | local loadstring = loadstring or load 15 | local luac = string.dump(loadstring "a = 1") 16 | local header = { luac:sub(1, 12):byte(1, 12) } 17 | SIZEOF_NUMBER = header[11] 18 | NUMBER_INTEGRAL = 1 == header[12] 19 | end 20 | 21 | local assert = assert 22 | local error = error 23 | local pairs = pairs 24 | local pcall = pcall 25 | local setmetatable = setmetatable 26 | local tostring = tostring 27 | local type = type 28 | local char = require'string'.char 29 | local floor = require'math'.floor 30 | local frexp = require'math'.frexp 31 | local ldexp = require'math'.ldexp 32 | local huge = require'math'.huge 33 | local tconcat = require'table'.concat 34 | 35 | --[[ debug only 36 | local format = require'string'.format 37 | local function hexadump (s) 38 | return (s:gsub('.', function (c) return format('%02X ', c:byte()) end)) 39 | end 40 | --]] 41 | 42 | _ENV = nil 43 | local m = {} 44 | 45 | --[[ debug only 46 | m.hexadump = hexadump 47 | --]] 48 | 49 | local function argerror (caller, narg, extramsg) 50 | error("bad argument #" .. tostring(narg) .. " to " 51 | .. caller .. " (" .. extramsg .. ")") 52 | end 53 | 54 | local function typeerror (caller, narg, arg, tname) 55 | argerror(caller, narg, tname .. " expected, got " .. type(arg)) 56 | end 57 | 58 | local function checktype (caller, narg, arg, tname) 59 | if type(arg) ~= tname then 60 | typeerror(caller, narg, arg, tname) 61 | end 62 | end 63 | 64 | local packers = setmetatable({}, { 65 | __index = function (t, k) error("pack '" .. k .. "' is unimplemented") end 66 | }) 67 | m.packers = packers 68 | 69 | packers['nil'] = function (buffer) 70 | buffer[#buffer+1] = char(0xC0) -- nil 71 | end 72 | 73 | packers['boolean'] = function (buffer, bool) 74 | if bool then 75 | buffer[#buffer+1] = char(0xC3) -- true 76 | else 77 | buffer[#buffer+1] = char(0xC2) -- false 78 | end 79 | end 80 | 81 | packers['string_compat'] = function (buffer, str) 82 | local n = #str 83 | if n <= 0x1F then 84 | buffer[#buffer+1] = char(0xA0 + n) -- fixstr 85 | elseif n <= 0xFFFF then 86 | buffer[#buffer+1] = char(0xDA, -- str16 87 | floor(n / 0x100), 88 | n % 0x100) 89 | elseif n <= 0xFFFFFFFF then 90 | buffer[#buffer+1] = char(0xDB, -- str32 91 | floor(n / 0x1000000), 92 | floor(n / 0x10000) % 0x100, 93 | floor(n / 0x100) % 0x100, 94 | n % 0x100) 95 | else 96 | error"overflow in pack 'string_compat'" 97 | end 98 | buffer[#buffer+1] = str 99 | end 100 | 101 | packers['_string'] = function (buffer, str) 102 | local n = #str 103 | if n <= 0x1F then 104 | buffer[#buffer+1] = char(0xA0 + n) -- fixstr 105 | elseif n <= 0xFF then 106 | buffer[#buffer+1] = char(0xD9, -- str8 107 | n) 108 | elseif n <= 0xFFFF then 109 | buffer[#buffer+1] = char(0xDA, -- str16 110 | floor(n / 0x100), 111 | n % 0x100) 112 | elseif n <= 0xFFFFFFFF then 113 | buffer[#buffer+1] = char(0xDB, -- str32 114 | floor(n / 0x1000000), 115 | floor(n / 0x10000) % 0x100, 116 | floor(n / 0x100) % 0x100, 117 | n % 0x100) 118 | else 119 | error"overflow in pack 'string'" 120 | end 121 | buffer[#buffer+1] = str 122 | end 123 | 124 | packers['binary'] = function (buffer, str) 125 | local n = #str 126 | if n <= 0xFF then 127 | buffer[#buffer+1] = char(0xC4, -- bin8 128 | n) 129 | elseif n <= 0xFFFF then 130 | buffer[#buffer+1] = char(0xC5, -- bin16 131 | floor(n / 0x100), 132 | n % 0x100) 133 | elseif n <= 0xFFFFFFFF then 134 | buffer[#buffer+1] = char(0xC6, -- bin32 135 | floor(n / 0x1000000), 136 | floor(n / 0x10000) % 0x100, 137 | floor(n / 0x100) % 0x100, 138 | n % 0x100) 139 | else 140 | error"overflow in pack 'binary'" 141 | end 142 | buffer[#buffer+1] = str 143 | end 144 | 145 | local set_string = function (str) 146 | if str == 'string_compat' then 147 | packers['string'] = packers['string_compat'] 148 | elseif str == 'string' then 149 | packers['string'] = packers['_string'] 150 | elseif str == 'binary' then 151 | packers['string'] = packers['binary'] 152 | else 153 | argerror('set_string', 1, "invalid option '" .. str .."'") 154 | end 155 | end 156 | m.set_string = set_string 157 | 158 | packers['map'] = function (buffer, tbl, n) 159 | if n <= 0x0F then 160 | buffer[#buffer+1] = char(0x80 + n) -- fixmap 161 | elseif n <= 0xFFFF then 162 | buffer[#buffer+1] = char(0xDE, -- map16 163 | floor(n / 0x100), 164 | n % 0x100) 165 | elseif n <= 0xFFFFFFFF then 166 | buffer[#buffer+1] = char(0xDF, -- map32 167 | floor(n / 0x1000000), 168 | floor(n / 0x10000) % 0x100, 169 | floor(n / 0x100) % 0x100, 170 | n % 0x100) 171 | else 172 | error"overflow in pack 'map'" 173 | end 174 | for k, v in pairs(tbl) do 175 | packers[type(k)](buffer, k) 176 | packers[type(v)](buffer, v) 177 | end 178 | end 179 | 180 | packers['array'] = function (buffer, tbl, n) 181 | if n <= 0x0F then 182 | buffer[#buffer+1] = char(0x90 + n) -- fixarray 183 | elseif n <= 0xFFFF then 184 | buffer[#buffer+1] = char(0xDC, -- array16 185 | floor(n / 0x100), 186 | n % 0x100) 187 | elseif n <= 0xFFFFFFFF then 188 | buffer[#buffer+1] = char(0xDD, -- array32 189 | floor(n / 0x1000000), 190 | floor(n / 0x10000) % 0x100, 191 | floor(n / 0x100) % 0x100, 192 | n % 0x100) 193 | else 194 | error"overflow in pack 'array'" 195 | end 196 | for i = 1, n do 197 | local v = tbl[i] 198 | packers[type(v)](buffer, v) 199 | end 200 | end 201 | 202 | local set_array = function (array) 203 | if array == 'without_hole' then 204 | packers['_table'] = function (buffer, tbl) 205 | local is_map, n, max = false, 0, 0 206 | for k in pairs(tbl) do 207 | if type(k) == 'number' and k > 0 then 208 | if k > max then 209 | max = k 210 | end 211 | else 212 | is_map = true 213 | end 214 | n = n + 1 215 | end 216 | if max ~= n then -- there are holes 217 | is_map = true 218 | end 219 | if is_map then 220 | return packers['map'](buffer, tbl, n) 221 | else 222 | return packers['array'](buffer, tbl, n) 223 | end 224 | end 225 | elseif array == 'with_hole' then 226 | packers['_table'] = function (buffer, tbl) 227 | local is_map, n, max = false, 0, 0 228 | for k in pairs(tbl) do 229 | if type(k) == 'number' and k > 0 then 230 | if k > max then 231 | max = k 232 | end 233 | else 234 | is_map = true 235 | end 236 | n = n + 1 237 | end 238 | if is_map then 239 | return packers['map'](buffer, tbl, n) 240 | else 241 | return packers['array'](buffer, tbl, max) 242 | end 243 | end 244 | else 245 | argerror('set_array', 1, "invalid option '" .. array .."'") 246 | end 247 | end 248 | m.set_array = set_array 249 | 250 | packers['table'] = function (buffer, tbl) 251 | return packers['_table'](buffer, tbl) 252 | end 253 | 254 | packers['unsigned'] = function (buffer, n) 255 | if n >= 0 then 256 | if n <= 0x7F then 257 | buffer[#buffer+1] = char(n) -- fixnum_pos 258 | elseif n <= 0xFF then 259 | buffer[#buffer+1] = char(0xCC, -- uint8 260 | n) 261 | elseif n <= 0xFFFF then 262 | buffer[#buffer+1] = char(0xCD, -- uint16 263 | floor(n / 0x100), 264 | n % 0x100) 265 | elseif n <= 0xFFFFFFFF then 266 | buffer[#buffer+1] = char(0xCE, -- uint32 267 | floor(n / 0x1000000), 268 | floor(n / 0x10000) % 0x100, 269 | floor(n / 0x100) % 0x100, 270 | n % 0x100) 271 | else 272 | buffer[#buffer+1] = char(0xCF, -- uint64 273 | 0, -- only 53 bits from double 274 | floor(n / 0x1000000000000) % 0x100, 275 | floor(n / 0x10000000000) % 0x100, 276 | floor(n / 0x100000000) % 0x100, 277 | floor(n / 0x1000000) % 0x100, 278 | floor(n / 0x10000) % 0x100, 279 | floor(n / 0x100) % 0x100, 280 | n % 0x100) 281 | end 282 | else 283 | if n >= -0x20 then 284 | buffer[#buffer+1] = char(0x100 + n) -- fixnum_neg 285 | elseif n >= -0x80 then 286 | buffer[#buffer+1] = char(0xD0, -- int8 287 | 0x100 + n) 288 | elseif n >= -0x8000 then 289 | n = 0x10000 + n 290 | buffer[#buffer+1] = char(0xD1, -- int16 291 | floor(n / 0x100), 292 | n % 0x100) 293 | elseif n >= -0x80000000 then 294 | n = 0x100000000 + n 295 | buffer[#buffer+1] = char(0xD2, -- int32 296 | floor(n / 0x1000000), 297 | floor(n / 0x10000) % 0x100, 298 | floor(n / 0x100) % 0x100, 299 | n % 0x100) 300 | else 301 | buffer[#buffer+1] = char(0xD3, -- int64 302 | 0xFF, -- only 53 bits from double 303 | floor(n / 0x1000000000000) % 0x100, 304 | floor(n / 0x10000000000) % 0x100, 305 | floor(n / 0x100000000) % 0x100, 306 | floor(n / 0x1000000) % 0x100, 307 | floor(n / 0x10000) % 0x100, 308 | floor(n / 0x100) % 0x100, 309 | n % 0x100) 310 | end 311 | end 312 | end 313 | 314 | packers['signed'] = function (buffer, n) 315 | if n >= 0 then 316 | if n <= 0x7F then 317 | buffer[#buffer+1] = char(n) -- fixnum_pos 318 | elseif n <= 0x7FFF then 319 | buffer[#buffer+1] = char(0xD1, -- int16 320 | floor(n / 0x100), 321 | n % 0x100) 322 | elseif n <= 0x7FFFFFFF then 323 | buffer[#buffer+1] = char(0xD2, -- int32 324 | floor(n / 0x1000000), 325 | floor(n / 0x10000) % 0x100, 326 | floor(n / 0x100) % 0x100, 327 | n % 0x100) 328 | else 329 | buffer[#buffer+1] = char(0xD3, -- int64 330 | 0, -- only 53 bits from double 331 | floor(n / 0x1000000000000) % 0x100, 332 | floor(n / 0x10000000000) % 0x100, 333 | floor(n / 0x100000000) % 0x100, 334 | floor(n / 0x1000000) % 0x100, 335 | floor(n / 0x10000) % 0x100, 336 | floor(n / 0x100) % 0x100, 337 | n % 0x100) 338 | end 339 | else 340 | if n >= -0x20 then 341 | buffer[#buffer+1] = char(0xE0 + 0x20 + n) -- fixnum_neg 342 | elseif n >= -0x80 then 343 | buffer[#buffer+1] = char(0xD0, -- int8 344 | 0x100 + n) 345 | elseif n >= -0x8000 then 346 | n = 0x10000 + n 347 | buffer[#buffer+1] = char(0xD1, -- int16 348 | floor(n / 0x100), 349 | n % 0x100) 350 | elseif n >= -0x80000000 then 351 | n = 0x100000000 + n 352 | buffer[#buffer+1] = char(0xD2, -- int32 353 | floor(n / 0x1000000), 354 | floor(n / 0x10000) % 0x100, 355 | floor(n / 0x100) % 0x100, 356 | n % 0x100) 357 | else 358 | buffer[#buffer+1] = char(0xD3, -- int64 359 | 0xFF, -- only 53 bits from double 360 | floor(n / 0x1000000000000) % 0x100, 361 | floor(n / 0x10000000000) % 0x100, 362 | floor(n / 0x100000000) % 0x100, 363 | floor(n / 0x1000000) % 0x100, 364 | floor(n / 0x10000) % 0x100, 365 | floor(n / 0x100) % 0x100, 366 | n % 0x100) 367 | end 368 | end 369 | end 370 | 371 | local set_integer = function (integer) 372 | if integer == 'unsigned' then 373 | packers['integer'] = packers['unsigned'] 374 | elseif integer == 'signed' then 375 | packers['integer'] = packers['signed'] 376 | else 377 | argerror('set_integer', 1, "invalid option '" .. integer .."'") 378 | end 379 | end 380 | m.set_integer = set_integer 381 | 382 | packers['float'] = function (buffer, n) 383 | local sign = 0 384 | if n < 0.0 then 385 | sign = 0x80 386 | n = -n 387 | end 388 | local mant, expo = frexp(n) 389 | if mant ~= mant then 390 | buffer[#buffer+1] = char(0xCA, -- nan 391 | 0xFF, 0x88, 0x00, 0x00) 392 | elseif mant == huge or expo > 0x80 then 393 | if sign == 0 then 394 | buffer[#buffer+1] = char(0xCA, -- inf 395 | 0x7F, 0x80, 0x00, 0x00) 396 | else 397 | buffer[#buffer+1] = char(0xCA, -- -inf 398 | 0xFF, 0x80, 0x00, 0x00) 399 | end 400 | elseif (mant == 0.0 and expo == 0) or expo < -0x7E then 401 | buffer[#buffer+1] = char(0xCA, -- zero 402 | sign, 0x00, 0x00, 0x00) 403 | else 404 | expo = expo + 0x7E 405 | mant = (mant * 2.0 - 1.0) * ldexp(0.5, 24) 406 | buffer[#buffer+1] = char(0xCA, 407 | sign + floor(expo / 0x2), 408 | (expo % 0x2) * 0x80 + floor(mant / 0x10000), 409 | floor(mant / 0x100) % 0x100, 410 | mant % 0x100) 411 | end 412 | end 413 | 414 | packers['double'] = function (buffer, n) 415 | local sign = 0 416 | if n < 0.0 then 417 | sign = 0x80 418 | n = -n 419 | end 420 | local mant, expo = frexp(n) 421 | if mant ~= mant then 422 | buffer[#buffer+1] = char(0xCB, -- nan 423 | 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) 424 | elseif mant == huge then 425 | if sign == 0 then 426 | buffer[#buffer+1] = char(0xCB, -- inf 427 | 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) 428 | else 429 | buffer[#buffer+1] = char(0xCB, -- -inf 430 | 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) 431 | end 432 | elseif mant == 0.0 and expo == 0 then 433 | buffer[#buffer+1] = char(0xCB, -- zero 434 | sign, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) 435 | else 436 | expo = expo + 0x3FE 437 | mant = (mant * 2.0 - 1.0) * ldexp(0.5, 53) 438 | buffer[#buffer+1] = char(0xCB, 439 | sign + floor(expo / 0x10), 440 | (expo % 0x10) * 0x10 + floor(mant / 0x1000000000000), 441 | floor(mant / 0x10000000000) % 0x100, 442 | floor(mant / 0x100000000) % 0x100, 443 | floor(mant / 0x1000000) % 0x100, 444 | floor(mant / 0x10000) % 0x100, 445 | floor(mant / 0x100) % 0x100, 446 | mant % 0x100) 447 | end 448 | end 449 | 450 | local set_number = function (number) 451 | if number == 'integer' then 452 | packers['number'] = packers['signed'] 453 | elseif number == 'float' then 454 | packers['number'] = function (buffer, n) 455 | if floor(n) ~= n or n ~= n or n > 3.40282347e+38 or n < -3.40282347e+38 then 456 | return packers['float'](buffer, n) 457 | else 458 | return packers['integer'](buffer, n) 459 | end 460 | end 461 | elseif number == 'double' then 462 | packers['number'] = function (buffer, n) 463 | if floor(n) ~= n or n ~= n or n == huge or n == -huge then 464 | return packers['double'](buffer, n) 465 | else 466 | return packers['integer'](buffer, n) 467 | end 468 | end 469 | else 470 | argerror('set_number', 1, "invalid option '" .. number .."'") 471 | end 472 | end 473 | m.set_number = set_number 474 | 475 | for k = 0, 4 do 476 | local n = 2^k 477 | local fixext = 0xD4 + k 478 | packers['fixext' .. n] = function (buffer, tag, data) 479 | assert(#data == n, "bad length for fixext" .. n) 480 | buffer[#buffer+1] = char(fixext, 481 | tag < 0 and tag + 0x100 or tag) 482 | buffer[#buffer+1] = data 483 | end 484 | end 485 | 486 | packers['ext'] = function (buffer, tag, data) 487 | local n = #data 488 | if n <= 0xFF then 489 | buffer[#buffer+1] = char(0xC7, -- ext8 490 | n, 491 | tag < 0 and tag + 0x100 or tag) 492 | elseif n <= 0xFFFF then 493 | buffer[#buffer+1] = char(0xC8, -- ext16 494 | floor(n / 0x100), 495 | n % 0x100, 496 | tag < 0 and tag + 0x100 or tag) 497 | elseif n <= 0xFFFFFFFF then 498 | buffer[#buffer+1] = char(0xC9, -- ext&32 499 | floor(n / 0x1000000), 500 | floor(n / 0x10000) % 0x100, 501 | floor(n / 0x100) % 0x100, 502 | n % 0x100, 503 | tag < 0 and tag + 0x100 or tag) 504 | else 505 | error"overflow in pack 'ext'" 506 | end 507 | buffer[#buffer+1] = data 508 | end 509 | 510 | function m.pack (data) 511 | local buffer = {} 512 | packers[type(data)](buffer, data) 513 | return tconcat(buffer) 514 | end 515 | 516 | 517 | local types_map = setmetatable({ 518 | [0xC0] = 'nil', 519 | [0xC2] = 'false', 520 | [0xC3] = 'true', 521 | [0xC4] = 'bin8', 522 | [0xC5] = 'bin16', 523 | [0xC6] = 'bin32', 524 | [0xC7] = 'ext8', 525 | [0xC8] = 'ext16', 526 | [0xC9] = 'ext32', 527 | [0xCA] = 'float', 528 | [0xCB] = 'double', 529 | [0xCC] = 'uint8', 530 | [0xCD] = 'uint16', 531 | [0xCE] = 'uint32', 532 | [0xCF] = 'uint64', 533 | [0xD0] = 'int8', 534 | [0xD1] = 'int16', 535 | [0xD2] = 'int32', 536 | [0xD3] = 'int64', 537 | [0xD4] = 'fixext1', 538 | [0xD5] = 'fixext2', 539 | [0xD6] = 'fixext4', 540 | [0xD7] = 'fixext8', 541 | [0xD8] = 'fixext16', 542 | [0xD9] = 'str8', 543 | [0xDA] = 'str16', 544 | [0xDB] = 'str32', 545 | [0xDC] = 'array16', 546 | [0xDD] = 'array32', 547 | [0xDE] = 'map16', 548 | [0xDF] = 'map32', 549 | }, { __index = function (t, k) 550 | if k < 0xC0 then 551 | if k < 0x80 then 552 | return 'fixnum_pos' 553 | elseif k < 0x90 then 554 | return 'fixmap' 555 | elseif k < 0xA0 then 556 | return 'fixarray' 557 | else 558 | return 'fixstr' 559 | end 560 | elseif k > 0xDF then 561 | return 'fixnum_neg' 562 | else 563 | return 'reserved' .. k 564 | end 565 | end }) 566 | m.types_map = types_map 567 | 568 | local unpackers = setmetatable({}, { 569 | __index = function (t, k) error("unpack '" .. k .. "' is unimplemented") end 570 | }) 571 | m.unpackers = unpackers 572 | 573 | local function unpack_array (c, n) 574 | local t = {} 575 | local decode = unpackers['any'] 576 | for i = 1, n do 577 | t[i] = decode(c) 578 | end 579 | return t 580 | end 581 | 582 | local function unpack_map (c, n) 583 | local t = {} 584 | local decode = unpackers['any'] 585 | for i = 1, n do 586 | local k = decode(c) 587 | t[k] = decode(c) 588 | end 589 | return t 590 | end 591 | 592 | unpackers['any'] = function (c) 593 | local s, i, j = c.s, c.i, c.j 594 | if i > j then 595 | c:underflow(i) 596 | s, i, j = c.s, c.i, c.j 597 | end 598 | local val = s:sub(i, i):byte() 599 | c.i = i+1 600 | return unpackers[types_map[val]](c, val) 601 | end 602 | 603 | unpackers['nil'] = function () 604 | return nil 605 | end 606 | 607 | unpackers['false'] = function () 608 | return false 609 | end 610 | 611 | unpackers['true'] = function () 612 | return true 613 | end 614 | 615 | unpackers['float'] = function (c) 616 | local s, i, j = c.s, c.i, c.j 617 | if i+3 > j then 618 | c:underflow(i+3) 619 | s, i, j = c.s, c.i, c.j 620 | end 621 | local b1, b2, b3, b4 = s:sub(i, i+3):byte(1, 4) 622 | local sign = b1 > 0x7F 623 | local expo = (b1 % 0x80) * 0x2 + floor(b2 / 0x80) 624 | local mant = ((b2 % 0x80) * 0x100 + b3) * 0x100 + b4 625 | if sign then 626 | sign = -1 627 | else 628 | sign = 1 629 | end 630 | local n 631 | if mant == 0 and expo == 0 then 632 | n = sign * 0.0 633 | elseif expo == 0xFF then 634 | if mant == 0 then 635 | n = sign * huge 636 | else 637 | n = 0.0/0.0 638 | end 639 | else 640 | n = sign * ldexp(1.0 + mant / 0x800000, expo - 0x7F) 641 | end 642 | c.i = i+4 643 | return n 644 | end 645 | 646 | unpackers['double'] = function (c) 647 | local s, i, j = c.s, c.i, c.j 648 | if i+7 > j then 649 | c:underflow(i+7) 650 | s, i, j = c.s, c.i, c.j 651 | end 652 | local b1, b2, b3, b4, b5, b6, b7, b8 = s:sub(i, i+7):byte(1, 8) 653 | local sign = b1 > 0x7F 654 | local expo = (b1 % 0x80) * 0x10 + floor(b2 / 0x10) 655 | local mant = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 656 | if sign then 657 | sign = -1 658 | else 659 | sign = 1 660 | end 661 | local n 662 | if mant == 0 and expo == 0 then 663 | n = sign * 0.0 664 | elseif expo == 0x7FF then 665 | if mant == 0 then 666 | n = sign * huge 667 | else 668 | n = 0.0/0.0 669 | end 670 | else 671 | n = sign * ldexp(1.0 + mant / 0x10000000000000, expo - 0x3FF) 672 | end 673 | c.i = i+8 674 | return n 675 | end 676 | 677 | unpackers['fixnum_pos'] = function (c, val) 678 | return val 679 | end 680 | 681 | unpackers['uint8'] = function (c) 682 | local s, i, j = c.s, c.i, c.j 683 | if i > j then 684 | c:underflow(i) 685 | s, i, j = c.s, c.i, c.j 686 | end 687 | local b1 = s:sub(i, i):byte() 688 | c.i = i+1 689 | return b1 690 | end 691 | 692 | unpackers['uint16'] = function (c) 693 | local s, i, j = c.s, c.i, c.j 694 | if i+1 > j then 695 | c:underflow(i+1) 696 | s, i, j = c.s, c.i, c.j 697 | end 698 | local b1, b2 = s:sub(i, i+1):byte(1, 2) 699 | c.i = i+2 700 | return b1 * 0x100 + b2 701 | end 702 | 703 | unpackers['uint32'] = function (c) 704 | local s, i, j = c.s, c.i, c.j 705 | if i+3 > j then 706 | c:underflow(i+3) 707 | s, i, j = c.s, c.i, c.j 708 | end 709 | local b1, b2, b3, b4 = s:sub(i, i+3):byte(1, 4) 710 | c.i = i+4 711 | return ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4 712 | end 713 | 714 | unpackers['uint64'] = function (c) 715 | local s, i, j = c.s, c.i, c.j 716 | if i+7 > j then 717 | c:underflow(i+7) 718 | s, i, j = c.s, c.i, c.j 719 | end 720 | local b1, b2, b3, b4, b5, b6, b7, b8 = s:sub(i, i+7):byte(1, 8) 721 | c.i = i+8 722 | return ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 723 | end 724 | 725 | unpackers['fixnum_neg'] = function (c, val) 726 | return val - 0x100 727 | end 728 | 729 | unpackers['int8'] = function (c) 730 | local s, i, j = c.s, c.i, c.j 731 | if i > j then 732 | c:underflow(i) 733 | s, i, j = c.s, c.i, c.j 734 | end 735 | local b1 = s:sub(i, i):byte() 736 | c.i = i+1 737 | if b1 < 0x80 then 738 | return b1 739 | else 740 | return b1 - 0x100 741 | end 742 | end 743 | 744 | unpackers['int16'] = function (c) 745 | local s, i, j = c.s, c.i, c.j 746 | if i+1 > j then 747 | c:underflow(i+1) 748 | s, i, j = c.s, c.i, c.j 749 | end 750 | local b1, b2 = s:sub(i, i+1):byte(1, 2) 751 | c.i = i+2 752 | if b1 < 0x80 then 753 | return b1 * 0x100 + b2 754 | else 755 | return ((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) - 1 756 | end 757 | end 758 | 759 | unpackers['int32'] = function (c) 760 | local s, i, j = c.s, c.i, c.j 761 | if i+3 > j then 762 | c:underflow(i+3) 763 | s, i, j = c.s, c.i, c.j 764 | end 765 | local b1, b2, b3, b4 = s:sub(i, i+3):byte(1, 4) 766 | c.i = i+4 767 | if b1 < 0x80 then 768 | return ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4 769 | else 770 | return ((((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) * 0x100 + (b3 - 0xFF)) * 0x100 + (b4 - 0xFF)) - 1 771 | end 772 | end 773 | 774 | unpackers['int64'] = function (c) 775 | local s, i, j = c.s, c.i, c.j 776 | if i+7 > j then 777 | c:underflow(i+7) 778 | s, i, j = c.s, c.i, c.j 779 | end 780 | local b1, b2, b3, b4, b5, b6, b7, b8 = s:sub(i, i+7):byte(1, 8) 781 | c.i = i+8 782 | if b1 < 0x80 then 783 | return ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 784 | else 785 | return ((((((((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) * 0x100 + (b3 - 0xFF)) * 0x100 + (b4 - 0xFF)) * 0x100 + (b5 - 0xFF)) * 0x100 + (b6 - 0xFF)) * 0x100 + (b7 - 0xFF)) * 0x100 + (b8 - 0xFF)) - 1 786 | end 787 | end 788 | 789 | unpackers['fixstr'] = function (c, val) 790 | local s, i, j = c.s, c.i, c.j 791 | local n = val % 0x20 792 | local e = i+n-1 793 | if e > j then 794 | c:underflow(e) 795 | s, i, j = c.s, c.i, c.j 796 | end 797 | c.i = i+n 798 | return s:sub(i, e) 799 | end 800 | 801 | unpackers['str8'] = function (c) 802 | local s, i, j = c.s, c.i, c.j 803 | if i > j then 804 | c:underflow(i) 805 | s, i, j = c.s, c.i, c.j 806 | end 807 | local n = s:sub(i, i):byte() 808 | i = i+1 809 | c.i = i 810 | local e = i+n-1 811 | if e > j then 812 | c:underflow(e) 813 | s, i, j = c.s, c.i, c.j 814 | end 815 | c.i = i+n 816 | return s:sub(i, e) 817 | end 818 | 819 | unpackers['str16'] = function (c) 820 | local s, i, j = c.s, c.i, c.j 821 | if i+1 > j then 822 | c:underflow(i+1) 823 | s, i, j = c.s, c.i, c.j 824 | end 825 | local b1, b2 = s:sub(i, i+1):byte(1, 2) 826 | i = i+2 827 | c.i = i 828 | local n = b1 * 0x100 + b2 829 | local e = i+n-1 830 | if e > j then 831 | c:underflow(e) 832 | s, i, j = c.s, c.i, c.j 833 | end 834 | c.i = i+n 835 | return s:sub(i, e) 836 | end 837 | 838 | unpackers['str32'] = function (c) 839 | local s, i, j = c.s, c.i, c.j 840 | if i+3 > j then 841 | c:underflow(i+3) 842 | s, i, j = c.s, c.i, c.j 843 | end 844 | local b1, b2, b3, b4 = s:sub(i, i+3):byte(1, 4) 845 | i = i+4 846 | c.i = i 847 | local n = ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4 848 | local e = i+n-1 849 | if e > j then 850 | c:underflow(e) 851 | s, i, j = c.s, c.i, c.j 852 | end 853 | c.i = i+n 854 | return s:sub(i, e) 855 | end 856 | 857 | unpackers['bin8'] = unpackers['str8'] 858 | unpackers['bin16'] = unpackers['str16'] 859 | unpackers['bin32'] = unpackers['str32'] 860 | 861 | unpackers['fixarray'] = function (c, val) 862 | return unpack_array(c, val % 0x10) 863 | end 864 | 865 | unpackers['array16'] = function (c) 866 | local s, i, j = c.s, c.i, c.j 867 | if i+1 > j then 868 | c:underflow(i+1) 869 | s, i, j = c.s, c.i, c.j 870 | end 871 | local b1, b2 = s:sub(i, i+1):byte(1, 2) 872 | c.i = i+2 873 | return unpack_array(c, b1 * 0x100 + b2) 874 | end 875 | 876 | unpackers['array32'] = function (c) 877 | local s, i, j = c.s, c.i, c.j 878 | if i+3 > j then 879 | c:underflow(i+3) 880 | s, i, j = c.s, c.i, c.j 881 | end 882 | local b1, b2, b3, b4 = s:sub(i, i+3):byte(1, 4) 883 | c.i = i+4 884 | return unpack_array(c, ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) 885 | end 886 | 887 | unpackers['fixmap'] = function (c, val) 888 | return unpack_map(c, val % 0x10) 889 | end 890 | 891 | unpackers['map16'] = function (c) 892 | local s, i, j = c.s, c.i, c.j 893 | if i+1 > j then 894 | c:underflow(i+1) 895 | s, i, j = c.s, c.i, c.j 896 | end 897 | local b1, b2 = s:sub(i, i+1):byte(1, 2) 898 | c.i = i+2 899 | return unpack_map(c, b1 * 0x100 + b2) 900 | end 901 | 902 | unpackers['map32'] = function (c) 903 | local s, i, j = c.s, c.i, c.j 904 | if i+3 > j then 905 | c:underflow(i+3) 906 | s, i, j = c.s, c.i, c.j 907 | end 908 | local b1, b2, b3, b4 = s:sub(i, i+3):byte(1, 4) 909 | c.i = i+4 910 | return unpack_map(c, ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) 911 | end 912 | 913 | function m.build_ext (tag, data) 914 | return nil 915 | end 916 | 917 | for k = 0, 4 do 918 | local n = 2^k 919 | unpackers['fixext' .. n] = function (c) 920 | local s, i, j = c.s, c.i, c.j 921 | if i > j then 922 | c:underflow(i) 923 | s, i, j = c.s, c.i, c.j 924 | end 925 | local tag = s:sub(i, i):byte() 926 | i = i+1 927 | c.i = i 928 | local e = i+n-1 929 | if e > j then 930 | c:underflow(e) 931 | s, i, j = c.s, c.i, c.j 932 | end 933 | c.i = i+n 934 | return m.build_ext(tag < 0x80 and tag or tag - 0x100, s:sub(i, e)) 935 | end 936 | end 937 | 938 | unpackers['ext8'] = function (c) 939 | local s, i, j = c.s, c.i, c.j 940 | if i > j then 941 | c:underflow(i) 942 | s, i, j = c.s, c.i, c.j 943 | end 944 | local n = s:sub(i, i):byte() 945 | i = i+1 946 | c.i = i 947 | if i > j then 948 | c:underflow(i) 949 | s, i, j = c.s, c.i, c.j 950 | end 951 | local tag = s:sub(i, i):byte() 952 | i = i+1 953 | c.i = i 954 | local e = i+n-1 955 | if e > j then 956 | c:underflow(e) 957 | s, i, j = c.s, c.i, c.j 958 | end 959 | c.i = i+n 960 | return m.build_ext(tag < 0x80 and tag or tag - 0x100, s:sub(i, e)) 961 | end 962 | 963 | unpackers['ext16'] = function (c) 964 | local s, i, j = c.s, c.i, c.j 965 | if i+1 > j then 966 | c:underflow(i+1) 967 | s, i, j = c.s, c.i, c.j 968 | end 969 | local b1, b2 = s:sub(i, i+1):byte(1, 2) 970 | i = i+2 971 | c.i = i 972 | local n = b1 * 0x100 + b2 973 | if i > j then 974 | c:underflow(i) 975 | s, i, j = c.s, c.i, c.j 976 | end 977 | local tag = s:sub(i, i):byte() 978 | i = i+1 979 | c.i = i 980 | local e = i+n-1 981 | if e > j then 982 | c:underflow(e) 983 | s, i, j = c.s, c.i, c.j 984 | end 985 | c.i = i+n 986 | return m.build_ext(tag < 0x80 and tag or tag - 0x100, s:sub(i, e)) 987 | end 988 | 989 | unpackers['ext32'] = function (c) 990 | local s, i, j = c.s, c.i, c.j 991 | if i+3 > j then 992 | c:underflow(i+3) 993 | s, i, j = c.s, c.i, c.j 994 | end 995 | local b1, b2, b3, b4 = s:sub(i, i+3):byte(1, 4) 996 | i = i+4 997 | c.i = i 998 | local n = ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4 999 | if i > j then 1000 | c:underflow(i) 1001 | s, i, j = c.s, c.i, c.j 1002 | end 1003 | local tag = s:sub(i, i):byte() 1004 | i = i+1 1005 | c.i = i 1006 | local e = i+n-1 1007 | if e > j then 1008 | c:underflow(e) 1009 | s, i, j = c.s, c.i, c.j 1010 | end 1011 | c.i = i+n 1012 | return m.build_ext(tag < 0x80 and tag or tag - 0x100, s:sub(i, e)) 1013 | end 1014 | 1015 | 1016 | function m.unpack (s) 1017 | checktype('unpack', 1, s, 'string') 1018 | local cursor = { 1019 | s = s, 1020 | i = 1, 1021 | j = #s, 1022 | underflow = function (self) 1023 | error "missing bytes" 1024 | end, 1025 | } 1026 | local data = unpackers['any'](cursor) 1027 | if cursor.i < cursor.j then 1028 | error "extra bytes" 1029 | end 1030 | return data 1031 | end 1032 | 1033 | function m.unpacker (src) 1034 | if type(src) == 'string' then 1035 | local cursor = { 1036 | s = src, 1037 | i = 1, 1038 | j = #src, 1039 | underflow = function (self) 1040 | error "missing bytes" 1041 | end, 1042 | } 1043 | return function () 1044 | if cursor.i <= cursor.j then 1045 | return cursor.i, unpackers['any'](cursor) 1046 | end 1047 | end 1048 | elseif type(src) == 'function' then 1049 | local cursor = { 1050 | s = '', 1051 | i = 1, 1052 | j = 0, 1053 | underflow = function (self, e) 1054 | self.s = self.s:sub(self.i) 1055 | e = e - self.i + 1 1056 | self.i = 1 1057 | self.j = 0 1058 | while e > self.j do 1059 | local chunk = src() 1060 | if not chunk then 1061 | error "missing bytes" 1062 | end 1063 | self.s = self.s .. chunk 1064 | self.j = #self.s 1065 | end 1066 | end, 1067 | } 1068 | return function () 1069 | if cursor.i > cursor.j then 1070 | pcall(cursor.underflow, cursor, cursor.i) 1071 | end 1072 | if cursor.i <= cursor.j then 1073 | return true, unpackers['any'](cursor) 1074 | end 1075 | end 1076 | else 1077 | argerror('unpacker', 1, "string or function expected, got " .. type(src)) 1078 | end 1079 | end 1080 | 1081 | set_string'string_compat' 1082 | set_integer'signed' 1083 | if NUMBER_INTEGRAL then 1084 | set_number'integer' 1085 | elseif SIZEOF_NUMBER == 4 then 1086 | set_number'float' 1087 | else 1088 | set_number'double' 1089 | end 1090 | set_array'without_hole' 1091 | 1092 | m._VERSION = "0.3.0" 1093 | m._DESCRIPTION = "lua-MessagePack : a pure Lua implementation" 1094 | m._COPYRIGHT = "Copyright (c) 2012-2013 Francois Perrad" 1095 | return m 1096 | -- 1097 | -- This library is licensed under the terms of the MIT/X11 license, 1098 | -- like Lua itself. 1099 | -- 1100 | -------------------------------------------------------------------------------- /test/00-require.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | plan(9) 6 | 7 | if not require_ok 'MessagePack' then 8 | BAIL_OUT "no lib" 9 | end 10 | 11 | local m = require 'MessagePack' 12 | type_ok( m, 'table' ) 13 | like( m._COPYRIGHT, 'Perrad', "_COPYRIGHT" ) 14 | like( m._DESCRIPTION, 'MessagePack', "_DESCRIPTION" ) 15 | like( m._VERSION, '^%d%.%d%.%d$', "_VERSION" ) 16 | 17 | type_ok( m.packers, 'table', "table packers" ) 18 | type_ok( m.unpackers, 'table', "table unpackers" ) 19 | type_ok( m.types_map, 'table', "table types_map" ) 20 | type_ok( m.build_ext, 'function', "function build_ext" ) 21 | 22 | -------------------------------------------------------------------------------- /test/01-spectest.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | local unpack = table.unpack or unpack 4 | 5 | require 'Test.More' 6 | 7 | local mp = require 'MessagePack' 8 | 9 | local data = { 10 | false, "false", 11 | true, "true", 12 | nil, "nil", 13 | 0, "0 Positive FixNum", 14 | 0, "0 uint8", 15 | 0, "0 uint16", 16 | 0, "0 uint32", 17 | 0, "0 uint64", 18 | 0, "0 int8", 19 | 0, "0 int16", 20 | 0, "0 int32", 21 | 0, "0 int64", 22 | -1, "-1 Negative FixNum", 23 | -1, "-1 int8", 24 | -1, "-1 int16", 25 | -1, "-1 int32", 26 | -1, "-1 int64", 27 | 127, "127 Positive FixNum", 28 | 127, "127 uint8", 29 | 255, "255 uint16", 30 | 65535, "65535 uint32", 31 | 4294967295, "4294967295 uint64", 32 | -32, "-32 Negative FixNum", 33 | -32, "-32 int8", 34 | -128, "-128 int16", 35 | -32768, "-32768 int32", 36 | -2147483648, "-2147483648 int64", 37 | 0.0, "0.0 float", 38 | 0.0, "0.0 double", 39 | -0.0, "-0.0 float", 40 | -0.0, "-0.0 double", 41 | 1.0, "1.0 double", 42 | -1.0, "-1.0 double", 43 | "a", "\"a\" FixStr", 44 | "a", "\"a\" str 8", 45 | "a", "\"a\" str 16", 46 | "a", "\"a\" str 32", 47 | "", "\"\" FixStr", 48 | "", "\"\" str 8", 49 | "", "\"\" str 16", 50 | "", "\"\" str 32", 51 | "a", "\"a\" bin 8", 52 | "a", "\"a\" bin 16", 53 | "a", "\"a\" bin 32", 54 | "", "\"\" bin 8", 55 | "", "\"\" bin 16", 56 | "", "\"\" bin 32", 57 | { 0 }, "[0] FixArray", 58 | { 0 }, "[0] array 16", 59 | { 0 }, "[0] array 32", 60 | {}, "[] FixArray", 61 | {}, "[] array 16", 62 | {}, "[] array 32", 63 | {}, "{} FixMap", 64 | {}, "{} map 16", 65 | {}, "{} map 32", 66 | { a=97 }, "{\"a\"=>97} FixMap", 67 | { a=97}, "{\"a\"=>97} map 16", 68 | { a=97 }, "{\"a\"=>97} map 32", 69 | { {} }, "[[]]", 70 | { {"a"} }, "[[\"a\"]]", 71 | 72 | nil, "fixext 1", 73 | nil, "fixext 2", 74 | nil, "fixext 4", 75 | nil, "fixext 8", 76 | nil, "fixext 16", 77 | nil, "ext 8", 78 | nil, "ext 16", 79 | nil, "ext 32", 80 | } 81 | 82 | plan(8 * 69) 83 | 84 | -- see http://github.com/msgpack/msgpack/blob/master/test/cases_gen.rb 85 | local source = [===[ 86 | c2 # false 87 | c3 # true 88 | c0 # nil 89 | 00 # 0 Positive FixNum 90 | cc 00 # 0 uint8 91 | cd 00 00 # 0 uint16 92 | ce 00 00 00 00 # 0 uint32 93 | cf 00 00 00 00 00 00 00 00 # 0 uint64 94 | d0 00 # 0 int8 95 | d1 00 00 # 0 int16 96 | d2 00 00 00 00 # 0 int32 97 | d3 00 00 00 00 00 00 00 00 # 0 int64 98 | ff # -1 Negative FixNum 99 | d0 ff # -1 int8 100 | d1 ff ff # -1 int16 101 | d2 ff ff ff ff # -1 int32 102 | d3 ff ff ff ff ff ff ff ff # -1 int64 103 | 7f # 127 Positive FixNum 104 | cc 7f # 127 uint8 105 | cd 00 ff # 255 uint16 106 | ce 00 00 ff ff # 65535 uint32 107 | cf 00 00 00 00 ff ff ff ff # 4294967295 uint64 108 | e0 # -32 Negative FixNum 109 | d0 e0 # -32 int8 110 | d1 ff 80 # -128 int16 111 | d2 ff ff 80 00 # -32768 int32 112 | d3 ff ff ff ff 80 00 00 00 # -2147483648 int64 113 | ca 00 00 00 00 # 0.0 float 114 | cb 00 00 00 00 00 00 00 00 # 0.0 double 115 | ca 80 00 00 00 # -0.0 float 116 | cb 80 00 00 00 00 00 00 00 # -0.0 double 117 | cb 3f f0 00 00 00 00 00 00 # 1.0 double 118 | cb bf f0 00 00 00 00 00 00 # -1.0 double 119 | a1 61 # "a" FixStr 120 | d9 01 61 # "a" str 8 121 | da 00 01 61 # "a" str 16 122 | db 00 00 00 01 61 # "a" str 32 123 | a0 # "" FixStr 124 | d9 00 # "" str 8 125 | da 00 00 # "" str 16 126 | db 00 00 00 00 # "" str 32 127 | c4 01 61 # "a" bin 8 128 | c5 00 01 61 # "a" bin 16 129 | c6 00 00 00 01 61 # "a" bin 32 130 | c4 00 # "" bin 8 131 | c5 00 00 # "" bin 16 132 | c6 00 00 00 00 # "" bin 32 133 | 91 00 # [0] FixArray 134 | dc 00 01 00 # [0] array 16 135 | dd 00 00 00 01 00 # [0] array 32 136 | 90 # [] FixArray 137 | dc 00 00 # [] array 16 138 | dd 00 00 00 00 # [] array 32 139 | 80 # {} FixMap 140 | de 00 00 # {} map 16 141 | df 00 00 00 00 # {} map 32 142 | 81 a1 61 61 # {"a"=>97} FixMap 143 | de 00 01 a1 61 61 # {"a"=>97} map 16 144 | df 00 00 00 01 a1 61 61 # {"a"=>97} map 32 145 | 91 90 # [[]] 146 | 91 91 a1 61 # [["a"]] 147 | 148 | d4 01 01 # fixext 1 149 | d5 02 02 01 # fixext 2 150 | d6 04 04 03 02 01 # fixext 4 151 | d7 08 08 07 06 05 04 03 02 01 # fixext 8 152 | d8 16 10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 # fixext 16 153 | c7 01 08 61 # ext 8 154 | c8 00 01 16 61 # ext 16 155 | c9 00 00 00 01 32 61 # ext 32 156 | ]===] 157 | 158 | source = source:gsub('#[^\n]+', '') 159 | local t = {} 160 | for v in source:gmatch'%x%x' do 161 | t[#t+1] = tonumber(v, 16) 162 | end 163 | local mpac = string.char(unpack(t)) 164 | 165 | 166 | local i = 1 167 | for _, val in mp.unpacker(mpac) do 168 | if type(val) == 'table' then 169 | is_deeply(val, data[i], "reference " .. data[i+1]) 170 | is_deeply(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 171 | else 172 | is(val, data[i], "reference " .. data[i+1]) 173 | is(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 174 | end 175 | i = i + 2 176 | end 177 | 178 | local f = io.open('cases.mpac', 'w') 179 | f:write(mpac) 180 | f:close() 181 | local r, ltn12 = pcall(require, 'ltn12') -- from LuaSocket 182 | if not r then 183 | diag "ltn12.source.file emulated" 184 | ltn12 = { source = {} } 185 | 186 | function ltn12.source.file (handle) 187 | if handle then 188 | return function () 189 | local chunk = handle:read(1) 190 | if not chunk then 191 | handle:close() 192 | end 193 | return chunk 194 | end 195 | else return function () 196 | return nil, "unable to open file" 197 | end 198 | end 199 | end 200 | end 201 | local i = 1 202 | local f = io.open('cases.mpac', 'r') 203 | local s = ltn12.source.file(f) 204 | for _, val in mp.unpacker(s) do 205 | if type(val) == 'table' then 206 | is_deeply(val, data[i], "reference " .. data[i+1]) 207 | else 208 | is(val, data[i], "reference " .. data[i+1]) 209 | end 210 | i = i + 2 211 | end 212 | os.remove 'cases.mpac' -- clean up 213 | 214 | diag("set_string'string'") 215 | mp.set_string'string' 216 | local i = 1 217 | for _, val in mp.unpacker(mpac) do 218 | if type(val) == 'table' then 219 | is_deeply(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 220 | else 221 | is(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 222 | end 223 | i = i + 2 224 | end 225 | 226 | diag("set_string'binary'") 227 | mp.set_string'binary' 228 | local i = 1 229 | for _, val in mp.unpacker(mpac) do 230 | if type(val) == 'table' then 231 | is_deeply(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 232 | else 233 | is(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 234 | end 235 | i = i + 2 236 | end 237 | mp.set_string'string_compat' 238 | 239 | diag("set_integer'unsigned'") 240 | mp.set_integer'unsigned' 241 | local i = 1 242 | for _, val in mp.unpacker(mpac) do 243 | if type(val) == 'table' then 244 | is_deeply(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 245 | else 246 | is(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 247 | end 248 | i = i + 2 249 | end 250 | mp.set_integer'signed' 251 | 252 | diag("set_number'float'") 253 | mp.set_number'float' 254 | local i = 1 255 | for _, val in mp.unpacker(mpac) do 256 | if type(val) == 'table' then 257 | is_deeply(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 258 | else 259 | is(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 260 | end 261 | i = i + 2 262 | end 263 | 264 | diag("set_number'integer'") 265 | mp.set_number'integer' 266 | local i = 1 267 | for _, val in mp.unpacker(mpac) do 268 | if type(val) == 'table' then 269 | is_deeply(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 270 | else 271 | is(mp.unpack(mp.pack(data[i])), data[i], "unpack/pack " .. data[i+1]) 272 | end 273 | i = i + 2 274 | end 275 | -------------------------------------------------------------------------------- /test/02-except.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | plan(20) 6 | 7 | local mp = require 'MessagePack' 8 | 9 | error_like( function () 10 | mp.pack( print ) 11 | end, 12 | "pack 'function' is unimplemented" ) 13 | 14 | error_like( function () 15 | mp.pack( coroutine.create(plan) ) 16 | end, 17 | "pack 'thread' is unimplemented" ) 18 | 19 | error_like( function () 20 | mp.pack( io.stdin ) 21 | end, 22 | "pack 'userdata' is unimplemented" ) 23 | 24 | error_like( function () 25 | local a = {} 26 | a.foo = a 27 | mp.pack( a ) 28 | end, 29 | "stack overflow", -- from Lua interpreter 30 | "direct cycle" ) 31 | 32 | error_like( function () 33 | local a = {} 34 | local b = {} 35 | a.foo = b 36 | b.foo = a 37 | mp.pack( a ) 38 | end, 39 | "stack overflow", -- from Lua interpreter 40 | "indirect cycle" ) 41 | 42 | is( mp.unpack(mp.pack("text")), "text" ) 43 | 44 | error_like( function () 45 | mp.unpack(mp.pack("text"):sub(1, -2)) 46 | end, 47 | "missing bytes" ) 48 | 49 | error_like( function () 50 | mp.unpack(mp.pack("text") .. "more") 51 | end, 52 | "extra bytes" ) 53 | 54 | error_like( function () 55 | mp.unpack( {} ) 56 | end, 57 | "bad argument #1 to unpack %(string expected, got table%)" ) 58 | 59 | error_like( function () 60 | mp.unpacker( false ) 61 | end, 62 | "bad argument #1 to unpacker %(string or function expected, got boolean%)" ) 63 | 64 | error_like( function () 65 | mp.unpacker( {} ) 66 | end, 67 | "bad argument #1 to unpacker %(string or function expected, got table%)" ) 68 | 69 | for _, val in mp.unpacker(string.rep(mp.pack("text"), 2)) do 70 | is( val, "text" ) 71 | end 72 | 73 | error_like( function () 74 | for _, val in mp.unpacker(string.rep(mp.pack("text"), 2):sub(1, -2)) do 75 | is( val, "text" ) 76 | end 77 | end, 78 | "missing bytes" ) 79 | 80 | error_like( function () 81 | mp.set_string'bad' 82 | end, 83 | "bad argument #1 to set_string %(invalid option 'bad'%)" ) 84 | 85 | error_like( function () 86 | mp.set_number'bad' 87 | end, 88 | "bad argument #1 to set_number %(invalid option 'bad'%)" ) 89 | 90 | error_like( function () 91 | mp.set_integer'bad' 92 | end, 93 | "bad argument #1 to set_integer %(invalid option 'bad'%)" ) 94 | 95 | error_like( function () 96 | mp.set_array'bad' 97 | end, 98 | "bad argument #1 to set_array %(invalid option 'bad'%)" ) 99 | 100 | error_like( function () 101 | mp.packers['fixext4'](nil, 1, '123') 102 | end, 103 | "bad length for fixext4" ) 104 | 105 | -------------------------------------------------------------------------------- /test/03-edge.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | plan(24) 6 | 7 | local mp = require 'MessagePack' 8 | 9 | is( mp.unpack(mp.pack(1/0)), 1/0, "inf" ) 10 | 11 | is( mp.unpack(mp.pack(-1/0)), -1/0, "-inf" ) 12 | 13 | local nan = mp.unpack(mp.pack(0/0)) 14 | type_ok( nan, 'number', "nan" ) 15 | ok( nan ~= nan ) 16 | 17 | is( mp.pack{}:byte(), 0x90, "empty table as array" ) 18 | 19 | local t = setmetatable( { 'a', 'b', 'c' }, { __index = { [4] = 'd' } } ) 20 | is( t[4], 'd' ) 21 | t = mp.unpack(mp.pack(t)) 22 | is( t[2], 'b' ) 23 | is( t[4], nil, "don't follow metatable" ) 24 | 25 | local t = setmetatable( { a = 1, b = 2, c = 3 }, { __index = { d = 4 } } ) 26 | is( t.d, 4 ) 27 | t = mp.unpack(mp.pack(t)) 28 | is( t.b, 2 ) 29 | is( t.d, nil, "don't follow metatable" ) 30 | 31 | local t = { 10, 20, nil, 40 } 32 | mp.set_array'without_hole' 33 | is( mp.pack(t):byte(), 0x80 + 3, "array with hole as map" ) 34 | is_deeply( mp.unpack(mp.pack(t)), t ) 35 | mp.set_array'with_hole' 36 | is( mp.pack(t):byte(), 0x90 + 4, "array with hole as array" ) 37 | is_deeply( mp.unpack(mp.pack(t)), t ) 38 | 39 | mp.set_number'float' 40 | is( mp.pack(3.402824e+38), mp.pack(1/0), "float 3.402824e+38") 41 | is( mp.pack(7e42), mp.pack(1/0), "inf (downcast double -> float)") 42 | is( mp.pack(-7e42), mp.pack(-1/0), "-inf (downcast double -> float)") 43 | is( mp.unpack(mp.pack(7e42)), 1/0, "inf (downcast double -> float)") 44 | is( mp.unpack(mp.pack(-7e42)), -1/0, "-inf (downcast double -> float)") 45 | is( mp.unpack(mp.pack(7e-42)), 0, "epsilon (downcast double -> float)") 46 | is( mp.unpack(mp.pack(-7e-42)), -0, "-epsilon (downcast double -> float)") 47 | 48 | local buffer = {} 49 | mp.packers.float(buffer, 0) 50 | is( mp.unpack(table.concat(buffer)), 0) 51 | buffer = {} 52 | mp.packers.double(buffer, 0) 53 | is( mp.unpack(table.concat(buffer)), 0) 54 | 55 | -------------------------------------------------------------------------------- /test/04-cover.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | plan(32) 6 | 7 | local mp = require 'MessagePack' 8 | 9 | mp.set_number'float' 10 | is( mp.unpack(mp.pack(3.140625)), 3.140625, "3.140625" ) 11 | mp.set_number'double' 12 | is( mp.unpack(mp.pack(math.pi)), math.pi, "pi" ) 13 | 14 | mp.set_integer'signed' 15 | is( mp.unpack(mp.pack(2^5)), 2^5, "2^5" ) 16 | is( mp.unpack(mp.pack(-2^5)), -2^5, "-2^5" ) 17 | is( mp.unpack(mp.pack(2^11)), 2^11, "2^11" ) 18 | is( mp.unpack(mp.pack(-2^11)), -2^11, "-2^11" ) 19 | is( mp.unpack(mp.pack(2^21)), 2^21, "2^21" ) 20 | is( mp.unpack(mp.pack(-2^21)), -2^21, "-2^21" ) 21 | is( mp.unpack(mp.pack(2^51)), 2^51, "2^51" ) 22 | is( mp.unpack(mp.pack(-2^51)), -2^51, "-2^51" ) 23 | 24 | mp.set_integer'unsigned' 25 | is( mp.unpack(mp.pack(2^5)), 2^5, "2^5" ) 26 | is( mp.unpack(mp.pack(-2^5)), -2^5, "-2^5" ) 27 | is( mp.unpack(mp.pack(2^11)), 2^11, "2^11" ) 28 | is( mp.unpack(mp.pack(-2^11)), -2^11, "-2^11" ) 29 | is( mp.unpack(mp.pack(2^21)), 2^21, "2^21" ) 30 | is( mp.unpack(mp.pack(-2^21)), -2^21, "-2^21" ) 31 | is( mp.unpack(mp.pack(2^51)), 2^51, "2^51" ) 32 | is( mp.unpack(mp.pack(-2^51)), -2^51, "-2^51" ) 33 | 34 | mp.set_string'string' 35 | s = string.rep('x', 2^3) 36 | is( mp.unpack(mp.pack(s)), s, "#s 2^3" ) -- fixstr 37 | s = string.rep('x', 2^7) 38 | is( mp.unpack(mp.pack(s)), s, "#s 2^7" ) -- str 8 39 | s = string.rep('x', 2^11) 40 | is( mp.unpack(mp.pack(s)), s, "#s 2^11" ) -- str 16 41 | s = string.rep('x', 2^19) 42 | is( mp.unpack(mp.pack(s)), s, "#s 2^19" ) -- str 32 43 | 44 | mp.set_string'string_compat' 45 | s = string.rep('x', 2^3) 46 | is( mp.unpack(mp.pack(s)), s, "#s 2^3" ) -- fixstr 47 | s = string.rep('x', 2^11) 48 | is( mp.unpack(mp.pack(s)), s, "#s 2^11" ) -- str 16 49 | s = string.rep('x', 2^19) 50 | is( mp.unpack(mp.pack(s)), s, "#s 2^19" ) -- str 32 51 | 52 | mp.set_string'binary' 53 | s = string.rep('x', 2^5) 54 | is( mp.unpack(mp.pack(s)), s, "#s 2^5" ) -- bin 8 55 | s = string.rep('x', 2^11) 56 | is( mp.unpack(mp.pack(s)), s, "#s 2^11" ) -- bin 16 57 | s = string.rep('x', 2^19) 58 | is( mp.unpack(mp.pack(s)), s, "#s 2^19" ) -- bin 32 59 | 60 | t = { string.rep('x', 2^3):byte(1, -1) } 61 | is_deeply( mp.unpack(mp.pack(t)), t, "#t 2^3" ) 62 | t = { string.rep('x', 2^9):byte(1, -1) } 63 | is_deeply( mp.unpack(mp.pack(t)), t, "#t 2^9" ) 64 | 65 | h = {} 66 | for i = 1, 2^3 do h[10*i] = i end 67 | is_deeply( mp.unpack(mp.pack(h)), h, "#h 2^3" ) 68 | h = {} 69 | for i = 1, 2^9 do h[10*i] = i end 70 | is_deeply( mp.unpack(mp.pack(h)), h, "#h 2^9" ) 71 | 72 | -------------------------------------------------------------------------------- /test/11-userdata-bc.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | if not pcall(require, 'bc') then 6 | skip_all 'no bc' 7 | end 8 | 9 | plan(3) 10 | 11 | local mp = require 'MessagePack' 12 | local bc = require 'bc' 13 | local EXT_BC = 42 14 | bc.digits(65) 15 | 16 | mp.packers['userdata'] = function (buffer, u) 17 | if getmetatable(u) == bc then 18 | mp.packers['ext'](buffer, EXT_BC, tostring(u)) 19 | else 20 | error("pack 'userdata' is unimplemented") 21 | end 22 | end 23 | 24 | mp.build_ext = function (tag, data) 25 | if tag == EXT_BC then 26 | return bc.number(data) 27 | end 28 | end 29 | 30 | local orig = bc.sqrt(2) 31 | local dest = mp.unpack(mp.pack(orig)) 32 | is( dest, orig, "bc" ) 33 | nok( rawequal(orig, dest) ) 34 | 35 | error_like( function () 36 | mp.pack( io.stdin ) 37 | end, 38 | "pack 'userdata' is unimplemented" ) 39 | 40 | -------------------------------------------------------------------------------- /test/12-metatable.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | plan(6) 6 | 7 | local mp = require 'MessagePack' 8 | 9 | local EXT_METATABLE = 42 10 | 11 | mp.packers['table'] = function (buffer, t) 12 | local mt = getmetatable(t) 13 | if mt then 14 | local buf = {} 15 | mp.packers['_table'](buf, t) 16 | mp.packers['table'](buf, mt) 17 | mp.packers['ext'](buffer, EXT_METATABLE, table.concat(buf)) 18 | else 19 | mp.packers['_table'](buffer, t) 20 | end 21 | end 22 | 23 | mp.build_ext = function (tag, data) 24 | if tag == EXT_METATABLE then 25 | local f = mp.unpacker(data) 26 | local _, t = f() 27 | local _, mt = f() 28 | return setmetatable(t, mt) 29 | end 30 | end 31 | 32 | local t = setmetatable( { 'a', 'b', 'c' }, { __index = { [4] = 'd' } } ) 33 | is( t[4], 'd' ) 34 | t = mp.unpack(mp.pack(t)) 35 | is( t[2], 'b' ) 36 | is( t[4], 'd', "follow metatable" ) 37 | 38 | local t = setmetatable( { a = 1, b = 2, c = 3 }, { __index = { d = 4 } } ) 39 | is( t.d, 4 ) 40 | t = mp.unpack(mp.pack(t)) 41 | is( t.b, 2 ) 42 | is( t.d, 4, "follow metatable" ) 43 | 44 | -------------------------------------------------------------------------------- /test/13-coat.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | if not pcall(require, 'Coat') then 6 | skip_all 'no Coat' 7 | end 8 | 9 | plan(4) 10 | 11 | local Meta = require 'Coat.Meta.Class' 12 | local mp = require 'MessagePack' 13 | local EXT_COAT = 4 14 | 15 | mp.packers['table'] = function (buffer, obj) 16 | local classname = obj._CLASS 17 | if classname then 18 | local buf = {} 19 | mp.packers['string'](buf, classname) 20 | mp.packers['table'](buf, obj._VALUES) 21 | mp.packers['ext'](buffer, EXT_COAT, table.concat(buf)) 22 | else 23 | mp.packers['_table'](buffer, obj) 24 | end 25 | end 26 | 27 | mp.build_ext = function (tag, data) 28 | if tag == EXT_COAT then 29 | local f = mp.unpacker(data) 30 | local _, classname = f() 31 | local _, values = f() 32 | local class = assert(Meta.class(classname)) 33 | return class.new(values) 34 | end 35 | end 36 | 37 | class 'Point' 38 | 39 | has.x = { is = 'rw', isa = 'number', default = 0 } 40 | has.y = { is = 'rw', isa = 'number', default = 0 } 41 | 42 | function overload:__tostring () 43 | return '(' .. self.x .. ', ' .. self.y .. ')' 44 | end 45 | 46 | function method:draw () 47 | return "drawing " .. self._CLASS .. tostring(self) 48 | end 49 | 50 | a = Point{x = 1, y = 2} 51 | ok( a:isa 'Point' ) 52 | is( tostring(a), "(1, 2)" ) 53 | 54 | b = mp.unpack(mp.pack(a)) 55 | ok( b:isa 'Point' ) 56 | is( tostring(b), "(1, 2)" ) 57 | 58 | -------------------------------------------------------------------------------- /test/14-function.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | plan(4) 6 | 7 | local loadstring = loadstring or load 8 | local mp = require 'MessagePack' 9 | local EXT_FUNCTION = 7 10 | 11 | mp.packers['function'] = function (buffer, fct) 12 | mp.packers['ext'](buffer, EXT_FUNCTION, assert(string.dump(fct))) 13 | end 14 | 15 | mp.build_ext = function (tag, data) 16 | if tag == EXT_FUNCTION then 17 | return assert(loadstring(data)) 18 | end 19 | end 20 | 21 | local function square (n) return n * n end 22 | is( square(2), 4 ) 23 | local result = mp.unpack(mp.pack(square)) 24 | type_ok( result, 'function' ) 25 | nok( rawequal(square, result) ) 26 | is( result(3), 9 ) 27 | 28 | -------------------------------------------------------------------------------- /test/15-ffi-cdata.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/lua 2 | 3 | require 'Test.More' 4 | 5 | if not pcall(require, 'ffi') then 6 | skip_all 'no ffi' 7 | end 8 | 9 | plan(20) 10 | 11 | local mp = require 'MessagePack' 12 | local ffi = require 'ffi' 13 | local EXT_UINT = 0x40 14 | 15 | local uint8_t = ffi.typeof'uint8_t' 16 | local uint16_t = ffi.typeof'uint16_t' 17 | local uint32_t = ffi.typeof'uint32_t' 18 | local uint64_t = ffi.typeof'uint64_t' 19 | local uint8_a1 = ffi.typeof'uint8_t[1]' 20 | local uint16_a1 = ffi.typeof'uint16_t[1]' 21 | local uint32_a1 = ffi.typeof'uint32_t[1]' 22 | local uint64_a1 = ffi.typeof'uint64_t[1]' 23 | local uint8_p = ffi.typeof'uint8_t *' 24 | local uint16_p = ffi.typeof'uint16_t *' 25 | local uint32_p = ffi.typeof'uint32_t *' 26 | local uint64_p = ffi.typeof'uint64_t *' 27 | 28 | mp.packers['cdata'] = function (buffer, cdata) 29 | if ffi.istype(uint8_t, cdata) then 30 | mp.packers['fixext1'](buffer, EXT_UINT, ffi.string(uint8_a1(cdata), 1)) 31 | elseif ffi.istype(uint16_t, cdata) then 32 | mp.packers['fixext2'](buffer, EXT_UINT+1, ffi.string(uint16_a1(cdata), 2)) 33 | elseif ffi.istype(uint32_t, cdata) then 34 | mp.packers['fixext4'](buffer, EXT_UINT+2, ffi.string(uint32_a1(cdata), 4)) 35 | elseif ffi.istype(uint64_t, cdata) then 36 | mp.packers['fixext8'](buffer, EXT_UINT+3, ffi.string(uint64_a1(cdata), 8)) 37 | else 38 | error("pack 'cdata' is unimplemented") 39 | end 40 | end 41 | 42 | mp.build_ext = function (tag, data) 43 | if tag == EXT_UINT then 44 | return uint8_t(ffi.cast(uint8_p, data)[0]) 45 | elseif tag == EXT_UINT+1 then 46 | return uint16_t(ffi.cast(uint16_p, data)[0]) 47 | elseif tag == EXT_UINT+2 then 48 | return uint32_t(ffi.cast(uint32_p, data)[0]) 49 | elseif tag == EXT_UINT+3 then 50 | return uint64_t(ffi.cast(uint64_p, data)[0]) 51 | end 52 | end 53 | 54 | 55 | local a = ffi.new('uint8_t', 100) 56 | ok( ffi.istype(uint8_t, a) ) 57 | -- diag(mp.hexadump(mp.pack(a))) 58 | local b = mp.unpack(mp.pack(a)) 59 | type_ok( b, 'cdata' ) 60 | is( tonumber(b), 100 ) 61 | nok( rawequal(a, b) ) 62 | ok( ffi.istype(uint8_t, b) ) 63 | 64 | local a = ffi.new('uint16_t', 10000) 65 | ok( ffi.istype(uint16_t, a) ) 66 | -- diag(mp.hexadump(mp.pack(a))) 67 | local b = mp.unpack(mp.pack(a)) 68 | type_ok( b, 'cdata' ) 69 | is( tonumber(b), 10000 ) 70 | nok( rawequal(a, b) ) 71 | ok( ffi.istype(uint16_t, b) ) 72 | 73 | local a = ffi.new('uint32_t', 100000000) 74 | ok( ffi.istype(uint32_t, a) ) 75 | -- diag(mp.hexadump(mp.pack(a))) 76 | local b = mp.unpack(mp.pack(a)) 77 | type_ok( b, 'cdata' ) 78 | is( tonumber(b), 100000000 ) 79 | nok( rawequal(a, b) ) 80 | ok( ffi.istype(uint32_t, b) ) 81 | 82 | local a = ffi.new('uint64_t', 1000000000000) 83 | ok( ffi.istype(uint64_t, a) ) 84 | -- diag(mp.hexadump(mp.pack(a))) 85 | local b = mp.unpack(mp.pack(a)) 86 | type_ok( b, 'cdata' ) 87 | is( tonumber(b), 1000000000000 ) 88 | nok( rawequal(a, b) ) 89 | ok( ffi.istype(uint64_t, b) ) 90 | 91 | --------------------------------------------------------------------------------