19. State

Monolith has the ability to save and load data. We call this data state.

19.1. SQLite Header Include

<<system_includes>>=
#include "sqlite3.h"

19.2. Monolith State in Top-level Struct

At any given time, exactly one state file is open. This state file is managed inside of the struct.

<<struct_contents>>=
monolith_state state;
<<init>>=
monolith_state_init(&m->state);

On cleanup, monolith will automatically close any open monolith state. Nothing will happen if there isn't anything open.

<<cleanup>>=
monolith_state_close(m);

State can be retrieved with monolith_state_get.

<<function_declarations>>=
monolith_state* monolith_state_get(monolith_d *m);
<<functions>>=
monolith_state* monolith_state_get(monolith_d *m)
{
    return &m->state;
}

19.3. Monolith State Data

The monolith state is mostly powered by SQLite! SQLite doesn't need to be seen by other parts of monolith, so we wrap all the bits inside of monolith state and make SQLite calls via monolith state functions.

<<typedefs>>=
typedef struct monolith_state monolith_state;
<<structs>>=
struct monolith_state {
    sqlite3 *db;
};

It is initialized with the funciton monolith_state_init.

<<function_declarations>>=
void monolith_state_init(monolith_state *s);
<<functions>>=
void monolith_state_init(monolith_state *s)
{
    s->db = NULL;
}


The function monolith_state_empty checks to see if the database is empty.

<<function_declarations>>=
int monolith_state_empty(monolith_state *s);
<<functions>>=
int monolith_state_empty(monolith_state *s)
{
    return s->db == NULL;
}

19.4. Parameter and Schema State Data

While the monolith state does use SQLite, pages shouldn't be writing SQLite statements directly. Parameters can be saved and loaded using this parameter interface.

19.4.1. Parameter Data

A parameter is a single unit which stores a single data value. It is known as a monolith_state_param.

<<typedefs>>=
typedef struct monolith_state_param monolith_state_param;
<<structs>>=
struct monolith_state_param {
<<state_param_contents>>
};
19.4.1.1. Parameter Struct Initialization/Cleanup

The struct is initialized with monolith_state_param_init.

<<function_declarations>>=
void monolith_state_param_init(monolith_state_param *msp);
<<functions>>=
void monolith_state_param_init(monolith_state_param *msp)
{
<<monolith_state_param_init>>
}

Any thing allocated and bound to this particular param instance is freed with monolith_state_param_cleanup.

<<function_declarations>>=
void monolith_state_param_cleanup(monolith_state_param *msp);
<<functions>>=
void monolith_state_param_cleanup(monolith_state_param *msp)
{
<<monolith_state_param_cleanup>>
}
19.4.1.2. Parameter Struct Contents

19.4.1.2.1. Param Name

The parameter name is the string used to identify what the parameter is. In addition to the name, the string length is stored as well.

<<state_param_contents>>=
char *name;
int len;
<<monolith_state_param_init>>=
msp->name = NULL;
msp->len = 0;
<<monolith_state_param_cleanup>>=
if(msp->name != NULL) free(msp->name);

The name for a particular parameter can be set using the function monolith_state_param_name_set. It will allocate and set the name of the string. The length of the string must also be known and explicitely provided.

<<function_declarations>>=
int monolith_state_param_name_set(monolith_state_param *p,
                                  const char *name,
                                  int len);

The function will allocate and copy the string to the internal state. If the allocation fails, the function will return false (0), otherwise it will return true (1). The function can also fail if the length is non-zero or the name is non-null.

<<functions>>=
int monolith_state_param_name_set(monolith_state_param *p,
                                  const char *name,
                                  int len)
{
    if(p->len != 0) return 0;
    if(p->name != NULL) return 0;

    p->name = calloc(1, len + 1);
    if(p->name == NULL) return 0;
    strncpy(p->name, name, len);
    return 1;
}

The parameter name can be retrieved using the function monolith_state_param_name_get

<<function_declarations>>=
const char * monolith_state_param_name_get(monolith_state_param *p);
<<functions>>=
const char * monolith_state_param_name_get(monolith_state_param *p)
{
    return p->name;
}
19.4.1.2.2. Param Type Flag

<<state_param_contents>>=
int type;
<<monolith_state_param_init>>=
msp->type = 0;

The type can be set with the function monolith_state_param_type_set, and retrieved with the function monolith_state_param_type_get.

<<function_declarations>>=
void monolith_state_param_type_set(monolith_state_param *p, int type);
int monolith_state_param_type_get(monolith_state_param *p);
<<functions>>=
void monolith_state_param_type_set(monolith_state_param *p, int type)
{
    p->type = type;
}
int monolith_state_param_type_get(monolith_state_param *p)
{
    return p->type;
}
19.4.1.2.3. Param User Data

<<state_param_contents>>=
void *ud;
<<monolith_state_param_init>>=
msp->ud = NULL;


Data assigned to the user data pointer ud is freed by monolith_state_paramduring cleanup. By default, the data is freed using the system free function. If the internal destructor function has been set with monolith_state_param_dtor_set.

<<monolith_state_param_cleanup>>=
if(msp->ud != NULL) {
    if(msp->dtor != NULL) {
        msp->dtor(msp->ud); /* use external destructor */
    } else {
        free(msp->ud); /* use system free */
    }
}

The user data can be set/get with the functions monolith_state_param_data_set and monolith_state_param_data_get.

<<function_declarations>>=
void monolith_state_param_data_set(monolith_state_param *p, void *ud);
void * monolith_state_param_data_get(monolith_state_param *p);
<<functions>>=
void monolith_state_param_data_set(monolith_state_param *p, void *ud)
{
    p->ud = ud;
}
void * monolith_state_param_data_get(monolith_state_param *p)
{
    return p->ud;
}
19.4.1.2.4. Param Destructor (Optional)

A destructor function pointer is used as an alternative to free for freeing the internal userdata. If it is non-null, it will call this function when the function is being cleaned up. Otherwise, it will call the default system free.

<<state_param_contents>>=
void (*dtor)(void *);
<<monolith_state_param_init>>=
msp->dtor = NULL;

The destructor can be set using the function monolith_state_param_dtor_set.

<<function_declarations>>=
void monolith_state_param_dtor_set(monolith_state_param *msp,
                                   void (*dtor)(void *));
<<functions>>=
void monolith_state_param_dtor_set(monolith_state_param *msp,
                                   void (*dtor)(void *))
{
    msp->dtor = dtor;
}
19.4.1.3. Parameter State Types

19.4.1.3.1. Type Enum Declaration

All types are stored inside of an enum, which gets dynamically populated here.

<<macros>>=
enum {
    PARAM_NONE = 0,
<<parameter_enum>>
    PARAM_END /* so we don't have to worry about comma warning */
};
19.4.1.3.2. Floating Point Parameter

A floating point parameter uses the enum type PARAM_FLOAT.

<<parameter_enum>>=
PARAM_FLOAT,

A float point parameter is created using the function monolith_state_param_mkfloat.

19.4.1.3.2.1. state_param_mkfloat

<<function_declarations>>=
int monolith_state_param_mkfloat(monolith_state_param *p,
                                 const char *name,
                                 int len,
                                 float ival);
<<functions>>=
int monolith_state_param_mkfloat(monolith_state_param *p,
                                 const char *name,
                                 int len,
                                 float ival)
{
    int rc;
    float *x;
    rc = monolith_state_param_name_set(p, name, len);
    if(!rc) return 0;
    x = calloc(1, sizeof(float));
    *x = ival;
    monolith_state_param_data_set(p, x);
    monolith_state_param_type_set(p, PARAM_FLOAT);
    return 1;
}
19.4.1.3.2.2. state_param_float

The value of a float can be retrieved using the function monolith_param_float. This will do type checking. If it's not a float, it will return 0.

<<function_declarations>>=
int monolith_state_param_float(monolith_state_param *p, float **f);
<<functions>>=
int monolith_state_param_float(monolith_state_param *p, float **f)
{
    int t;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_FLOAT) return 0;
    *f = (float *)monolith_state_param_data_get(p);
    return 1;
}
19.4.1.3.2.3. state_param_float_set

The value of a float can be set using the function monolith_param_float_set. If the parameter is not a float, it will return 0.

<<function_declarations>>=
int monolith_state_param_float_set(monolith_state_param *p, float f);
<<functions>>=
int monolith_state_param_float_set(monolith_state_param *p, float f)
{
    int t;
    float *fp;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_FLOAT) return 0;
    fp = (float *)monolith_state_param_data_get(p);
    *fp = f;
    return 1;
}
19.4.1.3.3. Integer Parameter

An integer parameter uses the enum type PARAM_INT.

<<parameter_enum>>=
PARAM_INT,

An integer parameter is created using the function monolith_state_param_mkint.

19.4.1.3.3.1. state_param_mkint

<<function_declarations>>=
int monolith_state_param_mkint(monolith_state_param *p,
                               const char *name,
                               int len,
                               int ival);
<<functions>>=
int monolith_state_param_mkint(monolith_state_param *p,
                               const char *name,
                               int len,
                               int ival)
{
    int rc;
    int *x;
    rc = monolith_state_param_name_set(p, name, len);
    if(!rc) return 0;
    x = calloc(1, sizeof(int));
    *x = ival;
    monolith_state_param_data_set(p, x);
    monolith_state_param_type_set(p, PARAM_INT);
    return 1;
}

The value of an integer can be retrieved using the function monolith_state_param_int. This will do type checking. If it's not an integer, it will return 0.

19.4.1.3.3.2. state_param_int

<<function_declarations>>=
int monolith_state_param_int(monolith_state_param *p, int **i);
<<functions>>=
int monolith_state_param_int(monolith_state_param *p, int **i)
{
    int t;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_INT) return 0;
    *i = (int *)monolith_state_param_data_get(p);
    return 1;
}
19.4.1.3.3.3. state_param_int_set

The value of an integer can be set using the function monolith_param_int_set. If the parameter is not an integer, it will return 0.

<<function_declarations>>=
int monolith_state_param_int_set(monolith_state_param *p, int i);
<<functions>>=
int monolith_state_param_int_set(monolith_state_param *p, int i)
{
    int t;
    int *ip;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_INT) return 0;
    ip = (int *)monolith_state_param_data_get(p);
    *ip = i;
    return 1;
}
19.4.1.3.4. String Parameter

19.4.1.3.4.1. state_param_mkstring

CLOSED: [2019-01-13 Sun 18:57] A string parameter uses the enum type PARAM_STRING.

<<parameter_enum>>=
PARAM_STRING,

An string parameter is created using the function monolith_state_param_mkstring. The size of the string must be supplied as well.

<<function_declarations>>=
int monolith_state_param_mkstring(monolith_state_param *p,
                                  const char *name,
                                  int len,
                                  const char *str,
                                  unsigned int strlen);
<<functions>>=
int monolith_state_param_mkstring(monolith_state_param *p,
                                  const char *name,
                                  int len,
                                  const char *str,
                                  unsigned int strlen)
{
    int rc;
    char *x;
    rc = monolith_state_param_name_set(p, name, len);
    if(!rc) return 0;
    x = calloc(1, strlen + 1);
    if(x == NULL) return 0;
    strncpy(x, str, strlen);
    monolith_state_param_data_set(p, x);
    monolith_state_param_type_set(p, PARAM_STRING);
    return 1;
}

The value of a string can be retrieved using the function monolith_state_param_string. This will do type checking. If it's not a string, it will return 0.

19.4.1.3.4.2. state_param_string

<<function_declarations>>=
int monolith_state_param_string(monolith_state_param *p, char **str);
<<functions>>=
int monolith_state_param_string(monolith_state_param *p, char **str)
{
    int t;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_STRING) return 0;
    *str = (char *)monolith_state_param_data_get(p);
    return 1;
}
19.4.1.3.5. Monome State Parameter

The monome LED state has it's own parameter. The parameter itself stores the current state of the LED grid as a string. More information can be found in the section How monome state data is stored.

19.4.1.3.5.1. monome state type enum

A monome state parameter uses the enum type PARAM_MSTATE.

<<parameter_enum>>=
PARAM_MSTATE,
19.4.1.3.5.2. state_param_mkmstate

When the monolith state is saved, it only saves the string. The data allocated is all the data needed for the string, plus the null terminator. With 3 bytes per row, and 8 rows, this totals to be 25 bytes.

<<function_declarations>>=
int monolith_state_param_mkmstate(monolith_state_param *p,
                                  const char *name,
                                  int len,
                                  monolith_page_mstate *ms);
<<functions>>=
int monolith_state_param_mkmstate(monolith_state_param *p,
                                  const char *name,
                                  int len,
                                  monolith_page_mstate *ms)
{
    int rc;
    char *bytes;
    rc = monolith_state_param_name_set(p, name, len);
    if(!rc) return 0;
    bytes = calloc(1, 25); /* (8 x 3) + 1 */
    monolith_base64_grid_encode(ms, bytes);
    bytes[24] = 0;
    monolith_state_param_data_set(p, bytes);
    monolith_state_param_type_set(p, PARAM_MSTATE);
    return 1;
}
19.4.1.3.5.3. state_param_mstate

This function will read information from the state param and read it into the monolith monome state.

<<function_declarations>>=
int monolith_state_param_mstate(monolith_state_param *p,
                                monolith_page_mstate *ms);
<<functions>>=
int monolith_state_param_mstate(monolith_state_param *p,
                                monolith_page_mstate *ms)
{
    int t;
    char *bytes;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_MSTATE) return 0;
    bytes = (char *)monolith_state_param_data_get(p);
    monolith_base64_grid_decode(ms, bytes);
    return 1;
}
19.4.1.3.5.4. state_param_mstate_set

The value of an integer can be set using the function monolith_param_int_set. If the parameter is not an integer, it will return 0.

<<function_declarations>>=
int monolith_state_param_mstate_set(monolith_state_param *p, const char *str);
<<functions>>=
int monolith_state_param_mstate_set(monolith_state_param *p, const char *str)
{
    int t;
    char *in;
    int i;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_MSTATE) return 0;
    in = (char *)monolith_state_param_data_get(p);
    for(i = 0; i < 24; i++) {
        in[i] = str[i];
    }
    return 1;
}
19.4.1.3.6. DONE Blob Parameter [4/4]

CLOSED: [2019-04-05 Fri 21:36]

19.4.1.3.6.1. DONE Blob Enum

CLOSED: [2019-03-17 Sun 23:05] A blob is used to store binary data.

<<parameter_enum>>=
PARAM_BLOB,
19.4.1.3.6.2. DONE state_param_mkblob

CLOSED: [2019-04-05 Fri 21:26] The function state_param_mkblob creates a blob entry. If the blob needs to be freed, a destructor callback dtor should be passed in as an argument.

<<function_declarations>>=
int monolith_state_param_mkblob(monolith_state_param *p,
                                const char *name,
                                int len,
                                void *blob,
                                unsigned int bloblen,
                                void (*dtor)(void*));

In order to keep track of both the blob data and the size, a special struct is created. The custom destructor callback is stored here as well.

<<mkblob_struct>>=
typedef struct {
    unsigned int blobsize;
    void *ud;
    void (*dtor)(void*);
} mkblob_struct;


<<functions>>=
<<mkblob_struct>>

static void blob_free(void *ud)
{
    mkblob_struct *x;
    x = ud;

    if(x->dtor != NULL) x->dtor(x->ud);
    free(ud);
}

int monolith_state_param_mkblob(monolith_state_param *p,
                                const char *name,
                                int len,
                                void *blob,
                                unsigned int bloblen,
                                void (*dtor)(void*))
{
    int rc;
    mkblob_struct *x;
    rc = monolith_state_param_name_set(p, name, len);
    if(!rc) return 0;

    x = calloc(1, sizeof(mkblob_struct));
    if(x == NULL) return 0;

    x->ud = blob;
    x->blobsize = bloblen;
    x->dtor = dtor;
    monolith_state_param_data_set(p, x);
    monolith_state_param_type_set(p, PARAM_BLOB);
    monolith_state_param_dtor_set(p, blob_free);
    return 1;
}
19.4.1.3.6.3. DONE state_param_blob

CLOSED: [2019-04-05 Fri 21:32] This function will read a state parameter, and store the pointer and size into the variables blob and blobsize.

<<function_declarations>>=
int monolith_state_param_blob(monolith_state_param *p,
                              void **blob,
                              unsigned int *blobsize);
<<functions>>=
int monolith_state_param_blob(monolith_state_param *p,
                              void **blob,
                              unsigned int *blobsize)
{
    int t;
    mkblob_struct *x;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_BLOB) return 0;
    x = monolith_state_param_data_get(p);
    if(blob != NULL) *blob = x->ud;
    if(blobsize != NULL) *blobsize = x->blobsize;
    return 1;
}
19.4.1.3.6.4. DONE state_param_blob_set

CLOSED: [2019-05-12 Sun 10:42] An initialized Blob parameter (initialized with empty values) can be set to hold real values with the function state_param_blob_set. This will require the blob, the blob size, and an optional destructor callback.

<<function_declarations>>=
int monolith_state_param_blob_set(monolith_state_param *p,
                                  void *blob,
                                  unsigned int blobsize,
                                  void (*dtor)(void*));
<<functions>>=
int monolith_state_param_blob_set(monolith_state_param *p,
                                  void *blob,
                                  unsigned int blobsize,
                                  void (*dtor)(void*))
{
    int t;
    mkblob_struct *x;
    t = monolith_state_param_type_get(p);
    if(t != PARAM_BLOB) return 0;
    x = (mkblob_struct *)monolith_state_param_data_get(p);
    x->ud = blob;
    x->blobsize = blobsize;
    x->dtor = dtor;
    return 1;
}

19.4.2. Monolith Schema Data

A group of parameters (for say, a page) is known as a schema. In practice, one uses schemas to read and write data to a SQLite table.

The schema struct is an opaque pointer that must be manually allocated and destroyed when it is being used.

19.4.2.1. Schema Initialization/Cleanup

When schema is initialized, it pre-allocates a fixed number of parameters. The number of parameters must be node. From there, the schema can be populated with variable names and values.

<<function_declarations>>=
void monolith_state_schema_init(monolith_state_schema **p_s, int nparams);
<<functions>>=
void monolith_state_schema_init(monolith_state_schema **p_s, int nparams)
{
    int i;
    monolith_state_schema *s;

    s = calloc(1, sizeof(monolith_state_schema));
<<schema_init>>
    *p_s = s;
}

When the schema no longer needs to be used, it must be freed using monolith_state_schema_cleanup.

<<function_declarations>>=
void monolith_state_schema_cleanup(monolith_state_schema **p_s);
<<functions>>=
void monolith_state_schema_cleanup(monolith_state_schema **p_s)
{
    int i;
    monolith_state_schema *s;

    s = *p_s;
<<schema_cleanup>>
    free(s);
}
19.4.2.2. Schema Contents

19.4.2.2.1. Schema Struct Declaration

<<typedefs>>=
typedef struct monolith_state_schema monolith_state_schema;
<<structs>>=
struct monolith_state_schema {
<<schema_contents>>
};
19.4.2.2.2. Param Array

<<schema_contents>>=
monolith_state_param *params;

On init, this gets allocated, then each parameter is initialized.

<<schema_init>>=
s->params = calloc(1, nparams * sizeof(monolith_state_param));
for(i = 0; i < nparams; i++) {
    monolith_state_param_init(&s->params[i]);
}
<<schema_cleanup>>=
for(i = 0; i < s->nparams; i++) {
    monolith_state_param_cleanup(&s->params[i]);
}
if(s->params != NULL) free(s->params);

A single parameter can be retrieved using the function monolith_state_schema_param. This will do bounds checking, and will return 0 on error.

<<function_declarations>>=
int monolith_state_schema_param(monolith_state_schema *s,
                                int pos,
                                monolith_state_param **p);
<<functions>>=
int monolith_state_schema_param(monolith_state_schema *s,
                                int pos,
                                monolith_state_param **p)
{
    if(pos < 0) return 0;
    if(pos >= s->nparams) return 0;
    *p = &s->params[pos];
    return 1;
}
19.4.2.2.3. Number of Parameters

The number of parameters is stored inside of a variable called nparams.

<<schema_contents>>=
int nparams;
<<schema_init>>=
s->nparams = nparams;
19.4.2.3. Schema Parameter Setters/Getters

Parameters inside of the schema can be indirectly set using schema functions. Pages will uses these functions, so they get the shorter prefix monolith_param.

19.4.2.3.1. Schema Floating Point Parameter

A monolith parameter can be created using the function monolith_param_mkfloat.

<<function_declarations>>=
int monolith_param_mkfloat(monolith_state_schema *s,
                           int pos,
                           const char *name,
                           int len,
                           float ival);
<<functions>>=
int monolith_param_mkfloat(monolith_state_schema *s,
                           int pos,
                           const char *name,
                           int len,
                           float ival)
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_mkfloat(p, name, len, ival);
}

A monolith parameter float can be retrieved using the function monolith_param_float#+NAME: function_declarations

int monolith_param_float(monolith_state_schema *s, int pos, float *val);
<<functions>>=
int monolith_param_float(monolith_state_schema *s, int pos, float *val)
{
    monolith_state_param *p;
    float *tmp;
    int rc;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    rc = monolith_state_param_float(p, &tmp);
    if(rc) *val = *tmp;
    return rc;
}
19.4.2.3.2. Schema Integer Parameter

19.4.2.3.2.1. Integer Param Maker

A monolith parameter can be created using the function monolith_param_mkfloat.

<<function_declarations>>=
int monolith_param_mkint(monolith_state_schema *s,
                         int pos,
                         const char *name,
                         int len,
                         int ival);
<<functions>>=
int monolith_param_mkint(monolith_state_schema *s,
                         int pos,
                         const char *name,
                         int len,
                         int ival)
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_mkint(p, name, len, ival);
}

A monolith parameter integer can be retrieved using the function monolith_param_int** 19.4.2.3.2.2. Integer Param Getter

<<function_declarations>>=
int monolith_param_int(monolith_state_schema *s, int pos, int *val);
<<functions>>=
int monolith_param_int(monolith_state_schema *s, int pos, int *val)
{
    monolith_state_param *p;
    int *tmp;
    int rc;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    rc = monolith_state_param_int(p, &tmp);
    if(rc) *val = *tmp;
    return rc;
}
19.4.2.3.3. Schema String Parameter

19.4.2.3.3.1. String Param Maker

A monolith parameter can be created using the function monolith_param_mkfloat.

<<function_declarations>>=
int monolith_param_mkstring(monolith_state_schema *s,
                            int pos,
                            const char *name,
                            int len,
                            const char *str,
                            unsigned int strlen);
<<functions>>=
int monolith_param_mkstring(monolith_state_schema *s,
                            int pos,
                            const char *name,
                            int len,
                            const char *str,
                            unsigned int strlen)
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_mkstring(p, name, len, str, strlen);
}

A monolith parameter string can be retrieved using the function monolith_param_string** 19.4.2.3.3.2. String Param Getter

<<function_declarations>>=
int monolith_param_string(monolith_state_schema *s, int pos, char **val);
<<functions>>=
int monolith_param_string(monolith_state_schema *s, int pos, char **val)
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_string(p, val);
}
19.4.2.3.3.3. Const String Param Getter

In many situations, const strings are used more often, so a wrapper function has been created called monolith_param_stringc. On error, the function will return NULL.

<<function_declarations>>=
const char * monolith_param_stringc(monolith_state_schema *s, int pos);
<<functions>>=
const char * monolith_param_stringc(monolith_state_schema *s, int pos)
{
    char *str;
    if(!monolith_param_string(s, pos, &str)) return NULL;
    return str;
}
19.4.2.3.4. Schema Monolith State Parameter

This refers to the LED state of the monome grid.

19.4.2.3.4.1. Schema State Param Maker

<<function_declarations>>=
int monolith_param_mkmstate(monolith_state_schema *s,
                            int pos,
                            const char *name,
                            int len,
                            monolith_page_mstate *ms);
<<functions>>=
int monolith_param_mkmstate(monolith_state_schema *s,
                            int pos,
                            const char *name,
                            int len,
                            monolith_page_mstate *ms)
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_mkmstate(p, name, len, ms);
}
19.4.2.3.4.2. Schema State Param Getter

<<function_declarations>>=
int monolith_param_mstate(monolith_state_schema *s,
                          int pos,
                          monolith_page_mstate *ms);
<<functions>>=
int monolith_param_mstate(monolith_state_schema *s,
                          int pos,
                          monolith_page_mstate *ms)
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_mstate(p, ms);
}
19.4.2.3.5. DONE Schema Blob Parameter

CLOSED: [2019-04-05 Fri 21:36]

19.4.2.3.5.1. DONE Blob Param Maker

CLOSED: [2019-04-05 Fri 21:34]

<<function_declarations>>=
int monolith_param_mkblob(monolith_state_schema *s,
                          int pos,
                          const char *name,
                          int len,
                          void *blob,
                          unsigned int blobsize,
                          void (*dtor)(void *));
<<functions>>=
int monolith_param_mkblob(monolith_state_schema *s,
                          int pos,
                          const char *name,
                          int len,
                          void *blob,
                          unsigned int blobsize,
                          void (*dtor)(void *))
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_mkblob(p, name, len, blob, blobsize, dtor);
}
19.4.2.3.5.2. DONE Blob Param Getter

CLOSED: [2019-04-05 Fri 21:36]

<<function_declarations>>=
int monolith_param_blob(monolith_state_schema *s,
                        int pos,
                        void **blob,
                        unsigned int *blobsize);
<<functions>>=
int monolith_param_blob(monolith_state_schema *s,
                        int pos,
                        void **blob,
                        unsigned int *blobsize)
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_blob(p, blob, blobsize);
}
19.4.2.3.5.3. DONE Blob Param Setter

CLOSED: [2019-05-09 Thu 08:20] Unlike other parameter times, making a blob parameter and setting a blob parameter are two separate things. The blob value, once allocated is set with the function monolith_param_setblob#+NAME: function_declarations

int monolith_param_setblob(monolith_state_schema *s,
                           int pos,
                           void *blob,
                           unsigned int blobsize,
                           void (*dtor)(void *));
<<functions>>=
int monolith_param_setblob(monolith_state_schema *s,
                           int pos,
                           void *blob,
                           unsigned int blobsize,
                           void (*dtor)(void *))
{
    monolith_state_param *p;
    if(!monolith_state_schema_param(s, pos, &p)) return 0;
    return monolith_state_param_blob_set(p, blob, blobsize, dtor);
}

If the system mallocs and frees are used, then the function monolith_param_setblob_default function can be used.

<<function_declarations>>=
int monolith_param_setblob_default(monolith_state_schema *s,
                           int pos,
                           void *blob,
                           unsigned int blobsize);
<<functions>>=
static void free_blob(void *ud) {
    free(ud);
}
int monolith_param_setblob_default(monolith_state_schema *s,
                           int pos,
                           void *blob,
                           unsigned int blobsize)
{
    return monolith_param_setblob(s, pos, blob, blobsize, free_blob);
}

19.4.3. Monolith Schema write test

This is a quick function to make sure the schema things work out okay. From there, this can be swapped out to write to an actual SQLite database.

<<function_declarations>>=
void monolith_state_schema_test_write(monolith_state_schema *s,
                                      const char *key,
                                      unsigned int len);
<<functions>>=
void monolith_state_schema_test_write(monolith_state_schema *s,
                                      const char *key,
                                      unsigned int len)
{
    unsigned int n;
    monolith_state_param *param;
    char buf[256];
    int pos;
    pos = 0;
    strncpy(&buf[pos], "CREATE TABLE IF NOT EXISTS ", 28);
    pos += 27;
    strncpy(&buf[pos], key, len);
    pos += len;
    strncpy(&buf[pos], "(", 2);
    pos += 1;

    strncpy(&buf[pos], "key UNIQUE STRING, ", 20);
    pos += 19;

    for(n = 0; n < s->nparams; n++) {
        param = &s->params[n];
        switch(param->type) {
            case PARAM_NONE:
                printf("NONE");
                break;
            case PARAM_FLOAT:
                pos += sprintf(&buf[pos], "%s FLOAT", param->name);
                break;
            case PARAM_INT:
                pos += sprintf(&buf[pos], "%s INT", param->name);
                break;
            case PARAM_STRING:
                pos += sprintf(&buf[pos], "%s STRING", param->name);
                break;
            case PARAM_MSTATE:
                pos += sprintf(&buf[pos], "%s STRING", param->name);
                break;
            default:
                break;
        }

        if(n < s->nparams - 1) {
            pos += sprintf(&buf[pos], ", ");
        }
    }
    pos += sprintf(&buf[pos], ");");
    buf[pos] = 0;
    printf("%s\n", buf);
}

19.5. How monome state data is stored

The Grid state data is encoded as in a non standard ascii base 64 string, which allows the data to be stored in a portable manner, while sacrificing some storage efficiency. Each character in a base64 string encodes a 6 bit value. A grid row (16 bits, 2 bytes) is stored amongst base64 ascii characters, with 2 extra bits to spare. To mimic how the monome stores data (with "1" being on the far left), the bitfield is encoded in little endian format.

In the monolith base64 system, numbers end at ascii position 122 ('z') and move down to ascii position 58 (':'). Between this range are all printable ascii characters that also contain the entire alphabet (capital and lowercase). This chosen range is an aesthetic choice by the author.

19.5.1. Encoding/Decoding 6 bit values

The atomic operation for the monolith base64 algorithm are 6 bit values, which get represented as printable ascii character.

A 6 bit value is encoded with the function monolith_base64_6bit_encode, and decoded with the function monolith_base64_6bit_decode.

<<function_declarations>>=
char monolith_base64_6bit_encode(unsigned char x);
unsigned char monolith_base64_6bit_decode(char x);

Converting a 6-bit value to an ascii is a matter of masking the 8 bit value and adding the starting bias of 58.

<<functions>>=
char monolith_base64_6bit_encode(unsigned char x)
{
    return (x & 63) + 58;
}

Decoding an ascii value to a number is the reverse process of this.

<<functions>>=
unsigned char monolith_base64_6bit_decode(char x)
{
    return x - 58;
}

19.5.2. Encoding/Decoding a monome row

A monome state is encoded/decoded one row at a time. Rows are represented as 2-byte unsigned short values (16 bits), which get mapped to the monome in a little endian sequence. The base 64 encoding also respects this little endian storage.

A monome row is encoded using the function mononolith_base64_16bit_encodeand decoded with the function monolith_base64_16bit_decode.

<<function_declarations>>=
void monolith_base64_16bit_encode(unsigned short row, char *a, char *b, char *c);
unsigned short monolith_base64_16bit_decode(char a, char b, char c);

When a 16 bit value is encoded, it maps the first 6 bits into the character a, the second set of 6-bits into character b, and the remaining 4 bits into the character c.

<<functions>>=
void monolith_base64_16bit_encode(unsigned short row, char *a, char *b, char *c)
{
    *a = monolith_base64_6bit_encode(row & 63);
    *b = monolith_base64_6bit_encode((row >> 6) & 63);
    *c = monolith_base64_6bit_encode((row >> 12) & 63);
}

The encoding process does the reverse of this, taking the and A, B, C values, and using bitshifting operations to create a row.

<<functions>>=
unsigned short monolith_base64_16bit_decode(char a, char b, char c)
{
    unsigned short out;
    out = monolith_base64_6bit_decode(a);
    out |= monolith_base64_6bit_decode(b) << 6;
    out |= monolith_base64_6bit_decode(c) << 12;
    return out;
}

19.5.3. Encoding/Decoding a monome grid

The monome grid consists of 8 rows of 16 bits. The encode/decode functions handle converting to and from the C representation of 8 16-bit values to a ascii string buffer of 24 bytes (not including the null terminator). These functions are monolith_base64_grid_encode and monolith_base64_grid_decode, respectively.

<<function_declarations>>=
void monolith_base64_grid_encode(monolith_page_mstate *mstate, char *bytes);
void monolith_base64_grid_decode(monolith_page_mstate *mstate, char *bytes);
<<functions>>=
void monolith_base64_grid_encode(monolith_page_mstate *mstate, char *bytes)
{
    int row;
    unsigned short *grid;
    grid = (unsigned short *)mstate->data;
    for(row = 0; row < 8; row++) {
        monolith_base64_16bit_encode(grid[row],
                                     &bytes[0],
                                     &bytes[1],
                                     &bytes[2]);
        bytes+=3;
    }
}

void monolith_base64_grid_decode(monolith_page_mstate *mstate, char *bytes)
{
    int row;
    unsigned short *grid;
    grid = (unsigned short *)mstate->data;
    for(row = 0; row < 8; row++) {
        grid[row] = monolith_base64_16bit_decode(bytes[0],
                                                 bytes[1],
                                                 bytes[2]);
        bytes+=3;
    }
}

19.5.4. Base64 Scheme functions

Rows are encoded and decoded with monolith:base64-16bit-encode and monolith:base64-16bit-decode.

<<primitive_entries>>=
{"monolith:base64-16bit-encode", pp_base64_16bit_encode, 1, 1, {INT,___,___}},
{"monolith:base64-16bit-decode", pp_base64_16bit_decode, 1, 1, {STR,___,___}},
<<scheme_functions>>=
static cell pp_base64_16bit_encode(cell p)
{
    unsigned short x;
    char name[] = "monolith:base64-16bit-encode";
    char str[4];
    x = (unsigned short) integer_value(name, car(p));
    monolith_base64_16bit_encode(x, &str[0], &str[1], &str[2]);
    str[3] = 0;
    return s9_make_string(str, 3);
}
<<scheme_functions>>=
static cell pp_base64_16bit_decode(cell p)
{
    unsigned short x;
    char *str;
    str = string(car(p));
    x = monolith_base64_16bit_decode(str[0], str[1], str[2]);
    return s9_make_integer(x);
}

19.6. Generating SQL commands from Schema data

The Schema, once populated with parameters, is passed of to the monolith state to be translated to a SQL database. In order to do this, the Schema needs to be coverted to a SQL command.

There are two major SQL commands: a create table command, and a row insert command.

The functions to run these commands are expected to run twice: first, to do a runthrough to get the number of bytes needed to allocate. second, to actually write to the buffer.

19.6.1. SQL Create Command from Schema

This will create the necessary table creation command. This SQL command will check if the table exists first before writing, so it is safe to call every time.

19.6.1.1. monolith_state_schema_sql_create

<<function_declarations>>=
unsigned int monolith_state_schema_sql_create(monolith_state_schema *s,
                                              const char *tabname,
                                              unsigned int len,
                                              char *buf,
                                              int write);
<<functions>>=
unsigned int monolith_state_schema_sql_create(monolith_state_schema *s,
                                              const char *tabname,
                                              unsigned int len,
                                              char *buf,
                                              int write)
{
    unsigned int pos;
    unsigned int n;
    monolith_state_param *param;

    pos = 0;
    if(write) strncpy(&buf[pos], "CREATE TABLE IF NOT EXISTS ", 28);
    pos += 27;
    if(write) strncpy(&buf[pos], tabname, len);
    pos += len;
    if(write) strncpy(&buf[pos], "(", 2);
    pos += 1;

    if(write) strncpy(&buf[pos], "key TEXT UNIQUE, ", 18);
    pos += 17;

    for(n = 0; n < s->nparams; n++) {
        param = &s->params[n];
        switch(param->type) {
<<schema_sql_create_types>>
            default:
                break;
        }

        if(n < s->nparams - 1) {
            if(write) pos += sprintf(&buf[pos], ", ");
            else pos += snprintf(NULL, 0, ", ");
        }
    }
    if(write) pos += sprintf(&buf[pos], ");");
    else pos += snprintf(NULL, 0, ");");
    if(write) buf[pos] = 0;
    return pos + 1; /* don't forget the NULL terminator :) */
}
19.6.1.2. Parameter types

19.6.1.2.1. No Parameter

<<schema_sql_create_types>>=
case PARAM_NONE:
    break;
19.6.1.2.2. SQLite Float

<<schema_sql_create_types>>=
case PARAM_FLOAT:
    if(write) pos += sprintf(&buf[pos], "%s FLOAT", param->name);
    else pos += snprintf(NULL, 0, "%s FLOAT", param->name);
    break;
19.6.1.2.3. SQLite Integer

<<schema_sql_create_types>>=
case PARAM_INT:
    if(write) pos += sprintf(&buf[pos], "%s INT", param->name);
    else pos += snprintf(NULL, 0, "%s INT", param->name);
    break;
19.6.1.2.4. SQLite String

<<schema_sql_create_types>>=
case PARAM_STRING:
    if(write) pos += sprintf(&buf[pos], "%s TEXT", param->name);
    else pos += snprintf(NULL, 0, "%s TEXT", param->name);
    break;
19.6.1.2.5. SQLite Monome State

Well, this is really represented as the TEXT type.

<<schema_sql_create_types>>=
case PARAM_MSTATE:
    if(write) pos += sprintf(&buf[pos], "%s TEXT", param->name);
    else pos += snprintf(NULL, 0, "%s TEXT", param->name);
    break;
19.6.1.2.6. SQLite Blob

SQLite uses its own BLOB type.

<<schema_sql_create_types>>=
case PARAM_BLOB:
    if(write) pos += sprintf(&buf[pos], "%s BLOB", param->name);
    else pos += snprintf(NULL, 0, "%s BLOB", param->name);
    break;

19.6.2. SQL Insert Command from Schema

The following function described below will create an SQLite template string, which can then be used by the SQLite API to write data into a string.

<<function_declarations>>=
unsigned int monolith_state_schema_sql_insert(monolith_state_schema *s,
                                              const char *tab,
                                              unsigned int keylen,
                                              char *buf,
                                              int write);
<<functions>>=
unsigned int monolith_state_schema_sql_insert(monolith_state_schema *s,
                                              const char *tab,
                                              unsigned int len,
                                              char *buf,
                                              int write)
{
    unsigned int pos;
    unsigned int n;
    monolith_state_param *param;
    int p;

    pos = 0;
    if(write) strncpy(&buf[pos], "REPLACE INTO ", 14);
    pos += 13;
    if(write) strncpy(&buf[pos], tab, len);
    pos += len;
    if(write) strncpy(&buf[pos], " (key, ", 8);
    pos += 7;

    for(n = 0; n < s->nparams; n++) {
        param = &s->params[n];
        if(write) pos += sprintf(&buf[pos], "%s", param->name);
        else pos += snprintf(NULL, 0, "%s", param->name);
        if(n < s->nparams - 1) {
            if(write) pos += sprintf(&buf[pos], ", ");
            else pos += snprintf(NULL, 0, ", ");
        }
    }

    if(write) pos += sprintf(&buf[pos], ")\n");
    else pos += snprintf(NULL, 0, ")");

    if(write) strncpy(&buf[pos], "VALUES(?1, ", 12);
    pos += 11;

    p = 2;
    for(n = 0; n < s->nparams; n++) {
        param = &s->params[n];
        if(write) pos += sprintf(&buf[pos], "?%d", p);
        else pos += snprintf(NULL, 0, "?%d", p);
        if(n < s->nparams - 1) {
            if(write) pos += sprintf(&buf[pos], ", ");
            else pos += snprintf(NULL, 0, ", ");
        }
        p++;
    }

    if(write) pos += sprintf(&buf[pos], ");");
    else pos += snprintf(NULL, 0, ");");
    if(write) buf[pos] = 0;
    return pos + 1; /* don't forget the NULL terminator :) */
}

19.6.3. SQL Select Command from Schema

When a state is read from disk, a SQLite select command must be prepared.

<<function_declarations>>=
unsigned int monolith_state_schema_sql_select(monolith_state_schema *s,
                                              const char *tab,
                                              unsigned int tablen,
                                              const char *key,
                                              unsigned int keylen,
                                              char *buf,
                                              int write);
<<functions>>=
unsigned int monolith_state_schema_sql_select(monolith_state_schema *s,
                                              const char *tab,
                                              unsigned int tablen,
                                              const char *key,
                                              unsigned int keylen,
                                              char *buf,
                                              int write)
{
    unsigned int pos;

    pos = 0;


    if(write) strncpy(&buf[pos], "SELECT * FROM ", 15);
    pos += 14;
    if(write) strncpy(&buf[pos], tab, tablen);
    pos += tablen;
    if(write) strncpy(&buf[pos], " WHERE key == '", 16);
    pos += 15;
    if(write) strncpy(&buf[pos], key, keylen + 1);
    pos += keylen;
    if(write) strncpy(&buf[pos], "';", 3);
    pos += 2;

    if(write) buf[pos] = 0;
    return pos + 1; /* don't forget the NULL terminator :) */
}

19.7. SQLite File I/O

Actual operations involving the SQLite API happen with these functions below.

19.7.1. Opening/Closing a SQLite database.

A database is opened with the function monolith_state_dbopen. This is a wrapper around sqlite3_open. A database is closed with monolith_state_dbclose. This is a wrapper around sqlite3_close.

NOTE: these functions have been slightly renamed from state_open and state_close to state_dbopen and state_dbclose. This was done because I wanted to use that namespace to handle the global monolith state pointer (a design addition I made after this section).

<<function_declarations>>=
int monolith_state_dbopen(monolith_state *state, const char *filename);
<<functions>>=
int monolith_state_dbopen(monolith_state *state, const char *filename)
{
    int rc;
    rc = sqlite3_open(filename, &state->db);
    if(rc) {
        fprintf(stderr, "Could not open %s: %s\n",
                filename,
                sqlite3_errmsg(state->db));
        sqlite3_close(state->db);
        return 0;
    }
    return 1;
}
<<function_declarations>>=
void monolith_state_dbclose(monolith_state *state);
<<functions>>=
void monolith_state_dbclose(monolith_state *state)
{
    if(state->db == NULL) return;
    sqlite3_close(state->db);
}

19.7.2. A Note on Blobs

NOTE: blobs opt to use the normal blob rather than the blob64 type, which takes in a signed integer for size (sqlite3_bind_blob).

This will potentially truncate some larger blobs. This is done because the way to get size (sqlite3bytes) returns an integer, and not an unsigned int.

<<bind_blob_parameter>>=
void *blob;
unsigned int blobsize;
blobsize = 0;
blob = NULL;
monolith_state_param_blob(param, &blob, &blobsize);
sqlite3_bind_blob(stmt, p, blob, (int)blobsize, NULL);
break;

When reading a blob, an integer size is used to match the type in sqlite3_column_bytes.

The blob handled by sqlite is freed internally by SQLite, so the data must be copied over and allocated.

<<get_blob_parameter>>=
int bytes;
int b;
const unsigned char *blob;
unsigned char *buf;
blob = (unsigned char *)sqlite3_column_blob(stmt, p);
bytes = sqlite3_column_bytes(stmt, p);
buf = malloc(bytes);
for(b = 0; b < bytes; b++) buf[b] = blob[b];
monolith_state_param_blob_set(param, buf, bytes, free_the_blob);
break;

The callback used to free the copied data is called free_the_blob. Just a wrapper around the system free.

<<static_function_declarations>>=
static void free_the_blob(void *ud);
<<functions>>=
static void free_the_blob(void *ud)
{
    free(ud);
}

19.7.3. Writing a Schema to a SQLite database

The function monolith_state_write_schema writes a schema to disk. This function typically gets called inside of a page save callback.

<<function_declarations>>=
int monolith_state_write_schema(monolith_state *ms,
                                monolith_state_schema *schema,
                                const char *tab,
                                unsigned int tablen,
                                const char *key,
                                unsigned int keylen);


<<functions>>=
int monolith_state_write_schema(monolith_state *ms,
                                monolith_state_schema *schema,
                                const char *tab,
                                unsigned int tablen,
                                const char *key,
                                unsigned int keylen)
{
    unsigned int nbytes_create;
    unsigned int nbytes_insert;
    char *tmp;
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc;
    int n;
    monolith_state_param *param;
    float *ftmp;
    int *itmp;
    char *stmp;
    int p;

    rc = 1;
    db = ms->db;

    if(db == NULL) return 0;

    nbytes_create = monolith_state_schema_sql_create(schema,
                                                     tab,
                                                     tablen,
                                                     NULL,
                                                     0);

    tmp = malloc(nbytes_create);

    monolith_state_schema_sql_create(schema, tab, tablen, tmp, 1);

    sqlite3_prepare(db, tmp, -1, &stmt, NULL);
    rc = sqlite3_step(stmt);

    if(rc != SQLITE_DONE) {
        fprintf(stderr, "SQLite error: %s\n", sqlite3_errmsg(db));
    }

    sqlite3_finalize(stmt);

    nbytes_insert = monolith_state_schema_sql_insert(schema,
                                                     tab,
                                                     tablen,
                                                     NULL,
                                                     0);
    if(nbytes_insert > nbytes_create) {
        tmp = realloc(tmp, nbytes_insert);
    }
    monolith_state_schema_sql_insert(schema, tab, tablen, tmp, 1);

    sqlite3_prepare(db, tmp, -1, &stmt, NULL);

    sqlite3_bind_text(stmt, 1, key, keylen, NULL);

    p = 2;
    for(n = 0; n < schema->nparams; n++) {
        param = &schema->params[n];
        switch(param->type) {
            case PARAM_NONE:
                break;
            case PARAM_FLOAT:
                monolith_state_param_float(param, &ftmp);
                sqlite3_bind_double(stmt, p, *ftmp);
                break;
            case PARAM_INT:
                monolith_state_param_int(param, &itmp);
                sqlite3_bind_double(stmt, p, *itmp);
                break;
            case PARAM_STRING:
                monolith_state_param_string(param, &stmp);
                sqlite3_bind_text(stmt, p, stmp, -1, NULL);
                break;
            case PARAM_MSTATE:
                stmp = param->ud;
                sqlite3_bind_text(stmt, p, stmp, 24, NULL);
                break;
            case PARAM_BLOB: {
<<bind_blob_parameter>>
            }
            default:
                break;
        }
        p++;
    }

    rc = sqlite3_step(stmt);
    if(rc != SQLITE_DONE) {
        fprintf(stderr, "SQLite error: %s\n", sqlite3_errmsg(db));
    }

    sqlite3_finalize(stmt);

    free(tmp);
    return 1;
}

19.7.4. Reading from a SQLite databse to a Schema

The function monolith_state_read_schema reads a schema from disk. This function typically gets called inside of a page load callback.

<<function_declarations>>=
int monolith_state_read_schema(monolith_state *ms,
                               monolith_state_schema *schema,
                               const char *tab,
                               unsigned int tablen,
                               const char *key,
                               unsigned int keylen);
<<functions>>=
int monolith_state_read_schema(monolith_state *ms,
                               monolith_state_schema *schema,
                               const char *tab,
                               unsigned int tablen,
                               const char *key,
                               unsigned int keylen)
{
    unsigned int nbytes;
    char *tmp;
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc;
    int n;
    monolith_state_param *param;
    float ftmp;
    int itmp;
    int p;

    rc = 1;
    db = ms->db;

    if(db == NULL) return 0;

    nbytes = monolith_state_schema_sql_select(schema,
                                              tab, tablen,
                                              key, keylen, NULL, 0);

    tmp = malloc(nbytes);

    monolith_state_schema_sql_select(schema,
                                     tab, tablen,
                                     key, keylen, tmp, 1);
    sqlite3_prepare(db, tmp, -1, &stmt, NULL);
    rc = sqlite3_step(stmt);

    if(rc == SQLITE_DONE) {
        fprintf(stderr, "Key doesn't exist: ");
        fwrite(key, 1, keylen, stderr);
        fprintf(stderr, "\n");
        sqlite3_finalize(stmt);
        free(tmp);
        return 0;
    }

    p = 1;

    for(n = 0; n < schema->nparams; n++) {
        param = &schema->params[n];
        switch(param->type) {
            case PARAM_NONE:
                break;
            case PARAM_FLOAT:
                ftmp = sqlite3_column_double(stmt, p);
                monolith_state_param_float_set(param, ftmp);
                break;
            case PARAM_INT:
                itmp = sqlite3_column_int(stmt, p);
                monolith_state_param_int_set(param, itmp);
                break;
            case PARAM_STRING:
                fprintf(stderr, "Oops. Not ready to read strings\n");
                /* monolith_state_param_string(param, &stmp); */
                /* sqlite3_bind_text(stmt, p, stmp, -1, NULL); */
                break;
            case PARAM_MSTATE: {
                const char *str;
                str = (const char *)sqlite3_column_text(stmt, p);
                monolith_state_param_mstate_set(param, str);
                break;
            }
            case PARAM_BLOB: {
<<get_blob_parameter>>
            }
            default:
                break;
        }
        p++;
    }

    sqlite3_finalize(stmt);
    free(tmp);
    return 1;
}

19.8. Opening/Closing a Monolith State database

This opens the state stored inside of the monolith struct.

19.8.1. Opening The Monolith State

19.8.1.1. Opening State From C

This can be done with the function monolith_state_open.

<<function_declarations>>=
int monolith_state_open(monolith_d *m, const char *filename);
<<functions>>=
int monolith_state_open(monolith_d *m, const char *filename)
{
    return monolith_state_dbopen(&m->state, filename);
}
19.8.1.2. Opening State from Scheme

This can be done with the function monolith:state-open.

<<primitive_entries>>=
{"monolith:state-open", pp_state_open, 1, 1, {STR,___,___}},
<<scheme_functions>>=
static cell pp_state_open(cell p) {
    monolith_d *m;
    const char *filename;
    int rc;

    filename = string(car(p));
    m = monolith_data_get();
    rc = monolith_state_open(m, filename);
    if(!rc) error("Could not open file", car(p));
    return UNSPECIFIC;
}
19.8.1.3. Opening State from Janet

This is done with monolith/state-open.

<<core_janet_entries>>=
{
"monolith/state-open",
j_state_open,
"Opens a state database.\n"
},
<<janet_functions>>=
static Janet j_state_open(int32_t argc, Janet *argv)
{
    const char *filename;
    int rc;
    monolith_d *m;

    janet_fixarity(argc, 1);
    filename = (const char *)janet_getstring(argv, 0);

    m = monolith_data_get();

    rc = monolith_state_open(m, filename);
    if (!rc) fprintf(stderr,
                    "Could not open file '%s'",
                    filename);

    return janet_wrap_nil();
}

19.8.2. Closing Monolith State

19.8.2.1. Closing the State from C

This can be done with monolith_state_close#+NAME: function_declarations

void monolith_state_close(monolith_d *m);
<<functions>>=
void monolith_state_close(monolith_d *m)
{
    monolith_state_dbclose(&m->state);
}
19.8.2.2. Closing the State from Scheme

State can be explicitely closed with monolith:state-close.

<<primitive_entries>>=
{"monolith:state-close", pp_state_close, 0, 0, {___,___,___}},
<<scheme_functions>>=
static cell pp_state_close(cell p) {
    monolith_d *m;
    m = monolith_data_get();
    monolith_state_close(m);
    return UNSPECIFIC;
}
19.8.2.3. Opening State from Janet

This is done with monolith/state-close.

<<core_janet_entries>>=
{
"monolith/state-close",
j_state_close,
"Closes a state database.\n"
},
<<janet_functions>>=
static Janet j_state_close(int32_t argc, Janet *argv)
{
    monolith_d *m;

    janet_fixarity(argc, 0);
    m = monolith_data_get();
    monolith_state_close(m);

    return janet_wrap_nil();
}

19.9. Raw SQLite commands on open state file

Occasionally, it is desirable to evaluate raw sqlite commands on an open state file.

19.9.1. In C

This can be done with monolith_state_sql.

<<function_declarations>>=
int monolith_state_sql(monolith_d *m, const char *cmd);
<<functions>>=
int monolith_state_sql(monolith_d *m, const char *cmd)
{
    monolith_state *state;
    sqlite3 *db;

    state = &m->state;
    db = state->db;

    if (db == NULL) return 0;

    sqlite3_exec(db, cmd, NULL, NULL, NULL);

    return 1;
}

19.9.2. In Scheme

This can be done with monolith:state-sql#+NAME: primitive_entries

{"monolith:state-sql", pp_state_sql, 1, 1, {STR,___,___}},
<<scheme_functions>>=
static cell pp_state_sql(cell p)
{
    monolith_d *m;
    const char *cmd;

    cmd = string(car(p));
    m = monolith_data_get();

    monolith_state_sql(m, cmd);

    return UNSPECIFIC;
}



prev | home | next