1 -- Used to hold tables that aren't currently being used.
3 local used
, free
= 0, 0
5 -- Creates a new table, optionally assigning a metatable to it.
6 -- If the metatable contains the key 'onCreate', that function is invoked on the new table, along with any
7 -- additional arguments passed to create.
8 local function create(metatable
, ...)
9 -- Can't allow metatable to have __metatable set, otherwise we won't be able to remove it later when release is called.
10 assert(not metatable
or not metatable
.__metatable
)
12 local tbl
= next(free_tables
)
16 free_tables
[tbl
] = nil
21 setmetatable(tbl
, metatable
)
23 local onCreate
= metatable
and metatable
.onCreate
24 if onCreate
then onCreate(tbl
, ...) end
30 -- Creates a reference to a table.
31 -- Adds a 'ref_count' key to the table, or increases it if it already exists.
32 local function reference(tbl
)
33 rawset(tbl
, "ref_count", (rawget(tbl
, "ref_count") or 1)+1)
37 -- Removes a reference to a table, and then returns that table.
38 -- This is similar to release, except the table isn't released if the reference count reaches 0.
40 -- Assuming the a called function invokes reference on the table, this would make
41 -- func(unreference(table))
43 -- func(table) release(table)
45 -- This function is just a convenience, and the second form is safer in case for
46 -- some reason func doesn't actually create their own reference.
47 local function unreference(tbl
)
48 local ref_count
= (rawget(tbl
, "ref_count") or 1)
49 assert(ref_count
> 0, "Attempt to unreference a table with no references.")
50 rawset(tbl
, "ref_count", ref_count
-1)
55 -- If the table has a 'ref_count' key, it is decreased, and the table is not released unless this value reached 0.
56 -- If the table's metatable has an "onRelease" key, it is invoked before the metatable is removed from the table.
57 local function release(tbl
)
58 assert(type(tbl
) == "table", "Argument must be a table.")
59 assert(free_tables
[tbl
] == nil, "Already released.")
61 -- Decrease the tables ref_count if it has one.
62 local ref_count
= rawget(tbl
, "ref_count")
63 if ref_count
and ref_count
> 1 then
64 rawset(tbl
, "ref_count", ref_count
- 1)
68 -- If table has an onRelease function, we'll invoke it.
69 local metatable
= getmetatable(tbl
)
70 local onRelease
= metatable
and metatable
.onRelease
71 if onRelease
then onRelease(tbl
) end
73 -- Remove the table's metatable, and store it in free_tables.
74 free_tables
[setmetatable(tbl
, nil)] = true
78 -- Remove any keys left by the table.
79 for key
in pairs(tbl
) do tbl
[key
] = nil end
82 -- Returns how many tables have been created but not yet released,
83 -- and the number of released tables that are available to be
84 -- recycled in future table creations.
89 -- Gets a value from nested tables.
90 -- get(x, a, b, c) is the same as x[a][b][c], except that if any of the nested tables don't exist,
91 -- nil is returned instead of raising an error.
92 local function get(tbl
, ...)
93 assert(type(tbl
) == "table", "Expected table argument.")
95 for i
= 1,select("#", ...) do
96 tbl
= rawget(tbl
, select(i
, ...))
97 if not tbl
then return end
103 -- Sets a value in a nested table, creating any missing tables if they don't exist.
104 -- get(x, y, a, b, c) is the same as x[a][b][c] = y, except that if any of the nested tables don't exist,
106 -- Returns the previous value it replaces.
107 local function set(tbl
, value
, ...)
108 local c
= select("#", ...)
111 local k
= select(i
, ...)
112 local tbl2
= rawget(tbl
, k
)
122 local k
= select(c
, ...)
123 local oldvalue
= rawget(tbl
, k
)
124 rawset(tbl
, k
, value
)
129 -- Helper for the array(...) function, populates the table.
130 local function append(tbl
, ...)
132 for i
= 1,select("#", ...) do
133 rawset(tbl
, base
+i
, select(i
, ...))
137 -- Creates an array from its arguments.
138 -- Unlike {...}, will recycle an unused table if one exists, rather than creating
139 -- a new table each call.
140 local function array(...)
146 QuestHelper
.create
= create
147 QuestHelper
.reference
= reference
148 QuestHelper
.unreference
= unreference
149 QuestHelper
.release
= release
150 QuestHelper
.pool
= pool
151 QuestHelper
.get
= get
152 QuestHelper
.set
= set
153 QuestHelper
.append
= append
154 QuestHelper
.array
= array