--[[----------------------------------------------------------------------------

KSFunctionUtils_memoize.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2011 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

------------------------------------------------------------------------------]]

--[[

Split off KSFunctionUtils.memoizeFn so we can have a low-overhead version
of this namespace.

	function memoizeFn( fn )
		-- Returns a function that maintains a fully weak table mapping single
		-- parameter values to single results. This even properly handles nil
		-- and NaN as parameters and results.
		
]]

--------------------------------------------------------------------------------

local setmetatable = setmetatable

--------------------------------------------------------------------------------

local kNil = { } -- encoding for nils in the memoize cache
local kNaN = { } -- encoding for Nan. Used only for keys.
local kForget = { } -- encoding for explicitly purging a value.

--------------------------------------------------------------------------------

local mtFullyWeak = { __mode = 'kv' }
	-- Avoids a dependence on KSTableUtils to simply build our own weak
	-- table metatable. That code should perhaps move below KSTableUtils
	-- to something we really trust to be free of import dependencies.

--------------------------------------------------------------------------------

local function memoize( fn )

	local cache = setmetatable( { }, mtFullyWeak )
	
	local function keyForValue( x )
		local xkey
		
		if x == nil then
			xkey = kNil
		elseif x ~= x then
			xkey = kNaN
		else
			xkey = x
		end
		return xkey
	end
	
	return function( x, forgetWhat )
		
		if x == kForget then
			cache[ keyForValue( forgetWhat ) ] = nil
			return
		end
		
		-- Encode x
		
		local xkey = keyForValue( x )
		
		-- Read from the cache
		
		local cachedValue = cache[ xkey ]
		
		-- If we've got a value, translate it back appropriately and return it
		
		-- FIXME [mhamburg 2011-04-11]: Close savviness would probably be good
		-- here but means that we need to know about the close support logic
		-- in this module.
		
		if cachedValue ~= nil then
			if cachedValue == kNil then
				return nil
			else
				return cachedValue
			end
		end
		
		-- If we haven't got a value, then compute one.
		
		local value = fn( x )
		
		-- Encode into the appropriate stored form.
		
		if value == nil then
			cachedValue = kNil
		else
			cachedValue = value
		end
		
		-- Store the encoded value in the cache.
		
		cache[ xkey ] = cachedValue
		
		-- Return the computed (rather than the encoded) value.
		
		return value
	
	end

end

memoize = memoize( memoize )
	-- We might as well memoize the results of memoize!

--------------------------------------------------------------------------------

return {
	memoizeFn = memoize,
	kForget = kForget,
}
