mirror of https://github.com/rxi/json.lua.git
Merge cb4bc745c6
into dbf4b2dd2e
commit
3a962afc66
|
@ -63,10 +63,10 @@ for i, name in ipairs(libs) do
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Warmup (for LuaJIT)
|
-- Warmup (for LuaJIT)
|
||||||
bench.run(name, 1, function() json.decode(text) end)
|
bench.run(name, 10, function() json.decode(text) end)
|
||||||
|
|
||||||
-- Run and push results
|
-- Run and push results
|
||||||
local res = bench.run(name, 10, function() json.decode(text) end)
|
local res = bench.run(name, 100, function() json.decode(text) end)
|
||||||
table.insert(results, res)
|
table.insert(results, res)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,10 @@ for i, name in ipairs(libs) do
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Warmup (for LuaJIT)
|
-- Warmup (for LuaJIT)
|
||||||
bench.run(name, 1, function() json.encode(data) end)
|
bench.run(name, 10, function() json.encode(data) end)
|
||||||
|
|
||||||
-- Run and push results
|
-- Run and push results
|
||||||
local res = bench.run(name, 10, function() json.encode(data) end)
|
local res = bench.run(name, 100, function() json.encode(data) end)
|
||||||
table.insert(results, res)
|
table.insert(results, res)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ local fmt = string.format
|
||||||
|
|
||||||
|
|
||||||
function bench.run(name, count, func)
|
function bench.run(name, count, func)
|
||||||
|
-- Ensure all garbage is collected
|
||||||
|
for _ = 1, 10 do -- cycles
|
||||||
|
collectgarbage()
|
||||||
|
end
|
||||||
-- Run bench
|
-- Run bench
|
||||||
local res = {}
|
local res = {}
|
||||||
for i = 1, count do
|
for i = 1, count do
|
||||||
|
|
76
json.lua
76
json.lua
|
@ -51,20 +51,16 @@ local function escape_char(c)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function encode_nil(val)
|
local function encode_nil(rope)
|
||||||
return "null"
|
rope[#rope + 1] = "null"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function encode_table(val, stack)
|
local function encode_table(rope, val, stack)
|
||||||
local res = {}
|
|
||||||
stack = stack or {}
|
|
||||||
|
|
||||||
-- Circular reference?
|
-- Circular reference?
|
||||||
if stack[val] then error("circular reference") end
|
if stack[val] then error("circular reference") end
|
||||||
|
|
||||||
stack[val] = true
|
stack[val] = true
|
||||||
|
|
||||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||||
-- Treat as array -- check keys are valid and it is not sparse
|
-- Treat as array -- check keys are valid and it is not sparse
|
||||||
local n = 0
|
local n = 0
|
||||||
|
@ -78,61 +74,80 @@ local function encode_table(val, stack)
|
||||||
error("invalid table: sparse array")
|
error("invalid table: sparse array")
|
||||||
end
|
end
|
||||||
-- Encode
|
-- Encode
|
||||||
|
rope[#rope + 1] = "["
|
||||||
for i, v in ipairs(val) do
|
for i, v in ipairs(val) do
|
||||||
table.insert(res, encode(v, stack))
|
if i > 1 then
|
||||||
|
rope[#rope + 1] = ","
|
||||||
end
|
end
|
||||||
stack[val] = nil
|
encode(rope, v, stack)
|
||||||
return "[" .. table.concat(res, ",") .. "]"
|
end
|
||||||
|
rope[#rope + 1] = "]"
|
||||||
else
|
else
|
||||||
-- Treat as an object
|
-- Treat as an object
|
||||||
|
rope[#rope + 1] = "{"
|
||||||
|
local first = true
|
||||||
for k, v in pairs(val) do
|
for k, v in pairs(val) do
|
||||||
if type(k) ~= "string" then
|
if type(k) ~= "string" then
|
||||||
error("invalid table: mixed or invalid key types")
|
error("invalid table: mixed or invalid key types")
|
||||||
end
|
end
|
||||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
if not first then
|
||||||
|
rope[#rope + 1] = ","
|
||||||
|
end
|
||||||
|
encode(rope, k, stack)
|
||||||
|
rope[#rope + 1] = ":"
|
||||||
|
encode(rope, v, stack)
|
||||||
|
first = false
|
||||||
|
end
|
||||||
|
rope[#rope + 1] = "}"
|
||||||
end
|
end
|
||||||
stack[val] = nil
|
stack[val] = nil
|
||||||
return "{" .. table.concat(res, ",") .. "}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function encode_string(val)
|
local function encode_string(rope, val)
|
||||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
rope[#rope + 1] = '"'
|
||||||
|
rope[#rope + 1] = val:gsub('[%z\1-\31\\"]', escape_char)
|
||||||
|
rope[#rope + 1] = '"'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function encode_number(val)
|
local function encode_number(rope, val)
|
||||||
-- Check for NaN, -inf and inf
|
-- Check for NaN, -inf and inf
|
||||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||||
error("unexpected number value '" .. tostring(val) .. "'")
|
error("unexpected number value '" .. tostring(val) .. "'")
|
||||||
end
|
end
|
||||||
return string.format("%.14g", val)
|
-- See www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF
|
||||||
|
-- 17 digits suffice to losslessly represent 64-bit IEEE754 floats
|
||||||
|
rope[#rope + 1] = ("%.17g"):format(val)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function encode_boolean(rope, val)
|
||||||
|
rope[#rope + 1] = val and "true" or "false"
|
||||||
|
end
|
||||||
|
|
||||||
local type_func_map = {
|
local type_func_map = {
|
||||||
[ "nil" ] = encode_nil,
|
[ "nil" ] = encode_nil,
|
||||||
[ "table" ] = encode_table,
|
[ "table" ] = encode_table,
|
||||||
[ "string" ] = encode_string,
|
[ "string" ] = encode_string,
|
||||||
[ "number" ] = encode_number,
|
[ "number" ] = encode_number,
|
||||||
[ "boolean" ] = tostring,
|
[ "boolean" ] = encode_boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
encode = function(val, stack)
|
function encode(rope, val, stack)
|
||||||
local t = type(val)
|
local t = type(val)
|
||||||
local f = type_func_map[t]
|
local encoder = type_func_map[t]
|
||||||
if f then
|
if encoder then
|
||||||
return f(val, stack)
|
return encoder(rope, val, stack)
|
||||||
end
|
end
|
||||||
error("unexpected type '" .. t .. "'")
|
error("unexpected type '" .. t .. "'")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function json.encode(val)
|
function json.encode(val)
|
||||||
return ( encode(val) )
|
local rope = {}
|
||||||
|
encode(rope, val, {})
|
||||||
|
return table.concat(rope)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -216,7 +231,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
local function parse_string(str, i)
|
local function parse_string(str, i)
|
||||||
local res = ""
|
local res = {}
|
||||||
local j = i + 1
|
local j = i + 1
|
||||||
local k = j
|
local k = j
|
||||||
|
|
||||||
|
@ -227,32 +242,33 @@ local function parse_string(str, i)
|
||||||
decode_error(str, j, "control character in string")
|
decode_error(str, j, "control character in string")
|
||||||
|
|
||||||
elseif x == 92 then -- `\`: Escape
|
elseif x == 92 then -- `\`: Escape
|
||||||
res = res .. str:sub(k, j - 1)
|
res[#res + 1] = str:sub(k, j - 1)
|
||||||
j = j + 1
|
j = j + 1
|
||||||
local c = str:sub(j, j)
|
local c = str:sub(j, j)
|
||||||
if c == "u" then
|
if c == "u" then
|
||||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||||
or str:match("^%x%x%x%x", j + 1)
|
or str:match("^%x%x%x%x", j + 1)
|
||||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||||
res = res .. parse_unicode_escape(hex)
|
res[#res + 1] = parse_unicode_escape(hex)
|
||||||
j = j + #hex
|
j = j + #hex
|
||||||
else
|
else
|
||||||
if not escape_chars[c] then
|
if not escape_chars[c] then
|
||||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||||
end
|
end
|
||||||
res = res .. escape_char_map_inv[c]
|
res[#res + 1] = escape_char_map_inv[c]
|
||||||
end
|
end
|
||||||
k = j + 1
|
k = j + 1
|
||||||
|
|
||||||
elseif x == 34 then -- `"`: End of string
|
elseif x == 34 then -- `"`: End of string
|
||||||
res = res .. str:sub(k, j - 1)
|
res[#res + 1] = str:sub(k, j - 1)
|
||||||
return res, j + 1
|
return table.concat(res), j + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
j = j + 1
|
j = j + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
decode_error(str, i, "expected closing quote for string")
|
decode_error(str, i, "expected closing quote for string")
|
||||||
|
return table.concat(res)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@ test("numbers", function()
|
||||||
local t = {
|
local t = {
|
||||||
[ "123.456" ] = 123.456,
|
[ "123.456" ] = 123.456,
|
||||||
[ "-123" ] = -123,
|
[ "-123" ] = -123,
|
||||||
[ "-567.765" ] = -567.765,
|
[ "-567.76499999999999" ] = -567.765,
|
||||||
[ "12.3" ] = 12.3,
|
[ "12.300000000000001" ] = 12.3,
|
||||||
[ "0" ] = 0,
|
[ "0" ] = 0,
|
||||||
[ "0.10000000012" ] = 0.10000000012,
|
[ "0.10000000012" ] = 0.10000000012,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue