local bit = require("luaScript.ModuleEapSdk.EapFunc.websocket.bit") -- local ffi = require'ffi' local proto = {} -- return base 128 varint string function proto.serializeVarint(i) assert(math.fmod(i, 1) == 0) local result = {} while true do local b = bit.band(i, 0x7F) i = bit.rshift(i, 7) if i ~= 0 then -- has more table.insert(result, bit.bor(0x80, b)) else table.insert(result, b) break end end return string.char(unpack(result)) end -- return value and next index function proto.parseVarint(buff, index) assert(type(buff) == "string" and index > 0) local result = 0; local current = index while true do local thisByte = string.byte(buff, current) local thisByte2 = bit.band(0x7F, thisByte) result = bit.bor(result, bit.lshift(thisByte2, 7*(current-index))) current = current + 1 if bit.band(thisByte, 0x80) == 0 then break end end return result, current end -- ffi.cdef[[ -- union bar { uint8_t b[8]; double d; }; -- union bar1 { uint8_t b[4]; float d; }; -- union myuint64 { uint8_t b[8]; uint64_t d; }; -- ]] --- return 8-byte string with same memory layout from double "d" function proto.serializeDouble(dbl) -- local u = ffi.new("union bar", {d=dbl}) -- local v = u.b -- return string.char(v[0], v[1], v[2], v[3], -- v[4], v[5], v[6], v[7]) end function proto.parseDouble(buff, index) -- assert(index+7 <= #buff) -- local u = ffi.new("union bar") -- local v = u.b -- v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7] = string.byte(buff, index, index+7) -- return u.d, index+8 end function proto.parseFloat(buff, index) -- assert(index+3 <= #buff) -- local u = ffi.new("union bar1") -- local v = u.b -- v[0], v[1], v[2], v[3] = string.byte(buff, index, index+3) -- return u.d, index+4 local h1,h2,h3,h4 = string.byte(buff, index, index+3) print("proto.parseFloat = ", h1, h2, h3, h4) local hexNum = bit.lshift(h4, 24) + bit.lshift(h3, 16) + bit.lshift(h2, 8) + h1 -- local hexNum = bit.lshift(h1, 24) + bit.lshift(h2, 16) + bit.lshift(h3, 8) + h4 local res = proto.DatToFloat(hexNum) return res, index+4 end -- return tag, wire type function proto.extractKey(k) return bit.rshift(k, 3), bit.band(k, 0x7) end function proto.findName(tag, prt) for k, v in pairs(prt) do if v[1] == tag then return k end end return nil end -- tbl: table value to be serialized -- prt: protobuf description table -- return: a string with serialized value -- -- detailed mapping -- lua type | prontobuf type -- string | length-delimited (1) -- double | 64-bit (2) -- int | varint (0) -- note, there is no "int" in lua, -- only when math.fmod(x, 1) == 0 function proto.serialize(tbl, prt) assert(tbl and prt) local result = {} for k, v in pairs(tbl) do local isRepeat = false -- repeated string like local t = type(v) local tag = prt[k][1] local ext = prt[k][3] if not tag then assert(false, "can't find " .. k .. " in proto") end local key, value if t == "string" then key = bit.bor(bit.lshift(tag, 3), 2) key = proto.serializeVarint(key) value = proto.serializeVarint(#v) .. v elseif t == "number" then if math.fmod(v, 1) == 0 then -- varint if prt[k][2] == "sint32" or prt[k][2] == "sint64" then v = 2*v if v < 0 then -- v = v - 1 v = math.abs(v)-1 end end key = bit.bor(bit.lshift(tag, 3), 0) key = proto.serializeVarint(key) value = proto.serializeVarint(v) else -- double key = bit.bor(bit.lshift(tag, 3), 1) key = proto.serializeVarint(key) value = proto.serializeDouble(v) end elseif t == "boolean" then key = bit.bor(bit.lshift(tag, 3), 0) key = proto.serializeVarint(key) value = proto.serializeVarint(1) elseif t == "table" then if ext and ext == "repeated" then print("proto serialize vector packed") if #v > 0 then local subType = type(v[1]) if subType == "number" then local zigZag = false if prt[k][2] == "sint32" or prt[k][2] == "sint64" then zigZag = true end local v_totalLen = 0 for k2=1, #v do local v2 = v[k2] if zigZag then v2 = 2*v2 if v2 < 0 then v2 = math.abs(v2)-1 end end local itemValue = proto.serializeVarint(v2) v_totalLen = v_totalLen + #itemValue end local itemValue = proto.serializeVarint(v[1]) key = bit.bor(bit.lshift(tag, 3), 2) key = proto.serializeVarint(key) value = proto.serializeVarint(v_totalLen) for k1, v1 in pairs(v) do if zigZag then v1 = 2*v1 if v1 < 0 then v1 = math.abs(v1)-1 end end local tmpValue = proto.serializeVarint(v1) value = value..tmpValue end elseif subType == "string" then isRepeat = true for k1, v1 in pairs(v) do key = bit.bor(bit.lshift(tag, 3), 2) key = proto.serializeVarint(key) value = proto.serializeVarint(#v1) .. v1 table.insert(result, key .. value) end end end else local subPrt = G_EapProtos[prt[k][2]] -- embedded messages if subPrt then local v1 = proto.serialize(v, subPrt) key = bit.bor(bit.lshift(tag, 3), 2) key = proto.serializeVarint(key) value = proto.serializeVarint(#v1) .. v1 else print("can't serilize table embedded " .. prt[k][2]) end end else print(false, "can't support " .. t) end if not isRepeat then table.insert(result, key .. value) end end return table.concat(result) end -- str: a string value with serialized value -- prt: protobuf -- return: a parsed table value function proto.parse(str, prt) local result = {} local len = string.len(str) local index = 1 while index <= len do local key key, index = proto.parseVarint(str, index) local tag, wire_type = proto.extractKey(key) local value local tagName = proto.findName(tag, prt) local prtInfo = prt[tagName] if wire_type == 0 then if prtInfo then value, index = proto.parseVarint(str, index) end elseif wire_type == 1 then value, index = proto.parseDouble(str, index) elseif wire_type == 2 then local len len, index = proto.parseVarint(str, index) value = string.sub(str, index, index+len-1) index = index + len elseif wire_type == 5 then value, index = proto.parseFloat(str, index) else print(false, "doesn't support wire type " .. wire_type) end -- set pair in table if prtInfo then if prtInfo[2] == "sint32" or prtInfo[2] == "sint64" then if type(value) == "number" then if value%2 == 0 then value = value/2 else value = -(value+1)/2 end end else local subPrt = G_EapProtos[prtInfo[2]] -- embedded messages if subPrt then value = proto.parse(value, subPrt) end end -- repeated embedded messages if prtInfo[3] and prtInfo[3] == "repeated" then result[tagName] = result[tagName] or {} end end if tagName and value then if type(result[tagName]) == "table" then result[tagName][#result[tagName] + 1] = value else result[tagName] = value end else print(false, "can't find the name : ") end end return result end function proto.int16ToBufStr(num) local str = ""; str = str .. string.char(bit.rshift(num, 8)); str = str .. string.char(bit.band(num, 0x00FF)); return str; end function proto.buffToInt16(num1, num2) return bit.lshift(num1, 8) + num2 end function proto.hexToFloat( hexNums ) local sign = math.modf(hexNums/(2^31)) local exponent = hexNums % (2^31) exponent = math.modf(exponent/(2^23)) -127 local mantissa = hexNums % (2^23) for i=1,23 do mantissa = mantissa / 2 end mantissa = 1+mantissa local result = (-1)^sign * mantissa * 2^exponent return result end function DatToFloat1(x) local temp local aa = 8388608 local s = bit.rshift(bit.band(x, 0x80000000), 31)--(x & 0x80000000)>>31 local e = bit.rshift(bit.band(x, 0x7F800000), 23)--(x & 0x7F800000)>>23 temp = bit.band(x, 0X7FFFFF) / aa--(x&0X7FFFFF)/aa local res = bit.lshift(e-127, 1) * (1+temp)--(1<<(e-127)) * (1+temp) if s==1 then res = 0-res end return res end function pow(n) local temp =1 for m =1, n do temp = temp * 0.5 end return temp end function DatToFloat2(x) local s = bit.rshift(bit.band(x, 0x80000000), 31)--(x & 0x80000000)>>31 local e = bit.rshift(bit.band(x, 0x7F800000), 23)--(x & 0x7F800000)>>23 local m = 127-e local temp = pow(m) local weishu = bit.band(x, 0x7FFFFF)--x & 0x7FFFFF local sum = 0 local j = 23+m local weishutemp = bit.bxor(weishu, bit.lshift(0x1, 23))--weishu ~ (0x1<<23) for m = 1, 24 do bitdat = bit.band(weishutemp, 0x1)--weishutemp & 0x1 if bitdat == 1 then sum = sum + pow(j) end weishutemp = bit.rshift(weishutemp, 1)--weishutemp >>1 j = j -1 end if s==1 then sum = 0-sum end return sum end function proto.DatToFloat(x) local result local e = bit.rshift(bit.band(x, 0x7F800000), 23)--(x & 0x7F800000)>>23 if e==255 and bit.band(x, 0x7FFFFF) == 0 then return "InF" end if e==255 and bit.band(x, 0x7FFFFF) ~= 0 then return "NaN" end if e>=128 then result = DatToFloat1(x) else result = DatToFloat2(x) end return result end return proto