--[[ -- Copyright Lorenzo Leonini --]] --[[ Socket wrapper to add llenconding capabilities (send and receive whatever you want, including (flat) arrays). For more complexes structure use json. You can use this module like a socket wrapper using wrap() or calling the functions directly. --]] local string = require"string" local tostring = tostring local setmetatable = setmetatable local pairs = pairs local type = type local print = print module("llenc") function enc(data) local out = "" -- If data is a table, recursivly encode values if type(data) == "table" then for _, d in pairs(data) do out = out..enc(d) end else data = tostring(data) local dl = #data if dl >= 10 then -- Case 2 local t_dl = string.format("%d", dl) local t_ll = string.char(#t_dl + 64) out = t_ll .. t_dl .. data else -- Case 1 out = string.format("%d", dl)..data end end return out end function send(socket, data) return socket:send(enc(data)) end function receive_one(socket) local dl = 0 -- Data length local l, status = socket:receive(1) if not l then return nil, status end l = string.byte(l) if l >= 48 and l <= 57 then -- Case 1 dl = l - 48 else -- Case 2 if l >= 65 and l <= 90 then l, status = socket:receive(l - 64) if not l then return nil, status end for i = 1, #l do dl = dl * 10 + (string.byte(string.sub(l, i, i)) - 48) end else return nil, "Invalid llenc:", l, string.byte(l) end end return socket:receive(dl) end function receive(socket, arg) if arg == nil then return receive_one(socket) end local r = {} if type(arg) == "number" then while #r < arg do local d, err = receive_one(socket) -- even 1 error, we return only the error and not an array if not d then return nil, err end r[#r + 1] = d end else return nil, "bad parameter" end return r end -- Object wrapper -- Use only with ':' methods or xxx.super:method() if you want to use the -- original one. function wrap(object, err) -- error forwarding if not object then return nil, err end local wrap_obj = {} wrap_obj.super = object mt = {} -- This object is called with ':', so the 'self' refer to him but, in -- the call, self is the wrapping table, we need to replace it by the object. mt.__index = function(table, key) return function(self, ...) return object[key](object, ...) end end setmetatable(wrap_obj, mt) wrap_obj.send = function(self, data) return send(self.super, data) end wrap_obj.receive = function(self, number) return receive(self.super, number) end -- to verify what is really our object wrap_obj.test = function(self) if self.super.test then return "[".._NAME.."] -> "..self.super.test() else return "[".._NAME.."]" end end return wrap_obj end