Module persist This module allows to save and retrieve Lua values in table-like objects, in a non-volatile way.
Once stored in a persisted table, these Lua values can be retrieved even after the Lua process and/or the CPU running it have been rebooted.

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:

Persisted Tables.

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

POSIX implementation details.

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.

Persisted Objects.

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.

POSIX implementation details.

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.

Type 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.

Type 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

Type table

table.empty(t) Empties a table and releases associateed resources.
table.new(name) Creates or loads a new persisted table.

Type cbuffer

Circular Buffer

Field(s)

cbuffer:clear()

Empties the circular buffer.

Return value

nothing.

cbuffer:deque(nobjs)

Removes values from the circular buffer.

Parameter

Return value

number of objects remaining within the circular buffer.

cbuffer:enque(value)

Adds a value to the circular buffer.

Parameter

Return values

  1. number of objects within the circular buffer on success.
  2. nil followed by an error message otherwise.

cbuffer:read(nobjs, totable)

Reads values from the circular buffer.
This function does not remove read objects from the circular buffer.

Parameters

Return values

  1. table with read objects if totable was false.
  2. string with concatenated read objects if totable was true.

Type persist

Field(s)

persist.load(name)

Retrieve from flash an object saved with #persist.save.

Parameter

Return value

the object stored under that name, or nil if no such object exists.

persist.newcbuffer(name, capacity)

Creates a new persisted circular buffer

See #cbuffer


Parameters

Return value

#cbuffer: the new persisted circular buffer.

persist.save(name, obj)

Saves an object for later retrieval.
If the saving operation cannot be performed successfully, an error is thrown. Objects saved with this function can be retrieved with #persist.load, by giving back the same name, even after a reboot.

Parameters

Usage:



 persist.save ('xxx', 1357) -- Save it in the store as 'xxx'
 [...] -- reboot
 x = persist.load 'xxx' -- Retrieve it from the store
 assert(x == 1357)

#table persist.table

The table sub-module of persist

Type table

The table sub-module of persist

Field(s)

table.empty(t)

Empties a table and releases associateed resources.

Parameter

table.new(name)

Creates or loads a new persisted table.
If a table already exists with the provided name, it is loaded; otherwise, a new one is created.

Parameter

Return values

  1. the persisted table on success.
  2. nil + error message otherwise.