encryption perf
This commit is contained in:
@@ -1,581 +0,0 @@
|
||||
-- Concise Binary Object Representation (CBOR)
|
||||
-- RFC 7049
|
||||
|
||||
local function softreq(pkg, field)
|
||||
local ok, mod = pcall(require, pkg);
|
||||
if not ok then return end
|
||||
if field then return mod[field]; end
|
||||
return mod;
|
||||
end
|
||||
local dostring = function (s)
|
||||
local ok, f = pcall(loadstring or load, s); -- luacheck: read globals loadstring
|
||||
if ok and f then return f(); end
|
||||
end
|
||||
|
||||
local setmetatable = setmetatable;
|
||||
local getmetatable = getmetatable;
|
||||
local dbg_getmetatable = debug.getmetatable;
|
||||
local assert = assert;
|
||||
local error = error;
|
||||
local type = type;
|
||||
local pairs = pairs;
|
||||
local ipairs = ipairs;
|
||||
local tostring = tostring;
|
||||
local s_char = string.char;
|
||||
local t_concat = table.concat;
|
||||
local t_sort = table.sort;
|
||||
local m_floor = math.floor;
|
||||
local m_abs = math.abs;
|
||||
local m_huge = math.huge;
|
||||
local m_max = math.max;
|
||||
local maxint = math.maxinteger or 9007199254740992;
|
||||
local minint = math.mininteger or -9007199254740992;
|
||||
local NaN = 0/0;
|
||||
local m_frexp = math.frexp;
|
||||
local m_ldexp = math.ldexp or function (x, exp) return x * 2.0 ^ exp; end;
|
||||
local m_type = math.type or function (n) return n % 1 == 0 and n <= maxint and n >= minint and "integer" or "float" end;
|
||||
local s_pack = string.pack or softreq("struct", "pack");
|
||||
local s_unpack = string.unpack or softreq("struct", "unpack");
|
||||
local b_rshift = softreq("bit32", "rshift") or softreq("bit", "rshift") or
|
||||
dostring "return function(a,b) return a >> b end" or
|
||||
function (a, b) return m_max(0, m_floor(a / (2 ^ b))); end;
|
||||
|
||||
-- sanity check
|
||||
if s_pack and s_pack(">I2", 0) ~= "\0\0" then
|
||||
s_pack = nil;
|
||||
end
|
||||
if s_unpack and s_unpack(">I2", "\1\2\3\4") ~= 0x102 then
|
||||
s_unpack = nil;
|
||||
end
|
||||
|
||||
local _ENV = nil; -- luacheck: ignore 211
|
||||
|
||||
local encoder = {};
|
||||
|
||||
local function encode(obj, opts)
|
||||
return encoder[type(obj)](obj, opts);
|
||||
end
|
||||
|
||||
-- Major types 0, 1 and length encoding for others
|
||||
local function integer(num, m)
|
||||
if m == 0 and num < 0 then
|
||||
-- negative integer, major type 1
|
||||
num, m = - num - 1, 32;
|
||||
end
|
||||
if num < 24 then
|
||||
return s_char(m + num);
|
||||
elseif num < 2 ^ 8 then
|
||||
return s_char(m + 24, num);
|
||||
elseif num < 2 ^ 16 then
|
||||
return s_char(m + 25, b_rshift(num, 8), num % 0x100);
|
||||
elseif num < 2 ^ 32 then
|
||||
return s_char(m + 26,
|
||||
b_rshift(num, 24) % 0x100,
|
||||
b_rshift(num, 16) % 0x100,
|
||||
b_rshift(num, 8) % 0x100,
|
||||
num % 0x100);
|
||||
elseif num < 2 ^ 64 then
|
||||
local high = m_floor(num / 2 ^ 32);
|
||||
num = num % 2 ^ 32;
|
||||
return s_char(m + 27,
|
||||
b_rshift(high, 24) % 0x100,
|
||||
b_rshift(high, 16) % 0x100,
|
||||
b_rshift(high, 8) % 0x100,
|
||||
high % 0x100,
|
||||
b_rshift(num, 24) % 0x100,
|
||||
b_rshift(num, 16) % 0x100,
|
||||
b_rshift(num, 8) % 0x100,
|
||||
num % 0x100);
|
||||
end
|
||||
error "int too large";
|
||||
end
|
||||
|
||||
if s_pack then
|
||||
function integer(num, m)
|
||||
local fmt;
|
||||
m = m or 0;
|
||||
if num < 24 then
|
||||
fmt, m = ">B", m + num;
|
||||
elseif num < 256 then
|
||||
fmt, m = ">BB", m + 24;
|
||||
elseif num < 65536 then
|
||||
fmt, m = ">BI2", m + 25;
|
||||
elseif num < 4294967296 then
|
||||
fmt, m = ">BI4", m + 26;
|
||||
else
|
||||
fmt, m = ">BI8", m + 27;
|
||||
end
|
||||
return s_pack(fmt, m, num);
|
||||
end
|
||||
end
|
||||
|
||||
local simple_mt = {};
|
||||
function simple_mt:__tostring() return self.name or ("simple(%d)"):format(self.value); end
|
||||
function simple_mt:__tocbor() return self.cbor or integer(self.value, 224); end
|
||||
|
||||
local function simple(value, name, cbor)
|
||||
assert(value >= 0 and value <= 255, "bad argument #1 to 'simple' (integer in range 0..255 expected)");
|
||||
return setmetatable({ value = value, name = name, cbor = cbor }, simple_mt);
|
||||
end
|
||||
|
||||
local tagged_mt = {};
|
||||
function tagged_mt:__tostring() return ("%d(%s)"):format(self.tag, tostring(self.value)); end
|
||||
function tagged_mt:__tocbor() return integer(self.tag, 192) .. encode(self.value); end
|
||||
|
||||
local function tagged(tag, value)
|
||||
assert(tag >= 0, "bad argument #1 to 'tagged' (positive integer expected)");
|
||||
return setmetatable({ tag = tag, value = value }, tagged_mt);
|
||||
end
|
||||
|
||||
local null = simple(22, "null"); -- explicit null
|
||||
local undefined = simple(23, "undefined"); -- undefined or nil
|
||||
local BREAK = simple(31, "break", "\255");
|
||||
|
||||
-- Number types dispatch
|
||||
function encoder.number(num)
|
||||
return encoder[m_type(num)](num);
|
||||
end
|
||||
|
||||
-- Major types 0, 1
|
||||
function encoder.integer(num)
|
||||
if num < 0 then
|
||||
return integer(-1 - num, 32);
|
||||
end
|
||||
return integer(num, 0);
|
||||
end
|
||||
|
||||
-- Major type 7
|
||||
function encoder.float(num)
|
||||
if num ~= num then -- NaN shortcut
|
||||
return "\251\127\255\255\255\255\255\255\255";
|
||||
end
|
||||
local sign = (num > 0 or 1 / num > 0) and 0 or 1;
|
||||
num = m_abs(num)
|
||||
if num == m_huge then
|
||||
return s_char(251, sign * 128 + 128 - 1) .. "\240\0\0\0\0\0\0";
|
||||
end
|
||||
local fraction, exponent = m_frexp(num)
|
||||
if fraction == 0 then
|
||||
return s_char(251, sign * 128) .. "\0\0\0\0\0\0\0";
|
||||
end
|
||||
fraction = fraction * 2;
|
||||
exponent = exponent + 1024 - 2;
|
||||
if exponent <= 0 then
|
||||
fraction = fraction * 2 ^ (exponent - 1)
|
||||
exponent = 0;
|
||||
else
|
||||
fraction = fraction - 1;
|
||||
end
|
||||
return s_char(251,
|
||||
sign * 2 ^ 7 + m_floor(exponent / 2 ^ 4) % 2 ^ 7,
|
||||
exponent % 2 ^ 4 * 2 ^ 4 +
|
||||
m_floor(fraction * 2 ^ 4 % 0x100),
|
||||
m_floor(fraction * 2 ^ 12 % 0x100),
|
||||
m_floor(fraction * 2 ^ 20 % 0x100),
|
||||
m_floor(fraction * 2 ^ 28 % 0x100),
|
||||
m_floor(fraction * 2 ^ 36 % 0x100),
|
||||
m_floor(fraction * 2 ^ 44 % 0x100),
|
||||
m_floor(fraction * 2 ^ 52 % 0x100)
|
||||
)
|
||||
end
|
||||
|
||||
if s_pack then
|
||||
function encoder.float(num)
|
||||
return s_pack(">Bd", 251, num);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Major type 2 - byte strings
|
||||
function encoder.bytestring(s)
|
||||
return integer(#s, 64) .. s;
|
||||
end
|
||||
|
||||
-- Major type 3 - UTF-8 strings
|
||||
function encoder.utf8string(s)
|
||||
return integer(#s, 96) .. s;
|
||||
end
|
||||
|
||||
-- Lua strings are byte strings
|
||||
encoder.string = encoder.bytestring;
|
||||
|
||||
function encoder.boolean(bool)
|
||||
return bool and "\245" or "\244";
|
||||
end
|
||||
|
||||
encoder["nil"] = function() return "\246"; end
|
||||
|
||||
function encoder.userdata(ud, opts)
|
||||
local mt = dbg_getmetatable(ud);
|
||||
if mt then
|
||||
local encode_ud = opts and opts[mt] or mt.__tocbor;
|
||||
if encode_ud then
|
||||
return encode_ud(ud, opts);
|
||||
end
|
||||
end
|
||||
error "can't encode userdata";
|
||||
end
|
||||
|
||||
function encoder.table(t, opts)
|
||||
local mt = getmetatable(t);
|
||||
if mt then
|
||||
local encode_t = opts and opts[mt] or mt.__tocbor;
|
||||
if encode_t then
|
||||
return encode_t(t, opts);
|
||||
end
|
||||
end
|
||||
-- the table is encoded as an array iff when we iterate over it,
|
||||
-- we see successive integer keys starting from 1. The lua
|
||||
-- language doesn't actually guarantee that this will be the case
|
||||
-- when we iterate over a table with successive integer keys, but
|
||||
-- due an implementation detail in PUC Rio Lua, this is what we
|
||||
-- usually observe. See the Lua manual regarding the # (length)
|
||||
-- operator. In the case that this does not happen, we will fall
|
||||
-- back to a map with integer keys, which becomes a bit larger.
|
||||
local array, map, i, p = { integer(#t, 128) }, { "\191" }, 1, 2;
|
||||
local is_array = true;
|
||||
for k, v in pairs(t) do
|
||||
is_array = is_array and i == k;
|
||||
i = i + 1;
|
||||
|
||||
local encoded_v = encode(v, opts);
|
||||
array[i] = encoded_v;
|
||||
|
||||
map[p], p = encode(k, opts), p + 1;
|
||||
map[p], p = encoded_v, p + 1;
|
||||
end
|
||||
-- map[p] = "\255";
|
||||
map[1] = integer(i - 1, 160);
|
||||
return t_concat(is_array and array or map);
|
||||
end
|
||||
|
||||
-- Array or dict-only encoders, which can be set as __tocbor metamethod
|
||||
function encoder.array(t, opts)
|
||||
local array = { };
|
||||
for i, v in ipairs(t) do
|
||||
array[i] = encode(v, opts);
|
||||
end
|
||||
return integer(#array, 128) .. t_concat(array);
|
||||
end
|
||||
|
||||
function encoder.map(t, opts)
|
||||
local map, p, len = { "\191" }, 2, 0;
|
||||
for k, v in pairs(t) do
|
||||
map[p], p = encode(k, opts), p + 1;
|
||||
map[p], p = encode(v, opts), p + 1;
|
||||
len = len + 1;
|
||||
end
|
||||
-- map[p] = "\255";
|
||||
map[1] = integer(len, 160);
|
||||
return t_concat(map);
|
||||
end
|
||||
encoder.dict = encoder.map; -- COMPAT
|
||||
|
||||
function encoder.ordered_map(t, opts)
|
||||
local map = {};
|
||||
if not t[1] then -- no predefined order
|
||||
local i = 0;
|
||||
for k in pairs(t) do
|
||||
i = i + 1;
|
||||
map[i] = k;
|
||||
end
|
||||
t_sort(map);
|
||||
end
|
||||
for i, k in ipairs(t[1] and t or map) do
|
||||
map[i] = encode(k, opts) .. encode(t[k], opts);
|
||||
end
|
||||
return integer(#map, 160) .. t_concat(map);
|
||||
end
|
||||
|
||||
encoder["function"] = function ()
|
||||
error "can't encode function";
|
||||
end
|
||||
|
||||
-- Decoder
|
||||
-- Reads from a file-handle like object
|
||||
local function read_bytes(fh, len)
|
||||
return fh:read(len);
|
||||
end
|
||||
|
||||
local function read_byte(fh)
|
||||
return fh:read(1):byte();
|
||||
end
|
||||
|
||||
local function read_length(fh, mintyp)
|
||||
if mintyp < 24 then
|
||||
return mintyp;
|
||||
elseif mintyp < 28 then
|
||||
local out = 0;
|
||||
for _ = 1, 2 ^ (mintyp - 24) do
|
||||
out = out * 256 + read_byte(fh);
|
||||
end
|
||||
return out;
|
||||
else
|
||||
error "invalid length";
|
||||
end
|
||||
end
|
||||
|
||||
local decoder = {};
|
||||
|
||||
local function read_type(fh)
|
||||
local byte = read_byte(fh);
|
||||
return b_rshift(byte, 5), byte % 32;
|
||||
end
|
||||
|
||||
local function read_object(fh, opts)
|
||||
local typ, mintyp = read_type(fh);
|
||||
return decoder[typ](fh, mintyp, opts);
|
||||
end
|
||||
|
||||
local function read_integer(fh, mintyp)
|
||||
return read_length(fh, mintyp);
|
||||
end
|
||||
|
||||
local function read_negative_integer(fh, mintyp)
|
||||
return -1 - read_length(fh, mintyp);
|
||||
end
|
||||
|
||||
local function read_string(fh, mintyp)
|
||||
if mintyp ~= 31 then
|
||||
return read_bytes(fh, read_length(fh, mintyp));
|
||||
end
|
||||
local out = {};
|
||||
local i = 1;
|
||||
local v = read_object(fh);
|
||||
while v ~= BREAK do
|
||||
out[i], i = v, i + 1;
|
||||
v = read_object(fh);
|
||||
end
|
||||
return t_concat(out);
|
||||
end
|
||||
|
||||
local function read_unicode_string(fh, mintyp)
|
||||
return read_string(fh, mintyp);
|
||||
-- local str = read_string(fh, mintyp);
|
||||
-- if have_utf8 and not utf8.len(str) then
|
||||
-- TODO How to handle this?
|
||||
-- end
|
||||
-- return str;
|
||||
end
|
||||
|
||||
local function read_array(fh, mintyp, opts)
|
||||
local out = {};
|
||||
if mintyp == 31 then
|
||||
local i = 1;
|
||||
local v = read_object(fh, opts);
|
||||
while v ~= BREAK do
|
||||
out[i], i = v, i + 1;
|
||||
v = read_object(fh, opts);
|
||||
end
|
||||
else
|
||||
local len = read_length(fh, mintyp);
|
||||
for i = 1, len do
|
||||
out[i] = read_object(fh, opts);
|
||||
end
|
||||
end
|
||||
return out;
|
||||
end
|
||||
|
||||
local function read_map(fh, mintyp, opts)
|
||||
local out = {};
|
||||
local k;
|
||||
if mintyp == 31 then
|
||||
local i = 1;
|
||||
k = read_object(fh, opts);
|
||||
while k ~= BREAK do
|
||||
out[k], i = read_object(fh, opts), i + 1;
|
||||
k = read_object(fh, opts);
|
||||
end
|
||||
else
|
||||
local len = read_length(fh, mintyp);
|
||||
for _ = 1, len do
|
||||
k = read_object(fh, opts);
|
||||
out[k] = read_object(fh, opts);
|
||||
end
|
||||
end
|
||||
return out;
|
||||
end
|
||||
|
||||
local tagged_decoders = {};
|
||||
|
||||
local function read_semantic(fh, mintyp, opts)
|
||||
local tag = read_length(fh, mintyp);
|
||||
local value = read_object(fh, opts);
|
||||
local postproc = opts and opts[tag] or tagged_decoders[tag];
|
||||
if postproc then
|
||||
return postproc(value);
|
||||
end
|
||||
return tagged(tag, value);
|
||||
end
|
||||
|
||||
local function read_half_float(fh)
|
||||
local exponent = read_byte(fh);
|
||||
local fraction = read_byte(fh);
|
||||
local sign = exponent < 128 and 1 or -1; -- sign is highest bit
|
||||
|
||||
fraction = fraction + (exponent * 256) % 1024; -- copy two(?) bits from exponent to fraction
|
||||
exponent = b_rshift(exponent, 2) % 32; -- remove sign bit and two low bits from fraction;
|
||||
|
||||
if exponent == 0 then
|
||||
return sign * m_ldexp(fraction, -24);
|
||||
elseif exponent ~= 31 then
|
||||
return sign * m_ldexp(fraction + 1024, exponent - 25);
|
||||
elseif fraction == 0 then
|
||||
return sign * m_huge;
|
||||
else
|
||||
return NaN;
|
||||
end
|
||||
end
|
||||
|
||||
local function read_float(fh)
|
||||
local exponent = read_byte(fh);
|
||||
local fraction = read_byte(fh);
|
||||
local sign = exponent < 128 and 1 or -1; -- sign is highest bit
|
||||
exponent = exponent * 2 % 256 + b_rshift(fraction, 7);
|
||||
fraction = fraction % 128;
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
|
||||
if exponent == 0 then
|
||||
return sign * m_ldexp(exponent, -149);
|
||||
elseif exponent ~= 0xff then
|
||||
return sign * m_ldexp(fraction + 2 ^ 23, exponent - 150);
|
||||
elseif fraction == 0 then
|
||||
return sign * m_huge;
|
||||
else
|
||||
return NaN;
|
||||
end
|
||||
end
|
||||
|
||||
local function read_double(fh)
|
||||
local exponent = read_byte(fh);
|
||||
local fraction = read_byte(fh);
|
||||
local sign = exponent < 128 and 1 or -1; -- sign is highest bit
|
||||
|
||||
exponent = exponent % 128 * 16 + b_rshift(fraction, 4);
|
||||
fraction = fraction % 16;
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
fraction = fraction * 256 + read_byte(fh);
|
||||
|
||||
if exponent == 0 then
|
||||
return sign * m_ldexp(exponent, -149);
|
||||
elseif exponent ~= 0xff then
|
||||
return sign * m_ldexp(fraction + 2 ^ 52, exponent - 1075);
|
||||
elseif fraction == 0 then
|
||||
return sign * m_huge;
|
||||
else
|
||||
return NaN;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if s_unpack then
|
||||
function read_float(fh) return s_unpack(">f", read_bytes(fh, 4)) end
|
||||
function read_double(fh) return s_unpack(">d", read_bytes(fh, 8)) end
|
||||
end
|
||||
|
||||
local function read_simple(fh, value, opts)
|
||||
if value == 24 then
|
||||
value = read_byte(fh);
|
||||
end
|
||||
if value == 20 then
|
||||
return false;
|
||||
elseif value == 21 then
|
||||
return true;
|
||||
elseif value == 22 then
|
||||
return null;
|
||||
elseif value == 23 then
|
||||
return undefined;
|
||||
elseif value == 25 then
|
||||
return read_half_float(fh);
|
||||
elseif value == 26 then
|
||||
return read_float(fh);
|
||||
elseif value == 27 then
|
||||
return read_double(fh);
|
||||
elseif value == 31 then
|
||||
return BREAK;
|
||||
end
|
||||
if opts and opts.simple then
|
||||
return opts.simple(value);
|
||||
end
|
||||
return simple(value);
|
||||
end
|
||||
|
||||
decoder[0] = read_integer;
|
||||
decoder[1] = read_negative_integer;
|
||||
decoder[2] = read_string;
|
||||
decoder[3] = read_unicode_string;
|
||||
decoder[4] = read_array;
|
||||
decoder[5] = read_map;
|
||||
decoder[6] = read_semantic;
|
||||
decoder[7] = read_simple;
|
||||
|
||||
-- opts.more(n) -> want more data
|
||||
-- opts.simple -> decode simple value
|
||||
-- opts[int] -> tagged decoder
|
||||
local function decode(s, opts)
|
||||
local fh = {};
|
||||
local pos = 1;
|
||||
|
||||
local more;
|
||||
if type(opts) == "function" then
|
||||
more = opts;
|
||||
elseif type(opts) == "table" then
|
||||
more = opts.more;
|
||||
elseif opts ~= nil then
|
||||
error(("bad argument #2 to 'decode' (function or table expected, got %s)"):format(type(opts)));
|
||||
end
|
||||
if type(more) ~= "function" then
|
||||
function more()
|
||||
error "input too short";
|
||||
end
|
||||
end
|
||||
|
||||
function fh:read(bytes)
|
||||
local ret = s:sub(pos, pos + bytes - 1);
|
||||
if #ret < bytes then
|
||||
ret = more(bytes - #ret, fh, opts);
|
||||
if ret then self:write(ret); end
|
||||
return self:read(bytes);
|
||||
end
|
||||
pos = pos + bytes;
|
||||
return ret;
|
||||
end
|
||||
|
||||
function fh:write(bytes) -- luacheck: no self
|
||||
s = s .. bytes;
|
||||
if pos > 256 then
|
||||
s = s:sub(pos + 1);
|
||||
pos = 1;
|
||||
end
|
||||
return #bytes;
|
||||
end
|
||||
|
||||
return read_object(fh, opts);
|
||||
end
|
||||
|
||||
return {
|
||||
-- en-/decoder functions
|
||||
encode = encode;
|
||||
decode = decode;
|
||||
decode_file = read_object;
|
||||
|
||||
-- tables of per-type en-/decoders
|
||||
type_encoders = encoder;
|
||||
type_decoders = decoder;
|
||||
|
||||
-- special treatment for tagged values
|
||||
tagged_decoders = tagged_decoders;
|
||||
|
||||
-- constructors for annotated types
|
||||
simple = simple;
|
||||
tagged = tagged;
|
||||
|
||||
-- pre-defined simple values
|
||||
null = null;
|
||||
undefined = undefined;
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
-- Chacha20 cipher in ComputerCraft
|
||||
-- By Anavrins
|
||||
|
||||
local LZW = require('opus.crypto.lualzw')
|
||||
local cbor = require('opus.cbor')
|
||||
local sha2 = require('opus.crypto.sha2')
|
||||
local Serializer = require('opus.crypto.serializer')
|
||||
local Util = require('opus.util')
|
||||
|
||||
local ROUNDS = 8 -- Adjust this for speed tradeoff
|
||||
@@ -153,10 +152,10 @@ end
|
||||
|
||||
local function encrypt(data, key)
|
||||
local nonce = genNonce(12)
|
||||
data = Serializer.serialize(data)
|
||||
data = LZW.compress(data)
|
||||
data = cbor.encode(data)
|
||||
key = sha2.digest(key)
|
||||
local ctx = crypt(data, key, nonce, 1, ROUNDS)
|
||||
|
||||
return { nonce:toHex(), ctx:toHex() }
|
||||
end
|
||||
|
||||
@@ -165,7 +164,7 @@ local function decrypt(data, key)
|
||||
data = Util.hexToByteArray(data[2])
|
||||
key = sha2.digest(key)
|
||||
local ptx = crypt(data, key, nonce, 1, ROUNDS)
|
||||
return textutils.unserialise(LZW.decompress(tostring(ptx)))
|
||||
return cbor.decode(tostring(ptx))
|
||||
end
|
||||
|
||||
local obj = {}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
-- see: https://github.com/Rochet2/lualzw
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Rochet2
|
||||
|
||||
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 char = string.char
|
||||
local type = type
|
||||
local sub = string.sub
|
||||
local tconcat = table.concat
|
||||
|
||||
local basedictcompress = {}
|
||||
local basedictdecompress = {}
|
||||
for i = 0, 255 do
|
||||
local ic, iic = char(i), char(i, 0)
|
||||
basedictcompress[ic] = iic
|
||||
basedictdecompress[iic] = ic
|
||||
end
|
||||
|
||||
local function dictAddA(str, dict, a, b)
|
||||
if a >= 256 then
|
||||
a, b = 0, b+1
|
||||
if b >= 256 then
|
||||
dict = {}
|
||||
b = 1
|
||||
end
|
||||
end
|
||||
dict[str] = char(a,b)
|
||||
a = a+1
|
||||
return dict, a, b
|
||||
end
|
||||
|
||||
local function compress(input)
|
||||
if type(input) ~= "string" then
|
||||
error ("string expected, got "..type(input))
|
||||
end
|
||||
local len = #input
|
||||
if len <= 1 then
|
||||
return "u"..input
|
||||
end
|
||||
|
||||
local dict = {}
|
||||
local a, b = 0, 1
|
||||
|
||||
local result = {"c"}
|
||||
local resultlen = 1
|
||||
local n = 2
|
||||
local word = ""
|
||||
for i = 1, len do
|
||||
local c = sub(input, i, i)
|
||||
local wc = word..c
|
||||
if not (basedictcompress[wc] or dict[wc]) then
|
||||
local write = basedictcompress[word] or dict[word]
|
||||
if not write then
|
||||
error "algorithm error, could not fetch word"
|
||||
end
|
||||
result[n] = write
|
||||
resultlen = resultlen + #write
|
||||
n = n+1
|
||||
if len <= resultlen then
|
||||
return "u"..input
|
||||
end
|
||||
dict, a, b = dictAddA(wc, dict, a, b)
|
||||
word = c
|
||||
else
|
||||
word = wc
|
||||
end
|
||||
end
|
||||
result[n] = basedictcompress[word] or dict[word]
|
||||
resultlen = resultlen+#result[n]
|
||||
if len <= resultlen then
|
||||
return "u"..input
|
||||
end
|
||||
return tconcat(result)
|
||||
end
|
||||
|
||||
local function dictAddB(str, dict, a, b)
|
||||
if a >= 256 then
|
||||
a, b = 0, b+1
|
||||
if b >= 256 then
|
||||
dict = {}
|
||||
b = 1
|
||||
end
|
||||
end
|
||||
dict[char(a,b)] = str
|
||||
a = a+1
|
||||
return dict, a, b
|
||||
end
|
||||
|
||||
local function decompress(input)
|
||||
if type(input) ~= "string" then
|
||||
error( "string expected, got "..type(input))
|
||||
end
|
||||
|
||||
if #input < 1 then
|
||||
error("invalid input - not a compressed string")
|
||||
end
|
||||
|
||||
local control = sub(input, 1, 1)
|
||||
if control == "u" then
|
||||
return sub(input, 2)
|
||||
elseif control ~= "c" then
|
||||
error( "invalid input - not a compressed string")
|
||||
end
|
||||
input = sub(input, 2)
|
||||
local len = #input
|
||||
|
||||
if len < 2 then
|
||||
error( "invalid input - not a compressed string")
|
||||
end
|
||||
|
||||
local dict = {}
|
||||
local a, b = 0, 1
|
||||
|
||||
local result = {}
|
||||
local n = 1
|
||||
local last = sub(input, 1, 2)
|
||||
result[n] = basedictdecompress[last] or dict[last]
|
||||
n = n+1
|
||||
for i = 3, len, 2 do
|
||||
local code = sub(input, i, i+1)
|
||||
local lastStr = basedictdecompress[last] or dict[last]
|
||||
if not lastStr then
|
||||
error( "could not find last from dict. Invalid input?")
|
||||
end
|
||||
local toAdd = basedictdecompress[code] or dict[code]
|
||||
if toAdd then
|
||||
result[n] = toAdd
|
||||
n = n+1
|
||||
dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b)
|
||||
else
|
||||
local tmp = lastStr..sub(lastStr, 1, 1)
|
||||
result[n] = tmp
|
||||
n = n+1
|
||||
dict, a, b = dictAddB(tmp, dict, a, b)
|
||||
end
|
||||
last = code
|
||||
end
|
||||
return tconcat(result)
|
||||
end
|
||||
|
||||
return {
|
||||
compress = compress,
|
||||
decompress = decompress,
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
local Serializer = { }
|
||||
|
||||
local insert = table.insert
|
||||
local format = string.format
|
||||
|
||||
function Serializer.serialize(tbl)
|
||||
local output = { }
|
||||
|
||||
local function recurse(t)
|
||||
local sType = type(t)
|
||||
if sType == 'table' then
|
||||
if next(t) == nil then
|
||||
insert(output, '{}')
|
||||
else
|
||||
insert(output, '{')
|
||||
local tSeen = {}
|
||||
for k, v in ipairs(t) do
|
||||
tSeen[k] = true
|
||||
recurse(v)
|
||||
insert(output, ',')
|
||||
end
|
||||
for k, v in pairs(t) do
|
||||
if not tSeen[k] then
|
||||
if type(k) == 'string' and string.match(k, '^[%a_][%a%d_]*$') then
|
||||
insert(output, k .. '=')
|
||||
recurse(v)
|
||||
insert(output, ',')
|
||||
else
|
||||
insert(output, '[')
|
||||
recurse(k)
|
||||
insert(output, ']=')
|
||||
recurse(v)
|
||||
insert(output, ',')
|
||||
end
|
||||
end
|
||||
end
|
||||
insert(output, '}')
|
||||
end
|
||||
elseif sType == 'string' then
|
||||
insert(output, format('%q', t))
|
||||
else
|
||||
insert(output, tostring(t))
|
||||
end
|
||||
end
|
||||
|
||||
recurse(tbl)
|
||||
return table.concat(output)
|
||||
end
|
||||
|
||||
return Serializer
|
||||
Reference in New Issue
Block a user