528 lines
No EOL
12 KiB
Lua
528 lines
No EOL
12 KiB
Lua
local BinaryTable = {}
|
|
local Binary = require("Binary/Binary")
|
|
local tableEmpty = {}
|
|
|
|
local BinaryPool = {}
|
|
local BINARY_LIMIT_COUNT = 10
|
|
|
|
local Reader = require("Binary/Reader")
|
|
|
|
|
|
local DefaultOfTypeNew = {
|
|
[1] = false,
|
|
[2] = nil,
|
|
[3] = fix.zero,
|
|
[4] = tableEmpty,
|
|
[5] = tableEmpty,
|
|
[6] = tableEmpty,
|
|
[7] = tableEmpty,
|
|
[8] = tableEmpty,
|
|
[9] = tableEmpty,
|
|
[10] = tableEmpty,
|
|
[11] = tableEmpty,
|
|
[12] = tableEmpty,
|
|
[13] = tableEmpty,
|
|
[14] = 0,
|
|
[15] = 0,
|
|
}
|
|
|
|
|
|
function BinaryTable.New(path)
|
|
local temp = {}
|
|
setmetatable(temp, { __index = BinaryTable })
|
|
temp:Ctor(path)
|
|
return temp
|
|
end
|
|
|
|
function BinaryTable:Ctor(path)
|
|
self.filePath = path
|
|
self.canRead = false
|
|
end
|
|
|
|
--读取全部
|
|
function BinaryTable.ReadAll(path, identifier)
|
|
local temp = BinaryTable.New(path)
|
|
|
|
if not temp or not temp:InitBinary() then
|
|
return
|
|
end
|
|
|
|
local tab = temp:ReadAllContent(identifier)
|
|
|
|
temp:Release(true)
|
|
temp = nil
|
|
|
|
return tab
|
|
end
|
|
|
|
--读取句柄
|
|
function BinaryTable.ReadHandle(path)
|
|
local temp = BinaryTable.New(path)
|
|
|
|
if not temp or not temp:InitBinary() then
|
|
return
|
|
end
|
|
|
|
return temp
|
|
end
|
|
|
|
|
|
|
|
function BinaryTable:InitBinary()
|
|
|
|
local bytes = CS.BinaryManager.LoadBytes(self.filePath)
|
|
|
|
if not bytes then
|
|
XLog.Error(string.format("BinaryTable.InitBinary 加载文件失败 %s", self.filePath))
|
|
return
|
|
end
|
|
|
|
self.len = string.len(bytes)
|
|
self.bytes = bytes
|
|
local result = self:Init()
|
|
|
|
CS.PrefProfiler.ProfilerCore.RecordTableBinaryLoad(self.filePath, self.len, self.row)
|
|
|
|
return result
|
|
end
|
|
|
|
--初始化基础信息
|
|
function BinaryTable:Init()
|
|
local reader = self:GetReader()
|
|
|
|
|
|
local len = reader:ReadIntFix()
|
|
self.col = reader:ReadInt()
|
|
self.infoTrunkLen = len
|
|
|
|
self.colTypes = {}
|
|
self.colNames = {}
|
|
for i = 1, self.col do
|
|
table.insert(self.colTypes, reader:ReadInt())
|
|
local name = reader:ReadString()
|
|
table.insert(self.colNames, name)
|
|
end
|
|
|
|
local hasPrimarykey = reader:ReadBool()
|
|
self.primarykeyCount = 0
|
|
|
|
if hasPrimarykey then
|
|
self.primarykeyCount = 1
|
|
self.primarykey = reader:ReadString()
|
|
self.primarykeyLen = reader:ReadInt()
|
|
end
|
|
|
|
for i = 1, #self.colNames do
|
|
local name = self.colNames[i]
|
|
if self.primarykey == name then
|
|
self.primarykeyType = self.colTypes[i]
|
|
end
|
|
end
|
|
|
|
self.rowTrunkLen = reader:ReadInt()
|
|
self.row = reader:ReadInt()
|
|
self.contentTrunkLen = reader:ReadInt()
|
|
|
|
if not self.contentTrunkLen then
|
|
if XMain.IsDebug then
|
|
XLog.Warning(string.format("BinaryTable:InitBinary,%s, 空表", self.filePath))
|
|
end
|
|
return
|
|
end
|
|
|
|
local position = self:GetContenTrunkPosition()
|
|
|
|
reader:Close()
|
|
reader = nil
|
|
|
|
self.canRead = true
|
|
self.caches = {}
|
|
self.cachesCount = 0
|
|
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
--获取内容块
|
|
function BinaryTable:GetContentTrunkReader()
|
|
local position = self:GetContenTrunkPosition()
|
|
|
|
if position < 0 then
|
|
return
|
|
end
|
|
|
|
local reader = self:GetReader(position)
|
|
return reader
|
|
end
|
|
|
|
--获取内容块位置
|
|
function BinaryTable:GetContenTrunkPosition()
|
|
local position = self.infoTrunkLen + 4
|
|
local count = self.primarykeyCount
|
|
|
|
if count > 0 then
|
|
position = position + self.primarykeyLen
|
|
end
|
|
|
|
position = position + self.rowTrunkLen
|
|
return position
|
|
end
|
|
|
|
function BinaryTable:ReadAllContent(identifier)
|
|
|
|
local reader = self:GetContentTrunkReader()
|
|
|
|
if not reader then
|
|
XLog.Error(string.format("可能是空表 路径:%s 请检查", self.filePath))
|
|
return tableEmpty
|
|
end
|
|
|
|
local row = self.row
|
|
local col = self.col
|
|
local colType = self.colTypes
|
|
local colNames = self.colNames
|
|
|
|
local colNameIndex = {}
|
|
|
|
local index = 0
|
|
for i = 1, #colNames do
|
|
local name = colNames[i]
|
|
if name == identifier then
|
|
index = i
|
|
end
|
|
colNameIndex[name] = i
|
|
end
|
|
|
|
if index <= 0 then
|
|
XLog.Warning(string.format("找不到键值 Key:%s 请检查该键值和表头是否匹配", self.filePath))
|
|
end
|
|
|
|
---每一个表对应一个元表
|
|
local metaTable = {}
|
|
|
|
metaTable.__index = function(tbl, colName)
|
|
local idx = colNameIndex[colName]
|
|
|
|
if not idx or not tbl then
|
|
return nil
|
|
end
|
|
|
|
local result = rawget(tbl, idx)
|
|
|
|
if not result then
|
|
local resultType = colType[idx]
|
|
|
|
if not resultType then
|
|
XLog.Error(string.format("找不到键值 Key:%s 请检查该键值和表头是否匹配", colName))
|
|
end
|
|
|
|
result = DefaultOfTypeNew[resultType]
|
|
end
|
|
|
|
|
|
return result
|
|
end
|
|
|
|
metaTable.__newindex = function()
|
|
XLog.Error("attempt to update a readonly table")
|
|
end
|
|
|
|
metaTable.__metatable = "readonly table"
|
|
|
|
|
|
metaTable.__pairs = function(t)
|
|
local function stateless_iter(tbl, key)
|
|
local nk, v = next(tbl, key)
|
|
|
|
if nk and v then
|
|
local nv = t[v] or t[nk]
|
|
return nk, nv
|
|
end
|
|
end
|
|
|
|
return stateless_iter, colNameIndex, nil
|
|
end
|
|
|
|
local tab = {}
|
|
for i = 1, row do
|
|
local temp = {}
|
|
local keyValue = nil
|
|
for j = 1, col do
|
|
local type = colType[j]
|
|
local value = reader:Read(type)
|
|
temp[j] = value
|
|
if index > 0 and j == index then
|
|
keyValue = value or 0
|
|
end
|
|
|
|
end
|
|
|
|
if index == 0 then
|
|
keyValue = i
|
|
end
|
|
|
|
setmetatable(temp, metaTable)
|
|
tab[keyValue] = temp
|
|
|
|
self.caches[keyValue] = temp
|
|
CS.PrefProfiler.ProfilerCore.RecordGetTableBinaryItem(self.filePath)
|
|
end
|
|
|
|
self.cachesCount = self.row
|
|
reader:Close()
|
|
reader = nil
|
|
|
|
return tab
|
|
end
|
|
|
|
function BinaryTable:GetLength()
|
|
return self.row
|
|
end
|
|
|
|
function BinaryTable:Get(key)
|
|
local v = self.caches[key]
|
|
if v then
|
|
return v
|
|
end
|
|
|
|
local t = self:ReadElement(key)
|
|
self.caches[key] = t
|
|
if t ~= nil then
|
|
self.cachesCount = self.cachesCount + 1
|
|
end
|
|
CS.PrefProfiler.ProfilerCore.RecordGetTableBinaryItem(self.filePath)
|
|
|
|
return t
|
|
end
|
|
|
|
--读取内存块
|
|
function BinaryTable:GetReader(offset)
|
|
|
|
if not self.bytes then
|
|
self.bytes = CS.BinaryManager.LoadBytes(self.filePath)
|
|
CS.PrefProfiler.ProfilerCore.RecordTableBinaryLoad(self.filePath, self.len, self.row)
|
|
end
|
|
|
|
offset = offset or 0
|
|
local reader = Reader.New(self.bytes, self.len, offset + 1)
|
|
return reader
|
|
end
|
|
|
|
--读取索引块
|
|
function BinaryTable:ReadIndexTrunk()
|
|
|
|
local len = self.primarykeyLen
|
|
local position = self.infoTrunkLen + 4
|
|
|
|
if len <= 0 or position < 0 then
|
|
XLog.Error(string.format("%s,读取索引块失败!! primarykey = %s", self.filePath, self.primarykey))
|
|
return
|
|
end
|
|
|
|
self.primarykeyList = {}
|
|
|
|
local reader = self:GetReader(position)
|
|
for i = 1, self.row do
|
|
local temp = reader:Read(self.primarykeyType) or 0
|
|
self.primarykeyList[temp] = i
|
|
--table.insert(self.primarykeyList, temp)
|
|
end
|
|
|
|
reader:Close()
|
|
return true
|
|
end
|
|
|
|
|
|
-- 读取每行的位置和长度
|
|
function BinaryTable:ReadRowInfoTrunk()
|
|
|
|
local len = self.rowTrunkLen
|
|
local position = self.infoTrunkLen + 4
|
|
|
|
if self.primarykeyCount > 0 then
|
|
position = position + self.primarykeyLen
|
|
end
|
|
|
|
if len <= 0 or position < 0 then
|
|
XLog.Error(string.format("%s,BinaryTable:ReadRowInfoTrunk 读取行位置块失败!", self.filePath))
|
|
return
|
|
end
|
|
|
|
self.rowInfoArray = {}
|
|
local reader = self:GetReader(position)
|
|
for i = 1, self.row do
|
|
|
|
local rowInfo = {}
|
|
rowInfo.start = reader:ReadInt() or 0
|
|
rowInfo.tail = reader:ReadInt() or 0
|
|
table.insert(self.rowInfoArray, rowInfo)
|
|
end
|
|
|
|
reader:Close()
|
|
reader = nil
|
|
end
|
|
|
|
|
|
--获取某一行信息
|
|
function BinaryTable:TryGetRowInfo(index)
|
|
|
|
if not self.rowInfoArray then
|
|
self:ReadRowInfoTrunk()
|
|
end
|
|
|
|
if not self.rowInfoArray or #self.rowInfoArray <= 0 then
|
|
XLog.Error(string.format("%s,BinaryTable:TryGetRowInfo 读取行位置数据失败", self.filePath))
|
|
return
|
|
end
|
|
|
|
if index > #self.rowInfoArray then
|
|
XLog.Error(string.format("%s,BinaryTable:TryGetRowInfo 超出总行数长度 : %s 查询长度 : %s", self.filePath, #self.rowInfoArray, index))
|
|
return
|
|
end
|
|
|
|
return self.rowInfoArray[index]
|
|
end
|
|
|
|
--读取条目
|
|
function BinaryTable:ReadElement(value)
|
|
|
|
if not self.primarykey then
|
|
XLog.Error(string.format("%s,主键未初始化 ", self.filePath))
|
|
return nil
|
|
end
|
|
|
|
if not self.primarykeyList then
|
|
self:ReadIndexTrunk()
|
|
end
|
|
|
|
local element = nil
|
|
local index = self.primarykeyList[value]
|
|
if index then
|
|
local info = self:TryGetRowInfo(index)
|
|
element = self:ReadElementInner(info, index, value)
|
|
end
|
|
|
|
if not element then
|
|
-- XLog.Warning(string.format("%s,BinaryTable:ReadElement,查询失败,未找到条目 %s = %s", self.filePath, self.primarykey, value))
|
|
return
|
|
end
|
|
|
|
return element
|
|
end
|
|
|
|
function BinaryTable:ReadRow(info, offset)
|
|
|
|
if info.start < 0 or info.tail <= 0 then
|
|
XLog.Error(string.format("%s,BinaryTable:ReadRow,行数据异常 %s = %s", self.filePath, info.start, info.tail))
|
|
return
|
|
end
|
|
|
|
local len = info.tail - info.start
|
|
local startIndex = offset + info.start
|
|
|
|
local reader = self:GetReader(startIndex)
|
|
return reader
|
|
end
|
|
|
|
|
|
function BinaryTable:ReadElementInner(info, index, value)
|
|
|
|
local position = self:GetContenTrunkPosition()
|
|
local reader = self:ReadRow(info, position)
|
|
if not reader then
|
|
XLog.Warning(string.format("%s,BinaryTable:ReadElementInner,查询数据失败 %s = %s", self.filePath, self.primarykey, value))
|
|
return
|
|
end
|
|
|
|
local colType = self.colTypes
|
|
local colNames = self.colNames
|
|
|
|
self.colNameIndex = {}
|
|
|
|
for i = 1, #colNames do
|
|
local name = colNames[i]
|
|
self.colNameIndex[name] = i
|
|
end
|
|
|
|
---每一个表对应一个元表
|
|
local metaTable = {}
|
|
|
|
metaTable.__index = function(tbl, colName)
|
|
local idx = self.colNameIndex[colName]
|
|
|
|
if not idx or not tbl then
|
|
return nil
|
|
end
|
|
|
|
local result = rawget(tbl, idx)
|
|
|
|
if not result then
|
|
local resultType = colType[idx]
|
|
|
|
if not resultType then
|
|
XLog.Error(string.format("找不到键值 Key:%s 请检查该键值和表头是否匹配", colName))
|
|
end
|
|
|
|
result = DefaultOfTypeNew[resultType]
|
|
end
|
|
|
|
|
|
return result
|
|
end
|
|
|
|
metaTable.__newindex = function()
|
|
XLog.Error("attempt to update a readonly table")
|
|
end
|
|
|
|
metaTable.__metatable = "readonly table"
|
|
|
|
|
|
metaTable.__pairs = function(t)
|
|
local function stateless_iter(tbl, key)
|
|
local nk, v = next(tbl, key)
|
|
|
|
if nk and v then
|
|
local nv = t[v] or t[nk]
|
|
return nk, nv
|
|
end
|
|
end
|
|
|
|
return stateless_iter, self.colNameIndex, nil
|
|
end
|
|
|
|
local temp = {}
|
|
local keyValue = nil
|
|
|
|
for j = 1, self.col do
|
|
local type = colType[j]
|
|
local value = reader:Read(type)
|
|
temp[j] = value
|
|
end
|
|
|
|
setmetatable(temp, metaTable)
|
|
|
|
reader:Close()
|
|
reader = nil
|
|
return temp
|
|
end
|
|
|
|
|
|
function BinaryTable:Release(uload)
|
|
|
|
CS.PrefProfiler.ProfilerCore.RecordTableBinaryUnload(self.filePath, uload and true or false)
|
|
|
|
if uload then
|
|
self.bytes = nil
|
|
end
|
|
|
|
self.caches = {}
|
|
self.cachesCount = 0
|
|
end
|
|
|
|
|
|
function BinaryTable:Close()
|
|
self.bytes = nil
|
|
end
|
|
|
|
return BinaryTable |