Persistent Tables

lua-users home
wiki

[!] VersionNotice: The below code pertains to an older Lua version, Lua 4. Certain features like settagmethod are no longer present in Lua 5.

I had to wait for a big download today, so i had time to implement this fancy idea, that was roaming around in my head for quite some time: persistent tables! Ain't Lua surprising? The basic implementation is less than 100 lines! and it works without any additional C-code!

Code follows:

-- Copyright (c) 2001 by Peter Prade - all rights reserved



-- this file introduces a new table "type" that is persistent between sessions

-- each entry in such a table will be saved as a seperate file which 

-- returns the value when executed with dofile



-- tag methods are used to keep those files up to date and to load values that

-- are not yet loaded



-- todo: make all the globals local

persistent_tag = newtag()

persistent_location = {} -- lookup table for locations of each persistent table

persistent_delimiter = "." -- how to delimit one index from another in filenames

persistent_debug = nil -- set to non-nil for debugging output



-- we need to convert any type index to a string (which we use for the location)

function index_to_string(index)

  if type(index) == "string" then

    return index

  end

  if type(index) == "number" then

    return "-"..tostring(index)

  end

  

  -- todo: at least allow persistent tables as indizes (via links ;-)

  print("cannot make indexstring from type: "..type(index)) 

  return nil

end



-- loads a value from the given location

function persistent_load(location)

  if persistent_debug then print("load from", location) end

  local value = dofile(location)

  if type(value) == "table" then

    make_persistent(value, location)

  end

  return value

end



-- save a value to the given location

function persistent_save(location, value)

  local string_to_save = ""

  -- todo: check if old value was a table, and if so, delete old contents

  if type(value) == "nil" then

    string_to_save = "return nil" -- todo: better delete file

  elseif type(value) == "table" then

    make_persistent(value, location)

    string_to_save = "return {}"

  elseif type(value) == "string" then

    string_to_save = "return \""..value.."\""

  elseif type(value) == "number" then

    string_to_save = "return "..value..""

  else

    -- todo: at least functions can be saved as chunks

    print("cannot save type "..type(value).." yet!") 

    return

  end

  -- todo: if we want to build a directory tree (using a delimiter of "/") 

  -- we need to create the directories here: create_path(location)

  if persistent_debug then print("save to", location) end

  local f = openfile(location,"w")

  write(f, string_to_save)

  closefile(f)

end



-- tag method for reading from persistent tables

function persistent_get(table, index)

  local value = rawget(table, index)

  if value == nil then	

    local index_string = index_to_string(index)

    if index_string then

      value = persistent_load(persistent_location[table]..

                  persistent_delimiter..index_string)

      rawset(table, index, value)

    end

  end

  return value

end



-- tag method for writing to persistent tables

function persistent_set(table, index, value)

  rawset(table, index, value) -- todo: if i overwrite a table here, free it!

  local index_string = index_to_string(index)

  if index_string then

    persistent_save(persistent_location[table]..

              persistent_delimiter..index_string, value)

  end

end



-- activate tag methods

settagmethod (persistent_tag, "gettable", persistent_get)

settagmethod (persistent_tag, "settable", persistent_set)



-- you only need to call this once for your "root" persistent table.

-- but you can make as many tables persistent as you like 

-- (as long as those tables are stored at different locations)

-- any table within this table will be made persistent as well

function make_persistent(table,location)

  if tag(table)~=persistent_tag then

    persistent_location[table] = location

    settag(table,persistent_tag)

    for index,value in table do

      persistent_set(table,index,value) -- rewrite table contents

    end

  else

    if persistent_location[table] ~= location then

      print("table with location of", persistent_location[table])

      print("reused at",location)

      print("-> cannot store other than simple trees yet!")

      -- todo: store links to other location...

    end

  end

end



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

-- use any directory you'd like as "root": (just make sure the directory 

-- exists, as i cannot create it with just the baselib)

persistent_root = "~/persistent_lua/" 



-- make a table persistent for testing:

persistent = {}

make_persistent(persistent,persistent_root)



-- works also nice: make_persistent(globals(),"C:/Anywhere/")



-- now let's do some fancy testing.

-- any contents of the table "persistent" will also be kept as files



if persistent.test == nil then

  persistent.test = "hello!"

  persistent.table = {"hello!","world!";x=1,y=999}

end



print(persistent.test, persistent.table[2], persistent.table.y)



for i = 1,20 do

  print(persistent.table[i])

end



for i = 1,10 do

  -- watch the strings grow every time you call this file:

  persistent.table[i] = tostring(persistent.table[i])..tostring(i)

end

for i = 11,20 do

  persistent.table[i] = i

end

for i = 8,12 do

  persistent.table[i] = nil

end



persistent.table.parent = persistent -- doesn't work correctly yet.

Add comments here

Wow, this is great! I was looking for something exactly like that. Now all that I would need is to use a db-file instead of the filesystem to store the tables. --MartinSpernau

OK, look here: PersistentTablesSqlite -- DougCurrie


RecentChanges · preferences
edit · history
Last edited January 6, 2007 3:14 am GMT (diff)