Quantcast
--[[--------------------------------------------------------------------
    Ovale Spell Priority
    Copyright (C) 2013 Johnny C. Lam

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License in the LICENSE
    file accompanying this program.
--]]--------------------------------------------------------------------

--[[
	Reference-counting table pool.

	Tables from the pool have four properties added automatically:

	_refcount_pool_object	reference to pool from which the table came.
	GetReference()			increments the count of references to this table.
	ReleaseReference()		decrements the count of references to this table and
							releases the table back to the pool if then count is zero.
	ReferenceCount()		the count of references to this table.
--]]

local _, Ovale = ...
local OvalePoolRefCount = {}
Ovale.OvalePoolRefCount = OvalePoolRefCount

--<private-static-properties>
-- Profiling set-up.
local Profiler = Ovale.Profiler
local profiler = nil
do
	local group = "OvalePoolRefCount"
	Profiler:RegisterProfilingGroup(group)
	profiler = Profiler:GetProfilingGroup(group)
end

local assert = assert
local setmetatable = setmetatable
local tinsert = table.insert
local tostring = tostring
local tremove = table.remove
local wipe = table.wipe
--</private-static-properties>

--<public-static-properties>
OvalePoolRefCount.name = "OvalePoolRefCount"
OvalePoolRefCount.pool = nil
OvalePoolRefCount.refcount = nil
OvalePoolRefCount.size = 0
OvalePoolRefCount.unused = 0
OvalePoolRefCount.__index = OvalePoolRefCount
--</public-static-properties>

--<private-static-methods>
local function ReferenceCount(item)
	return item._refcount_pool_object.refcount[item] or 0
end

local function GetReference(item)
	local poolObject = item._refcount_pool_object
	profiler.Start(poolObject.name)
	local refcount = item:ReferenceCount()
	poolObject.refcount[item] = refcount + 1
	profiler.Stop(poolObject.name)
	return item
end

local function ReleaseReference(item)
	local poolObject = item._refcount_pool_object
	profiler.Start(poolObject.name)
	local refcount = item:ReferenceCount()
	if refcount > 1 then
		poolObject.refcount[item] = refcount - 1
	else
		poolObject.refcount[item] = nil
		poolObject:Clean(item)
		wipe(item)
		tinsert(poolObject.pool, item)
		poolObject.unused = poolObject.unused + 1
	end
	profiler.Stop(poolObject.name)
	return item
end
--</private-static-methods>

--<private-static-properties>
local itemPrototype = {
	_refcount_pool_object = nil,
	GetReference = GetReference,
	ReferenceCount = ReferenceCount,
	ReleaseReference = ReleaseReference,
}
--</private-static-properties>

--<public-static-methods>
do
	-- Class constructor
	setmetatable(OvalePoolRefCount, { __call = function(self, ...) return self:NewPool(...) end })
end

function OvalePoolRefCount:NewPool(name)
	name = name or self.name
	local obj = setmetatable({ name = name }, self)
	obj.refcount = {}
	obj:Drain()
	return obj
end

function OvalePoolRefCount:Get()
	profiler.Start(self.name)
	assert(self.pool and self.refcount)
	local item = tremove(self.pool)
	if item then
		self.unused = self.unused - 1
	else
		self.size = self.size + 1
		item = {}
	end
	for name, method in pairs(itemPrototype) do
		item[name] = method
	end
	item._refcount_pool_object = self
	profiler.Stop(self.name)
	return item:GetReference()
end

function OvalePoolRefCount:Release(item)
	return item:ReleaseReference()
end

function OvalePoolRefCount:GetReference(item)
	return item:GetReference()
end

function OvalePoolRefCount:ReleaseReference(item)
	return item:ReleaseReference()
end

function OvalePoolRefCount:Clean(item)
	-- virtual function; override as needed.
end

function OvalePoolRefCount:Drain()
	profiler.Start(self.name)
	self.pool = {}
	self.size = self.size - self.unused
	self.unused = 0
	profiler.Stop(self.name)
end

function OvalePoolRefCount:Debug()
	Ovale:FormatPrint("Pool %s has size %d with %d item(s).", tostring(self.name), self.size, self.unused)
end
--</public-static-methods>