Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweak json.lua #9

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions json4lua/examples/example.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Demonstrates the simple functionality of the json module.
]]--
json = require('json')

-- lua 5.1+ compat
local foreach = table.foreach or loadstring("local t,f = ... for k,v in pairs(t) do if f(k,v) ~= nil then break end end")


-- Object to JSON encode
test = {
Expand All @@ -18,6 +21,6 @@ print('JSON encoded test is: ' .. jsonTest)
result = json.decode(jsonTest)

print ("The decoded table result:")
table.foreach(result,print)
foreach(result, print)
print ("The decoded table result.three")
table.foreach(result.three, print)
foreach(result.three, print)
46 changes: 44 additions & 2 deletions json4lua/examples/tests.lua
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function testJSON4Lua()

s = [["Test\u00A7\\"]]
r,e = json._decode_scanString(s,1)
assert(r=="Test\xC2\xA7\\" and e==9)
assert(r=="Test\194\167\\\"" and e==9)
print(s,r)

-- Test decode_scanNumber
Expand Down Expand Up @@ -162,10 +162,52 @@ function testJSON4Lua()

end

-- Test string decoding
s = [["Test\u00A7\\\""]]
r,e = json.decode(s)
assert(r=="Test\194\167\\\"")
print(s,r)

s = [["\\u"]]
r,e = json.decode(s)
assert(r=="\\u")
print(s,r)

s = [["\\"]]
r,e = json.decode(s)
assert(r=="Test\xC2\xA7\\\"", r)
assert(r=="\\")
print(s,r)

s = [["\v\///"]]
r,e = json.decode(s)
assert(r=="v///")
print(s,r)

-- Test invalid unicode escapes
s = [["\u000"]]
r,e = pcall(json.decode,s)
assert(not r and e)
print(s,r,e)

s = [["\u00"]]
r,e = pcall(json.decode,s)
assert(not r and e)
print(s,r,e)

s = [["\u0"]]
r,e = pcall(json.decode,s)
assert(not r and e)
print(s,r,e)

s = [["\u"]]
r,e = pcall(json.decode,s)
assert(not r and e)
print(s,r,e)

s = [["\uXXXX"]]
r,e = pcall(json.decode,s)
assert(not r and e)
print(s,r,e)

-- Test decode_scanObject
s = [[ {"one":1, "two":2, "three":"three", "four":true} ]]
Expand Down
15 changes: 9 additions & 6 deletions json4lua/examples/timetrials.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
--[[
Some Time Trails for the JSON4Lua package
]]--
--]]


require('json')
require('os')
require('table')

-- this is so we don't get shouted at for syntax errors
local mod = math.mod or math.fmod or (loadstring or load)("local a,b = ... return a % b")

local t1 = os.clock()
local jstr
Expand All @@ -16,7 +19,7 @@ for i=1,100 do
table.insert(t,j)
end
for j=1,500 do
table.insert(t,"VALUE")
table.insert(t,"VALUE\n\n\"\t")
end
jstr = json.encode(t)
v = json.decode(jstr)
Expand All @@ -25,8 +28,8 @@ end

for i = 1,100 do
local t = {}
for j=1,500 do
local m= math.mod(j,3)
for j=1,500 do
local m = mod(j,3)
if (m==0) then
t['a'..j] = true
elseif m==1 then
Expand All @@ -39,8 +42,8 @@ for i = 1,100 do
v = json.decode(jstr)
end

print (jstr)
--print (jstr)
--print(type(t1))
local t2 = os.clock()

print ("Elapsed time=" .. os.difftime(t2,t1) .. "s")
print ("Elapsed time=" .. t2 - t1 .. "s")
155 changes: 89 additions & 66 deletions json4lua/json/json.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,16 @@ local base = _G
-----------------------------------------------------------------------------
-- Module declaration
-----------------------------------------------------------------------------
module("json")
local _ENV = _ENV
local hasENV = false
-- Check for Lua 5.2
if not _ENV then
module("json")
else
-- Use the new module system
hasENV = true
end
_ENV = {}

-- Public functions

Expand All @@ -65,19 +74,19 @@ function encode (v)
if v==nil then
return "null"
end

local vtype = base.type(v)

-- Handle strings
if vtype=='string' then
return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string
end

-- Handle booleans
if vtype=='number' or vtype=='boolean' then
return base.tostring(v)
end

-- Handle tables
if vtype=='table' then
local rval = {}
Expand All @@ -100,12 +109,12 @@ function encode (v)
return '{' .. table.concat(rval,',') .. '}'
end
end

-- Handle null values
if vtype=='function' and v==null then
return 'null'
end

base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))
end

Expand Down Expand Up @@ -226,8 +235,8 @@ function decode_scanNumber(s,startPos)
local stringLen = string.len(s)
local acceptableChars = "+-0123456789.e"
while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
and endPos<=stringLen
) do
and endPos<=stringLen
) do
endPos = endPos + 1
end
local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
Expand Down Expand Up @@ -276,18 +285,21 @@ end
-- Initialize some things used by decode_scanString
-- You know, for efficiency
local escapeSequences = {
["\\t"] = "\t",
["\\f"] = "\f",
["\\r"] = "\r",
["\\n"] = "\n",
["\\b"] = "\b"
t = "\t",
f = "\f",
r = "\r",
n = "\n",
b = "\b"
}
local paddingSequences = {
u = "\\u"
}
base.setmetatable(escapeSequences, {__index = function(t,k)
-- skip "\" aka strip escape
return string.sub(k,2)
end})
-- END SoniEx2

-- 5.0, 5.1, 5.2, 5.3+
-- for _positive numbers_, math.fmod(a,b) == a % b
local mod = math.mod or math.fmod or ((base.loadstring or base.load)("return function(a,b) return a % b end")())

--- Scans a JSON string from the opening inverted comma or single quote to the
-- end of the string.
-- Returns the string extracted as a Lua string,
Expand All @@ -299,50 +311,58 @@ end})
function decode_scanString(s,startPos)
base.assert(startPos, 'decode_scanString(..) called without start position')
local startChar = string.sub(s,startPos,startPos)
-- START SoniEx2
-- PS: I don't think single quotes are valid JSON
base.assert(startChar == [["]] or startChar == [[']],'decode_scanString called for a non-string')
--base.assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart)
local t = {}
local i,j = startPos,startPos
while string.find(s, startChar, j+1) ~= j+1 do
local oldj = j
i,j = string.find(s, "\\.", j+1)
local x,y = string.find(s, startChar, oldj+1)
if not i or x < i then
base.print(s, startPos, string.sub(s,startPos,oldj))
i,j = x,y-1
if not x then base.print(s, startPos, string.sub(s,startPos,oldj)) end
end
table.insert(t, string.sub(s, oldj+1, i-1))
if string.sub(s, i, j) == "\\u" then
local a = string.sub(s,j+1,j+4)
j = j + 4
local n = base.tonumber(a, 16)
base.assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j)
-- math.floor(x/2^y) == lazy right shift
-- a % 2^b == bitwise_and(a, (2^b)-1)
-- 64 = 2^6
-- 4096 = 2^12 (or 2^6 * 2^6)
local x
if n < 0x80 then
x = string.char(n % 0x80)
elseif n < 0x800 then
-- [110x xxxx] [10xx xxxx]
x = string.char(0xC0 + (math.floor(n/64) % 0x20), 0x80 + (n % 0x40))
base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string')
local escaped = false
local endPos = startPos + 1
local bEnded = false
local stringLen = string.len(s)
local stringLenAdd1 = stringLen + 1
repeat
if not escaped then
local curChar = string.sub(s,endPos,endPos)
if curChar==[[\]] then
escaped = true
else
-- [1110 xxxx] [10xx xxxx] [10xx xxxx]
x = string.char(0xE0 + (math.floor(n/4096) % 0x10), 0x80 + (math.floor(n/64) % 0x40), 0x80 + (n % 0x40))
bEnded = curChar==startChar
end
table.insert(t, x)
else
table.insert(t, escapeSequences[string.sub(s, i, j)])
else
escaped = false
end
endPos = endPos + 1
base.assert(endPos <= stringLenAdd1, "unfinished string")
until bEnded
--local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)
--local stringEval = base.loadstring(stringValue)
--base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)
--return stringEval(), endPos
--return table.concat(t), endPos
local str = string.sub(s, startPos+1, endPos-2)
-- pad - we use a table here because we want to keep \u as \u
str = string.gsub(str, "\\(.)", function(x) return paddingSequences[x] or ("\\v" .. x .. "///") end)
if string.find(str, "\\u", -5) then
-- string end looks like `"...\uXXX"` or `"...\uXX"` or `"...\uX"` or even `"...\u"`
base.error("String decoding failed: bad Unicode escape")
end
table.insert(t,string.sub(j, j+1))
base.assert(string.find(s, startChar, j+1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")")
return table.concat(t,""), j+2
-- END SoniEx2
str = string.gsub(str, "\\([vu])((.)...)",
function(a,b,c)
if a == "v" then
return escapeSequences[c] or c
else
local n = base.tonumber(b, 16)
base.assert(n, "String decoding failed: bad Unicode escape")
if n < 128 then
-- [0xxx xxxx] aka ASCII
return string.char(n)
elseif n < 2048 then
-- [110x xxxx] [10xx xxxx]
return string.char(192+math.floor(n/64),128+mod(n,64))
else
-- [1110 xxxx] [10xx xxxx] [10xx xxxx]
return string.char(224+math.floor(n/4096),128+mod(math.floor(n/64),64),128+mod(n, 64))
end
end
end)
return str, endPos
end

--- Scans a JSON string skipping all whitespace from the current start position.
Expand All @@ -366,18 +386,18 @@ end
-- @return The string appropriately escaped.

local escapeList = {
['"'] = '\\"',
['\\'] = '\\\\',
['/'] = '\\/',
['\b'] = '\\b',
['\f'] = '\\f',
['\n'] = '\\n',
['\r'] = '\\r',
['\t'] = '\\t'
['"'] = '\\"',
['\\'] = '\\\\',
['/'] = '\\/',
['\b'] = '\\b',
['\f'] = '\\f',
['\n'] = '\\n',
['\r'] = '\\r',
['\t'] = '\\t'
}

function encodeString(s)
return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat
return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat
end

-- Determines whether the given Lua type is an array or a table / dictionary.
Expand Down Expand Up @@ -417,3 +437,6 @@ function isEncodable(o)
return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null)
end

if hasENV then
return _ENV
end
Loading