summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/LiteJson
diff options
context:
space:
mode:
Diffstat (limited to 'Data/BuiltIn/Libraries/LiteJson')
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/LICENSE20
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/README.md46
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/bench/bench_all.lua6
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/bench/bench_decode.lua75
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/bench/bench_encode.lua63
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/bench/get_json_libs.sh15
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/bench/util/bench.lua62
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/json.lua388
-rw-r--r--Data/BuiltIn/Libraries/LiteJson/test/test.lua245
9 files changed, 920 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/LiteJson/LICENSE b/Data/BuiltIn/Libraries/LiteJson/LICENSE
new file mode 100644
index 0000000..9eb37b1
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2020 rxi
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Data/BuiltIn/Libraries/LiteJson/README.md b/Data/BuiltIn/Libraries/LiteJson/README.md
new file mode 100644
index 0000000..96b9b66
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/README.md
@@ -0,0 +1,46 @@
+# ![json.lua](https://cloud.githubusercontent.com/assets/3920290/9281532/99e5e0cc-42bd-11e5-8fce-eaff2f7fc681.png)
+A lightweight JSON library for Lua
+
+
+## Features
+* Implemented in pure Lua: works with 5.1, 5.2, 5.3 and JIT
+* Fast: generally outperforms other pure Lua JSON implementations
+ ([benchmark scripts](bench/))
+* Tiny: around 280sloc, 9kb
+* Proper error messages, *eg:* `expected '}' or ',' at line 203 col 30`
+
+
+## Usage
+The [json.lua](json.lua?raw=1) file should be dropped into an existing project
+and required by it:
+```lua
+json = require "json"
+```
+The library provides the following functions:
+
+#### json.encode(value)
+Returns a string representing `value` encoded in JSON.
+```lua
+json.encode({ 1, 2, 3, { x = 10 } }) -- Returns '[1,2,3,{"x":10}]'
+```
+
+#### json.decode(str)
+Returns a value representing the decoded JSON string.
+```lua
+json.decode('[1,2,3,{"x":10}]') -- Returns { 1, 2, 3, { x = 10 } }
+```
+
+## Notes
+* Trying to encode values which are unrepresentable in JSON will never result
+ in type conversion or other magic: sparse arrays, tables with mixed key types
+ or invalid numbers (NaN, -inf, inf) will raise an error
+* `null` values contained within an array or object are converted to `nil` and
+ are therefore lost upon decoding
+* *Pretty* encoding is not supported, `json.encode()` only encodes to a compact
+ format
+
+
+## License
+This library is free software; you can redistribute it and/or modify it under
+the terms of the MIT license. See [LICENSE](LICENSE) for details.
+
diff --git a/Data/BuiltIn/Libraries/LiteJson/bench/bench_all.lua b/Data/BuiltIn/Libraries/LiteJson/bench/bench_all.lua
new file mode 100644
index 0000000..3f99e99
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/bench/bench_all.lua
@@ -0,0 +1,6 @@
+
+print("[decode]")
+loadfile("bench_decode.lua")()
+print()
+print("[encode]")
+loadfile("bench_encode.lua")()
diff --git a/Data/BuiltIn/Libraries/LiteJson/bench/bench_decode.lua b/Data/BuiltIn/Libraries/LiteJson/bench/bench_decode.lua
new file mode 100644
index 0000000..6c2e93a
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/bench/bench_decode.lua
@@ -0,0 +1,75 @@
+local bench = require "util.bench"
+
+
+local libs = {
+ "../json.lua", -- https://github.com/rxi/json.lua
+ "dkjson.lua", -- https://github.com/LuaDist/dkjson
+ "jfjson.lua", -- http://regex.info/blog/lua/json
+ --"json4lua.lua", -- https://github.com/craigmj/json4lua
+}
+
+
+-- JSON string: wikipedia example stored 1000 times in an array
+local text = "[" .. string.rep([[{
+ "firstName": "John",
+ "lastName": "Smith",
+ "isAlive": true,
+ "age": 25,
+ "address": {
+ "streetAddress": "21 2nd Street",
+ "city": "New York",
+ "state": "NY",
+ "postalCode": "10021-3100"
+ },
+ "phoneNumbers": [
+ {
+ "type": "home",
+ "number": "212 555-1234"
+ },
+ {
+ "type": "office",
+ "number": "646 555-4567"
+ }
+ ],
+ "children": [],
+ "spouse": null
+}, ]], 1000):sub(1, -3) .. "]"
+
+
+-- As this is meant to be a pure Lua benchmark, we remove the ability to
+-- require 'lpeg' so dkjson doesn't use it for parsing. (Incidentally json.lua
+-- seems to outperform libraries which use lpeg when both are using LuaJIT)
+local _require = require
+require = function(modname)
+ if modname == "lpeg" then error() end
+ return _require(modname)
+end
+
+-- Run benchmarks, store results
+local results = {}
+
+for i, name in ipairs(libs) do
+ local f = loadfile(name)
+ if not f then
+ error( "failed to load '" .. name .. "'; run './get_json_libs.sh'" )
+ end
+ local json = f()
+
+ -- Remap functions to work for jfjson.lua
+ if name == "jfjson.lua" then
+ local _encode, _decode = json.encode, json.decode
+ json.encode = function(...) return _encode(json, ...) end
+ json.decode = function(...) return _decode(json, ...) end
+ end
+
+ -- Warmup (for LuaJIT)
+ bench.run(name, 1, function() json.decode(text) end)
+
+ -- Run and push results
+ local res = bench.run(name, 10, function() json.decode(text) end)
+ table.insert(results, res)
+end
+
+
+bench.print_system_info()
+bench.print_results(results)
diff --git a/Data/BuiltIn/Libraries/LiteJson/bench/bench_encode.lua b/Data/BuiltIn/Libraries/LiteJson/bench/bench_encode.lua
new file mode 100644
index 0000000..426f7c8
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/bench/bench_encode.lua
@@ -0,0 +1,63 @@
+local bench = require "util.bench"
+
+
+local libs = {
+ "../json.lua", -- https://github.com/rxi/json.lua
+ "dkjson.lua", -- https://github.com/LuaDist/dkjson
+ "jfjson.lua", -- http://regex.info/blog/lua/json
+ "json4lua.lua", -- https://github.com/craigmj/json4lua
+}
+
+
+-- Build table which will be encoded: wikipedia example stored 1000 times
+local data = {}
+for i = 1, 1000 do
+ table.insert(data, {
+ firstName = "John",
+ lastName = "Smith",
+ isAlive = true,
+ age = 25,
+ address = {
+ streetAddress = "21 2nd Street",
+ city = "New York",
+ state = "NY",
+ postalCode = "10021-3100"
+ },
+ phoneNumbers = {
+ { type = "home", number = "212 555-1234" },
+ { type = "office", number = "646 555-4567" }
+ },
+ children = {},
+ spouse = nil
+ })
+end
+
+
+-- Run benchmarks
+local results = {}
+
+for i, name in ipairs(libs) do
+ local f = loadfile(name)
+ if not f then
+ error( "failed to load '" .. name .. "'; run './get_json_libs.sh'" )
+ end
+ local json = f()
+
+ -- Handle special cases
+ if name == "jfjson.lua" then
+ local _encode, _decode = json.encode, json.decode
+ json.encode = function(...) return _encode(json, ...) end
+ json.decode = function(...) return _decode(json, ...) end
+ end
+
+ -- Warmup (for LuaJIT)
+ bench.run(name, 1, function() json.encode(data) end)
+
+ -- Run and push results
+ local res = bench.run(name, 10, function() json.encode(data) end)
+ table.insert(results, res)
+end
+
+
+bench.print_system_info()
+bench.print_results(results)
diff --git a/Data/BuiltIn/Libraries/LiteJson/bench/get_json_libs.sh b/Data/BuiltIn/Libraries/LiteJson/bench/get_json_libs.sh
new file mode 100644
index 0000000..ecb4525
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/bench/get_json_libs.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# Downloads other JSON libraries for use in the benchmark scripts
+
+# Remove libraries
+rm dkjson.lua 2>/dev/null
+rm jfjson.lua 2>/dev/null
+rm json4lua.lua 2>/dev/null
+
+# Get libraries
+echo "Downloading json libs..."
+curl -sS -o dkjson.lua "https://raw.githubusercontent.com/LuaDist/dkjson/master/dkjson.lua"
+curl -sS -o json4lua.lua "https://raw.githubusercontent.com/craigmj/json4lua/master/json/json.lua"
+curl -sS -o jfjson.lua "http://regex.info/code/JSON.lua"
+
+echo "Done"
diff --git a/Data/BuiltIn/Libraries/LiteJson/bench/util/bench.lua b/Data/BuiltIn/Libraries/LiteJson/bench/util/bench.lua
new file mode 100644
index 0000000..9f02738
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/bench/util/bench.lua
@@ -0,0 +1,62 @@
+local bench = {}
+
+local unpack = unpack or table.unpack
+local fmt = string.format
+
+
+function bench.run(name, count, func)
+ -- Run bench
+ local res = {}
+ for i = 1, count do
+ local start_time = os.clock()
+ func()
+ table.insert(res, (os.clock() - start_time))
+ end
+ -- Calculate average
+ local avg = 0
+ for i, v in ipairs(res) do
+ avg = avg + v
+ end
+ avg = avg / #res
+ -- Build and return result table
+ return {
+ name = name,
+ avg = avg,
+ min = math.min(unpack(res)),
+ max = math.max(unpack(res)),
+ all = res,
+ }
+end
+
+
+function bench.get_cpu_name()
+ local fp = io.open("/proc/cpuinfo", "rb")
+ if not fp then
+ return "unknown"
+ end
+ local text = fp:read("*a")
+ return text:match("model name%s*:%s*(.-)\n")
+end
+
+
+function bench.print_system_info()
+ print( fmt("Lua version : %s", jit and jit.version or _VERSION) )
+ print( fmt("CPU name : %s", bench.get_cpu_name()) )
+end
+
+
+function bench.print_results(results)
+ -- Find best average
+ local best = math.huge
+ for i, v in ipairs(results) do
+ best = math.min(best, v.avg)
+ end
+ -- Print results
+ for i, v in ipairs(results) do
+ print( fmt("%-13s : %.03gs [x%1.3g] (min: %.03gs, max %.03gs)",
+ v.name, v.avg, v.avg / best, v.min, v.max) )
+ end
+end
+
+
+return bench
diff --git a/Data/BuiltIn/Libraries/LiteJson/json.lua b/Data/BuiltIn/Libraries/LiteJson/json.lua
new file mode 100644
index 0000000..711ef78
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/json.lua
@@ -0,0 +1,388 @@
+--
+-- json.lua
+--
+-- Copyright (c) 2020 rxi
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy of
+-- this software and associated documentation files (the "Software"), to deal in
+-- the Software without restriction, including without limitation the rights to
+-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+-- of the Software, and to permit persons to whom the Software is furnished to do
+-- so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in all
+-- copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+local json = { _version = "0.1.2" }
+
+-------------------------------------------------------------------------------
+-- Encode
+-------------------------------------------------------------------------------
+
+local encode
+
+local escape_char_map = {
+ [ "\\" ] = "\\",
+ [ "\"" ] = "\"",
+ [ "\b" ] = "b",
+ [ "\f" ] = "f",
+ [ "\n" ] = "n",
+ [ "\r" ] = "r",
+ [ "\t" ] = "t",
+}
+
+local escape_char_map_inv = { [ "/" ] = "/" }
+for k, v in pairs(escape_char_map) do
+ escape_char_map_inv[v] = k
+end
+
+
+local function escape_char(c)
+ return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
+end
+
+
+local function encode_nil(val)
+ return "null"
+end
+
+
+local function encode_table(val, stack)
+ local res = {}
+ stack = stack or {}
+
+ -- Circular reference?
+ if stack[val] then error("circular reference") end
+
+ stack[val] = true
+
+ if rawget(val, 1) ~= nil or next(val) == nil then
+ -- Treat as array -- check keys are valid and it is not sparse
+ local n = 0
+ for k in pairs(val) do
+ if type(k) ~= "number" then
+ error("invalid table: mixed or invalid key types")
+ end
+ n = n + 1
+ end
+ if n ~= #val then
+ error("invalid table: sparse array")
+ end
+ -- Encode
+ for i, v in ipairs(val) do
+ table.insert(res, encode(v, stack))
+ end
+ stack[val] = nil
+ return "[" .. table.concat(res, ",") .. "]"
+
+ else
+ -- Treat as an object
+ for k, v in pairs(val) do
+ if type(k) ~= "string" then
+ error("invalid table: mixed or invalid key types")
+ end
+ table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
+ end
+ stack[val] = nil
+ return "{" .. table.concat(res, ",") .. "}"
+ end
+end
+
+
+local function encode_string(val)
+ return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
+end
+
+
+local function encode_number(val)
+ -- Check for NaN, -inf and inf
+ if val ~= val or val <= -math.huge or val >= math.huge then
+ error("unexpected number value '" .. tostring(val) .. "'")
+ end
+ return string.format("%.14g", val)
+end
+
+
+local type_func_map = {
+ [ "nil" ] = encode_nil,
+ [ "table" ] = encode_table,
+ [ "string" ] = encode_string,
+ [ "number" ] = encode_number,
+ [ "boolean" ] = tostring,
+}
+
+
+encode = function(val, stack)
+ local t = type(val)
+ local f = type_func_map[t]
+ if f then
+ return f(val, stack)
+ end
+ error("unexpected type '" .. t .. "'")
+end
+
+
+function json.encode(val)
+ return ( encode(val) )
+end
+
+
+-------------------------------------------------------------------------------
+-- Decode
+-------------------------------------------------------------------------------
+
+local parse
+
+local function create_set(...)
+ local res = {}
+ for i = 1, select("#", ...) do
+ res[ select(i, ...) ] = true
+ end
+ return res
+end
+
+local space_chars = create_set(" ", "\t", "\r", "\n")
+local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
+local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
+local literals = create_set("true", "false", "null")
+
+local literal_map = {
+ [ "true" ] = true,
+ [ "false" ] = false,
+ [ "null" ] = nil,
+}
+
+
+local function next_char(str, idx, set, negate)
+ for i = idx, #str do
+ if set[str:sub(i, i)] ~= negate then
+ return i
+ end
+ end
+ return #str + 1
+end
+
+
+local function decode_error(str, idx, msg)
+ local line_count = 1
+ local col_count = 1
+ for i = 1, idx - 1 do
+ col_count = col_count + 1
+ if str:sub(i, i) == "\n" then
+ line_count = line_count + 1
+ col_count = 1
+ end
+ end
+ error( string.format("%s at line %d col %d", msg, line_count, col_count) )
+end
+
+
+local function codepoint_to_utf8(n)
+ -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
+ local f = math.floor
+ if n <= 0x7f then
+ return string.char(n)
+ elseif n <= 0x7ff then
+ return string.char(f(n / 64) + 192, n % 64 + 128)
+ elseif n <= 0xffff then
+ return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
+ elseif n <= 0x10ffff then
+ return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
+ f(n % 4096 / 64) + 128, n % 64 + 128)
+ end
+ error( string.format("invalid unicode codepoint '%x'", n) )
+end
+
+
+local function parse_unicode_escape(s)
+ local n1 = tonumber( s:sub(1, 4), 16 )
+ local n2 = tonumber( s:sub(7, 10), 16 )
+ -- Surrogate pair?
+ if n2 then
+ return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
+ else
+ return codepoint_to_utf8(n1)
+ end
+end
+
+
+local function parse_string(str, i)
+ local res = ""
+ local j = i + 1
+ local k = j
+
+ while j <= #str do
+ local x = str:byte(j)
+
+ if x < 32 then
+ decode_error(str, j, "control character in string")
+
+ elseif x == 92 then -- `\`: Escape
+ res = res .. str:sub(k, j - 1)
+ j = j + 1
+ local c = str:sub(j, j)
+ if c == "u" then
+ 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 decode_error(str, j - 1, "invalid unicode escape in string")
+ res = res .. parse_unicode_escape(hex)
+ j = j + #hex
+ else
+ if not escape_chars[c] then
+ decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
+ end
+ res = res .. escape_char_map_inv[c]
+ end
+ k = j + 1
+
+ elseif x == 34 then -- `"`: End of string
+ res = res .. str:sub(k, j - 1)
+ return res, j + 1
+ end
+
+ j = j + 1
+ end
+
+ decode_error(str, i, "expected closing quote for string")
+end
+
+
+local function parse_number(str, i)
+ local x = next_char(str, i, delim_chars)
+ local s = str:sub(i, x - 1)
+ local n = tonumber(s)
+ if not n then
+ decode_error(str, i, "invalid number '" .. s .. "'")
+ end
+ return n, x
+end
+
+
+local function parse_literal(str, i)
+ local x = next_char(str, i, delim_chars)
+ local word = str:sub(i, x - 1)
+ if not literals[word] then
+ decode_error(str, i, "invalid literal '" .. word .. "'")
+ end
+ return literal_map[word], x
+end
+
+
+local function parse_array(str, i)
+ local res = {}
+ local n = 1
+ i = i + 1
+ while 1 do
+ local x
+ i = next_char(str, i, space_chars, true)
+ -- Empty / end of array?
+ if str:sub(i, i) == "]" then
+ i = i + 1
+ break
+ end
+ -- Read token
+ x, i = parse(str, i)
+ res[n] = x
+ n = n + 1
+ -- Next token
+ i = next_char(str, i, space_chars, true)
+ local chr = str:sub(i, i)
+ i = i + 1
+ if chr == "]" then break end
+ if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
+ end
+ return res, i
+end
+
+
+local function parse_object(str, i)
+ local res = {}
+ i = i + 1
+ while 1 do
+ local key, val
+ i = next_char(str, i, space_chars, true)
+ -- Empty / end of object?
+ if str:sub(i, i) == "}" then
+ i = i + 1
+ break
+ end
+ -- Read key
+ if str:sub(i, i) ~= '"' then
+ decode_error(str, i, "expected string for key")
+ end
+ key, i = parse(str, i)
+ -- Read ':' delimiter
+ i = next_char(str, i, space_chars, true)
+ if str:sub(i, i) ~= ":" then
+ decode_error(str, i, "expected ':' after key")
+ end
+ i = next_char(str, i + 1, space_chars, true)
+ -- Read value
+ val, i = parse(str, i)
+ -- Set
+ res[key] = val
+ -- Next token
+ i = next_char(str, i, space_chars, true)
+ local chr = str:sub(i, i)
+ i = i + 1
+ if chr == "}" then break end
+ if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
+ end
+ return res, i
+end
+
+
+local char_func_map = {
+ [ '"' ] = parse_string,
+ [ "0" ] = parse_number,
+ [ "1" ] = parse_number,
+ [ "2" ] = parse_number,
+ [ "3" ] = parse_number,
+ [ "4" ] = parse_number,
+ [ "5" ] = parse_number,
+ [ "6" ] = parse_number,
+ [ "7" ] = parse_number,
+ [ "8" ] = parse_number,
+ [ "9" ] = parse_number,
+ [ "-" ] = parse_number,
+ [ "t" ] = parse_literal,
+ [ "f" ] = parse_literal,
+ [ "n" ] = parse_literal,
+ [ "[" ] = parse_array,
+ [ "{" ] = parse_object,
+}
+
+
+parse = function(str, idx)
+ local chr = str:sub(idx, idx)
+ local f = char_func_map[chr]
+ if f then
+ return f(str, idx)
+ end
+ decode_error(str, idx, "unexpected character '" .. chr .. "'")
+end
+
+
+function json.decode(str)
+ if type(str) ~= "string" then
+ error("expected argument of type string, got " .. type(str))
+ end
+ local res, idx = parse(str, next_char(str, 1, space_chars, true))
+ idx = next_char(str, idx, space_chars, true)
+ if idx <= #str then
+ decode_error(str, idx, "trailing garbage")
+ end
+ return res
+end
+
+
+return json
diff --git a/Data/BuiltIn/Libraries/LiteJson/test/test.lua b/Data/BuiltIn/Libraries/LiteJson/test/test.lua
new file mode 100644
index 0000000..74470b2
--- /dev/null
+++ b/Data/BuiltIn/Libraries/LiteJson/test/test.lua
@@ -0,0 +1,245 @@
+
+local json = loadfile("../json.lua")()
+
+
+local fmt = string.format
+
+local function test(name, func)
+ xpcall(function()
+ func()
+ print( fmt("[pass] %s", name) )
+ end, function(err)
+ print( fmt("[fail] %s : %s", name, err) )
+ end)
+end
+
+
+local function equal(a, b)
+ -- Handle table
+ if type(a) == "table" and type(b) == "table" then
+ for k in pairs(a) do
+ if not equal(a[k], b[k]) then
+ return false
+ end
+ end
+ for k in pairs(b) do
+ if not equal(b[k], a[k]) then
+ return false
+ end
+ end
+ return true
+ end
+ -- Handle scalar
+ return a == b
+end
+
+
+test("numbers", function()
+ local t = {
+ [ "123.456" ] = 123.456,
+ [ "-123" ] = -123,
+ [ "-567.765" ] = -567.765,
+ [ "12.3" ] = 12.3,
+ [ "0" ] = 0,
+ [ "0.10000000012" ] = 0.10000000012,
+ }
+ for k, v in pairs(t) do
+ local res = json.decode(k)
+ assert( res == v, fmt("expected '%s', got '%s'", k, res) )
+ local res = json.encode(v)
+ assert( res == k, fmt("expected '%s', got '%s'", v, res) )
+ end
+ assert( json.decode("13e2") == 13e2 )
+ assert( json.decode("13E+2") == 13e2 )
+ assert( json.decode("13e-2") == 13e-2 )
+end)
+
+
+test("literals", function()
+ assert( json.decode("true") == true )
+ assert( json.encode(true) == "true" )
+ assert( json.decode("false") == false )
+ assert( json.encode(false) == "false" )
+ assert( json.decode("null") == nil )
+ assert( json.encode(nil) == "null")
+end)
+
+
+test("strings", function()
+ local s = ""
+ assert( s == json.decode( json.encode(s) ) )
+ local s = "\\"
+ assert( s == json.decode( json.encode(s) ) )
+ local s = "Hello world"
+ assert( s == json.decode( json.encode(s) ) )
+ local s = "\0 \13 \27"
+ assert( s == json.decode( json.encode(s) ) )
+ local s = "\0\r\n\8"
+ assert( s == json.decode( json.encode(s) ) )
+end)
+
+
+test("unicode", function()
+ local s = "こんにちは世界"
+ assert( s == json.decode( json.encode(s) ) )
+end)
+
+
+test("arrays", function()
+ local t = { "cat", "dog", "owl" }
+ assert( equal( t, json.decode( json.encode(t) ) ) )
+end)
+
+
+test("objects", function()
+ local t = { x = 10, y = 20, z = 30 }
+ assert( equal( t, json.decode( json.encode(t) ) ) )
+end)
+
+
+--test("strict decode", function()
+-- local t = {
+-- '{x : 1}',
+-- '{x : hello}',
+-- "{'x' : 1}",
+-- '{"x" : nil}',
+-- '{"x" : 0x10}',
+-- '{"x" : 001}',
+-- '{"x" : .1}',
+-- '{"x" : 1, }',
+-- '[1, 2, 3, ]',
+-- }
+-- for i, v in ipairs(t) do
+-- local status = pcall(json.decode, v)
+-- assert( not status, fmt("'%s' was parsed without error", v) )
+-- end
+--end)
+
+
+test("decode invalid", function()
+ local t = {
+ '',
+ ' ',
+ '{',
+ '[',
+ '{"x" : ',
+ '{"x" : 1',
+ '{"x" : z }',
+ '{"x" : 123z }',
+ '{x : 123 }',
+ '{10 : 123 }',
+ '{]',
+ '[}',
+ '"a',
+ '10 xx',
+ '{}123'
+ }
+ for i, v in ipairs(t) do
+ local status = pcall(json.decode, v)
+ assert( not status, fmt("'%s' was parsed without error", v) )
+ end
+end)
+
+
+test("decode invalid string", function()
+ local t = {
+ [["\z"]],
+ [["\1"]],
+ [["\u000z"]],
+ [["\ud83d\ude0q"]],
+ '"x\ny"',
+ '"x\0y"',
+ }
+ for i, v in ipairs(t) do
+ local status, err = pcall(json.decode, v)
+ assert( not status, fmt("'%s' was parsed without error", v) )
+ end
+end)
+
+
+test("decode escape", function()
+ local t = {
+ [ [["\u263a"]] ] = '☺',
+ [ [["\ud83d\ude02"]] ] = '😂',
+ [ [["\r\n\t\\\""]] ] = '\r\n\t\\"',
+ [ [["\\"]] ] = '\\',
+ [ [["\\\\"]] ] = '\\\\',
+ [ [["\/"]] ] = '/',
+ [ [["\\u \u263a"]] ] = [[\u ☺]],
+ }
+ for k, v in pairs(t) do
+ local res = json.decode(k)
+ assert( res == v, fmt("expected '%s', got '%s'", v, res) )
+ end
+end)
+
+
+test("decode empty", function()
+ local t = {
+ [ '[]' ] = {},
+ [ '{}' ] = {},
+ [ '""' ] = "",
+ }
+ for k, v in pairs(t) do
+ local res = json.decode(k)
+ assert( equal(res, v), fmt("'%s' did not equal expected", k) )
+ end
+end)
+
+
+test("decode collection", function()
+ local t = {
+ [ '[1, 2, 3, 4, 5, 6]' ] = {1, 2, 3, 4, 5, 6},
+ [ '[1, 2, 3, "hello"]' ] = {1, 2, 3, "hello"},
+ [ '{ "name": "test", "id": 231 }' ] = {name = "test", id = 231},
+ [ '{"x":1,"y":2,"z":[1,2,3]}' ] = {x = 1, y = 2, z = {1, 2, 3}},
+ }
+ for k, v in pairs(t) do
+ local res = json.decode(k)
+ assert( equal(res, v), fmt("'%s' did not equal expected", k) )
+ end
+end)
+
+
+test("encode invalid", function()
+ local t = {
+ { [1000] = "b" },
+ { [ function() end ] = 12 },
+ { nil, 2, 3, 4 },
+ { x = 10, [1] = 2 },
+ { [1] = "a", [3] = "b" },
+ { x = 10, [4] = 5 },
+ }
+ for i, v in ipairs(t) do
+ local status, res = pcall(json.encode, v)
+ assert( not status, fmt("encoding idx %d did not result in an error", i) )
+ end
+end)
+
+
+test("encode invalid number", function()
+ local t = {
+ math.huge, -- inf
+ -math.huge, -- -inf
+ math.huge * 0, -- NaN
+ }
+ for i, v in ipairs(t) do
+ local status, res = pcall(json.encode, v)
+ assert( not status, fmt("encoding '%s' did not result in an error", v) )
+ end
+end)
+
+
+test("encode escape", function()
+ local t = {
+ [ '"x"' ] = [["\"x\""]],
+ [ 'x\ny' ] = [["x\ny"]],
+ [ 'x\0y' ] = [["x\u0000y"]],
+ [ 'x\27y' ] = [["x\u001by"]],
+ [ '\r\n\t\\"' ] = [["\r\n\t\\\""]],
+ }
+ for k, v in pairs(t) do
+ local res = json.encode(k)
+ assert( res == v, fmt("'%s' was not escaped properly", k) )
+ end
+end)