library/lua/persist-table.lua: now it uses metatables to make persistent memory storage even easier.

develop
expwnent 2014-11-16 17:06:59 -05:00
parent 9fd6e9f03c
commit 7560fbdb6b
1 changed files with 168 additions and 50 deletions

@ -16,56 +16,7 @@ It would be more efficient to cache stored information in global variables and u
Ask expwnent to try this if performance is bad. Ask expwnent to try this if performance is bad.
--]] --]]
prefix = 'persist-table' --[[
function ensure(name)
return dfhack.persistent.save({key=name})
end
function gensym()
local availables = dfhack.persistent.get_all(prefix .. '$available') or {}
local available = nil
local smallest = nil
for _,candidate in pairs(availables) do
--TODO: it would be great if we could iterate over these in order but we can't
local score = tonumber(candidate.value)
print('gensym', candidate, score, available, smallest)
if (score and (not available or score < smallest)) then
smallest = score
available = candidate
end
end
if available then
local value = available.value
available:delete()
print('gensym: allocate ' .. value)
return value
end
--none explicitly available, so smallest unused is the next available number
local smallestUnused = ensure(prefix .. '$smallest_unused')
if smallestUnused.value == '' then
smallestUnused.value = '0'
end
local result = smallestUnused.value
smallestUnused.value = tostring(1+tonumber(result))
smallestUnused:save()
print('gensym: allocate ' .. result)
return result
end
function releasesym(symb)
local availables = dfhack.persistent.get_all(prefix .. '$available') or {}
for _,available in pairs(availables) do
print('releasesym: ', symb, available.value, available)
if available.value == symb then
print('error: persist-table.releasesym(' .. symb .. '): available.value = ' .. available.value)
return
end
end
dfhack.persistent.save({key=prefix .. '$available', value=symb}, true)
print('releasesym: unallocate ' .. symb)
end
function accessTable(...) function accessTable(...)
local args = {...} local args = {...}
local name = 'mastertable' local name = 'mastertable'
@ -149,5 +100,172 @@ function setTable(...)
entry:save() entry:save()
return old return old
end end
--]]
prefix = 'persist-table'
function ensure(name)
return dfhack.persistent.save({key=name})
end
function gensym()
local availables = dfhack.persistent.get_all(prefix .. '$available') or {}
local available = nil
local smallest = nil
for _,candidate in pairs(availables) do
--TODO: it would be great if we could iterate over these in order but we can't
local score = tonumber(candidate.value)
print('gensym', candidate, score, available, smallest)
if (score and (not available or score < smallest)) then
smallest = score
available = candidate
end
end
if available then
local value = available.value
available:delete()
print('gensym: allocate ' .. value)
return value
end
--none explicitly available, so smallest unused is the next available number
local smallestUnused = ensure(prefix .. '$smallest_unused')
if smallestUnused.value == '' then
smallestUnused.value = '0'
end
local result = smallestUnused.value
smallestUnused.value = tostring(1+tonumber(result))
smallestUnused:save()
print('gensym: allocate ' .. result)
return result
end
function releasesym(symb)
local availables = dfhack.persistent.get_all(prefix .. '$available') or {}
for _,available in pairs(availables) do
print('releasesym: ', symb, available.value, available)
if available.value == symb then
print('error: persist-table.releasesym(' .. symb .. '): available.value = ' .. available.value)
return
end
end
dfhack.persistent.save({key=prefix .. '$available', value=symb}, true)
print('releasesym: unallocate ' .. symb)
end
local intCount = 7
local existIndex = intCount-0
local existValue = 1
local pointerIndex = intCount-1
local pointerValue = 1
local defaultValue = -1
local function isEmpty(table)
return next(table) == nil
end
local function deletePersistent(name)
if name == '' then
return
end
local children = dfhack.persistent.get_all(prefix .. name) or {}
for _,childKey in ipairs(children) do
local childEntry = ensure(prefix .. name .. '$$' .. childKey.value)
if childEntry.ints[existIndex] == existValue and childEntry.ints[pointerIndex] == pointerValue then
deletePersistent(childEntry.value)
end
childEntry:delete()
childKey:delete()
end
releasesym(name)
end
GlobalTable = {key = 'mastertable'}
GlobalTable.mt = {}
GlobalTable.mt.__index = function(table, key)
print(rawget(table,'key') .. '[' .. key .. ']')
local entry = ensure(prefix .. rawget(table,'key') .. '$$' .. key)
if entry.ints[existIndex] == existValue and entry.ints[pointerIndex] == defaultValue then
print('string: ' .. entry.value)
return entry.value
end
if entry.ints[pointerIndex] == pointerValue then
--pre-existing pointer
local result = {key = entry.value}
result.mt = rawget(GlobalTable,'mt')
setmetatable(result,rawget(GlobalTable,'mt'))
print('table: ' .. entry.value)
return result
end
entry:delete()
print 'table[key] does not exist.'
return nil
end
GlobalTable.mt.__newindex = function(table, key, value)
print(rawget(table,'key') .. '[' .. key .. '] = ' .. tostring(value))
local entry = ensure(prefix .. rawget(table,'key') .. '$$' .. key)
local old = entry.value
local isNew = entry.ints[existIndex] == defaultValue
if entry.ints[existIndex] == existValue and entry.ints[pointerIndex] == pointerValue then
if type(value) == 'table' and rawget(value,'mt') == rawget(GlobalTable,mt) and entry.value == rawget(value,'key') then
--if setting it to the same table it already is, then don't do anything
return
end
deletePersistent(entry.value)
end
if not value then
print('__newindesx: delete')
--delete
for i,child in ipairs(dfhack.persistent.get_all(prefix .. rawget(table,'key')) or {}) do
if child.value == key then
child:delete()
end
end
entry:delete()
return
elseif type(value) == 'string' then
print('__newindesx: string')
entry.value = value
entry.ints[pointerIndex] = defaultValue
entry.ints[existIndex] = existValue
entry:save()
if isNew then
print('new child!')
dfhack.persistent.save({key=prefix .. rawget(table,'key'), value=key}, true)
end
return
elseif type(value) == 'table' then
print('__newindesx: table')
if rawget(value,'mt') ~= rawget(GlobalTable,'mt') then
if not isEmpty(value) then
error('setting value to an invalid table')
end
print('__newindesx: empty table')
--empty table: allocate a thing
entry.ints[pointerIndex] = pointerValue
entry.ints[existIndex] = existValue
entry.value = gensym()
entry:save()
if isNew then
print('new child!')
dfhack.persistent.save({key=prefix .. rawget(table,'key'), value=key}, true)
end
return
end
print('__newindesx: table assignment')
entry.value = rawget(value,'key')
entry.ints[pointerIndex] = pointerValue
entry.ints[existIndex] = existValue
entry:save()
if isNew then
print('new child!')
dfhack.persistent.save({key=prefix .. rawget(table,'key'), value=key}, true)
end
return
else
error('type(value) = ' .. type(value))
end
end
setmetatable(GlobalTable, GlobalTable.mt)
return _ENV return _ENV