persist
This module allows to save and retrieve Lua values in table-like objects, in
a non-volatile way.
Compared to raw files, the persist
module offers a higher level of
abstraction, allowing to save and retrieve Lua objects directly, without
explicitly dealing with serialization, deserialization or other filesystem
issues. It can also be ported to environmnets which don't have a filesystem.
The POSIX port of this module relies on the QDBM library, which stores persisted objects on the filesystem.
Persistence services are offered through two public APIs:
general purpose persisted tables: they behave mostly as regular tables, except that their content survives across reboots. These tables are created with #table.new;
To easily save and retrieve isolated objects, #persist.save and #persist.load allow single-line operations with no extra bookkeeping.
Persisted tables behave mostly as regular Lua tables, except that their content survives across reboots. They can hold strings, numbers, booleans, and possibly nested tables thereof, both as their keys and as their values.
Functions can be persisted if and only if they don't capture any upvalue (see examples below).
Beware that tables are stored structurally; what's retrieved are copies of the objects store in them, not the objects themselves:
local persist = require 'persist'
t = persist.table.new('test')
t[1] = { }
assert(t[1] ~= t[1])
However, loops and shared table parts are preserved within a single item (key or value) stored and retrieved from a persisted table:
local y = { }
local x1 = { y1=y; y2=y } -- y1 and y2 point to the same object
x1.x = x -- x1 points to itself through its field x
assert(x1.x == x1)
assert(x1.y1 == x1.y2)
t[2] = x1 -- save it in a table
local x2 = t[2] -- retrieve a copy from the table
assert(x2 ~= x1) -- it's a copy, not the original
assert(x2.x == x2) -- but it points to itself
assert(x2.y1 == x2.y2) -- and its shared parts are still shared
Since tables retrieved from persisted tables are actually copies, alterations of these returned tables won't affect the store's content:
x = { foo = 'bar' }
t.x = x
x.foo = 42 -- modifying `x`, not the copy in `t`
assert (t.x.foo == 'bar') -- `t` remains unchanged
t.x = x -- overriding `t.x` with the whole `x` value
assert (t.x.foo == 42) -- now `t` reflects the change
"Simple" function, which don't capture any local variable, can be saved:
function plus_one(x) return x+1 end
t.plus_one = plus_one
assert(t.plus_one(1) == 2)
As for tables, persisted functions don't retain their identity:
assert(t.plus_one ~= t.plus_one)
Finally, functions which capture local variables cannot be saved. Below,
the alternative implementation of plus_one
captures a local variable i
,
and won't be properly serialized:
function make_incrementer (i)
return function(x) return x+i end
end
plus_one = make_incrementer(1)
t.plus_one = plus_one
Tables are stored on the filesystem, in QDBM database files.
These files are stored with a ".db"
extension, in the "persist"
sub-directory of the working directory (i.e. WORKING_DIR
if this global
variable is set, or the current directory "."
otherwise).
Persisted tables can be listed and deleted by manipulating these files.
The usual way to persist data with the persist
module is to create a table
object, then fill it with values to persist. However, this is neither
practical nor efficient when only one or a couple of small values need to be
saved.
To avoid a needless proliferation of tiny persisted tables, a pair of
persist.save
and persist.load
functions are provided, to easily store
individual objects with a single line of code.
Objects stored through this API have the same limitations as those stored in full-featured persisted tables: no userdata, no threads, no upvalues in functions, no preservation of table and function identities.
Individual persisted objects are stored in a QDBM database file called
"persist/PersistStore.db"
. It is a regular persisted table, identical to
what could have been generated with #table.new.
cbuffer
cbuffer:clear() | Empties the circular buffer. |
cbuffer:deque(nobjs) | Removes values from the circular buffer. |
cbuffer:enque(value) | Adds a value to the circular buffer. |
cbuffer:read(nobjs, totable) | Reads values from the circular buffer. |
persist
persist.load(name) | Retrieve from flash an object saved with #persist.save. |
persist.newcbuffer(name, capacity) | Creates a new persisted circular buffer
See #cbuffer
|
persist.save(name, obj) | Saves an object for later retrieval. |
persist.table | The table sub-module of persist |
table
table.empty(t) | Empties a table and releases associateed resources. |
table.new(name) | Creates or loads a new persisted table. |
cbuffer
Circular Buffer
cbuffer:clear()
nothing.
cbuffer:deque(nobjs)
nobjs
:
number of objects to remove.
number of objects remaining within the circular buffer.
cbuffer:enque(value)
value
:
the value to add to the circular buffer.
cbuffer:read(nobjs, totable)
nobjs
:
number of objects to read.
totable
:
boolean to set whether read objects are to be returned as a table or as a concatenated element.
persist
persist.load(name)
name
:
the name of the persisted object to load.
the object stored under that name, or nil
if no such object exists.
persist.newcbuffer(name, capacity)
See #cbuffer
name
:
the name of the circular buffer.
capacity
:
(optional) number to set maximum size of the circular buffer.
#cbuffer: the new persisted circular buffer.
persist.save(name, obj)
name
:
the name of the persisted object to save.
obj
:
the object to persist.
persist.save ('xxx', 1357) -- Save it in the store as 'xxx'
[...] -- reboot
x = persist.load 'xxx' -- Retrieve it from the store
assert(x == 1357)
persist.table
table
The table sub-module of persist
table.empty(t)
t
:
persited table returned by #table.new call.
table.new(name)
name
:
persisted table name.
nil
+ error message otherwise.