initial commit
This commit is contained in:
141
LibTSM/Util/TempTable.lua
Normal file
141
LibTSM/Util/TempTable.lua
Normal file
@@ -0,0 +1,141 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
--- TempTable Functions
|
||||
-- @module TempTable
|
||||
|
||||
local _, TSM = ...
|
||||
local TempTable = TSM.Init("Util.TempTable")
|
||||
local Debug = TSM.Include("Util.Debug")
|
||||
local private = {
|
||||
debugLeaks = TSM.__IS_TEST_ENV or false,
|
||||
freeTempTables = {},
|
||||
tempTableState = {},
|
||||
}
|
||||
local NUM_TEMP_TABLES = 100
|
||||
local RELEASED_TEMP_TABLE_MT = {
|
||||
__newindex = function(self, key, value)
|
||||
error("Attempt to access temp table after release")
|
||||
end,
|
||||
__index = function(self, key)
|
||||
error("Attempt to access temp table after release")
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Module Functions
|
||||
-- ============================================================================
|
||||
|
||||
--- Acquires a temporary table.
|
||||
-- Temporary tables are recycled tables which can be used instead of creating a new table every time one is needed for a
|
||||
-- defined lifecycle. This avoids relying on the garbage collector and improves overall performance.
|
||||
-- @param ... Any number of valuse to insert into the table initially
|
||||
-- @treturn table The temporary table
|
||||
function TempTable.Acquire(...)
|
||||
local tbl = tremove(private.freeTempTables, 1)
|
||||
assert(tbl, "Could not acquire temp table")
|
||||
setmetatable(tbl, nil)
|
||||
if private.debugLeaks then
|
||||
private.tempTableState[tbl] = (Debug.GetStackLevelLocation(2) or "?").." -> "..(Debug.GetStackLevelLocation(3) or "?")
|
||||
else
|
||||
private.tempTableState[tbl] = true
|
||||
end
|
||||
for i = 1, select("#", ...) do
|
||||
tbl[i] = select(i, ...)
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
--- Iterators over a temporary table, releasing it when done.
|
||||
-- NOTE: This iterator must be run to completion and not interrupted (i.e. with a `break` or `return`).
|
||||
-- @tparam table tbl The temporary table to iterator over
|
||||
-- @tparam[opt=1] number numFields The number of fields to unpack with each iteration
|
||||
-- @return An iterator with fields: `index, {numFields...}`
|
||||
function TempTable.Iterator(tbl, numFields)
|
||||
numFields = numFields or 1
|
||||
assert(numFields >= 1 and #tbl % numFields == 0)
|
||||
assert(private.tempTableState[tbl])
|
||||
tbl.__iterNumFields = numFields
|
||||
return private.TempTableIteratorHelper, tbl, 1 - numFields
|
||||
end
|
||||
|
||||
--- Releases a temporary table.
|
||||
-- The temporary table will be returned to the pool and must not be accessed after being released.
|
||||
-- @tparam table tbl The temporary table to release
|
||||
function TempTable.Release(tbl)
|
||||
private.TempTableReleaseHelper(tbl)
|
||||
end
|
||||
|
||||
--- Releases a temporary table and returns its values.
|
||||
-- Releases the temporary table (see @{TempTable.Release}) and returns its unpacked values.
|
||||
-- @tparam table tbl The temporary table to release and unpack
|
||||
-- @return The result of calling `unpack` on the table
|
||||
function TempTable.UnpackAndRelease(tbl)
|
||||
return private.TempTableReleaseHelper(tbl, unpack(tbl))
|
||||
end
|
||||
|
||||
function TempTable.EnableLeakDebug()
|
||||
private.debugLeaks = true
|
||||
end
|
||||
|
||||
function TempTable.GetDebugInfo()
|
||||
local debugInfo = {}
|
||||
local counts = {}
|
||||
for _, info in pairs(private.tempTableState) do
|
||||
counts[info] = (counts[info] or 0) + 1
|
||||
end
|
||||
for info, count in pairs(counts) do
|
||||
tinsert(debugInfo, format("[%d] %s", count, type(info) == "string" and info or "?"))
|
||||
end
|
||||
if #debugInfo == 0 then
|
||||
tinsert(debugInfo, "<none>")
|
||||
end
|
||||
return debugInfo
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.TempTableIteratorHelper(tbl, index)
|
||||
local numFields = tbl.__iterNumFields
|
||||
index = index + numFields
|
||||
if index > #tbl then
|
||||
TempTable.Release(tbl)
|
||||
return
|
||||
end
|
||||
if numFields == 1 then
|
||||
return index, tbl[index]
|
||||
else
|
||||
return index, unpack(tbl, index, index + numFields - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function private.TempTableReleaseHelper(tbl, ...)
|
||||
assert(private.tempTableState[tbl])
|
||||
wipe(tbl)
|
||||
tinsert(private.freeTempTables, tbl)
|
||||
private.tempTableState[tbl] = nil
|
||||
setmetatable(tbl, RELEASED_TEMP_TABLE_MT)
|
||||
return ...
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Temp Table Setup
|
||||
-- ============================================================================
|
||||
|
||||
do
|
||||
for _ = 1, NUM_TEMP_TABLES do
|
||||
local tempTbl = setmetatable({}, RELEASED_TEMP_TABLE_MT)
|
||||
tinsert(private.freeTempTables, tempTbl)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user