-- GLON: Garry's Mod Lua Object Notation -- A extension of LON: Lua Object Notation -- Made entirely by Deco Da Man -- Types: -- 2: table -- 3: array -- 4: fasle boolean -- 5: true boolean -- 6: number (NOT COMPRESSED, it isn't worth it) -- 7: string ---- non-LON types start here! -- 8: Vector (NOT COMPRESSED, it isn't worth it) -- 9: Angle (NOT COMPRESSED, it isn't worth it) -- 10: Entity (Can do players, vehicles, npcs, weapons and any other type of entity (-1 for null entity)) -- 11: Player (By UserID) -- 12: CEffectData -- 13: ConVar (Not ClientConVar) -- 15: Color -- 254: The number equal to -math.huge (tostring(math.huge) == "-1.#INF") -- 254: The number equal to math.huge (tostring(math.huge) == "1.#INF") -- 255: reference (Sends the ID of the table to use (for "local t = {} t.a=t")) local pairs = pairs local type = type local string = string local math = math local tostring = tostring local ValidEntity = ValidEntity local error = error local print = print local setmetatable = setmetatable local Vector = Vector local Angle = Angle local Entity = Entity local EffectData = EffectData local GetConVar = GetConVar local tonumber = tonumber local player = player local IsValid = IsValid local encode_types local decode_types local function InDataEscape(s) s = string.gsub(s, "([\1\2])", "\2%1") s = string.gsub(s, "%z", "\2\3") s = string.gsub(s, "\"", "\4") -- escape the " character to simplify client commands return s end encode_types = { ["nil"] = {nil, function() return "", nil end}, table = {2, function(o, rtabs) for k,v in pairs(rtabs) do if v == o then return tostring(k).."\1", 255 end end rtabs[#rtabs+1] = o local is_array = true local i = 0 for k,v in pairs(o) do i = i + 1 if k ~= i or type(k) ~= "number" or math.floor(k) ~= k then is_array = false break end end local s = "" for k,v in pairs(o) do if encode_types[type(v)] then if not is_array then s = s..Write(k, rtabs) end s = s..Write(v, rtabs) end end return s.."\1", is_array and 3 end}, boolean = {4, function(o) return "", o and 5 end}, number = {6, function(o) o = o == 0 and "" or o return o == ((o == math.huge or o == -math.huge) and "") or tostring(o).."\1", (o == math.huge and 254) or (o == -math.huge and 253) end}, string = {7, function(o) return InDataEscape(o).."\1" end}, -- non-LON types start here! Vector = {8, function(o) return o.x.."\1"..o.y.."\1"..o.z.."\1" end}, Angle = {9, function(o) return o.p.."\1"..o.y.."\1"..o.r.."\1" end}, Entity = {10, function(o) return (ValidEntity(o) and o:EntIndex() or -1).."\1" end}, NPC = {10, function(o) return (ValidEntity(o) and o:EntIndex() or -1).."\1" end}, Weapon = {10, function(o) return (ValidEntity(o) and o:EntIndex() or -1).."\1" end}, Player = {11, function(o) return o:EntIndex().."\1" end}, CEffectData = {12, function(o, rtabs) local t = {} if o:GetAngle() ~= Angle(0,0,0) then t.a = o:GetAngle() end if o:GetAttachment() ~= 0 then t.h = o:GetAttachment() end if o:GetEntity():IsValid() then t.e = o:GetEntity() end if o:GetMagnitude() ~= 0 then t.m = o:GetMagnitude() end if o:GetNormal() ~= Vector(0,0,0) then t.n = o:GetNormal() end if o:GetOrigin() ~= Vector(0,0,0) then t.o = o:GetOrigin() end if o:GetRadius() ~= 0 then t.r = o:GetRadius() end if o:GetScale() ~= 0 then t.c = o:GetScale() end if o:GetStart() ~= 0 then t.s = o:GetStart() end if o:GetSurfaceProp() ~= 0 then t.p = o:GetSurfaceProp() end return encode_types.table[2](t, rtabs) end}, ConVar = {13, function(o) return InDataEscape(o:GetName()).."\1" end}, PhysObj = {14, function(o) local parent, obj, id = o:GetEntity() for i = 1, parent:GetPhysicsObjectCount() do obj = parent:GetPhysicsObjectNum() if obj == o then id = i break end end return parent:EntIndex().."\1"..id.."\1" end}, Color = {15, function(o) return o.r.."\1"..o.g.."\1"..o.b.."\1"..o.a.."\1" end}, } function Write(data, rtabs) local t = encode_types[type(data)] if t then local data, id_override = t[2](data, rtabs) local char = id_override or t[1] or "" if char ~= "" then char = string.char(char) end return char..(data or "") else error(string.format("Tried to write unwriteable type: %s", type(data))) end end local CEffectDataTranslation = { a = "Angle", h = "Attachment", e = "Entity", m = "Magnitude", n = "Normal", o = "Origin", r = "Radius", c = "Scale", s = "Start", p = "SurfaceProp", } decode_types = { -- \2\6omg\1\6omgavalue\1\1 [2 ] = function(reader, rtabs) -- table local t, c, pos = {}, reader:Next() rtabs[#rtabs+1] = t local stage = false local key while true do c, pos = reader:Peek() if c == "\1" then if stage then error(string.format("Expected value to match key at %s! (Got EO Table)", pos)) else reader:Next() return t end else if stage then t[key] = Read(reader, rtabs) else key = Read(reader, rtabs) end stage = not stage end end end, [3 ] = function(reader, rtabs) -- array local t, i, c, pos = {}, 1, reader:Next() rtabs[#rtabs+1] = t while true do c, pos = reader:Peek() if c == "\1" then reader:Next() return t else t[i] = Read(reader, rtabs) i = i+1 end end end, [4 ] = function(reader) -- false boolean reader:Next() return false end, [5 ] = function(reader) -- true boolean reader:Next() return true end, [6 ] = function(reader) -- number local s, c, pos, e = "", reader:Next() while true do c = reader:Next() if not c then error(string.format("Expected \1 to end number at %s! (Got EOF!)", pos)) elseif c == "\1" then break else s = s..c end end if s == "" then s = "0" end local n = tonumber(s) if not n then error(string.format("Invalid number at %s! (%q)", pos, s)) end return n end, [7 ] = function(reader) -- string local s, c, pos, e = "", reader:Next() while true do c = reader:Next() if not c then error(string.format("Expected unescaped \1 to end string at position %s! (Got EOF)", pos)) elseif e then if c == "\3" then s = s.."\0" else s = s..c end e = false elseif c == "\2" then e = true elseif c == "\1" then s = string.gsub(s, "\4", "\"") -- unescape quotes return s else s = s..c end end end, [8 ] = function(reader) -- Vector local x = decode_types[6](reader) reader:StepBack() local y = decode_types[6](reader) reader:StepBack() local z = decode_types[6](reader) return Vector(x, y, z) end, [9 ] = function(reader) -- Angle local p = decode_types[6](reader) reader:StepBack() local y = decode_types[6](reader) reader:StepBack() local r = decode_types[6](reader) return Angle(p, y, r) end, [10 ] = function(reader) -- Entity return Entity(decode_types[6](reader)) end, [11 ] = function(reader) -- Player local num = decode_types[6](reader) return player.GetByID(num) end, [12 ] = function(reader, rtabs) -- CEffectData local t = decode_types[2](reader, rtabs) local d = EffectData() for k,v in pairs(t) do d["Set"..CEffectDataTranslation[k]](d, v) end return d end, [13 ] = function(reader) -- ConVar return GetConVar(decode_types[7](reader)) end, [14 ] = function(reader) -- PhysicsObject local ent = Entity(decode_types[6](reader)) local bone = decode_types[6](reader) if ( not IsValid( ent ) ) then return nil end; return ent:GetPhysicsObjectNum( bone ) end, [15 ] = function(reader) -- Color local r = decode_types[6](reader) reader:StepBack() local g = decode_types[6](reader) reader:StepBack() local b = decode_types[6](reader) reader:StepBack() local a = decode_types[6](reader) return Color(r, g, b, a) end, [253] = function(reader) -- -math.huge reader:Next() return -math.huge end, [254] = function(reader) -- math.huge reader:Next() return math.huge end, [255] = function(reader, rtabs) -- Reference return rtabs[decode_types[6](reader) - 1] end, } function Read(reader, rtabs) local t, pos = reader:Peek() if not t then error(string.format("Expected type ID at %s! (Got EOF)", pos)) else local dt = decode_types[string.byte(t)] if not dt then error(string.format("Unknown type ID, %s!", string.byte(t))) else return dt(reader, rtabs or {0}) end end end local reader_meta = {} reader_meta.__index = reader_meta function reader_meta:Next() self.i = self.i+1 self.c = string.sub(self.s, self.i, self.i) if self.c == "" then self.c = nil end self.p = string.sub(self.s, self.i+1, self.i+1) if self.p == "" then self.p = nil end return self.c, self.i end function reader_meta:StepBack() self.i = self.i-1 self.c = string.sub(self.s, self.i, self.i) if self.c == "" then self.c = nil end self.p = string.sub(self.s, self.i+1, self.i+1) if self.p == "" then self.p = nil end return self.c, self.i end function reader_meta:Peek() return self.p, self.i+1 end function decode(data) if type(data) == "nil" then return nil elseif type(data) ~= "string" then error(string.format("Expected string to decode! (Got type %s)", type(data) )) elseif data:len() == 0 then return nil end return Read(setmetatable({ s = data, i = 0, c = string.sub(data, 0, 0), p = string.sub(data, 1, 1), }, reader_meta), {}) end function encode(data) return Write(data, {0}) -- to use the first key, to prevent it from interfereing with \1s. You can have up to 254 unique tables (including arrays) end --[==[ David Kolf's JSON module for Lua 5.1/5.2 ======================================== *Version 2.1* This module writes no global values, not even the module table. Import it using json = require ("dkjson") Exported functions and values: `dkJson.encode (object [, state])` -------------------------------- Create a string representing the object. `Object` can be a table, a string, a number, a boolean, `nil`, `dkJson.null` or any object with a function `__tojson` in its metatable. A table can only use strings and numbers as keys and its values have to be valid objects as well. It raises an error for any invalid data types or reference cycles. `state` is an optional table with the following fields: - `indent` When `indent` (a boolean) is set, the created string will contain newlines and indentations. Otherwise it will be one long line. - `keyorder` `keyorder` is an array to specify the ordering of keys in the encoded output. If an object has keys which are not in this array they are written after the sorted keys. - `level` This is the initial level of indentation used when `indent` is set. For each level two spaces are added. When absent it is set to 0. - `buffer` `buffer` is an array to store the strings for the result so they can be concatenated at once. When it isn't given, the encode function will create it temporary and will return the concatenated result. - `bufferlen` When `bufferlen` is set, it has to be the index of the last element of `buffer`. - `tables` `tables` is a set to detect reference cycles. It is created temporary when absent. Every table that is currently processed is used as key, the value is `true`. When `state.buffer` was set, the return value will be `true` on success. Without `state.buffer` the return value will be a string. `dkJson.decode (string [, position [, null]])` -------------------------------------------- Decode `string` starting at `position` or at 1 if `position` was omitted. `null` is an optional value to be returned for null values. The default is `nil`, but you could set it to `dkJson.null` or any other value. The return values are the object or `nil`, the position of the next character that doesn't belong to the object, and in case of errors an error message. Two metatables are created. Every array or object that is decoded gets a metatable with the `__jsontype` field set to either `array` or `object`. If you want to provide your own metatables use the syntax dkJson.decode (string, position, null, objectmeta, arraymeta) `.__jsonorder` ------------------------- `__jsonorder` can overwrite the `keyorder` for a specific table. `.__jsontype` ------------------------ `__jsontype` can be either `"array"` or `"object"`. This is mainly useful for tables that can be empty. (The default for empty tables is `"array"`). `.__tojson (self, state)` ------------------------------------ You can provide your own `__tojson` function in a metatable. In this function you can either add directly to the buffer and return true, or you can return a string. On errors nil and a message should be returned. `dkJson.null` ----------- You can use this value for setting explicit `null` values. `dkJson.version` -------------- Set to `"dkjson 2.1"`. `dkJson.quotestring (string)` --------------------------- Quote a UTF-8 string and escape critical characters using JSON escape sequences. This function is only necessary when you build your own `__tojson` functions. `dkJson.addnewline (state)` ------------------------- When `state.indent` is set, add a newline to `state.buffer` and spaces according to `state.level`. LPeg support ------------ When the local configuration variable `always_try_using_lpeg` is set, this module tries to load LPeg to replace the functions `quotestring` and `decode`. The speed increase is significant. You can get the LPeg module at . When LPeg couldn't be loaded, the pure Lua functions stay active. In case you don't want this module to require LPeg on its own, disable this option: --]==] local always_try_using_lpeg = false --[==[ In this case you can later load LPeg support using ### `dkJson.use_lpeg ()` Require the LPeg module and replace the functions `quotestring` and and `decode` with functions that use LPeg patterns. This function returns the module table, so you can load the module using: json = require "dkjson".use_lpeg() Alternatively you can use `pcall` so the JSON module still works when LPeg isn't found. json = require "dkjson" pcall (dkJson.use_lpeg) ### `dkJson.using_lpeg` This variable is set to `true` when LPeg was loaded successfully. You can contact the author by sending an e-mail to 'kolf' at the e-mail provider 'gmx.de'. --------------------------------------------------------------------- *Copyright (C) 2010, 2011 David Heiko Kolf* 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. --[[ Copyright 2012 Alexandru-Mihai Maftei aka Vercas You may use this for any purpose as long as: - You don't remove this copyright notice. - You don't claim this to be your own. - You properly credit the author (Vercas) if you publish your work based on (and/or using) this. If you modify the code for any purpose, the above obligations still apply. Instead of copying this code over for sharing, rather use the link: https://dl.dropbox.com/u/1217587/GMod/Lua/von.lua If you disagree with the above, don't use the code. ----------------------------------------------------------------------------------------------------------------------------- Thanks to the following people for their contribution: - Divran Suggested improvements for making the code quicker. Suggested an excellent new way of deserializing strings. - pennerlord Provided some performance tests to help me improve the code. --]] local _deserialize, _serialize, _d_meta, _s_meta, d_findVariable, s_anyVariable local sub, gsub, find, insert, concat, error, tonumber, tostring, type, next = string.sub, string.gsub, string.find, table.insert, table.concat, error, tonumber, tostring, type, next -- This is kept away from the table for speed. function d_findVariable(s, i, len, lastType) local i, c, typeRead, val = i or 1 -- Keep looping through the string. while true do -- Stop at the end. Throw an error. This function MUST NOT meet the end! if i > len then error("vON: Reached end of string, cannot form proper variable.") end -- Cache the character. Nobody wants to look for the same character ten times. c = sub(s, i, i) -- If it just read a type definition, then a variable HAS to come after it. if typeRead then -- Attempt to deserialize a variable of the freshly read type. val, i = _deserialize[lastType](s, i, len) -- Return the value read, the index of the last processed character, and the type of the last read variable. return val, i, lastType -- @ means nil. It should not even appear in the output string of the serializer. Nils are useless to store. elseif c == "@" then return nil, i, lastType -- n means a number will follow. Base 10... :C elseif c == "n" then lastType = "number" typeRead = true -- b means boolean flags. elseif c == "b" then lastType = "boolean" typeRead = true -- " means the start of a string. elseif c == "\"" then lastType = "string" typeRead = true -- { means the start of a table! elseif c == "{" then lastType = "table" typeRead = true -- If no type has been found, attempt to deserialize the last type read. elseif lastType then val, i = _deserialize[lastType](s, i, len) return val, i, lastType -- This will occur if the very first character in the vON code is wrong. else error("vON: Malformed data... Can't find a proper type definition. Char#" .. i .. ":" .. c) end -- Move the pointer one step forward. i = i + 1 end end -- This is kept away from the table for speed. -- Yeah, crapload of parameters. function s_anyVariable(data, lastType, isNumeric, isKey, isLast, nice, indent) -- Basically, if the type changes. if lastType ~= type(data) then -- Remember the new type. Caching the type is useless. lastType = type(data) -- Return the serialized data and the (new) last type. -- The second argument, which is true now, means that the data type was just changed. return _serialize[lastType](data, true, isNumeric, isKey, isLast, nice, indent), lastType end -- Otherwise, simply serialize the data. return _serialize[lastType](data, false, isNumeric, isKey, isLast, nice, indent), lastType end _deserialize = { -- Well, tables are very loose... -- The first table doesn't have to begin and end with { and }. ["table"] = function(s, i, len, unnecessaryEnd) local ret, numeric, i, c, lastType, val, ind, expectValue, key = {}, true, i or 1 -- Locals, locals, locals, locals, locals, locals, locals, locals and locals. -- Keep looping. while true do -- Until it meets the end. if i > len then -- Yeah, if the end is unnecessary, it won't spit an error. The main chunk doesn't require an end, for example. if unnecessaryEnd then return ret, i -- Otherwise, the data has to be damaged. else error("vON: Reached end of string, incomplete table definition.") end end -- Cache the character. c = sub(s,i,i) --print(i, "table char:", c, tostring(unnecessaryEnd)) -- If it's the end of a table definition, return. if c == "}" then return ret, i -- If it's the component separator, switch to key:value pairs. elseif c == "~" then numeric = false elseif c == ";" then -- Lol, nothing! -- Remenant from numbers, for faster parsing. -- OK, now, if it's on the numeric component, simply add everything encountered. elseif numeric then -- Find a variable and it's value val, i, lastType = d_findVariable(s, i, len, lastType) -- Add it to the table. ret[#ret + 1] = val -- Otherwise, if it's the key:value component... else -- If a value is expected... if expectValue then -- Read it. val, i, lastType = d_findVariable(s, i, len, lastType) -- Add it? ret[key] = val -- Clean up. expectValue, key = false, nil -- If it's the separator... elseif c == ":" then -- Expect a value next. expectValue = true -- But, if there's a key read already... elseif key then -- Then this is malformed. error("vON: Malformed table... Two keys declared successively? Char#" .. i .. ":" .. c) -- Otherwise the key will be read. else -- I love multi-return and multi-assignement. key, i, lastType = d_findVariable(s, i, len, lastType) end end i = i + 1 end return nil, i end, -- Numbers are weakly defined. -- The declaration is not very explicit. It'll do it's best to parse the number. -- Has various endings: \n, }, ~, : and ;, some of which will force the table deserializer to go one char backwards. ["number"] = function(s, i, len) local i, a = i or 1 -- Locals, locals, locals, locals a = find(s, "[;:}~]", i) if a then return tonumber(sub(s, i, a - 1)), a - 1 end error("vON: Number definition started... Found no end.") end, -- A boolean is A SINGLE CHARACTER, either 1 for true or 0 for false. -- Any other attempt at boolean declaration will result in a failure. ["boolean"] = function(s, i, len) local c = sub(s,i,i) -- Only one character is needed. -- If it's 1, then it's true if c == "1" then return true, i -- If it's 0, then it's false. elseif c == "0" then return false, i end -- Any other supposely "boolean" is just a sign of malformed data. error("vON: Invalid value on boolean type... Char#" .. i .. ": " .. c) end, -- Strings are very easy to parse and also very explicit. -- " simply marks the type of a string. -- Then it is parsed until an unescaped " is countered. ["string"] = function(s, i, len) local res, i, a = "", i or 1 -- Locals, locals, locals, locals while true do a = find(s, "\"", i, true) if a then if sub(s, a - 1, a - 1) == "\\" then res = res .. sub(s, i, a - 2) .. "\"" i = a + 1 else return res .. sub(s, i, a - 1), a end else error("vON: String definition started... Found no end.") end end end } _serialize = { -- Uh. Nothing to comment. -- Shitload of parameters. -- Makes shit faster than simply passing it around in locals. -- table.concat works better than normal concatenations WITH LARGE-ISH STRINGS ONLY. ["table"] = function(data, mustInitiate, isNumeric, isKey, isLast, first) --print(string.format("data: %s; mustInitiate: %s; isKey: %s; isLast: %s; nice: %s; indent: %s; first: %s", tostring(data), tostring(mustInitiate), tostring(isKey), tostring(isLast), tostring(nice), tostring(indent), tostring(first))) local result, keyvals, len, keyvalsLen, keyvalsProgress, val, lastType, newIndent, indentString = {}, {}, #data, 0, 0 -- Locals, locals, locals, locals, locals, locals, locals, locals, locals and locals. -- First thing to be done is separate the numeric and key:value components of the given table in two tables. -- pairs(data) is slower than next, data as far as my tests tell me. for k, v in next, data do -- Skip the numeric keyz. if type(k) ~= "number" or k < 1 or k > len then keyvals[#keyvals + 1] = k end end keyvalsLen = #keyvals -- Main chunk - no initial character. if not first then result[#result + 1] = "{" end -- Add numeric values. if len > 0 then for i = 1, len do val, lastType = s_anyVariable(data[i], lastType, true, false, i == len and not first, false, 0) result[#result + 1] = val end end -- If there are key:value pairs. if keyvalsLen > 0 then -- Insert delimiter. result[#result + 1] = "~" -- Insert key:value pairs. for _i = 1, keyvalsLen do keyvalsProgress = keyvalsProgress + 1 val, lastType = s_anyVariable(keyvals[_i], lastType, false, true, false, false, 0) result[#result + 1] = val..":" val, lastType = s_anyVariable(data[keyvals[_i]], lastType, false, false, keyvalsProgress == keyvalsLen and not first, false, 0) result[#result + 1] = val end end -- Main chunk needs no ending character. if not first then result[#result + 1] = "}" end return concat(result) end, -- Normal concatenations is a lot faster with small strings than table.concat -- Also, not so branched-ish. ["number"] = function(data, mustInitiate, isNumeric, isKey, isLast) -- If a number hasn't been written before, add the type prefix. if mustInitiate then if isKey or isLast then return "n"..data else return "n"..data..";" end end if isKey or isLast then return "n"..data else return "n"..data..";" end end, -- I hope gsub is fast enough. ["string"] = function(data, mustInitiate, isNumeric, isKey, isLast) return "\"" .. gsub(data, "\"", "\\\"") .. "\"" end, -- Fastest. ["boolean"] = function(data, mustInitiate, isNumeric, isKey, isLast) -- Prefix if we must. if mustInitiate then if data then return "b1" else return "b0" end end if data then return "1" else return "0" end end, -- Fastest. ["nil"] = function(data, mustInitiate, isNumeric, isKey, isLast) return "@" end } local _s_table = _serialize.table local _d_table = _deserialize.table _d_meta = { __call = function(self, str) return _d_table(str, nil, #str, true) end } _s_meta = { __call = function(self, data, nice, indent) return _s_table(data, nil, nil, nil, nil, true) end } von = {} von.deserialize = setmetatable(_deserialize,_d_meta) von.serialize = setmetatable(_serialize,_s_meta) --[[ Last example from: http://www.json.org/example.html Converted to a Lua table, because this JSON module actually has trouble decoding that text! --]] local test6 = { menu = { header = "SVG Viewer", items = { { id = "Open" }, { id = "OpenNew", label = "Open New" }, nil, { id = "ZoomIn", label = "Zoom In" }, { id = "ZoomOut", label = "Zoom Out" }, { id = "OriginalView", label = "Original View" }, nil, { id = "Quality" }, { id = "OpenNew" }, { id = "Mute" }, nil, { id = "Find", label = "Find..." }, { id = "FindAgain", label = "Find Again" }, { id = "Copy" }, { id = "CopyAgain", label = "Copy Again" }, { id = "CopySVG", label = "Copy SVG" }, { id = "ViewSVG", label = "View SVG" }, { id = "ViewSource", label = "View Source" }, { id = "SaveAs", label = "Save As" }, nil, { id = "Help" }, { id = "About", label = "About Adobe CVG Viewer..." } } } } require("socket") local now = socket.gettime local Gencode, Gdecode = encode, decode local startT = now() local last = test6 for i = 1, 5000 do local s = Gencode(last) last = Gdecode(s) end a = now() - startT aSize = #Gencode(last) local Jencode, Jdecode = dkJson.encode, dkJson.decode startT = now() local last = test6 for i = 1, 5000 do local s = Jencode(last) last = Jdecode(s) end b = now() - startT bSize = #Jencode(last) local Vencode, Vdecode = von.serialize, von.deserialize startT = now() local last = test6 for i = 1, 5000 do local s = Vencode(last) last = Vdecode(s) end c = now() - startT cSize = #Vencode(last) print([[GLON: 5000 encoding/decoding successions took ]]..a..[[ seconds to finish. Length of final (probably overmutilated) data: ]]..aSize..[[. JSON: 5000 encoding/decoding successions took ]]..b..[[ seconds to finish. Length of the final (100% mutilated) data: ]]..bSize..[[. vON: 5000 encoding/decoding successions took ]]..c..[[ seconds to finish. Length of the final (100% healthy) data: ]]..cSize..[[.]])