PGRData/Script/launch/xlaunchcommon/XMessagePack.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