Created
August 8, 2024 15:21
-
-
Save terrancesnyder/cdc5c4e533a33b13ba0a1801bace7740 to your computer and use it in GitHub Desktop.
JSON encode LUA configmap for istio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apiVersion: v1 | |
kind: ConfigMap | |
metadata: | |
annotations: | |
name: lua-libs | |
namespace: gateways | |
data: | |
JSON.lua: "-- -*- coding: utf-8 -*-\n--\n-- Simple JSON encoding and decoding in | |
pure Lua.\n--\n-- Copyright 2010-2017 Jeffrey Friedl\n-- http://regex.info/blog/\n-- | |
Latest version: http://regex.info/blog/lua/json\n--\n-- This code is released | |
under a Creative Commons CC-BY \"Attribution\" License:\n-- http://creativecommons.org/licenses/by/3.0/deed.en_US\n--\n-- | |
It can be used for any purpose so long as:\n-- 1) the copyright notice above | |
is maintained\n-- 2) the web-page links above are maintained\n-- 3) the | |
'AUTHOR_NOTE' string below is maintained\n--\nlocal VERSION = '20170927.26' -- | |
version history at end of file\nlocal AUTHOR_NOTE = \"-[ JSON.lua package by Jeffrey | |
Friedl (http://regex.info/blog/lua/json) version 20170927.26 ]-\"\n\n--\n-- The | |
'AUTHOR_NOTE' variable exists so that information about the source\n-- of the | |
package is maintained even in compiled versions. It's also\n-- included in OBJDEF | |
below mostly to quiet warnings about unused variables.\n--\nlocal OBJDEF = {\n | |
\ VERSION = VERSION,\n AUTHOR_NOTE = AUTHOR_NOTE,\n}\n\n\n--\n-- Simple | |
JSON encoding and decoding in pure Lua.\n-- JSON definition: http://www.json.org/\n--\n--\n-- | |
\ JSON = assert(loadfile \"JSON.lua\")() -- one-time load of the routines\n--\n-- | |
\ local lua_value = JSON:decode(raw_json_text)\n--\n-- local raw_json_text | |
\ = JSON:encode(lua_table_or_value)\n-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) | |
-- \"pretty printed\" version for human readability\n--\n--\n--\n-- DECODING (from | |
a JSON string to a Lua table)\n--\n--\n-- JSON = assert(loadfile \"JSON.lua\")() | |
-- one-time load of the routines\n--\n-- local lua_value = JSON:decode(raw_json_text)\n--\n-- | |
\ If the JSON text is for an object or an array, e.g.\n-- { \"what\": \"books\", | |
\"count\": 3 }\n-- or\n-- [ \"Larry\", \"Curly\", \"Moe\" ]\n--\n-- the | |
result is a Lua table, e.g.\n-- { what = \"books\", count = 3 }\n-- or\n-- | |
\ { \"Larry\", \"Curly\", \"Moe\" }\n--\n--\n-- The encode and decode routines | |
accept an optional second argument,\n-- \"etc\", which is not used during encoding | |
or decoding, but upon error\n-- is passed along to error handlers. It can be | |
of any type (including nil).\n--\n--\n--\n-- ERROR HANDLING DURING DECODE\n--\n-- | |
\ With most errors during decoding, this code calls\n--\n-- JSON:onDecodeError(message, | |
text, location, etc)\n--\n-- with a message about the error, and if known, the | |
JSON text being\n-- parsed and the byte count where the problem was discovered. | |
You can\n-- replace the default JSON:onDecodeError() with your own function.\n--\n-- | |
\ The default onDecodeError() merely augments the message with data\n-- about | |
the text and the location (and, an 'etc' argument had been\n-- provided to decode(), | |
its value is tacked onto the message as well),\n-- and then calls JSON.assert(), | |
which itself defaults to Lua's built-in\n-- assert(), and can also be overridden.\n--\n-- | |
\ For example, in an Adobe Lightroom plugin, you might use something like\n--\n-- | |
\ function JSON:onDecodeError(message, text, location, etc)\n-- LrErrors.throwUserError(\"Internal | |
Error: invalid JSON data\")\n-- end\n--\n-- or even just\n--\n-- function | |
JSON.assert(message)\n-- LrErrors.throwUserError(\"Internal Error: | |
\" .. message)\n-- end\n--\n-- If JSON:decode() is passed a nil, this | |
is called instead:\n--\n-- JSON:onDecodeOfNilError(message, nil, nil, etc)\n--\n-- | |
\ and if JSON:decode() is passed HTML instead of JSON, this is called:\n--\n-- | |
\ JSON:onDecodeOfHTMLError(message, text, nil, etc)\n--\n-- The use of the | |
'etc' argument allows stronger coordination between\n-- decoding and error reporting, | |
especially when you provide your own\n-- error-handling routines. Continuing | |
with the the Adobe Lightroom\n-- plugin example:\n--\n-- function JSON:onDecodeError(message, | |
text, location, etc)\n-- local note = \"Internal Error: invalid JSON | |
data\"\n-- if type(etc) = 'table' and etc.photo then\n-- note | |
= note .. \" while processing for \" .. etc.photo:getFormattedMetadata('fileName')\n-- | |
\ end\n-- LrErrors.throwUserError(note)\n-- end\n--\n-- | |
\ :\n-- :\n--\n-- for i, photo in ipairs(photosToProcess) | |
do\n-- : \n-- : \n-- local | |
data = JSON:decode(someJsonText, { photo = photo })\n-- : \n-- | |
\ : \n-- end\n--\n--\n--\n-- If the JSON text | |
passed to decode() has trailing garbage (e.g. as with the JSON \"[123]xyzzy\"),\n-- | |
\ the method\n--\n-- JSON:onTrailingGarbage(json_text, location, parsed_value, | |
etc)\n--\n-- is invoked, where:\n--\n-- 'json_text' is the original JSON | |
text being parsed,\n-- 'location' is the count of bytes into 'json_text' | |
where the garbage starts (6 in the example),\n-- 'parsed_value' is the Lua | |
result of what was successfully parsed ({123} in the example),\n-- 'etc' | |
is as above.\n--\n-- If JSON:onTrailingGarbage() does not abort, it should return | |
the value decode() should return,\n-- or nil + an error message.\n--\n-- local | |
new_value, error_message = JSON:onTrailingGarbage()\n--\n-- The default JSON:onTrailingGarbage() | |
simply invokes JSON:onDecodeError(\"trailing garbage\"...),\n-- but you can | |
have this package ignore trailing garbage via\n--\n-- function JSON:onTrailingGarbage(json_text, | |
location, parsed_value, etc)\n-- return parsed_value\n-- end\n--\n--\n-- | |
DECODING AND STRICT TYPES\n--\n-- Because both JSON objects and JSON arrays | |
are converted to Lua tables,\n-- it's not normally possible to tell which original | |
JSON type a\n-- particular Lua table was derived from, or guarantee decode-encode\n-- | |
\ round-trip equivalency.\n--\n-- However, if you enable strictTypes, e.g.\n--\n-- | |
\ JSON = assert(loadfile \"JSON.lua\")() --load the routines\n-- JSON.strictTypes | |
= true\n--\n-- then the Lua table resulting from the decoding of a JSON object | |
or\n-- JSON array is marked via Lua metatable, so that when re-encoded with\n-- | |
\ JSON:encode() it ends up as the appropriate JSON type.\n--\n-- (This is not | |
the default because other routines may not work well with\n-- tables that have | |
a metatable set, for example, Lightroom API calls.)\n--\n--\n-- ENCODING (from | |
a lua table to a JSON string)\n--\n-- JSON = assert(loadfile \"JSON.lua\")() | |
-- one-time load of the routines\n--\n-- local raw_json_text = JSON:encode(lua_table_or_value)\n-- | |
\ local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- \"pretty | |
printed\" version for human readability\n-- local custom_pretty = JSON:encode(lua_table_or_value, | |
etc, { pretty = true, indent = \"| \", align_keys = false })\n--\n-- On error | |
during encoding, this code calls:\n--\n-- JSON:onEncodeError(message, etc)\n--\n-- | |
\ which you can override in your local JSON object. Also see \"HANDLING UNSUPPORTED | |
VALUE TYPES\" below.\n--\n-- The 'etc' in the error call is the second argument | |
to encode() and encode_pretty(), or nil if it wasn't provided.\n--\n--\n--\n--\n-- | |
ENCODING OPTIONS\n--\n-- An optional third argument, a table of options, can | |
be provided to encode().\n--\n-- encode_options = {\n-- -- options | |
for making \"pretty\" human-readable JSON (see \"PRETTY-PRINTING\" below)\n-- | |
\ pretty = true, -- turn pretty formatting on\n-- indent | |
\ = \" \", -- use this indent for each level of an array/object\n-- | |
\ align_keys = false, -- if true, align the keys in a way that sounds | |
like it should be nice, but is actually ugly\n-- array_newline = false, | |
\ -- if true, array elements become one to a line rather than inline\n-- \n-- | |
\ -- other output-related options\n-- null = \"\\0\", | |
\ -- see \"ENCODING JSON NULL VALUES\" below\n-- stringsAreUtf8 = false, | |
\ -- see \"HANDLING UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA\" below\n-- | |
\ }\n-- \n-- json_string = JSON:encode(mytable, etc, encode_options)\n--\n--\n--\n-- | |
For reference, the defaults are:\n--\n-- pretty = false\n-- | |
\ null = nil,\n-- stringsAreUtf8 = false,\n--\n--\n--\n-- | |
PRETTY-PRINTING\n--\n-- Enabling the 'pretty' encode option helps generate human-readable | |
JSON.\n--\n-- pretty = JSON:encode(val, etc, {\n-- pretty | |
= true,\n-- indent = \" \",\n-- align_keys | |
= false,\n-- })\n--\n-- encode_pretty() | |
is also provided: it's identical to encode() except\n-- that encode_pretty() | |
provides a default options table if none given in the call:\n--\n-- { pretty | |
= true, indent = \" \", align_keys = false, array_newline = false }\n--\n-- For | |
example, if\n--\n-- JSON:encode(data)\n--\n-- produces:\n--\n-- {\"city\":\"Kyoto\",\"climate\":{\"avg_temp\":16,\"humidity\":\"high\",\"snowfall\":\"minimal\"},\"country\":\"Japan\",\"wards\":11}\n--\n-- | |
\ then\n--\n-- JSON:encode_pretty(data)\n--\n-- produces:\n--\n-- {\n-- | |
\ \"city\": \"Kyoto\",\n-- \"climate\": {\n-- \"avg_temp\": | |
16,\n-- \"humidity\": \"high\",\n-- \"snowfall\": \"minimal\"\n-- | |
\ },\n-- \"country\": \"Japan\",\n-- \"wards\": 11\n-- }\n--\n-- | |
\ The following lines all return identical strings:\n-- JSON:encode_pretty(data)\n-- | |
\ JSON:encode_pretty(data, nil, { pretty = true, indent = \" \", align_keys | |
= false, array_newline = false})\n-- JSON:encode_pretty(data, nil, { pretty | |
= true, indent = \" \" })\n-- JSON:encode (data, nil, { pretty = | |
true, indent = \" \" })\n--\n-- An example of setting your own indent string:\n--\n-- | |
\ JSON:encode_pretty(data, nil, { pretty = true, indent = \"| \" })\n--\n-- | |
\ produces:\n--\n-- {\n-- | \"city\": \"Kyoto\",\n-- | \"climate\": | |
{\n-- | | \"avg_temp\": 16,\n-- | | \"humidity\": \"high\",\n-- | |
\ | | \"snowfall\": \"minimal\"\n-- | },\n-- | \"country\": | |
\"Japan\",\n-- | \"wards\": 11\n-- }\n--\n-- An example of setting | |
align_keys to true:\n--\n-- JSON:encode_pretty(data, nil, { pretty = true, | |
indent = \" \", align_keys = true })\n-- \n-- produces:\n-- \n-- {\n-- | |
\ \"city\": \"Kyoto\",\n-- \"climate\": {\n-- \"avg_temp\": | |
16,\n-- \"humidity\": \"high\",\n-- \"snowfall\": | |
\"minimal\"\n-- },\n-- \"country\": \"Japan\",\n-- \"wards\": | |
11\n-- }\n--\n-- which I must admit is kinda ugly, sorry. This was the | |
default for\n-- encode_pretty() prior to version 20141223.14.\n--\n--\n-- HANDLING | |
UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA\n--\n-- If the 'stringsAreUtf8' | |
encode option is set to true, consider Lua strings not as a sequence of bytes,\n-- | |
\ but as a sequence of UTF-8 characters.\n--\n-- Currently, the only practical | |
effect of setting this option is that Unicode LINE and PARAGRAPH\n-- separators, | |
if found in a string, are encoded with a JSON escape instead of being dumped as | |
is.\n-- The JSON is valid either way, but encoding this way, apparently, allows | |
the resulting JSON\n-- to also be valid Java.\n--\n-- AMBIGUOUS SITUATIONS | |
DURING THE ENCODING\n--\n-- During the encode, if a Lua table being encoded | |
contains both string\n-- and numeric keys, it fits neither JSON's idea of an | |
object, nor its\n-- idea of an array. To get around this, when any string key | |
exists (or\n-- when non-positive numeric keys exist), numeric keys are converted | |
to\n-- strings.\n--\n-- For example, \n-- JSON:encode({ \"one\", \"two\", | |
\"three\", SOMESTRING = \"some string\" }))\n-- produces the JSON object\n-- | |
\ {\"1\":\"one\",\"2\":\"two\",\"3\":\"three\",\"SOMESTRING\":\"some string\"}\n--\n-- | |
\ To prohibit this conversion and instead make it an error condition, set\n-- | |
\ JSON.noKeyConversion = true\n--\n--\n-- ENCODING JSON NULL VALUES\n--\n-- | |
\ Lua tables completely omit keys whose value is nil, so without special handling | |
there's\n-- no way to represent JSON object's null value in a Lua table. For | |
example\n-- JSON:encode({ username = \"admin\", password = nil })\n--\n-- | |
\ produces:\n--\n-- {\"username\":\"admin\"}\n--\n-- In order to actually | |
produce\n--\n-- {\"username\":\"admin\", \"password\":null}\n--\n\n-- one | |
can include a string value for a \"null\" field in the options table passed to | |
encode().... \n-- any Lua table entry with that value becomes null in the JSON | |
output:\n--\n-- JSON:encode({ username = \"admin\", password = \"xyzzy\" | |
}, -- First arg is the Lua table to encode as JSON.\n-- nil, | |
\ -- Second arg is the 'etc' value, ignored | |
here\n-- { null = \"xyzzy\" }) -- Third | |
arg is th options table\n--\n-- produces:\n--\n-- {\"username\":\"admin\", | |
\"password\":null}\n--\n-- Just be sure to use a string that is otherwise unlikely | |
to appear in your data.\n-- The string \"\\0\" (a string with one null byte) | |
may well be appropriate for many applications.\n--\n-- The \"null\" options | |
also applies to Lua tables that become JSON arrays.\n-- JSON:encode({ \"one\", | |
\"two\", nil, nil })\n--\n-- produces\n--\n-- [\"one\",\"two\"]\n--\n-- | |
\ while\n--\n-- NullPlaceholder = \"\\0\"\n-- encode_options = { null | |
= NullPlaceholder }\n-- JSON:encode({ \"one\", \"two\", NullPlaceholder, | |
NullPlaceholder}, nil, encode_options)\n-- produces\n--\n-- [\"one\",\"two\",null,null]\n--\n--\n--\n-- | |
HANDLING LARGE AND/OR PRECISE NUMBERS\n--\n--\n-- Without special handling, | |
numbers in JSON can lose precision in Lua.\n-- For example:\n-- \n-- T | |
= JSON:decode('{ \"small\":12345, \"big\":12345678901234567890123456789, \"precise\":9876.67890123456789012345 | |
\ }')\n--\n-- print(\"small: \", type(T.small), T.small)\n-- print(\"big: | |
\ \", type(T.big), T.big)\n-- print(\"precise: \", type(T.precise), | |
\ T.precise)\n-- \n-- produces\n-- \n-- small: number 12345\n-- | |
\ big: number 1.2345678901235e+28\n-- precise: number | |
\ 9876.6789012346\n--\n-- Precision is lost with both 'big' and 'precise'.\n--\n-- | |
\ This package offers ways to try to handle this better (for some definitions | |
of \"better\")...\n--\n-- The most precise method is by setting the global:\n-- | |
\ \n-- JSON.decodeNumbersAsObjects = true\n-- \n-- When this is set, | |
numeric JSON data is encoded into Lua in a form that preserves the exact\n-- JSON | |
numeric presentation when re-encoded back out to JSON, or accessed in Lua as a | |
string.\n--\n-- This is done by encoding the numeric data with a Lua table/metatable | |
that returns\n-- the possibly-imprecise numeric form when accessed numerically, | |
but the original precise\n-- representation when accessed as a string.\n--\n-- | |
\ Consider the example above, with this option turned on:\n--\n-- JSON.decodeNumbersAsObjects | |
= true\n-- \n-- T = JSON:decode('{ \"small\":12345, \"big\":12345678901234567890123456789, | |
\"precise\":9876.67890123456789012345 }')\n--\n-- print(\"small: \", type(T.small), | |
\ T.small)\n-- print(\"big: \", type(T.big), T.big)\n-- print(\"precise: | |
\", type(T.precise), T.precise)\n-- \n-- This now produces:\n-- \n-- small: | |
\ table 12345\n-- big: table 12345678901234567890123456789\n-- | |
\ precise: table 9876.67890123456789012345\n-- \n-- However, | |
within Lua you can still use the values (e.g. T.precise in the example above) | |
in numeric\n-- contexts. In such cases you'll get the possibly-imprecise numeric | |
version, but in string contexts\n-- and when the data finds its way to this | |
package's encode() function, the original full-precision\n-- representation | |
is used.\n--\n-- You can force access to the string or numeric version via\n-- | |
\ JSON:forceString()\n-- JSON:forceNumber()\n-- For example,\n-- | |
\ local probably_okay = JSON:forceNumber(T.small) -- 'probably_okay' is | |
a number\n--\n-- Code the inspects the JSON-turned-Lua data using type() can | |
run into troubles because what used to\n-- be a number can now be a table (e.g. | |
as the small/big/precise example above shows). Update these\n-- situations to | |
use JSON:isNumber(item), which returns nil if the item is neither a number nor | |
one\n-- of these number objects. If it is either, it returns the number itself. | |
For completeness there's\n-- also JSON:isString(item).\n--\n-- If you want | |
to try to avoid the hassles of this \"number as an object\" kludge for all but | |
really\n-- big numbers, you can set JSON.decodeNumbersAsObjects and then also | |
set one or both of\n-- JSON:decodeIntegerObjectificationLength\n-- | |
\ JSON:decodeDecimalObjectificationLength\n-- They refer to the length | |
of the part of the number before and after a decimal point. If they are\n-- set | |
and their part is at least that number of digits, objectification occurs. If both | |
are set,\n-- objectification occurs when either length is met.\n--\n-- -----------------------\n--\n-- | |
\ Even without using the JSON.decodeNumbersAsObjects option, you can encode numbers | |
in your Lua\n-- table that retain high precision upon encoding to JSON, by using | |
the JSON:asNumber() function:\n--\n-- T = {\n-- imprecise = 123456789123456789.123456789123456789,\n-- | |
\ precise = JSON:asNumber(\"123456789123456789.123456789123456789\")\n-- | |
\ }\n--\n-- print(JSON:encode_pretty(T))\n--\n-- This produces:\n--\n-- | |
\ { \n-- \"precise\": 123456789123456789.123456789123456789,\n-- \"imprecise\": | |
1.2345678912346e+17\n-- }\n--\n--\n-- -----------------------\n--\n-- A | |
different way to handle big/precise JSON numbers is to have decode() merely return | |
the exact\n-- string representation of the number instead of the number itself. | |
This approach might be useful\n-- when the numbers are merely some kind of opaque | |
object identifier and you want to work with them\n-- in Lua as strings anyway.\n-- | |
\ \n-- This approach is enabled by setting\n--\n-- JSON.decodeIntegerStringificationLength | |
= 10\n--\n-- The value is the number of digits (of the integer part of the number) | |
at which to stringify numbers.\n-- NOTE: this setting is ignored if JSON.decodeNumbersAsObjects | |
is true, as that takes precedence.\n--\n-- Consider our previous example with | |
this option set to 10:\n--\n-- JSON.decodeIntegerStringificationLength = | |
10\n-- \n-- T = JSON:decode('{ \"small\":12345, \"big\":12345678901234567890123456789, | |
\"precise\":9876.67890123456789012345 }')\n--\n-- print(\"small: \", type(T.small), | |
\ T.small)\n-- print(\"big: \", type(T.big), T.big)\n-- print(\"precise: | |
\", type(T.precise), T.precise)\n--\n-- This produces:\n--\n-- small: | |
\ number 12345\n-- big: string 12345678901234567890123456789\n-- | |
\ precise: number 9876.6789012346\n--\n-- The long integer of the | |
'big' field is at least JSON.decodeIntegerStringificationLength digits\n-- in | |
length, so it's converted not to a Lua integer but to a Lua string. Using a value | |
of 0 or 1 ensures\n-- that all JSON numeric data becomes strings in Lua.\n--\n-- | |
\ Note that unlike\n-- JSON.decodeNumbersAsObjects = true\n-- this stringification | |
is simple and unintelligent: the JSON number simply becomes a Lua string, and | |
that's the end of it.\n-- If the string is then converted back to JSON, it's | |
still a string. After running the code above, adding\n-- print(JSON:encode(T))\n-- | |
\ produces\n-- {\"big\":\"12345678901234567890123456789\",\"precise\":9876.6789012346,\"small\":12345}\n-- | |
\ which is unlikely to be desired.\n--\n-- There's a comparable option for | |
the length of the decimal part of a number:\n--\n-- JSON.decodeDecimalStringificationLength\n--\n-- | |
\ This can be used alone or in conjunction with\n--\n-- JSON.decodeIntegerStringificationLength\n--\n-- | |
\ to trip stringification on precise numbers with at least JSON.decodeIntegerStringificationLength | |
digits after\n-- the decimal point. (Both are ignored if JSON.decodeNumbersAsObjects | |
is true.)\n--\n-- This example:\n--\n-- JSON.decodeIntegerStringificationLength | |
= 10\n-- JSON.decodeDecimalStringificationLength = 5\n--\n-- T = JSON:decode('{ | |
\ \"small\":12345, \"big\":12345678901234567890123456789, \"precise\":9876.67890123456789012345 | |
\ }')\n-- \n-- print(\"small: \", type(T.small), T.small)\n-- | |
\ print(\"big: \", type(T.big), T.big)\n-- print(\"precise: | |
\", type(T.precise), T.precise)\n--\n-- produces:\n--\n-- small: number | |
\ 12345\n-- big: string 12345678901234567890123456789\n-- precise: | |
\ string 9876.67890123456789012345\n--\n--\n-- HANDLING UNSUPPORTED VALUE | |
TYPES\n--\n-- Among the encoding errors that might be raised is an attempt to | |
convert a table value that has a type\n-- that this package hasn't accounted | |
for: a function, userdata, or a thread. You can handle these types as table\n-- | |
\ values (but not as table keys) if you supply a JSON:unsupportedTypeEncoder() | |
method along the lines of the\n-- following example:\n-- \n-- function | |
JSON:unsupportedTypeEncoder(value_of_unsupported_type)\n-- if type(value_of_unsupported_type) | |
== 'function' then\n-- return \"a function value\"\n-- else\n-- | |
\ return nil\n-- end\n-- end\n-- \n-- Your | |
unsupportedTypeEncoder() method is actually called with a bunch of arguments:\n--\n-- | |
\ self:unsupportedTypeEncoder(value, parents, etc, options, indent, for_key)\n--\n-- | |
\ The 'value' is the function, thread, or userdata to be converted to JSON.\n--\n-- | |
\ The 'etc' and 'options' arguments are those passed to the original encode(). | |
The other arguments are\n-- probably of little interest; see the source code. | |
(Note that 'for_key' is never true, as this function\n-- is invoked only on | |
table values; table keys of these types still trigger the onEncodeError method.)\n--\n-- | |
\ If your unsupportedTypeEncoder() method returns a string, it's inserted into | |
the JSON as is.\n-- If it returns nil plus an error message, that error message | |
is passed through to an onEncodeError invocation.\n-- If it returns only nil, | |
processing falls through to a default onEncodeError invocation.\n--\n-- If you | |
want to handle everything in a simple way:\n--\n-- function JSON:unsupportedTypeEncoder(value)\n-- | |
\ return tostring(value)\n-- end\n--\n--\n-- SUMMARY OF METHODS | |
YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT\n--\n-- assert\n-- onDecodeError\n-- | |
\ onDecodeOfNilError\n-- onDecodeOfHTMLError\n-- onTrailingGarbage\n-- | |
\ onEncodeError\n-- unsupportedTypeEncoder\n--\n-- If you want to create | |
a separate Lua JSON object with its own error handlers,\n-- you can reload JSON.lua | |
or use the :new() method.\n--\n---------------------------------------------------------------------------\n\nlocal | |
default_pretty_indent = \" \"\nlocal default_pretty_options = { pretty = true, | |
indent = default_pretty_indent, align_keys = false, array_newline = false }\n\nlocal | |
isArray = { __tostring = function() return \"JSON array\" end } isArray.__index | |
\ = isArray\nlocal isObject = { __tostring = function() return \"JSON object\" | |
\ end } isObject.__index = isObject\n\nfunction OBJDEF:newArray(tbl)\n | |
\ return setmetatable(tbl or {}, isArray)\nend\n\nfunction OBJDEF:newObject(tbl)\n | |
\ return setmetatable(tbl or {}, isObject)\nend\n\n\n\n\nlocal function getnum(op)\n | |
\ return type(op) == 'number' and op or op.N\nend\n\nlocal isNumber = {\n __tostring | |
= function(T) return T.S end,\n __unm = function(op) return getnum(op) | |
end,\n\n __concat = function(op1, op2) return tostring(op1) .. tostring(op2) | |
end,\n __add = function(op1, op2) return getnum(op1) + getnum(op2) | |
\ end,\n __sub = function(op1, op2) return getnum(op1) - getnum(op2) | |
\ end,\n __mul = function(op1, op2) return getnum(op1) * getnum(op2) | |
\ end,\n __div = function(op1, op2) return getnum(op1) / getnum(op2) | |
\ end,\n __mod = function(op1, op2) return getnum(op1) % getnum(op2) | |
\ end,\n __pow = function(op1, op2) return getnum(op1) ^ getnum(op2) | |
\ end,\n __lt = function(op1, op2) return getnum(op1) < getnum(op2) | |
\ end,\n __eq = function(op1, op2) return getnum(op1) == getnum(op2) | |
\ end,\n __le = function(op1, op2) return getnum(op1) <= getnum(op2) | |
\ end,\n}\nisNumber.__index = isNumber\n\nfunction OBJDEF:asNumber(item)\n\n if | |
getmetatable(item) == isNumber then\n -- it's already a JSON number object.\n | |
\ return item\n elseif type(item) == 'table' and type(item.S) == 'string' | |
and type(item.N) == 'number' then\n -- it's a number-object table that lost | |
its metatable, so give it one\n return setmetatable(item, isNumber)\n else\n | |
\ -- the normal situation... given a number or a string representation of | |
a number....\n local holder = {\n S = tostring(item), -- S is the | |
representation of the number as a string, which remains precise\n N = | |
tonumber(item), -- N is the number as a Lua number.\n }\n return setmetatable(holder, | |
isNumber)\n end\nend\n\n--\n-- Given an item that might be a normal string or | |
number, or might be an 'isNumber' object defined above,\n-- return the string | |
version. This shouldn't be needed often because the 'isNumber' object should autoconvert\n-- | |
to a string in most cases, but it's here to allow it to be forced when needed.\n--\nfunction | |
OBJDEF:forceString(item)\n if type(item) == 'table' and type(item.S) == 'string' | |
then\n return item.S\n else\n return tostring(item)\n end\nend\n\n--\n-- | |
Given an item that might be a normal string or number, or might be an 'isNumber' | |
object defined above,\n-- return the numeric version.\n--\nfunction OBJDEF:forceNumber(item)\n | |
\ if type(item) == 'table' and type(item.N) == 'number' then\n return item.N\n | |
\ else\n return tonumber(item)\n end\nend\n\n--\n-- If the given item | |
is a number, return it. Otherwise, return nil.\n-- This, this can be used both | |
in a conditional and to access the number when you're not sure its form.\n--\nfunction | |
OBJDEF:isNumber(item)\n if type(item) == 'number' then\n return item\n | |
\ elseif type(item) == 'table' and type(item.N) == 'number' then\n return | |
item.N\n else\n return nil\n end\nend\n\nfunction OBJDEF:isString(item)\n | |
\ if type(item) == 'string' then\n return item\n elseif type(item) == | |
'table' and type(item.S) == 'string' then\n return item.S\n else\n return | |
nil\n end\nend\n\n\nlocal function unicode_codepoint_as_utf8(codepoint)\n --\n | |
\ -- codepoint is a number\n --\n if codepoint <= 127 then\n return | |
string.char(codepoint)\n\n elseif codepoint <= 2047 then\n --\n -- | |
110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8\n | |
\ --\n local highpart = math.floor(codepoint / 0x40)\n local lowpart | |
\ = codepoint - (0x40 * highpart)\n return string.char(0xC0 + highpart,\n | |
\ 0x80 + lowpart)\n\n elseif codepoint <= 65535 then\n | |
\ --\n -- 1110yyyy 10yyyyxx 10xxxxxx\n --\n local highpart | |
\ = math.floor(codepoint / 0x1000)\n local remainder = codepoint - 0x1000 | |
* highpart\n local midpart = math.floor(remainder / 0x40)\n local | |
lowpart = remainder - 0x40 * midpart\n\n highpart = 0xE0 + highpart\n midpart | |
\ = 0x80 + midpart\n lowpart = 0x80 + lowpart\n\n --\n -- Check | |
for an invalid character (thanks Andy R. at Adobe).\n -- See table 3.7, page | |
93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070\n --\n | |
\ if ( highpart == 0xE0 and midpart < 0xA0 ) or\n ( highpart == 0xED | |
and midpart > 0x9F ) or\n ( highpart == 0xF0 and midpart < 0x90 ) or\n | |
\ ( highpart == 0xF4 and midpart > 0x8F )\n then\n return | |
\"?\"\n else\n return string.char(highpart,\n midpart,\n | |
\ lowpart)\n end\n\n else\n --\n -- | |
11110zzz 10zzyyyy 10yyyyxx 10xxxxxx\n --\n local highpart = math.floor(codepoint | |
/ 0x40000)\n local remainder = codepoint - 0x40000 * highpart\n local | |
midA = math.floor(remainder / 0x1000)\n remainder = remainder | |
- 0x1000 * midA\n local midB = math.floor(remainder / 0x40)\n local | |
lowpart = remainder - 0x40 * midB\n\n return string.char(0xF0 + highpart,\n | |
\ 0x80 + midA,\n 0x80 + midB,\n | |
\ 0x80 + lowpart)\n end\nend\n\nfunction OBJDEF:onDecodeError(message, | |
text, location, etc)\n if text then\n if location then\n message | |
= string.format(\"%s at byte %d of: %s\", message, location, text)\n else\n | |
\ message = string.format(\"%s: %s\", message, text)\n end\n end\n\n | |
\ if etc ~= nil then\n message = message .. \" (\" .. OBJDEF:encode(etc) | |
.. \")\"\n end\n\n if self.assert then\n self.assert(false, message)\n | |
\ else\n assert(false, message)\n end\nend\n\nfunction OBJDEF:onTrailingGarbage(json_text, | |
location, parsed_value, etc)\n return self:onDecodeError(\"trailing garbage\", | |
json_text, location, etc)\nend\n\nOBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError\nOBJDEF.onDecodeOfHTMLError | |
= OBJDEF.onDecodeError\n\nfunction OBJDEF:onEncodeError(message, etc)\n if etc | |
~= nil then\n message = message .. \" (\" .. OBJDEF:encode(etc) .. \")\"\n | |
\ end\n\n if self.assert then\n self.assert(false, message)\n else\n | |
\ assert(false, message)\n end\nend\n\nlocal function grok_number(self, | |
text, start, options)\n --\n -- Grab the integer part\n --\n local integer_part | |
= text:match('^-?[1-9]%d*', start)\n or text:match(\"^-?0\", | |
\ start)\n\n if not integer_part then\n self:onDecodeError(\"expected | |
number\", text, start, options.etc)\n return nil, start -- in case the error | |
method doesn't abort, return something sensible\n end\n\n local i = start | |
+ integer_part:len()\n\n --\n -- Grab an optional decimal part\n --\n local | |
decimal_part = text:match('^%.%d+', i) or \"\"\n\n i = i + decimal_part:len()\n\n | |
\ --\n -- Grab an optional exponential part\n --\n local exponent_part | |
= text:match('^[eE][-+]?%d+', i) or \"\"\n\n i = i + exponent_part:len()\n\n | |
\ local full_number_text = integer_part .. decimal_part .. exponent_part\n\n | |
\ if options.decodeNumbersAsObjects then\n\n local objectify = false\n\n | |
\ if not options.decodeIntegerObjectificationLength and not options.decodeDecimalObjectificationLength | |
then\n -- no options, so objectify\n objectify = true\n\n elseif | |
(options.decodeIntegerObjectificationLength\n and\n (integer_part:len() | |
>= options.decodeIntegerObjectificationLength or exponent_part:len() > 0))\n\n | |
\ or\n (options.decodeDecimalObjectificationLength \n and\n | |
\ (decimal_part:len() >= options.decodeDecimalObjectificationLength or | |
exponent_part:len() > 0))\n then\n -- have options and they are triggered, | |
so objectify\n objectify = true\n end\n\n if objectify then\n | |
\ return OBJDEF:asNumber(full_number_text), i\n end\n -- else, | |
fall through to try to return as a straight-up number\n\n else\n\n -- Not | |
always decoding numbers as objects, so perhaps encode as strings?\n\n --\n | |
\ -- If we're told to stringify only under certain conditions, so do.\n -- | |
We punt a bit when there's an exponent by just stringifying no matter what.\n | |
\ -- I suppose we should really look to see whether the exponent is actually | |
big enough one\n -- way or the other to trip stringification, but I'll be | |
lazy about it until someone asks.\n --\n if (options.decodeIntegerStringificationLength\n | |
\ and\n (integer_part:len() >= options.decodeIntegerStringificationLength | |
or exponent_part:len() > 0))\n\n or\n\n (options.decodeDecimalStringificationLength | |
\n and\n (decimal_part:len() >= options.decodeDecimalStringificationLength | |
or exponent_part:len() > 0))\n then\n return full_number_text, i | |
-- this returns the exact string representation seen in the original JSON\n end\n\n | |
\ end\n\n\n local as_number = tonumber(full_number_text)\n\n if not as_number | |
then\n self:onDecodeError(\"bad number\", text, start, options.etc)\n return | |
nil, start -- in case the error method doesn't abort, return something sensible\n | |
\ end\n\n return as_number, i\nend\n\n\nlocal function grok_string(self, text, | |
start, options)\n\n if text:sub(start,start) ~= '\"' then\n self:onDecodeError(\"expected | |
string's opening quote\", text, start, options.etc)\n return nil, start -- | |
in case the error method doesn't abort, return something sensible\n end\n\n | |
\ local i = start + 1 -- +1 to bypass the initial quote\n local text_len = | |
text:len()\n local VALUE = \"\"\n while i <= text_len do\n local c = | |
text:sub(i,i)\n if c == '\"' then\n return VALUE, i + 1\n end\n | |
\ if c ~= '\\\\' then\n VALUE = VALUE .. c\n i = i + 1\n elseif | |
text:match('^\\\\b', i) then\n VALUE = VALUE .. \"\\b\"\n i = | |
i + 2\n elseif text:match('^\\\\f', i) then\n VALUE = VALUE .. \"\\f\"\n | |
\ i = i + 2\n elseif text:match('^\\\\n', i) then\n VALUE | |
= VALUE .. \"\\n\"\n i = i + 2\n elseif text:match('^\\\\r', i) then\n | |
\ VALUE = VALUE .. \"\\r\"\n i = i + 2\n elseif text:match('^\\\\t', | |
i) then\n VALUE = VALUE .. \"\\t\"\n i = i + 2\n else\n local | |
hex = text:match('^\\\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', | |
i)\n if hex then\n i = i + 6 -- bypass what we just read\n\n | |
\ -- We have a Unicode codepoint. It could be standalone, or if in the | |
proper range and\n -- followed by another in a specific range, it'll | |
be a two-code surrogate pair.\n local codepoint = tonumber(hex, 16)\n | |
\ if codepoint >= 0xD800 and codepoint <= 0xDBFF then\n -- | |
it's a hi surrogate... see whether we have a following low\n local | |
lo_surrogate = text:match('^\\\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', | |
i)\n if lo_surrogate then\n i = i + 6 -- bypass | |
the low surrogate we just read\n codepoint = 0x2400 + (codepoint | |
- 0xD800) * 0x400 + tonumber(lo_surrogate, 16)\n else\n -- | |
not a proper low, so we'll just leave the first codepoint as is and spit it out.\n | |
\ end\n end\n VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint)\n\n | |
\ else\n\n -- just pass through what's escaped\n VALUE | |
= VALUE .. text:match('^\\\\(.)', i)\n i = i + 2\n end\n end\n | |
\ end\n\n self:onDecodeError(\"unclosed string\", text, start, options.etc)\n | |
\ return nil, start -- in case the error method doesn't abort, return something | |
sensible\nend\n\nlocal function skip_whitespace(text, start)\n\n local _, match_end | |
= text:find(\"^[ \\n\\r\\t]+\", start) -- [http://www.ietf.org/rfc/rfc4627.txt] | |
Section 2\n if match_end then\n return match_end + 1\n else\n return | |
start\n end\nend\n\nlocal grok_one -- assigned later\n\nlocal function grok_object(self, | |
text, start, options)\n\n if text:sub(start,start) ~= '{' then\n self:onDecodeError(\"expected | |
'{'\", text, start, options.etc)\n return nil, start -- in case the error | |
method doesn't abort, return something sensible\n end\n\n local i = skip_whitespace(text, | |
start + 1) -- +1 to skip the '{'\n\n local VALUE = self.strictTypes and self:newObject | |
{ } or { }\n\n if text:sub(i,i) == '}' then\n return VALUE, i + 1\n end\n | |
\ local text_len = text:len()\n while i <= text_len do\n local key, new_i | |
= grok_string(self, text, i, options)\n\n i = skip_whitespace(text, new_i)\n\n | |
\ if text:sub(i, i) ~= ':' then\n self:onDecodeError(\"expected colon\", | |
text, i, options.etc)\n return nil, i -- in case the error method doesn't | |
abort, return something sensible\n end\n\n i = skip_whitespace(text, | |
i + 1)\n\n local new_val, new_i = grok_one(self, text, i, options)\n\n VALUE[key] | |
= new_val\n\n --\n -- Expect now either '}' to end things, or a ',' | |
to allow us to continue.\n --\n i = skip_whitespace(text, new_i)\n\n | |
\ local c = text:sub(i,i)\n\n if c == '}' then\n return VALUE, | |
i + 1\n end\n\n if text:sub(i, i) ~= ',' then\n self:onDecodeError(\"expected | |
comma or '}'\", text, i, options.etc)\n return nil, i -- in case the error | |
method doesn't abort, return something sensible\n end\n\n i = skip_whitespace(text, | |
i + 1)\n end\n\n self:onDecodeError(\"unclosed '{'\", text, start, options.etc)\n | |
\ return nil, start -- in case the error method doesn't abort, return something | |
sensible\nend\n\nlocal function grok_array(self, text, start, options)\n if | |
text:sub(start,start) ~= '[' then\n self:onDecodeError(\"expected '['\", | |
text, start, options.etc)\n return nil, start -- in case the error method | |
doesn't abort, return something sensible\n end\n\n local i = skip_whitespace(text, | |
start + 1) -- +1 to skip the '['\n local VALUE = self.strictTypes and self:newArray | |
{ } or { }\n if text:sub(i,i) == ']' then\n return VALUE, i + 1\n end\n\n | |
\ local VALUE_INDEX = 1\n\n local text_len = text:len()\n while i <= text_len | |
do\n local val, new_i = grok_one(self, text, i, options)\n\n -- can't | |
table.insert(VALUE, val) here because it's a no-op if val is nil\n VALUE[VALUE_INDEX] | |
= val\n VALUE_INDEX = VALUE_INDEX + 1\n\n i = skip_whitespace(text, | |
new_i)\n\n --\n -- Expect now either ']' to end things, or a ',' to | |
allow us to continue.\n --\n local c = text:sub(i,i)\n if c == | |
']' then\n return VALUE, i + 1\n end\n if text:sub(i, i) ~= | |
',' then\n self:onDecodeError(\"expected comma or ']'\", text, i, options.etc)\n | |
\ return nil, i -- in case the error method doesn't abort, return something | |
sensible\n end\n i = skip_whitespace(text, i + 1)\n end\n self:onDecodeError(\"unclosed | |
'['\", text, start, options.etc)\n return nil, i -- in case the error method | |
doesn't abort, return something sensible\nend\n\n\ngrok_one = function(self, text, | |
start, options)\n -- Skip any whitespace\n start = skip_whitespace(text, start)\n\n | |
\ if start > text:len() then\n self:onDecodeError(\"unexpected end of string\", | |
text, nil, options.etc)\n return nil, start -- in case the error method doesn't | |
abort, return something sensible\n end\n\n if text:find('^\"', start) then\n | |
\ return grok_string(self, text, start, options.etc)\n\n elseif text:find('^[-0123456789 | |
]', start) then\n return grok_number(self, text, start, options)\n\n elseif | |
text:find('^%{', start) then\n return grok_object(self, text, start, options)\n\n | |
\ elseif text:find('^%[', start) then\n return grok_array(self, text, start, | |
options)\n\n elseif text:find('^true', start) then\n return true, start | |
+ 4\n\n elseif text:find('^false', start) then\n return false, start + | |
5\n\n elseif text:find('^null', start) then\n return options.null, start | |
+ 4\n\n else\n self:onDecodeError(\"can't parse JSON\", text, start, options.etc)\n | |
\ return nil, 1 -- in case the error method doesn't abort, return something | |
sensible\n end\nend\n\nfunction OBJDEF:decode(text, etc, options)\n --\n -- | |
If the user didn't pass in a table of decode options, make an empty one.\n --\n | |
\ if type(options) ~= 'table' then\n options = {}\n end\n\n --\n -- | |
If they passed in an 'etc' argument, stuff it into the options.\n -- (If not, | |
any 'etc' field in the options they passed in remains to be used)\n --\n if | |
etc ~= nil then\n options.etc = etc\n end\n\n\n if type(self) ~= 'table' | |
or self.__index ~= OBJDEF then\n local error_message = \"JSON:decode must | |
be called in method format\"\n OBJDEF:onDecodeError(error_message, nil, nil, | |
options.etc)\n return nil, error_message -- in case the error method doesn't | |
abort, return something sensible\n end\n\n if text == nil then\n local | |
error_message = \"nil passed to JSON:decode()\"\n self:onDecodeOfNilError(error_message, | |
nil, nil, options.etc)\n return nil, error_message -- in case the error method | |
doesn't abort, return something sensible\n\n elseif type(text) ~= 'string' then\n | |
\ local error_message = \"expected string argument to JSON:decode()\"\n self:onDecodeError(string.format(\"%s, | |
got %s\", error_message, type(text)), nil, nil, options.etc)\n return nil, | |
error_message -- in case the error method doesn't abort, return something sensible\n | |
\ end\n\n if text:match('^%s*$') then\n -- an empty string is nothing, | |
but not an error\n return nil\n end\n\n if text:match('^%s*<') then\n | |
\ -- Can't be JSON... we'll assume it's HTML\n local error_message = | |
\"HTML passed to JSON:decode()\"\n self:onDecodeOfHTMLError(error_message, | |
text, nil, options.etc)\n return nil, error_message -- in case the error | |
method doesn't abort, return something sensible\n end\n\n --\n -- Ensure | |
that it's not UTF-32 or UTF-16.\n -- Those are perfectly valid encodings for | |
JSON (as per RFC 4627 section 3),\n -- but this package can't handle them.\n | |
\ --\n if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() | |
== 0) then\n local error_message = \"JSON package groks only UTF-8, sorry\"\n | |
\ self:onDecodeError(error_message, text, nil, options.etc)\n return | |
nil, error_message -- in case the error method doesn't abort, return something | |
sensible\n end\n\n --\n -- apply global options\n --\n if options.decodeNumbersAsObjects | |
== nil then\n options.decodeNumbersAsObjects = self.decodeNumbersAsObjects\n | |
\ end\n if options.decodeIntegerObjectificationLength == nil then\n options.decodeIntegerObjectificationLength | |
= self.decodeIntegerObjectificationLength\n end\n if options.decodeDecimalObjectificationLength | |
== nil then\n options.decodeDecimalObjectificationLength = self.decodeDecimalObjectificationLength\n | |
\ end\n if options.decodeIntegerStringificationLength == nil then\n options.decodeIntegerStringificationLength | |
= self.decodeIntegerStringificationLength\n end\n if options.decodeDecimalStringificationLength | |
== nil then\n options.decodeDecimalStringificationLength = self.decodeDecimalStringificationLength\n | |
\ end\n\n\n --\n -- Finally, go parse it\n --\n local success, value, | |
next_i = pcall(grok_one, self, text, 1, options)\n\n if success then\n\n local | |
error_message = nil\n if next_i ~= #text + 1 then\n -- something's | |
left over after we parsed the first thing.... whitespace is allowed.\n next_i | |
= skip_whitespace(text, next_i)\n\n -- if we have something left over | |
now, it's trailing garbage\n if next_i ~= #text + 1 then\n value, | |
error_message = self:onTrailingGarbage(text, next_i, value, options.etc)\n end\n | |
\ end\n return value, error_message\n\n else\n\n -- If JSON:onDecodeError() | |
didn't abort out of the pcall, we'll have received\n -- the error message | |
here as \"value\", so pass it along as an assert.\n local error_message = | |
value\n if self.assert then\n self.assert(false, error_message)\n | |
\ else\n assert(false, error_message)\n end\n -- ...and | |
if we're still here (because the assert didn't throw an error),\n -- return | |
a nil and throw the error message on as a second arg\n return nil, error_message\n\n | |
\ end\nend\n\nlocal function backslash_replacement_function(c)\n if c == \"\\n\" | |
then\n return \"\\\\n\"\n elseif c == \"\\r\" then\n return \"\\\\r\"\n | |
\ elseif c == \"\\t\" then\n return \"\\\\t\"\n elseif c == \"\\b\" then\n | |
\ return \"\\\\b\"\n elseif c == \"\\f\" then\n return \"\\\\f\"\n | |
\ elseif c == '\"' then\n return '\\\\\"'\n elseif c == '\\\\' then\n | |
\ return '\\\\\\\\'\n else\n return string.format(\"\\\\u%04x\", c:byte())\n | |
\ end\nend\n\nlocal chars_to_be_escaped_in_JSON_string\n = '['\n .. '\"' | |
\ -- class sub-pattern to match a double quote\n .. '%\\\\' -- class sub-pattern | |
to match a backslash\n .. '%z' -- class sub-pattern to match a null\n .. | |
\ '\\001' .. '-' .. '\\031' -- class sub-pattern to match control characters\n | |
\ .. ']'\n\n\nlocal LINE_SEPARATOR_as_utf8 = unicode_codepoint_as_utf8(0x2028)\nlocal | |
PARAGRAPH_SEPARATOR_as_utf8 = unicode_codepoint_as_utf8(0x2029)\nlocal function | |
json_string_literal(value, options)\n local newval = value:gsub(chars_to_be_escaped_in_JSON_string, | |
backslash_replacement_function)\n if options.stringsAreUtf8 then\n --\n | |
\ -- This feels really ugly to just look into a string for the sequence of | |
bytes that we know to be a particular utf8 character,\n -- but utf8 was designed | |
purposefully to make this kind of thing possible. Still, feels dirty.\n -- | |
I'd rather decode the byte stream into a character stream, but it's not technically | |
needed so\n -- not technically worth it.\n --\n newval = newval:gsub(LINE_SEPARATOR_as_utf8, | |
'\\\\u2028'):gsub(PARAGRAPH_SEPARATOR_as_utf8,'\\\\u2029')\n end\n return | |
'\"' .. newval .. '\"'\nend\n\nlocal function object_or_array(self, T, etc)\n | |
\ --\n -- We need to inspect all the keys... if there are any strings, we'll | |
convert to a JSON\n -- object. If there are only numbers, it's a JSON array.\n | |
\ --\n -- If we'll be converting to a JSON object, we'll want to sort the keys | |
so that the\n -- end result is deterministic.\n --\n local string_keys = | |
{ }\n local number_keys = { }\n local number_keys_must_be_strings = false\n | |
\ local maximum_number_key\n\n for key in pairs(T) do\n if type(key) == | |
'string' then\n table.insert(string_keys, key)\n elseif type(key) | |
== 'number' then\n table.insert(number_keys, key)\n if key <= | |
0 or key >= math.huge then\n number_keys_must_be_strings = true\n elseif | |
not maximum_number_key or key > maximum_number_key then\n maximum_number_key | |
= key\n end\n elseif type(key) == 'boolean' then\n table.insert(string_keys, | |
tostring(key))\n else\n self:onEncodeError(\"can't encode table with | |
a key of type \" .. type(key), etc)\n end\n end\n\n if #string_keys == | |
0 and not number_keys_must_be_strings then\n --\n -- An empty table, | |
or a numeric-only array\n --\n if #number_keys > 0 then\n return | |
nil, maximum_number_key -- an array\n elseif tostring(T) == \"JSON array\" | |
then\n return nil\n elseif tostring(T) == \"JSON object\" then\n | |
\ return { }\n else\n -- have to guess, so we'll pick array, | |
since empty arrays are likely more common than empty objects\n return | |
nil\n end\n end\n\n table.sort(string_keys)\n\n local map\n if #number_keys | |
> 0 then\n --\n -- If we're here then we have either mixed string/number | |
keys, or numbers inappropriate for a JSON array\n -- It's not ideal, but | |
we'll turn the numbers into strings so that we can at least create a JSON object.\n | |
\ --\n\n if self.noKeyConversion then\n self:onEncodeError(\"a | |
table with both numeric and string keys could be an object or array; aborting\", | |
etc)\n end\n\n --\n -- Have to make a shallow copy of the source | |
table so we can remap the numeric keys to be strings\n --\n map = { | |
}\n for key, val in pairs(T) do\n map[key] = val\n end\n\n table.sort(number_keys)\n\n | |
\ --\n -- Throw numeric keys in there as strings\n --\n for | |
_, number_key in ipairs(number_keys) do\n local string_key = tostring(number_key)\n | |
\ if map[string_key] == nil then\n table.insert(string_keys | |
, string_key)\n map[string_key] = T[number_key]\n else\n self:onEncodeError(\"conflict | |
converting table with mixed-type keys into a JSON object: key \" .. number_key | |
.. \" exists both as a string and a number.\", etc)\n end\n end\n | |
\ end\n\n return string_keys, nil, map\nend\n\n--\n-- Encode\n--\n-- 'options' | |
is nil, or a table with possible keys:\n--\n-- pretty -- If true, return | |
a pretty-printed version.\n--\n-- indent -- A string (usually of spaces) | |
used to indent each nested level.\n--\n-- align_keys -- If true, align | |
all the keys when formatting a table. The result is uglier than one might at first | |
imagine.\n-- Results are undefined if 'align_keys' is true | |
but 'pretty' is not.\n--\n-- array_newline -- If true, array elements are | |
formatted each to their own line. The default is to all fall inline.\n-- Results | |
are undefined if 'array_newline' is true but 'pretty' is not.\n--\n-- null | |
\ -- If this exists with a string value, table elements with this value | |
are output as JSON null.\n--\n-- stringsAreUtf8 -- If true, consider Lua strings | |
not as a sequence of bytes, but as a sequence of UTF-8 characters.\n-- (Currently, | |
the only practical effect of setting this option is that Unicode LINE and PARAGRAPH\n-- | |
\ separators, if found in a string, are encoded with a JSON | |
escape instead of as raw UTF-8.\n-- The JSON is valid either | |
way, but encoding this way, apparently, allows the resulting JSON\n-- to | |
also be valid Java.)\n--\n--\nlocal function encode_value(self, value, parents, | |
etc, options, indent, for_key)\n\n --\n -- keys in a JSON object can never | |
be null, so we don't even consider options.null when converting a key value\n | |
\ --\n if value == nil or (not for_key and options and options.null and value | |
== options.null) then\n return 'null'\n\n elseif type(value) == 'string' | |
then\n return json_string_literal(value, options)\n\n elseif type(value) | |
== 'number' then\n if value ~= value then\n --\n -- NaN (Not | |
a Number).\n -- JSON has no NaN, so we have to fudge the best we can. | |
This should really be a package option.\n --\n return \"null\"\n | |
\ elseif value >= math.huge then\n --\n -- Positive infinity. | |
JSON has no INF, so we have to fudge the best we can. This should\n -- | |
really be a package option. Note: at least with some implementations, positive | |
infinity\n -- is both \">= math.huge\" and \"<= -math.huge\", which makes | |
no sense but that's how it is.\n -- Negative infinity is properly \"<= | |
-math.huge\". So, we must be sure to check the \">=\"\n -- case first.\n | |
\ --\n return \"1e+9999\"\n elseif value <= -math.huge then\n | |
\ --\n -- Negative infinity.\n -- JSON has no INF, so we | |
have to fudge the best we can. This should really be a package option.\n --\n | |
\ return \"-1e+9999\"\n else\n return tostring(value)\n end\n\n | |
\ elseif type(value) == 'boolean' then\n return tostring(value)\n\n elseif | |
type(value) ~= 'table' then\n\n if self.unsupportedTypeEncoder then\n local | |
user_value, user_error = self:unsupportedTypeEncoder(value, parents, etc, options, | |
indent, for_key)\n -- If the user's handler returns a string, use that. | |
If it returns nil plus an error message, bail with that.\n -- If only | |
nil returned, fall through to the default error handler.\n if type(user_value) | |
== 'string' then\n return user_value\n elseif user_value ~= | |
nil then\n self:onEncodeError(\"unsupportedTypeEncoder method returned | |
a \" .. type(user_value), etc)\n elseif user_error then\n self:onEncodeError(tostring(user_error), | |
etc)\n end\n end\n\n self:onEncodeError(\"can't convert \" .. | |
type(value) .. \" to JSON\", etc)\n\n elseif getmetatable(value) == isNumber | |
then\n return tostring(value)\n else\n --\n -- A table to be | |
converted to either a JSON object or array.\n --\n local T = value\n\n | |
\ if type(options) ~= 'table' then\n options = {}\n end\n if | |
type(indent) ~= 'string' then\n indent = \"\"\n end\n\n if parents[T] | |
then\n self:onEncodeError(\"table \" .. tostring(T) .. \" is a child of | |
itself\", etc)\n else\n parents[T] = true\n end\n\n local | |
result_value\n\n local object_keys, maximum_number_key, map = object_or_array(self, | |
T, etc)\n if maximum_number_key then\n --\n -- An array...\n | |
\ --\n local key_indent\n if options.array_newline then\n | |
\ key_indent = indent .. tostring(options.indent or \"\")\n else\n | |
\ key_indent = indent\n end\n\n local ITEMS = { }\n | |
\ for i = 1, maximum_number_key do\n table.insert(ITEMS, encode_value(self, | |
T[i], parents, etc, options, key_indent))\n end\n\n if options.array_newline | |
then\n result_value = \"[\\n\" .. key_indent .. table.concat(ITEMS, | |
\",\\n\" .. key_indent) .. \"\\n\" .. indent .. \"]\"\n elseif options.pretty | |
then\n result_value = \"[ \" .. table.concat(ITEMS, \", \") .. \" ]\"\n | |
\ else\n result_value = \"[\" .. table.concat(ITEMS, \",\") | |
\ .. \"]\"\n end\n\n elseif object_keys then\n --\n -- | |
An object\n --\n local TT = map or T\n\n if options.pretty | |
then\n\n local KEYS = { }\n local max_key_length = 0\n for | |
_, key in ipairs(object_keys) do\n local encoded = encode_value(self, | |
tostring(key), parents, etc, options, indent, true)\n if options.align_keys | |
then\n max_key_length = math.max(max_key_length, #encoded)\n | |
\ end\n table.insert(KEYS, encoded)\n end\n | |
\ local key_indent = indent .. tostring(options.indent or \"\")\n local | |
subtable_indent = key_indent .. string.rep(\" \", max_key_length) .. (options.align_keys | |
and \" \" or \"\")\n local FORMAT = \"%s%\" .. string.format(\"%d\", | |
max_key_length) .. \"s: %s\"\n\n local COMBINED_PARTS = { }\n for | |
i, key in ipairs(object_keys) do\n local encoded_val = encode_value(self, | |
TT[key], parents, etc, options, subtable_indent)\n table.insert(COMBINED_PARTS, | |
string.format(FORMAT, key_indent, KEYS[i], encoded_val))\n end\n result_value | |
= \"{\\n\" .. table.concat(COMBINED_PARTS, \",\\n\") .. \"\\n\" .. indent .. \"}\"\n\n | |
\ else\n\n local PARTS = { }\n for _, key in ipairs(object_keys) | |
do\n local encoded_val = encode_value(self, TT[key], parents, | |
etc, options, indent)\n local encoded_key = encode_value(self, tostring(key), | |
parents, etc, options, indent, true)\n table.insert(PARTS, string.format(\"%s:%s\", | |
encoded_key, encoded_val))\n end\n result_value = \"{\" | |
.. table.concat(PARTS, \",\") .. \"}\"\n\n end\n else\n --\n | |
\ -- An empty array/object... we'll treat it as an array, though it should | |
really be an option\n --\n result_value = \"[]\"\n end\n\n | |
\ parents[T] = false\n return result_value\n end\nend\n\nlocal function | |
top_level_encode(self, value, etc, options)\n local val = encode_value(self, | |
value, {}, etc, options)\n if val == nil then\n --PRIVATE(\"may need to | |
revert to the previous public verison if I can't figure out what the guy wanted\")\n | |
\ return val\n else\n return val\n end\nend\n\nfunction OBJDEF:encode(value, | |
etc, options)\n if type(self) ~= 'table' or self.__index ~= OBJDEF then\n OBJDEF:onEncodeError(\"JSON:encode | |
must be called in method format\", etc)\n end\n\n --\n -- If the user didn't | |
pass in a table of decode options, make an empty one.\n --\n if type(options) | |
~= 'table' then\n options = {}\n end\n\n return top_level_encode(self, | |
value, etc, options)\nend\n\nfunction OBJDEF:encode_pretty(value, etc, options)\n | |
\ if type(self) ~= 'table' or self.__index ~= OBJDEF then\n OBJDEF:onEncodeError(\"JSON:encode_pretty | |
must be called in method format\", etc)\n end\n\n --\n -- If the user didn't | |
pass in a table of decode options, use the default pretty ones\n --\n if type(options) | |
~= 'table' then\n options = default_pretty_options\n end\n\n return top_level_encode(self, | |
value, etc, options)\nend\n\nfunction OBJDEF.__tostring()\n return \"JSON encode/decode | |
package\"\nend\n\nOBJDEF.__index = OBJDEF\n\nfunction OBJDEF:new(args)\n local | |
new = { }\n\n if args then\n for key, val in pairs(args) do\n new[key] | |
= val\n end\n end\n\n return setmetatable(new, OBJDEF)\nend\n\nreturn | |
OBJDEF:new()\n\n--\n-- Version history:\n--\n-- 20170927.26 Use option.null | |
in decoding as well. Thanks to Max Sindwani for the bump, and sorry to Oliver | |
Hitz\n-- whose first mention of it four years ago was completely | |
missed by me.\n--\n-- 20170823.25 Added support for JSON:unsupportedTypeEncoder().\n-- | |
\ Thanks to Chronos Phaenon Eosphoros (https://github.com/cpeosphoros) | |
for the idea.\n--\n-- 20170819.24 Added support for boolean keys in tables.\n--\n-- | |
\ 20170416.23 Added the \"array_newline\" formatting option suggested by yurenchen | |
(http://www.yurenchen.com/)\n--\n-- 20161128.22 Added:\n-- JSON:isString()\n-- | |
\ JSON:isNumber()\n-- JSON:decodeIntegerObjectificationLength\n-- | |
\ JSON:decodeDecimalObjectificationLength\n--\n-- 20161109.21 | |
\ Oops, had a small boo-boo in the previous update.\n--\n-- 20161103.20 Used | |
to silently ignore trailing garbage when decoding. Now fails via JSON:onTrailingGarbage()\n-- | |
\ http://seriot.ch/parsing_json.php\n--\n-- Built-in | |
error message about \"expected comma or ']'\" had mistakenly referred to '['\n--\n-- | |
\ Updated the built-in error reporting to refer to bytes rather | |
than characters.\n--\n-- The decode() method no longer assumes | |
that error handlers abort.\n--\n-- Made the VERSION string a string | |
instead of a number\n--\n\n-- 20160916.19 Fixed the isNumber.__index assignment | |
(thanks to Jack Taylor)\n-- \n-- 20160730.18 Added JSON:forceString() and | |
JSON:forceNumber()\n--\n-- 20160728.17 Added concatenation to the metatable | |
for JSON:asNumber()\n--\n-- 20160709.16 Could crash if not passed an options | |
table (thanks jarno heikkinen <[email protected]>).\n--\n-- Made | |
JSON:asNumber() a bit more resilient to being passed the results of itself.\n--\n-- | |
\ 20160526.15 Added the ability to easily encode null values in JSON, via the | |
new \"null\" encoding option.\n-- (Thanks to Adam B for bringing | |
up the issue.)\n--\n-- Added some support for very large numbers | |
and precise floats via\n-- JSON.decodeNumbersAsObjects\n-- | |
\ JSON.decodeIntegerStringificationLength\n-- JSON.decodeDecimalStringificationLength\n--\n-- | |
\ Added the \"stringsAreUtf8\" encoding option. (Hat tip to http://lua-users.org/wiki/JsonModules | |
)\n--\n-- 20141223.14 The encode_pretty() routine produced fine results for | |
small datasets, but isn't really\n-- appropriate for anything | |
large, so with help from Alex Aulbach I've made the encode routines\n-- more | |
flexible, and changed the default encode_pretty() to be more generally useful.\n--\n-- | |
\ Added a third 'options' argument to the encode() and encode_pretty() | |
routines, to control\n-- how the encoding takes place.\n--\n-- | |
\ Updated docs to add assert() call to the loadfile() line, just | |
as good practice so that\n-- if there is a problem loading JSON.lua, | |
the appropriate error message will percolate up.\n--\n-- 20140920.13 Put back | |
(in a way that doesn't cause warnings about unused variables) the author string,\n-- | |
\ so that the source of the package, and its version number, are | |
visible in compiled copies.\n--\n-- 20140911.12 Minor lua cleanup.\n-- Fixed | |
internal reference to 'JSON.noKeyConversion' to reference 'self' instead of 'JSON'.\n-- | |
\ (Thanks to SmugMug's David Parry for these.)\n--\n-- 20140418.11 | |
\ JSON nulls embedded within an array were being ignored, such that\n-- [\"1\",null,null,null,null,null,\"seven\"],\n-- | |
\ would return\n-- {1,\"seven\"}\n-- It's | |
now fixed to properly return\n-- {1, nil, nil, nil, nil, nil, | |
\"seven\"}\n-- Thanks to \"haddock\" for catching the error.\n--\n-- | |
\ 20140116.10 The user's JSON.assert() wasn't always being used. Thanks to | |
\"blue\" for the heads up.\n--\n-- 20131118.9 Update for Lua 5.3... it seems | |
that tostring(2/1) produces \"2.0\" instead of \"2\",\n-- and | |
this caused some problems.\n--\n-- 20131031.8 Unified the code for encode() | |
and encode_pretty(); they had been stupidly separate,\n-- and | |
had of course diverged (encode_pretty didn't get the fixes that encode got, so\n-- | |
\ sometimes produced incorrect results; thanks to Mattie for the | |
heads up).\n--\n-- Handle encoding tables with non-positive numeric | |
keys (unlikely, but possible).\n--\n-- If a table has both numeric | |
and string keys, or its numeric keys are inappropriate\n-- (such | |
as being non-positive or infinite), the numeric keys are turned into\n-- string | |
keys appropriate for a JSON object. So, as before,\n-- JSON:encode({ | |
\"one\", \"two\", \"three\" })\n-- produces the array\n-- [\"one\",\"two\",\"three\"]\n-- | |
\ but now something with mixed key types like\n-- JSON:encode({ | |
\"one\", \"two\", \"three\", SOMESTRING = \"some string\" }))\n-- instead | |
of throwing an error produces an object:\n-- {\"1\":\"one\",\"2\":\"two\",\"3\":\"three\",\"SOMESTRING\":\"some | |
string\"}\n--\n-- To maintain the prior throw-an-error semantics, | |
set\n-- JSON.noKeyConversion = true\n-- \n-- | |
\ 20131004.7 Release under a Creative Commons CC-BY license, which I should | |
have done from day one, sorry.\n--\n-- 20130120.6 Comment update: added a | |
link to the specific page on my blog where this code can\n-- be | |
found, so that folks who come across the code outside of my blog can find updates\n-- | |
\ more easily.\n--\n-- 20111207.5 Added support for the 'etc' | |
arguments, for better error reporting.\n--\n-- 20110731.4 More feedback from | |
David Kolf on how to make the tests for Nan/Infinity system independent.\n--\n-- | |
\ 20110730.3 Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules:\n--\n-- | |
\ * When encoding lua for JSON, Sparse numeric arrays are now | |
handled by\n-- spitting out full arrays, such that\n-- JSON:encode({\"one\", | |
\"two\", [10] = \"ten\"})\n-- returns\n-- [\"one\",\"two\",null,null,null,null,null,null,null,\"ten\"]\n--\n-- | |
\ In 20100810.2 and earlier, only up to the first non-null | |
value would have been retained.\n--\n-- * When encoding lua | |
for JSON, numeric value NaN gets spit out as null, and infinity as \"1+e9999\".\n-- | |
\ Version 20100810.2 and earlier created invalid JSON in both | |
cases.\n--\n-- * Unicode surrogate pairs are now detected when | |
decoding JSON.\n--\n-- 20100810.2 added some checking to ensure that an invalid | |
Unicode character couldn't leak in to the UTF-8 encoding\n--\n-- 20100731.1 | |
\ initial public release\n--\n" | |
uuid.lua: | | |
--------------------------------------------------------------------------------------- | |
-- Copyright 2012 Rackspace (original), 2013 Thijs Schreijer (modifications) | |
-- | |
-- Licensed under the Apache License, Version 2.0 (the "License"); | |
-- you may not use this file except in compliance with the License. | |
-- You may obtain a copy of the License at | |
-- | |
-- http://www.apache.org/licenses/LICENSE-2.0 | |
-- | |
-- Unless required by applicable law or agreed to in writing, software | |
-- distributed under the License is distributed on an "AS-IS" BASIS, | |
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
-- See the License for the specific language governing permissions and | |
-- limitations under the License. | |
-- | |
-- see http://www.ietf.org/rfc/rfc4122.txt | |
-- | |
-- Note that this is not a true version 4 (random) UUID. Since `os.time()` precision is only 1 second, it would be hard | |
-- to guarantee spacial uniqueness when two hosts generate a uuid after being seeded during the same second. This | |
-- is solved by using the node field from a version 1 UUID. It represents the mac address. | |
-- | |
-- 28-apr-2013 modified by Thijs Schreijer from the original [Rackspace code](https://github.com/kans/zirgo/blob/807250b1af6725bad4776c931c89a784c1e34db2/util/uuid.lua) as a generic Lua module. | |
-- Regarding the above mention on `os.time()`; the modifications use the `socket.gettime()` function from LuaSocket | |
-- if available and hence reduce that problem (provided LuaSocket has been loaded before uuid). | |
-- | |
-- **6-nov-2015 Please take note of this issue**; [https://github.com/Mashape/kong/issues/478](https://github.com/Mashape/kong/issues/478) | |
-- It demonstrates the problem of using time as a random seed. Specifically when used from multiple processes. | |
-- So make sure to seed only once, application wide. And to not have multiple processes do that | |
-- simultaneously (like nginx does for example). | |
local M = {} | |
local math = require('math') | |
local os = require('os') | |
local string = require('string') | |
local bitsize = 32 -- bitsize assumed for Lua VM. See randomseed function below. | |
local lua_version = tonumber(_VERSION:match("%d%.*%d*")) -- grab Lua version used | |
local MATRIX_AND = {{0,0},{0,1} } | |
local MATRIX_OR = {{0,1},{1,1}} | |
local HEXES = '0123456789abcdef' | |
local math_floor = math.floor | |
local math_random = math.random | |
local math_abs = math.abs | |
local string_sub = string.sub | |
local to_number = tonumber | |
local assert = assert | |
local type = type | |
-- performs the bitwise operation specified by truth matrix on two numbers. | |
local function BITWISE(x, y, matrix) | |
local z = 0 | |
local pow = 1 | |
while x > 0 or y > 0 do | |
z = z + (matrix[x%2+1][y%2+1] * pow) | |
pow = pow * 2 | |
x = math_floor(x/2) | |
y = math_floor(y/2) | |
end | |
return z | |
end | |
local function INT2HEX(x) | |
local s,base = '',16 | |
local d | |
while x > 0 do | |
d = x % base + 1 | |
x = math_floor(x/base) | |
s = string_sub(HEXES, d, d)..s | |
end | |
while #s < 2 do s = "0" .. s end | |
return s | |
end | |
---------------------------------------------------------------------------- | |
-- Creates a new uuid. Either provide a unique hex string, or make sure the | |
-- random seed is properly set. The module table itself is a shortcut to this | |
-- function, so `my_uuid = uuid.new()` equals `my_uuid = uuid()`. | |
-- | |
-- For proper use there are 3 options; | |
-- | |
-- 1. first require `luasocket`, then call `uuid.seed()`, and request a uuid using no | |
-- parameter, eg. `my_uuid = uuid()` | |
-- 2. use `uuid` without `luasocket`, set a random seed using `uuid.randomseed(some_good_seed)`, | |
-- and request a uuid using no parameter, eg. `my_uuid = uuid()` | |
-- 3. use `uuid` without `luasocket`, and request a uuid using an unique hex string, | |
-- eg. `my_uuid = uuid(my_networkcard_macaddress)` | |
-- | |
-- @return a properly formatted uuid string | |
-- @param hwaddr (optional) string containing a unique hex value (e.g.: `00:0c:29:69:41:c6`), to be used to compensate for the lesser `math_random()` function. Use a mac address for solid results. If omitted, a fully randomized uuid will be generated, but then you must ensure that the random seed is set properly! | |
-- @usage | |
-- local uuid = require("uuid") | |
-- print("here's a new uuid: ",uuid()) | |
function M.new(hwaddr) | |
-- bytes are treated as 8bit unsigned bytes. | |
local bytes = { | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255), | |
math_random(0, 255) | |
} | |
if hwaddr then | |
assert(type(hwaddr)=="string", "Expected hex string, got "..type(hwaddr)) | |
-- Cleanup provided string, assume mac address, so start from back and cleanup until we've got 12 characters | |
local i,str = #hwaddr, hwaddr | |
hwaddr = "" | |
while i>0 and #hwaddr<12 do | |
local c = str:sub(i,i):lower() | |
if HEXES:find(c, 1, true) then | |
-- valid HEX character, so append it | |
hwaddr = c..hwaddr | |
end | |
i = i - 1 | |
end | |
assert(#hwaddr == 12, "Provided string did not contain at least 12 hex characters, retrieved '"..hwaddr.."' from '"..str.."'") | |
-- no split() in lua. :( | |
bytes[11] = to_number(hwaddr:sub(1, 2), 16) | |
bytes[12] = to_number(hwaddr:sub(3, 4), 16) | |
bytes[13] = to_number(hwaddr:sub(5, 6), 16) | |
bytes[14] = to_number(hwaddr:sub(7, 8), 16) | |
bytes[15] = to_number(hwaddr:sub(9, 10), 16) | |
bytes[16] = to_number(hwaddr:sub(11, 12), 16) | |
end | |
-- set the version | |
bytes[7] = BITWISE(bytes[7], 0x0f, MATRIX_AND) | |
bytes[7] = BITWISE(bytes[7], 0x40, MATRIX_OR) | |
-- set the variant | |
bytes[9] = BITWISE(bytes[7], 0x3f, MATRIX_AND) | |
bytes[9] = BITWISE(bytes[7], 0x80, MATRIX_OR) | |
return INT2HEX(bytes[1])..INT2HEX(bytes[2])..INT2HEX(bytes[3])..INT2HEX(bytes[4]).."-".. | |
INT2HEX(bytes[5])..INT2HEX(bytes[6]).."-".. | |
INT2HEX(bytes[7])..INT2HEX(bytes[8]).."-".. | |
INT2HEX(bytes[9])..INT2HEX(bytes[10]).."-".. | |
INT2HEX(bytes[11])..INT2HEX(bytes[12])..INT2HEX(bytes[13])..INT2HEX(bytes[14])..INT2HEX(bytes[15])..INT2HEX(bytes[16]) | |
end | |
---------------------------------------------------------------------------- | |
-- Improved randomseed function. | |
-- Lua 5.1 and 5.2 both truncate the seed given if it exceeds the integer | |
-- range. If this happens, the seed will be 0 or 1 and all randomness will | |
-- be gone (each application run will generate the same sequence of random | |
-- numbers in that case). This improved version drops the most significant | |
-- bits in those cases to get the seed within the proper range again. | |
-- @param seed the random seed to set (integer from 0 - 2^32, negative values will be made positive) | |
-- @return the (potentially modified) seed used | |
-- @usage | |
-- local socket = require("socket") -- gettime() has higher precision than os.time() | |
-- local uuid = require("uuid") | |
-- -- see also example at uuid.seed() | |
-- uuid.randomseed(socket.gettime()*10000) | |
-- print("here's a new uuid: ",uuid()) | |
function M.randomseed(seed) | |
seed = math_floor(math_abs(seed)) | |
if seed >= (2^bitsize) then | |
-- integer overflow, so reduce to prevent a bad seed | |
seed = seed - math_floor(seed / 2^bitsize) * (2^bitsize) | |
end | |
if lua_version < 5.2 then | |
-- 5.1 uses (incorrect) signed int | |
math.randomseed(seed - 2^(bitsize-1)) | |
else | |
-- 5.2 uses (correct) unsigned int | |
math.randomseed(seed) | |
end | |
return seed | |
end | |
---------------------------------------------------------------------------- | |
-- Seeds the random generator. | |
-- It does so in 2 possible ways; | |
-- | |
-- 1. use `os.time()`: this only offers resolution to one second (used when | |
-- LuaSocket hasn't been loaded yet | |
-- 2. use luasocket `gettime()` function, but it only does so when LuaSocket | |
-- has been required already. | |
-- @usage | |
-- local socket = require("socket") -- gettime() has higher precision than os.time() | |
-- -- LuaSocket loaded, so below line does the same as the example from randomseed() | |
-- uuid.seed() | |
-- print("here's a new uuid: ",uuid()) | |
function M.seed() | |
if package.loaded["socket"] and package.loaded["socket"].gettime then | |
return M.randomseed(package.loaded["socket"].gettime()*10000) | |
else | |
return M.randomseed(os.time()) | |
end | |
end | |
return setmetatable( M, { __call = function(self, hwaddr) return self.new(hwaddr) end} ) | |
local M = {} | |
function M.base64_decode(input) | |
local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' | |
input = input:gsub('[^'..b..'=]', '') | |
return (input:gsub('.', function(x) | |
if (x == '=') then return '' end | |
local r, f = '', (b:find(x) - 1) | |
for i = 6, 1, -1 do r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and '1' or '0') end | |
return r; | |
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) | |
if (#x ~= 8) then return '' end | |
local c = 0 | |
for i = 1, 8 do c = c + (x:sub(i, i) == '1' and 2 ^ (8 - i) or 0) end | |
return string.char(c) | |
end)) | |
end | |
return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment