flyweightstore
flyweightstore
function flyweightstore() --> ( ... ) --> reference
Description
Creates a flyweight storage, which is a function that interns the list of arguments, i.e. it generates a reference for each possible list. When it is called multiple times with the same list, it will return the same reference. All the reference are automatically garbage collected when no more used.
Parameters
- …
-
List of argument to which associate a reference.
Return Values
- reference
-
Reference associated to the list of arguments.
Code
--ZFUNC-flyweightstore-v0 local function flyweightstore() --> ( ... ) --> reference local NIL, NAN = {}, {} local meta = { __index = function() error( "Can not access interned content directly.", 2 ) end, __newindex = function() error( "Can not change or add content to a flyweight store.", 2 ) end, } local internstore = setmetatable( {}, { __mode = "kv" } ) -- A map from child to parent is used to protect the internstore table's -- contents. -- In this way, they will he collected only when all the cildren are -- collected in turn. local parent = setmetatable( {}, { __mode = "k" } ) return function( ... ) local currentstore = internstore for a = 1, select( "#", ... ) do -- Get next intern field. Replace un-storable contents. local tonext = select( a, ... ) if tonext ~= tonext then tonext = NAN end if tonext == nil then tonext = NIL end -- Get or create the correspondent sub-intern local substore = rawget( currentstore, tonext ) if substore == nil then substore = setmetatable( {}, meta ) parent[ substore ] = currentstore rawset( currentstore, tonext, substore ) end currentstore = substore end return currentstore end end return flyweightstore
Examples
local t = require "taptest" local flyweightstore = require "flyweightstore" local function diff( a, b ) return a ~= b end t( type( flyweightstore() ), "function" ) local fly = flyweightstore() t( type( fly( 1 )), "table" ) t( fly( 1 ), fly( 2 ), diff ) t( type( fly( 1, nil, 0/0, 3 ) ), "table" ) t( fly( 1, nil, 0/0, 3 ), fly( 1, nil, 0/0, 3 ) ) t( fly( 1, nil, 0/0, 3 ), fly( 1, nil, 0/0 ), diff ) t( fly( 1, nil, 0/0, 3 ), fly( 1, nil ), diff ) t( fly( 1, nil, 0/0, 3 ), fly( 1 ), diff ) t( fly( 1, nil, 0/0, 3 ), fly( 1, nil, 0/0, 2 ), diff ) t( fly( 1, nil, 0/0, 3 ), fly( 1, nil, 0, 3 ), diff ) t( fly( 1, nil, 0/0, 3 ), fly( 1, '', 0/0, 3 ), diff ) t( fly( 1, nil, 0/0, 3 ), fly( 4, nil, 0/0, 3 ), diff ) -- Multiple store local alt = flyweightstore() t( type( alt( 1, nil, 0/0, 3 )), "table" ) t( alt( 1, nil, 0/0, 3 ), alt( 1, nil, 0/0, 3 ) ) t( alt( 1, nil, 0/0, 3 ), fly( 1, nil, 0/0, 3 ), diff ) -- Garbage collection test -- Check if the current lua version supports garbage collection metamethod local has_gc_meta setmetatable( {}, { __gc = function() has_gc_meta = true end } ) collectgarbage( "collect" ) local function skipon51( a, b ) if has_gc_meta then return a == b end return true, "skipped" end local gccount = 0 local x = fly( true, false ) x = setmetatable( x, { __gc = function( t ) gccount = gccount + 1 end } ) -- No collection if some reference is still around collectgarbage( "collect" ) t( gccount, 0, skipon51 ) -- Automatic collection x = nil collectgarbage( "collect" ) t( gccount, 1, skipon51 ) t()