bump to 3.1
[QuestHelper.git] / Generic / table.lua
blob13abde26e69e6788b8b46c36e92b57de557ea06a
1 -- Used to hold tables that aren't currently being used.
2 local free_tables = {}
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)
14 if tbl then
15 free = free - 1
16 free_tables[tbl] = nil
17 else
18 tbl = {}
19 end
21 setmetatable(tbl, metatable)
23 local onCreate = metatable and metatable.onCreate
24 if onCreate then onCreate(tbl, ...) end
26 used = used + 1
27 return tbl
28 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)
34 return tbl
35 end
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.
39 --
40 -- Assuming the a called function invokes reference on the table, this would make
41 -- func(unreference(table))
42 -- the same as
43 -- func(table) release(table)
44 --
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)
51 return tbl
52 end
54 -- Releases a table.
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)
65 return
66 end
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
75 free = free + 1
76 used = used - 1
78 -- Remove any keys left by the table.
79 for key in pairs(tbl) do tbl[key] = nil end
80 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.
85 local function pool()
86 return used, free
87 end
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
98 end
100 return tbl
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,
105 -- they're created.
106 -- Returns the previous value it replaces.
107 local function set(tbl, value, ...)
108 local c = select("#", ...)
110 for i = 1,c-1 do
111 local k = select(i, ...)
112 local tbl2 = rawget(tbl, k)
113 if tbl2 then
114 tbl = tbl2
115 else
116 tbl2 = create()
117 rawset(tbl, k, tbl2)
118 tbl = tbl2
122 local k = select(c, ...)
123 local oldvalue = rawget(tbl, k)
124 rawset(tbl, k, value)
126 return oldvalue
129 -- Helper for the array(...) function, populates the table.
130 local function append(tbl, ...)
131 local base = #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(...)
141 local tbl = create()
142 append(tbl, ...)
143 return tbl
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