MessagePack.lua 22.7 KB
Newer Older
François Perrad's avatar
François Perrad committed
1
--
François Perrad's avatar
François Perrad committed
2
-- lua-MessagePack : <http://fperrad.github.io/lua-MessagePack/>
François Perrad's avatar
François Perrad committed
3
4
5
6
7
8
9
10
11
12
--

local assert = assert
local error = error
local pairs = pairs
local pcall = pcall
local setmetatable = setmetatable
local tostring = tostring
local type = type
local char = require'string'.char
François Perrad's avatar
François Perrad committed
13
local math_type = require'math'.type
François Perrad's avatar
François Perrad committed
14
local tointeger = require'math'.tointeger
François Perrad's avatar
François Perrad committed
15
local tconcat = require'table'.concat
François Perrad's avatar
François Perrad committed
16
17
local pack = require'string'.pack
local unpack = require'string'.unpack
François Perrad's avatar
François Perrad committed
18
19
20
21
22
23
24
25

--[[ debug only
local format = require'string'.format
local function hexadump (s)
    return (s:gsub('.', function (c) return format('%02X ', c:byte()) end))
end
--]]

26
local _ENV = nil
François Perrad's avatar
François Perrad committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
local m = {}

--[[ debug only
m.hexadump = hexadump
--]]

local function argerror (caller, narg, extramsg)
    error("bad argument #" .. tostring(narg) .. " to "
          .. caller .. " (" .. extramsg .. ")")
end

local function typeerror (caller, narg, arg, tname)
    argerror(caller, narg, tname .. " expected, got " .. type(arg))
end

local function checktype (caller, narg, arg, tname)
    if type(arg) ~= tname then
        typeerror(caller, narg, arg, tname)
    end
end

local packers = setmetatable({}, {
    __index = function (t, k) error("pack '" .. k .. "' is unimplemented") end
})
m.packers = packers

packers['nil'] = function (buffer)
    buffer[#buffer+1] = char(0xC0)              -- nil
end

packers['boolean'] = function (buffer, bool)
    if bool then
        buffer[#buffer+1] = char(0xC3)          -- true
    else
        buffer[#buffer+1] = char(0xC2)          -- false
    end
end

packers['string_compat'] = function (buffer, str)
    local n = #str
    if n <= 0x1F then
        buffer[#buffer+1] = char(0xA0 + n)      -- fixstr
    elseif n <= 0xFFFF then
François Perrad's avatar
François Perrad committed
70
        buffer[#buffer+1] = char(0xDA)          -- str16
François Perrad's avatar
François Perrad committed
71
        buffer[#buffer+1] = pack('>I2', n)
François Perrad's avatar
François Perrad committed
72
    elseif n <= 0xFFFFFFFF.0 then
François Perrad's avatar
François Perrad committed
73
        buffer[#buffer+1] = char(0xDB)          -- str32
François Perrad's avatar
François Perrad committed
74
        buffer[#buffer+1] = pack('>I4', n)
François Perrad's avatar
François Perrad committed
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    else
        error"overflow in pack 'string_compat'"
    end
    buffer[#buffer+1] = str
end

packers['_string'] = function (buffer, str)
    local n = #str
    if n <= 0x1F then
        buffer[#buffer+1] = char(0xA0 + n)      -- fixstr
    elseif n <= 0xFF then
        buffer[#buffer+1] = char(0xD9,          -- str8
                                 n)
    elseif n <= 0xFFFF then
François Perrad's avatar
François Perrad committed
89
        buffer[#buffer+1] = char(0xDA)          -- str16
François Perrad's avatar
François Perrad committed
90
        buffer[#buffer+1] = pack('>I2', n)
François Perrad's avatar
François Perrad committed
91
    elseif n <= 0xFFFFFFFF.0 then
François Perrad's avatar
François Perrad committed
92
        buffer[#buffer+1] = char(0xDB)          -- str32
François Perrad's avatar
François Perrad committed
93
        buffer[#buffer+1] = pack('>I4', n)
François Perrad's avatar
François Perrad committed
94
95
96
97
98
99
100
101
102
103
104
105
    else
        error"overflow in pack 'string'"
    end
    buffer[#buffer+1] = str
end

packers['binary'] = function (buffer, str)
    local n = #str
    if n <= 0xFF then
        buffer[#buffer+1] = char(0xC4,          -- bin8
                                 n)
    elseif n <= 0xFFFF then
François Perrad's avatar
François Perrad committed
106
        buffer[#buffer+1] = char(0xC5)          -- bin16
François Perrad's avatar
François Perrad committed
107
        buffer[#buffer+1] = pack('>I2', n)
François Perrad's avatar
François Perrad committed
108
    elseif n <= 0xFFFFFFFF.0 then
François Perrad's avatar
François Perrad committed
109
        buffer[#buffer+1] = char(0xC6)          -- bin32
François Perrad's avatar
François Perrad committed
110
        buffer[#buffer+1] = pack('>I4', n)
François Perrad's avatar
François Perrad committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    else
        error"overflow in pack 'binary'"
    end
    buffer[#buffer+1] = str
end

local set_string = function (str)
    if str == 'string_compat' then
        packers['string'] = packers['string_compat']
    elseif str == 'string' then
        packers['string'] = packers['_string']
    elseif str == 'binary' then
        packers['string'] = packers['binary']
    else
        argerror('set_string', 1, "invalid option '" .. str .."'")
    end
end
m.set_string = set_string

packers['map'] = function (buffer, tbl, n)
    if n <= 0x0F then
        buffer[#buffer+1] = char(0x80 + n)      -- fixmap
    elseif n <= 0xFFFF then
François Perrad's avatar
François Perrad committed
134
        buffer[#buffer+1] = char(0xDE)          -- map16
François Perrad's avatar
François Perrad committed
135
        buffer[#buffer+1] = pack('>I2', n)
François Perrad's avatar
François Perrad committed
136
    elseif n <= 0xFFFFFFFF.0 then
François Perrad's avatar
François Perrad committed
137
        buffer[#buffer+1] = char(0xDF)          -- map32
François Perrad's avatar
François Perrad committed
138
        buffer[#buffer+1] = pack('>I4', n)
François Perrad's avatar
François Perrad committed
139
140
141
142
143
144
145
146
147
148
149
150
151
    else
        error"overflow in pack 'map'"
    end
    for k, v in pairs(tbl) do
        packers[type(k)](buffer, k)
        packers[type(v)](buffer, v)
    end
end

packers['array'] = function (buffer, tbl, n)
    if n <= 0x0F then
        buffer[#buffer+1] = char(0x90 + n)      -- fixarray
    elseif n <= 0xFFFF then
François Perrad's avatar
François Perrad committed
152
        buffer[#buffer+1] = char(0xDC)          -- array16
François Perrad's avatar
François Perrad committed
153
        buffer[#buffer+1] = pack('>I2', n)
François Perrad's avatar
François Perrad committed
154
    elseif n <= 0xFFFFFFFF.0 then
François Perrad's avatar
François Perrad committed
155
        buffer[#buffer+1] = char(0xDD)          -- array32
François Perrad's avatar
François Perrad committed
156
        buffer[#buffer+1] = pack('>I4', n)
François Perrad's avatar
François Perrad committed
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
    else
        error"overflow in pack 'array'"
    end
    for i = 1, n do
        local v = tbl[i]
        packers[type(v)](buffer, v)
    end
end

local set_array = function (array)
    if array == 'without_hole' then
        packers['_table'] = function (buffer, tbl)
            local is_map, n, max = false, 0, 0
            for k in pairs(tbl) do
                if type(k) == 'number' and k > 0 then
                    if k > max then
                        max = k
                    end
                else
                    is_map = true
                end
                n = n + 1
            end
            if max ~= n then    -- there are holes
                is_map = true
            end
            if is_map then
                return packers['map'](buffer, tbl, n)
            else
                return packers['array'](buffer, tbl, n)
            end
        end
    elseif array == 'with_hole' then
        packers['_table'] = function (buffer, tbl)
            local is_map, n, max = false, 0, 0
            for k in pairs(tbl) do
                if type(k) == 'number' and k > 0 then
                    if k > max then
                        max = k
                    end
                else
                    is_map = true
                end
                n = n + 1
            end
            if is_map then
                return packers['map'](buffer, tbl, n)
            else
                return packers['array'](buffer, tbl, max)
            end
        end
    elseif array == 'always_as_map' then
        packers['_table'] = function(buffer, tbl)
            local n = 0
            for k in pairs(tbl) do
                n = n + 1
            end
            return packers['map'](buffer, tbl, n)
        end
    else
        argerror('set_array', 1, "invalid option '" .. array .."'")
    end
end
m.set_array = set_array

packers['table'] = function (buffer, tbl)
    return packers['_table'](buffer, tbl)
end

packers['unsigned'] = function (buffer, n)
    if n >= 0 then
        if n <= 0x7F then
            buffer[#buffer+1] = char(n)         -- fixnum_pos
        elseif n <= 0xFF then
            buffer[#buffer+1] = char(0xCC,      -- uint8
                                     n)
        elseif n <= 0xFFFF then
François Perrad's avatar
François Perrad committed
234
            buffer[#buffer+1] = char(0xCD)      -- uint16
François Perrad's avatar
François Perrad committed
235
            buffer[#buffer+1] = pack('>I2', n)
François Perrad's avatar
François Perrad committed
236
        elseif n <= 0xFFFFFFFF.0 then
François Perrad's avatar
François Perrad committed
237
            buffer[#buffer+1] = char(0xCE)      -- uint32
François Perrad's avatar
François Perrad committed
238
            buffer[#buffer+1] = pack('>I4', n)
François Perrad's avatar
François Perrad committed
239
        else
François Perrad's avatar
François Perrad committed
240
            buffer[#buffer+1] = char(0xCF)      -- uint64
François Perrad's avatar
François Perrad committed
241
            buffer[#buffer+1] = pack('>I8', n)
François Perrad's avatar
François Perrad committed
242
243
244
245
246
        end
    else
        if n >= -0x20 then
            buffer[#buffer+1] = char(0x100 + n) -- fixnum_neg
        elseif n >= -0x80 then
François Perrad's avatar
François Perrad committed
247
            buffer[#buffer+1] = char(0xD0)      -- int8
François Perrad's avatar
François Perrad committed
248
            buffer[#buffer+1] = pack('>i1', n)
François Perrad's avatar
François Perrad committed
249
        elseif n >= -0x8000 then
François Perrad's avatar
François Perrad committed
250
            buffer[#buffer+1] = char(0xD1)      -- int16
François Perrad's avatar
François Perrad committed
251
            buffer[#buffer+1] = pack('>i2', n)
François Perrad's avatar
François Perrad committed
252
        elseif n >= -0x80000000 then
François Perrad's avatar
François Perrad committed
253
            buffer[#buffer+1] = char(0xD2)      -- int32
François Perrad's avatar
François Perrad committed
254
            buffer[#buffer+1] = pack('>i4', n)
François Perrad's avatar
François Perrad committed
255
        else
François Perrad's avatar
François Perrad committed
256
            buffer[#buffer+1] = char(0xD3)      -- int64
François Perrad's avatar
François Perrad committed
257
            buffer[#buffer+1] = pack('>i8', n)
François Perrad's avatar
François Perrad committed
258
259
260
261
262
263
264
265
266
        end
    end
end

packers['signed'] = function (buffer, n)
    if n >= 0 then
        if n <= 0x7F then
            buffer[#buffer+1] = char(n)         -- fixnum_pos
        elseif n <= 0x7FFF then
François Perrad's avatar
François Perrad committed
267
            buffer[#buffer+1] = char(0xD1)      -- int16
François Perrad's avatar
François Perrad committed
268
            buffer[#buffer+1] = pack('>i2', n)
François Perrad's avatar
François Perrad committed
269
        elseif n <= 0x7FFFFFFF then
François Perrad's avatar
François Perrad committed
270
            buffer[#buffer+1] = char(0xD2)      -- int32
François Perrad's avatar
François Perrad committed
271
            buffer[#buffer+1] = pack('>i4', n)
François Perrad's avatar
François Perrad committed
272
        else
François Perrad's avatar
François Perrad committed
273
            buffer[#buffer+1] = char(0xD3)      -- int64
François Perrad's avatar
François Perrad committed
274
            buffer[#buffer+1] = pack('>i8', n)
François Perrad's avatar
François Perrad committed
275
276
277
278
279
        end
    else
        if n >= -0x20 then
            buffer[#buffer+1] = char(0xE0 + 0x20 + n)   -- fixnum_neg
        elseif n >= -0x80 then
François Perrad's avatar
François Perrad committed
280
            buffer[#buffer+1] = char(0xD0)      -- int8
François Perrad's avatar
François Perrad committed
281
            buffer[#buffer+1] = pack('>i1', n)
François Perrad's avatar
François Perrad committed
282
        elseif n >= -0x8000 then
François Perrad's avatar
François Perrad committed
283
            buffer[#buffer+1] = char(0xD1)      -- int16
François Perrad's avatar
François Perrad committed
284
            buffer[#buffer+1] = pack('>i2', n)
François Perrad's avatar
François Perrad committed
285
        elseif n >= -0x80000000 then
François Perrad's avatar
François Perrad committed
286
            buffer[#buffer+1] = char(0xD2)      -- int32
François Perrad's avatar
François Perrad committed
287
            buffer[#buffer+1] = pack('>i4', n)
François Perrad's avatar
François Perrad committed
288
        else
François Perrad's avatar
François Perrad committed
289
            buffer[#buffer+1] = char(0xD3)      -- int64
François Perrad's avatar
François Perrad committed
290
            buffer[#buffer+1] = pack('>i8', n)
François Perrad's avatar
François Perrad committed
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
        end
    end
end

local set_integer = function (integer)
    if integer == 'unsigned' then
        packers['integer'] = packers['unsigned']
    elseif integer == 'signed' then
        packers['integer'] = packers['signed']
    else
        argerror('set_integer', 1, "invalid option '" .. integer .."'")
    end
end
m.set_integer = set_integer

packers['float'] = function (buffer, n)
François Perrad's avatar
François Perrad committed
307
    buffer[#buffer+1] = char(0xCA)
François Perrad's avatar
François Perrad committed
308
    buffer[#buffer+1] = pack('>f', n)
François Perrad's avatar
François Perrad committed
309
310
311
end

packers['double'] = function (buffer, n)
François Perrad's avatar
François Perrad committed
312
    buffer[#buffer+1] = char(0xCB)
François Perrad's avatar
François Perrad committed
313
    buffer[#buffer+1] = pack('>d', n)
François Perrad's avatar
François Perrad committed
314
315
316
317
318
319
320
end

local set_number = function (number)
    if number == 'integer' then
        packers['number'] = packers['signed']
    elseif number == 'float' then
        packers['number'] = function (buffer, n)
François Perrad's avatar
François Perrad committed
321
            if math_type(n) == 'integer' then
François Perrad's avatar
François Perrad committed
322
                return packers['integer'](buffer, n)
François Perrad's avatar
François Perrad committed
323
324
            else
                return packers['float'](buffer, n)
François Perrad's avatar
François Perrad committed
325
326
327
328
            end
        end
    elseif number == 'double' then
        packers['number'] = function (buffer, n)
François Perrad's avatar
François Perrad committed
329
            if math_type(n) == 'integer' then
François Perrad's avatar
François Perrad committed
330
                return packers['integer'](buffer, n)
François Perrad's avatar
François Perrad committed
331
332
            else
                return packers['double'](buffer, n)
François Perrad's avatar
François Perrad committed
333
334
335
336
337
338
339
340
341
            end
        end
    else
        argerror('set_number', 1, "invalid option '" .. number .."'")
    end
end
m.set_number = set_number

for k = 0, 4 do
François Perrad's avatar
François Perrad committed
342
    local n = tointeger(2^k)
François Perrad's avatar
François Perrad committed
343
    local fixext = 0xD4 + k
344
345
    packers['fixext' .. tostring(n)] = function (buffer, tag, data)
        assert(#data == n, "bad length for fixext" .. tostring(n))
François Perrad's avatar
François Perrad committed
346
        buffer[#buffer+1] = char(fixext)
François Perrad's avatar
François Perrad committed
347
        buffer[#buffer+1] = pack('>i1', tag)
François Perrad's avatar
François Perrad committed
348
349
350
351
352
353
354
355
        buffer[#buffer+1] = data
    end
end

packers['ext'] = function (buffer, tag, data)
    local n = #data
    if n <= 0xFF then
        buffer[#buffer+1] = char(0xC7,          -- ext8
François Perrad's avatar
François Perrad committed
356
                                 n)
François Perrad's avatar
François Perrad committed
357
        buffer[#buffer+1] = pack('>i1', tag)
François Perrad's avatar
François Perrad committed
358
    elseif n <= 0xFFFF then
François Perrad's avatar
François Perrad committed
359
        buffer[#buffer+1] = char(0xC8)          -- ext16
François Perrad's avatar
François Perrad committed
360
361
        buffer[#buffer+1] = pack('>I2', n)
        buffer[#buffer+1] = pack('>i1', tag)
François Perrad's avatar
François Perrad committed
362
    elseif n <= 0xFFFFFFFF.0 then
François Perrad's avatar
François Perrad committed
363
        buffer[#buffer+1] = char(0xC9)          -- ext32
François Perrad's avatar
François Perrad committed
364
365
        buffer[#buffer+1] = pack('>I4', n)
        buffer[#buffer+1] = pack('>i1', tag)
François Perrad's avatar
François Perrad committed
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
    else
        error"overflow in pack 'ext'"
    end
    buffer[#buffer+1] = data
end

function m.pack (data)
    local buffer = {}
    packers[type(data)](buffer, data)
    return tconcat(buffer)
end


local types_map = setmetatable({
    [0xC0] = 'nil',
    [0xC2] = 'false',
    [0xC3] = 'true',
    [0xC4] = 'bin8',
    [0xC5] = 'bin16',
    [0xC6] = 'bin32',
    [0xC7] = 'ext8',
    [0xC8] = 'ext16',
    [0xC9] = 'ext32',
    [0xCA] = 'float',
    [0xCB] = 'double',
    [0xCC] = 'uint8',
    [0xCD] = 'uint16',
    [0xCE] = 'uint32',
    [0xCF] = 'uint64',
    [0xD0] = 'int8',
    [0xD1] = 'int16',
    [0xD2] = 'int32',
    [0xD3] = 'int64',
    [0xD4] = 'fixext1',
    [0xD5] = 'fixext2',
    [0xD6] = 'fixext4',
    [0xD7] = 'fixext8',
    [0xD8] = 'fixext16',
    [0xD9] = 'str8',
    [0xDA] = 'str16',
    [0xDB] = 'str32',
    [0xDC] = 'array16',
    [0xDD] = 'array32',
    [0xDE] = 'map16',
    [0xDF] = 'map32',
}, { __index = function (t, k)
        if k < 0xC0 then
            if k < 0x80 then
                return 'fixnum_pos'
            elseif k < 0x90 then
                return 'fixmap'
            elseif k < 0xA0 then
                return 'fixarray'
            else
                return 'fixstr'
            end
        elseif k > 0xDF then
            return 'fixnum_neg'
        else
425
            return 'reserved' .. tostring(k)
François Perrad's avatar
François Perrad committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
        end
end })
m.types_map = types_map

local unpackers = setmetatable({}, {
    __index = function (t, k) error("unpack '" .. k .. "' is unimplemented") end
})
m.unpackers = unpackers

local function unpack_array (c, n)
    local t = {}
    local decode = unpackers['any']
    for i = 1, n do
        t[i] = decode(c)
    end
    return t
end

local function unpack_map (c, n)
    local t = {}
    local decode = unpackers['any']
    for i = 1, n do
        local k = decode(c)
François Perrad's avatar
François Perrad committed
449
        local val = decode(c)
François Perrad's avatar
François Perrad committed
450
        if k == nil or k ~= k then
François Perrad's avatar
François Perrad committed
451
452
453
454
455
            k = m.sentinel
        end
        if k ~= nil then
            t[k] = val
        end
François Perrad's avatar
François Perrad committed
456
457
458
459
460
461
462
463
464
465
    end
    return t
end

unpackers['any'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
466
    local val = s:sub(i, i):byte()
François Perrad's avatar
François Perrad committed
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    c.i = i+1
    return unpackers[types_map[val]](c, val)
end

unpackers['nil'] = function ()
    return nil
end

unpackers['false'] = function ()
    return false
end

unpackers['true'] = function ()
    return true
end

unpackers['float'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+3 > j then
        c:underflow(i+3)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+4
François Perrad's avatar
François Perrad committed
490
    return unpack('>f', s, i)
François Perrad's avatar
François Perrad committed
491
492
493
494
495
496
497
498
499
end

unpackers['double'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+7 > j then
        c:underflow(i+7)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+8
François Perrad's avatar
François Perrad committed
500
    return unpack('>d', s, i)
François Perrad's avatar
François Perrad committed
501
502
503
504
505
506
507
508
509
510
511
512
513
end

unpackers['fixnum_pos'] = function (c, val)
    return val
end

unpackers['uint8'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+1
François Perrad's avatar
François Perrad committed
514
    return unpack('>I1', s, i)
François Perrad's avatar
François Perrad committed
515
516
517
518
519
520
521
522
523
end

unpackers['uint16'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+1 > j then
        c:underflow(i+1)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+2
François Perrad's avatar
François Perrad committed
524
    return unpack('>I2', s, i)
François Perrad's avatar
François Perrad committed
525
526
527
528
529
530
531
532
533
end

unpackers['uint32'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+3 > j then
        c:underflow(i+3)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+4
François Perrad's avatar
François Perrad committed
534
    return unpack('>I4', s, i)
François Perrad's avatar
François Perrad committed
535
536
537
538
539
540
541
542
543
end

unpackers['uint64'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+7 > j then
        c:underflow(i+7)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+8
François Perrad's avatar
François Perrad committed
544
    return unpack('>I8', s, i)
François Perrad's avatar
François Perrad committed
545
546
547
548
549
550
551
552
553
554
555
556
557
end

unpackers['fixnum_neg'] = function (c, val)
    return val - 0x100
end

unpackers['int8'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+1
François Perrad's avatar
François Perrad committed
558
    return unpack('>i1', s, i)
François Perrad's avatar
François Perrad committed
559
560
561
562
563
564
565
566
567
end

unpackers['int16'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+1 > j then
        c:underflow(i+1)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+2
François Perrad's avatar
François Perrad committed
568
    return unpack('>i2', s, i)
François Perrad's avatar
François Perrad committed
569
570
571
572
573
574
575
576
577
end

unpackers['int32'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+3 > j then
        c:underflow(i+3)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+4
François Perrad's avatar
François Perrad committed
578
    return unpack('>i4', s, i)
François Perrad's avatar
François Perrad committed
579
580
581
582
583
584
585
586
587
end

unpackers['int64'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+7 > j then
        c:underflow(i+7)
        s, i, j = c.s, c.i, c.j
    end
    c.i = i+8
François Perrad's avatar
François Perrad committed
588
    return unpack('>i8', s, i)
François Perrad's avatar
François Perrad committed
589
590
591
592
end

unpackers['fixstr'] = function (c, val)
    local s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
593
    local n = val & 0x1F
François Perrad's avatar
François Perrad committed
594
595
596
597
    local e = i+n-1
    if e > j then
        c:underflow(e)
        s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
598
        e = i+n-1
François Perrad's avatar
François Perrad committed
599
600
601
602
603
604
605
606
607
608
609
    end
    c.i = i+n
    return s:sub(i, e)
end

unpackers['str8'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
610
    local n = unpack('>I1', s, i)
François Perrad's avatar
François Perrad committed
611
612
613
614
615
616
    i = i+1
    c.i = i
    local e = i+n-1
    if e > j then
        c:underflow(e)
        s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
617
        e = i+n-1
François Perrad's avatar
François Perrad committed
618
619
620
621
622
623
624
625
626
627
628
    end
    c.i = i+n
    return s:sub(i, e)
end

unpackers['str16'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+1 > j then
        c:underflow(i+1)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
629
    local n = unpack('>I2', s, i)
François Perrad's avatar
François Perrad committed
630
631
632
633
634
635
    i = i+2
    c.i = i
    local e = i+n-1
    if e > j then
        c:underflow(e)
        s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
636
        e = i+n-1
François Perrad's avatar
François Perrad committed
637
638
639
640
641
642
643
644
645
646
647
    end
    c.i = i+n
    return s:sub(i, e)
end

unpackers['str32'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+3 > j then
        c:underflow(i+3)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
648
    local n = unpack('>I4', s, i)
François Perrad's avatar
François Perrad committed
649
650
651
    i = i+4
    c.i = i
    local e = i+n-1
652
    if e > j or n < 0 then
François Perrad's avatar
François Perrad committed
653
654
        c:underflow(e)
        s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
655
        e = i+n-1
François Perrad's avatar
François Perrad committed
656
657
658
659
660
661
662
663
664
665
    end
    c.i = i+n
    return s:sub(i, e)
end

unpackers['bin8'] = unpackers['str8']
unpackers['bin16'] = unpackers['str16']
unpackers['bin32'] = unpackers['str32']

unpackers['fixarray'] = function (c, val)
François Perrad's avatar
François Perrad committed
666
    return unpack_array(c, val & 0xF)
François Perrad's avatar
François Perrad committed
667
668
669
670
671
672
673
674
end

unpackers['array16'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+1 > j then
        c:underflow(i+1)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
675
    local n = unpack('>I2', s, i)
François Perrad's avatar
François Perrad committed
676
    c.i = i+2
François Perrad's avatar
François Perrad committed
677
    return unpack_array(c, n)
François Perrad's avatar
François Perrad committed
678
679
680
681
682
683
684
685
end

unpackers['array32'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+3 > j then
        c:underflow(i+3)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
686
    local n = unpack('>I4', s, i)
François Perrad's avatar
François Perrad committed
687
    c.i = i+4
François Perrad's avatar
François Perrad committed
688
    return unpack_array(c, n)
François Perrad's avatar
François Perrad committed
689
690
691
end

unpackers['fixmap'] = function (c, val)
François Perrad's avatar
François Perrad committed
692
    return unpack_map(c, val & 0xF)
François Perrad's avatar
François Perrad committed
693
694
695
696
697
698
699
700
end

unpackers['map16'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+1 > j then
        c:underflow(i+1)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
701
    local n = unpack('>I2', s, i)
François Perrad's avatar
François Perrad committed
702
    c.i = i+2
François Perrad's avatar
François Perrad committed
703
    return unpack_map(c, n)
François Perrad's avatar
François Perrad committed
704
705
706
707
708
709
710
711
end

unpackers['map32'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+3 > j then
        c:underflow(i+3)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
712
    local n = unpack('>I4', s, i)
François Perrad's avatar
François Perrad committed
713
    c.i = i+4
François Perrad's avatar
François Perrad committed
714
    return unpack_map(c, n)
François Perrad's avatar
François Perrad committed
715
716
717
718
719
720
721
end

function m.build_ext (tag, data)
    return nil
end

for k = 0, 4 do
François Perrad's avatar
François Perrad committed
722
    local n = tointeger(2^k)
723
    unpackers['fixext' .. tostring(n)] = function (c)
François Perrad's avatar
François Perrad committed
724
725
726
727
728
        local s, i, j = c.s, c.i, c.j
        if i > j then
            c:underflow(i)
            s, i, j = c.s, c.i, c.j
        end
François Perrad's avatar
François Perrad committed
729
        local tag = unpack('>i1', s, i)
François Perrad's avatar
François Perrad committed
730
731
732
733
734
735
        i = i+1
        c.i = i
        local e = i+n-1
        if e > j then
            c:underflow(e)
            s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
736
            e = i+n-1
François Perrad's avatar
François Perrad committed
737
738
        end
        c.i = i+n
François Perrad's avatar
François Perrad committed
739
        return m.build_ext(tag, s:sub(i, e))
François Perrad's avatar
François Perrad committed
740
741
742
743
744
745
746
747
748
    end
end

unpackers['ext8'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
749
    local n = unpack('>I1', s, i)
François Perrad's avatar
François Perrad committed
750
751
752
753
754
755
    i = i+1
    c.i = i
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
756
    local tag = unpack('>i1', s, i)
François Perrad's avatar
François Perrad committed
757
758
759
760
761
762
    i = i+1
    c.i = i
    local e = i+n-1
    if e > j then
        c:underflow(e)
        s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
763
        e = i+n-1
François Perrad's avatar
François Perrad committed
764
765
    end
    c.i = i+n
François Perrad's avatar
François Perrad committed
766
    return m.build_ext(tag, s:sub(i, e))
François Perrad's avatar
François Perrad committed
767
768
769
770
771
772
773
774
end

unpackers['ext16'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+1 > j then
        c:underflow(i+1)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
775
    local n = unpack('>I2', s, i)
François Perrad's avatar
François Perrad committed
776
777
778
779
780
781
    i = i+2
    c.i = i
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
782
    local tag = unpack('>i1', s, i)
François Perrad's avatar
François Perrad committed
783
784
785
786
787
788
    i = i+1
    c.i = i
    local e = i+n-1
    if e > j then
        c:underflow(e)
        s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
789
        e = i+n-1
François Perrad's avatar
François Perrad committed
790
791
    end
    c.i = i+n
François Perrad's avatar
François Perrad committed
792
    return m.build_ext(tag, s:sub(i, e))
François Perrad's avatar
François Perrad committed
793
794
795
796
797
798
799
800
end

unpackers['ext32'] = function (c)
    local s, i, j = c.s, c.i, c.j
    if i+3 > j then
        c:underflow(i+3)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
801
    local n = unpack('>I4', s, i)
François Perrad's avatar
François Perrad committed
802
803
804
805
806
807
    i = i+4
    c.i = i
    if i > j then
        c:underflow(i)
        s, i, j = c.s, c.i, c.j
    end
François Perrad's avatar
François Perrad committed
808
    local tag = unpack('>i1', s, i)
François Perrad's avatar
François Perrad committed
809
810
811
    i = i+1
    c.i = i
    local e = i+n-1
812
    if e > j or n < 0 then
François Perrad's avatar
François Perrad committed
813
814
        c:underflow(e)
        s, i, j = c.s, c.i, c.j
François Perrad's avatar
François Perrad committed
815
        e = i+n-1
François Perrad's avatar
François Perrad committed
816
817
    end
    c.i = i+n
François Perrad's avatar
François Perrad committed
818
    return m.build_ext(tag, s:sub(i, e))
François Perrad's avatar
François Perrad committed
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
end


local function cursor_string (str)
    return {
        s = str,
        i = 1,
        j = #str,
        underflow = function (self)
                        error "missing bytes"
                    end,
    }
end

local function cursor_loader (ld)
    return {
        s = '',
        i = 1,
        j = 0,
        underflow = function (self, e)
                        self.s = self.s:sub(self.i)
                        e = e - self.i + 1
                        self.i = 1
                        self.j = 0
                        while e > self.j do
                            local chunk = ld()
                            if not chunk then
                                error "missing bytes"
                            end
                            self.s = self.s .. chunk
                            self.j = #self.s
                        end
                    end,
    }
end

function m.unpack (s)
    checktype('unpack', 1, s, 'string')
    local cursor = cursor_string(s)
    local data = unpackers['any'](cursor)
    if cursor.i < cursor.j then
        error "extra bytes"
    end
    return data
end

function m.unpacker (src)
    if type(src) == 'string' then
        local cursor = cursor_string(src)
        return function ()
            if cursor.i <= cursor.j then
                return cursor.i, unpackers['any'](cursor)
            end
        end
    elseif type(src) == 'function' then
        local cursor = cursor_loader(src)
        return function ()
            if cursor.i > cursor.j then
                pcall(cursor.underflow, cursor, cursor.i)
            end
            if cursor.i <= cursor.j then
                return true, unpackers['any'](cursor)
            end
        end
    else
        argerror('unpacker', 1, "string or function expected, got " .. type(src))
    end
end

set_string'string_compat'
889
set_integer'unsigned'
François Perrad's avatar
François Perrad committed
890
if math_type(0.0) == math_type(0) then
François Perrad's avatar
François Perrad committed
891
    set_number'integer'
892
elseif #pack('n', 0.0) == 4 then
893
    m.small_lua = true
894
895
896
    unpackers['double'] = nil
    unpackers['uint64'] = nil
    unpackers['int64'] = nil
François Perrad's avatar
François Perrad committed
897
898
    set_number'float'
else
899
    m.full64bits = true
François Perrad's avatar
François Perrad committed
900
    set_number'double'
François Perrad's avatar
François Perrad committed
901
902
903
    if #pack('n', 0.0) > 8 then
        m.long_double = true
    end
François Perrad's avatar
François Perrad committed
904
905
906
end
set_array'without_hole'

François Perrad's avatar
François Perrad committed
907
m._VERSION = '0.3.6'
François Perrad's avatar
François Perrad committed
908
m._DESCRIPTION = "lua-MessagePack : a pure Lua implementation"
François Perrad's avatar
François Perrad committed
909
m._COPYRIGHT = "Copyright (c) 2012-2016 Francois Perrad"
François Perrad's avatar
François Perrad committed
910
911
912
913
914
return m
--
-- This library is licensed under the terms of the MIT/X11 license,
-- like Lua itself.
--