forked from endernon/PGRData
293 lines
9.2 KiB
Lua
293 lines
9.2 KiB
Lua
local type = type
|
|
local pairs = pairs
|
|
local pcall = pcall
|
|
local select = select
|
|
local table = table
|
|
local string = string
|
|
local math = math
|
|
local utf8 = utf8
|
|
|
|
local tableConcat = table.concat
|
|
local tableInsert = table.insert
|
|
local tableUnpack = table.unpack
|
|
local stringPack = string.pack
|
|
local stringUnpack = string.unpack
|
|
local stringFormat = string.format
|
|
local mathType = math.type
|
|
local utf8Len = utf8.len
|
|
|
|
local decoderTable
|
|
|
|
local function Unpack(context, format)
|
|
local value, position = stringUnpack(format, context.input, context.position)
|
|
context.position = position
|
|
return value
|
|
end
|
|
|
|
local function DecodeNext(context)
|
|
return decoderTable[Unpack(context, ">B")](context)
|
|
end
|
|
|
|
local function DecodeArray(context, length)
|
|
local array = {}
|
|
for i = 1, length do
|
|
array[i] = DecodeNext(context)
|
|
end
|
|
return array
|
|
end
|
|
|
|
local function DecodeMap(context, length)
|
|
local map = {}
|
|
for _ = 1, length do
|
|
local k = DecodeNext(context)
|
|
local v = DecodeNext(context)
|
|
map[k] = v
|
|
end
|
|
return map
|
|
end
|
|
|
|
decoderTable = {
|
|
[192] = function() return nil end,
|
|
[194] = function() return false end,
|
|
[195] = function() return true end,
|
|
[196] = function(context) return Unpack(context, ">s1") end,
|
|
[197] = function(context) return Unpack(context, ">s2") end,
|
|
[198] = function(context) return Unpack(context, ">s4") end,
|
|
[202] = function(context) return Unpack(context, ">f") end,
|
|
[203] = function(context) return Unpack(context, ">d") end,
|
|
[204] = function(context) return Unpack(context, ">I1") end,
|
|
[205] = function(context) return Unpack(context, ">I2") end,
|
|
[206] = function(context) return Unpack(context, ">I4") end,
|
|
[207] = function(context) return Unpack(context, ">I8") end,
|
|
[208] = function(context) return Unpack(context, ">i1") end,
|
|
[209] = function(context) return Unpack(context, ">i2") end,
|
|
[210] = function(context) return Unpack(context, ">i4") end,
|
|
[211] = function(context) return Unpack(context, ">i8") end,
|
|
[217] = function(context) return Unpack(context, ">s1") end,
|
|
[218] = function(context) return Unpack(context, ">s2") end,
|
|
[219] = function(context) return Unpack(context, ">s4") end,
|
|
[220] = function(context) return DecodeArray(context, Unpack(context, ">I2")) end,
|
|
[221] = function(context) return DecodeArray(context, Unpack(context, ">I4")) end,
|
|
[222] = function(context) return DecodeMap(context, Unpack(context, ">I2")) end,
|
|
[223] = function(context) return DecodeMap(context, Unpack(context, ">I4")) end,
|
|
}
|
|
|
|
-- add single byte integers
|
|
for i = 0, 127 do
|
|
decoderTable[i] = function() return i end
|
|
end
|
|
for i = 224, 255 do
|
|
decoderTable[i] = function() return -32 + i - 224 end
|
|
end
|
|
|
|
-- add fixed maps
|
|
for i = 128, 143 do
|
|
decoderTable[i] = function(context) return DecodeMap(context, i - 128) end
|
|
end
|
|
|
|
-- add fixed arrays
|
|
for i = 144, 159 do
|
|
decoderTable[i] = function(context) return DecodeArray(context, i - 144) end
|
|
end
|
|
|
|
-- add fixed strings
|
|
for i = 160, 191 do
|
|
local format = stringFormat(">c%d", i - 160)
|
|
decoderTable[i] = function(context) return Unpack(context, format) end
|
|
end
|
|
|
|
local encoderTable
|
|
|
|
local function EncodeValue(value)
|
|
return encoderTable[type(value)](value)
|
|
end
|
|
|
|
local function CheckArray(value) -- simple function to verify a table is a proper array
|
|
local count = 0
|
|
for k in pairs(value) do
|
|
if type(k) ~= "number" then
|
|
return false
|
|
else
|
|
count = count + 1
|
|
end
|
|
end
|
|
for i = 1, count do
|
|
if not value[i] and type(value[i]) ~= "nil" then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
encoderTable = {
|
|
["nil"] = function()
|
|
return stringPack(">B", 192)
|
|
end,
|
|
|
|
["boolean"] = function(value)
|
|
return stringPack(">B", value and 195 or 194)
|
|
end,
|
|
|
|
["string"] = function(value)
|
|
local length = #value
|
|
if utf8Len(value) then -- valid UTF-8 ... encode as string
|
|
if length < 32 then
|
|
return stringPack(">B", 160 + length) .. value
|
|
elseif length <= 255 then
|
|
return stringPack(">B s1", 217, value)
|
|
elseif length <= 65535 then
|
|
return stringPack(">B s2", 218, value)
|
|
else
|
|
return stringPack(">B s4", 219, value)
|
|
end
|
|
else -- encode as binary
|
|
if length <= 255 then
|
|
return stringPack(">B s1", 196, value)
|
|
elseif length <= 65535 then
|
|
return stringPack(">B s2", 197, value)
|
|
else
|
|
return stringPack(">B s4", 198, value)
|
|
end
|
|
end
|
|
end,
|
|
|
|
["number"] = function(value)
|
|
if mathType(value) == "integer" then
|
|
if value >= 0 then
|
|
if value <= 127 then
|
|
return stringPack(">B", value)
|
|
elseif value <= 255 then
|
|
return stringPack(">B I1", 204, value)
|
|
elseif value <= 65535 then
|
|
return stringPack(">B I2", 205, value)
|
|
elseif value <= 4294967295 then
|
|
return stringPack(">B I4", 206, value)
|
|
else
|
|
return stringPack(">B I8", 207, value)
|
|
end
|
|
else
|
|
if value >= -32 then
|
|
return stringPack(">B", 224 + value + 32)
|
|
elseif value >= -127 then
|
|
return stringPack(">B i1", 208, value)
|
|
elseif value >= -32767 then
|
|
return stringPack(">B i2", 209, value)
|
|
elseif value >= -2147483647 then
|
|
return stringPack(">B i4", 210, value)
|
|
else
|
|
return stringPack(">B i8", 211, value)
|
|
end
|
|
end
|
|
else
|
|
local converted = stringUnpack(">f", stringPack(">f", value))
|
|
if converted == value then
|
|
return stringPack(">B f", 202, value)
|
|
else
|
|
return stringPack(">B d", 203, value)
|
|
end
|
|
end
|
|
end,
|
|
|
|
["table"] = function(value)
|
|
local meta = getmetatable(value)
|
|
if CheckArray(value) and (meta == nil or not meta.IsTable) then
|
|
local elements = {}
|
|
for i, v in pairs(value) do
|
|
elements[i] = EncodeValue(v)
|
|
end
|
|
|
|
local length = #elements
|
|
if length == 0 then
|
|
return stringPack(">B", 192)
|
|
end
|
|
|
|
if length <= 15 then
|
|
return stringPack(">B", 144 + length) .. tableConcat(elements)
|
|
elseif length <= 65535 then
|
|
return stringPack(">B I2", 220, length) .. tableConcat(elements)
|
|
else
|
|
return stringPack(">B I4", 221, length) .. tableConcat(elements)
|
|
end
|
|
else
|
|
local elements = {}
|
|
for k, v in pairs(value) do
|
|
elements[#elements + 1] = EncodeValue(k)
|
|
elements[#elements + 1] = EncodeValue(v)
|
|
end
|
|
|
|
local length = #elements // 2
|
|
if length == 0 then
|
|
return stringPack(">B", 192)
|
|
end
|
|
|
|
if length <= 15 then
|
|
return stringPack(">B", 128 + length) .. tableConcat(elements)
|
|
elseif length <= 65535 then
|
|
return stringPack(">B I2", 222, length) .. tableConcat(elements)
|
|
else
|
|
return stringPack(">B I4", 223, length) .. tableConcat(elements)
|
|
end
|
|
end
|
|
end,
|
|
}
|
|
|
|
XMessagePack = XMessagePack or {}
|
|
|
|
function XMessagePack.Encode(value)
|
|
local ok, result = pcall(EncodeValue, value)
|
|
if ok then
|
|
return result
|
|
else
|
|
return nil, stringFormat("XMessagePack cannot encode type %s", type(value))
|
|
end
|
|
end
|
|
|
|
function XMessagePack.EncodeAll(...)
|
|
local result = {}
|
|
for i = 1, select("#", ...) do
|
|
local data, error = XMessagePack.Encode(select(i, ...))
|
|
if data then
|
|
tableInsert(result, data)
|
|
else
|
|
return nil, error
|
|
end
|
|
end
|
|
return tableConcat(result)
|
|
end
|
|
|
|
function XMessagePack.Decode(input, position)
|
|
local context = { input = input, position = position or 1 }
|
|
local ok, result = pcall(DecodeNext, context)
|
|
if ok then
|
|
return result, context.position
|
|
else
|
|
return nil, stringFormat("XMessagePack cannot decode position %d", context.position)
|
|
end
|
|
end
|
|
|
|
function XMessagePack.DecodeAll(input, position)
|
|
local context = { input = input, position = position or 1 }
|
|
local result = {}
|
|
while context.position <= #context.input do
|
|
local ok, value = pcall(DecodeNext, context)
|
|
if ok then
|
|
tableInsert(result, value)
|
|
else
|
|
return nil, stringFormat("XMessagePack cannot decode position %d", context.position)
|
|
end
|
|
end
|
|
return tableUnpack(result)
|
|
end
|
|
|
|
function XMessagePack.MarkAsTable(value)
|
|
if value == nil then
|
|
CS.XLog.Error("XMessagePack.MarkAsTable 函数错误, 参数value不能为空")
|
|
return
|
|
end
|
|
if type(value) ~= "table" then
|
|
local errorInfo = "XMessagePack.MarkAsTable 函数错误, 参数value的类型需要是table类型的, value的类型是" .. type(value)
|
|
CS.XLog.Error(errorInfo)
|
|
return
|
|
end
|
|
setmetatable(value, {IsTable = true})
|
|
end
|