From: Ralph Ronnquist Date: Mon, 18 Jul 2022 06:00:26 +0000 (+1000) Subject: major reorganisation X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=1d530c65782b499bb75afff76f1214edfe30ec21;p=rrq%2Frrqmisc.git major reorganisation --- diff --git a/Doxyfile b/Doxyfile index a32e453..d4690db 100644 --- a/Doxyfile +++ b/Doxyfile @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = /home/ralph/src/borta/misc/docs +OUTPUT_DIRECTORY = /home/ralph/src/borta/rrqmisc/docs # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -864,7 +864,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = /home/ralph/src/borta/misc/vector +INPUT = /home/ralph/src/borta/rrqmisc/vector # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/htable/Makefile b/htable/Makefile index 4d0ed95..e8bd7e1 100644 --- a/htable/Makefile +++ b/htable/Makefile @@ -1,18 +1,14 @@ -default: libhtable.a +LIBRARY = librrqhtable.a +LIBOBJS = htable.o -.INTERMEDIATE: htable.o -htable.o: CFLAGS = -Wall -g -htable.o: htable.c +default: $(LIBRARY) example-htable +CLEANRM += $(LIBRARY) -libhtable.a: htable.o - $(AR) r $@ $^ -CLEANRM += libhtable.a +include ../common.mk .INTERMEDIATE: example-htable.o example-htable: CFLAGS = -Wall -g -example-htable: LDLIBS = libhtable.a -example-htable: example-htable.o libhtable.a +example-htable: example-htable.o $(LIBRARY) + CLEANRM += example-htable -clean: - rm -f $(CLEANRM) diff --git a/logic/AndQuery.c b/logic/AndQuery.c new file mode 100644 index 0000000..169ff9e --- /dev/null +++ b/logic/AndQuery.c @@ -0,0 +1,85 @@ +#include +#include +#include + +static void AndQuery_reclaim(Query *this) { + AndQuery *q = (AndQuery*) this; + int i; + for ( i = 0; i < q->size; i++ ) { + Query_reclaim( q->conjuncts[i] ); + } + free( q->conjuncts ); + free( this ); +} + +static int AndQuery_next( + Query *this,BindingTable *bt,enum NextState state) +{ + AndQuery *q = (AndQuery*) this; + int i = q->size - 1; + enum NextState s = subsequent; + switch ( state ) { + case initial: + q->active = 1; + i = 0; + s = initial; + // Fall through? + case subsequent: + while ( i < q->size ) { + if ( Query_next( q->conjuncts[i], bt, s ) ) { + continue; + } + Query_next( q->conjuncts[i], bt, restore ); + if ( i-- == 0 ) { + // The first conjunct now exhausted + q->active = 0; + return 0; + } + s = subsequent; + } + return 1; + case restore: + if ( q->active ) { + for ( ; i >= 0; i-- ) { + Query_next( q->conjuncts[i], bt, restore ); + } + } + q->active = 0; + return 0; + } + return 0; +} + +static void AndQuery_variables(Query *this,HashVector *hv) { + AndQuery *q = (AndQuery*) this; + int i; + for ( i = 0; i < q->size; i++ ) { + Query_variables( q->conjuncts[i], hv ); + } +} + +static struct QueryCallbacks AndQuery_def = { + .reclaim = AndQuery_reclaim, + .next = AndQuery_next, + .variables = AndQuery_variables +}; + +Query *Query_and(int n,...) { + va_list args; + AndQuery *q = (AndQuery *) + malloc( sizeof( AndQuery ) ); + (*q) = (AndQuery) { + .def = &AndQuery_def, + .active = 0, + .size = n, + .conjuncts = (Query**) malloc( n * sizeof( Query* ) ) + }; + int i; + va_start( args, n ); + for ( i = 0; i < n; i++ ) { + q->conjuncts[i] = va_arg( args, Query* ); + } + va_end( args ); + return (Query*) q; +} + diff --git a/logic/AndQuery.h b/logic/AndQuery.h new file mode 100644 index 0000000..7871eea --- /dev/null +++ b/logic/AndQuery.h @@ -0,0 +1,19 @@ +#ifndef AndQuery_H +#define AndQuery_H + +#include + +/** + * AndQuery represents a conjunction of sub queries. + * \extends Query + * \related Query + */ +typedef struct { + struct QueryCallbacks *def; + int active; + int size; + Query **conjuncts; +} AndQuery; + + +#endif diff --git a/logic/AssignQuery.c b/logic/AssignQuery.c new file mode 100644 index 0000000..bb264aa --- /dev/null +++ b/logic/AssignQuery.c @@ -0,0 +1,84 @@ +#include +#include +#include + +// Release any memory. +static void AssignQuery_reclaim(Query *this) { + AssignQuery *q = (AssignQuery*) this; + free( q->saved ); + free( this ); +} + +static int AssignQuery_check(Tuple *a,Tuple *b) { + int i; + for ( i = 0; i < a->size; i++ ) { + char *value = a->elements[i]; + char *current = b->elements[i]; + if ( value && current && current != value && + strcmp( current, value ) != 0 ) { + return 0; + } + } + return 1; +} + +// Make names have given values and return 1. If any name has a +// different value then return 0. Values are strings. +static int AssignQuery_next( + Query *this,BindingTable *bt,enum NextState state) { + AssignQuery *q = (AssignQuery*) this; + switch ( state ) { + case initial: + if ( q->saved == 0 ) { + q->saved = Tuple_clone( q->names ); + } else { + memcpy( q->saved, q->names, q->names->size * sizeof( void* ) ); + } + BindingTable_deref( bt, q->saved ); + // Check with new values + if ( AssignQuery_check( q->values, q->saved ) ) { + BindingTable_set_all( bt, q->names, q->values, 0 ); + return 1; + } + // Fall through + case subsequent: + case restore: + if ( q->saved ) { + BindingTable_set_all( bt, q->names, q->saved, 1 ); + free( q->saved ); + q->saved = 0; + } + return 0; + } + return 0; +} + +static void AssignQuery_variables(Query *this,HashVector *hv) { + AssignQuery *q = (AssignQuery*) this; + unsigned long i; + for ( i = 0; i < q->names->size; i++ ) { + HashVector_add( hv, q->names->elements[i] ); + } +} + +static struct QueryCallbacks AssignQuery_def = { + .reclaim = AssignQuery_reclaim, + .next = AssignQuery_next, + .variables = AssignQuery_variables +}; + +/** + * Return a Query object representing an assignment of one or more + * variables. + */ +Query *Query_assign(int arity,Tuple *names,Tuple *values) { + AssignQuery *q = (AssignQuery*) + malloc( sizeof( AssignQuery ) ); + (*q) = (AssignQuery) { + .def = &AssignQuery_def, + .names = names, + .values = values, + .saved = 0 + }; + return (Query*) q; +} diff --git a/logic/AssignQuery.h b/logic/AssignQuery.h new file mode 100644 index 0000000..2e38b87 --- /dev/null +++ b/logic/AssignQuery.h @@ -0,0 +1,23 @@ +#ifndef AssignQuery_H +#define AssignQuery_H + +#include +#include + +/** + * AssignQuery represents an assignment of values to names. + * + * \extends Query + * \related Query + */ +typedef struct { + struct QueryCallbacks *def; + Tuple *names; + Tuple *values; + Tuple *saved; +} AssignQuery; + +void AssignQuery_assign( + BindingTable *bt,Tuple *names,Tuple *values,int all); + +#endif diff --git a/logic/Binding.c b/logic/Binding.c new file mode 100644 index 0000000..9bcc0c1 --- /dev/null +++ b/logic/Binding.c @@ -0,0 +1,60 @@ +#include +#include +#include + +/** + * This callback function returns the hashcode of a Binding key, which + * is its name string. + */ +static unsigned long Binding_hashcode(void *this,void *key) { + return HashVector_hashcode( + (unsigned char *) key, strlen( (char*) key ) ); +} + +/** + * This callback function determines whether a Binding item has a + * given key or not. + */ +static int Binding_haskey(void *this,void *item,void *key) { + Binding *b = (Binding*) item; + char *name = (char *) key; + return strcmp( name, (char*) b->name ) == 0; +} + +/** + * This callback function returns the key of a Binding itme, which is + * its name string. + */ +static void *Binding_itemkey(void *this,void *item) { + return ((Binding*) item)->name; +} + + +/** + * This callback function handles the "release" of a Binding key, + * which is to do nothing, since the name string memory is not managed + * here. + */ +static void Binding_releasekey(void *this,void *key) { +} + +/** + * This callback function writes a representation of a Binding item + * into a character buffer. + */ +static int Binding_tostring(void *this,void *item,char *buffer,int limit) { + Binding *b = (Binding*) item; + return snprintf( buffer, limit, "{%s,%p}", b->name, b->value ); +} + +/** + * This is the "item type" for Binding items. + */ +ItemKeyFun Bindingitem = { + .hashcode = Binding_hashcode, + .haskey = Binding_haskey, + .itemkey = Binding_itemkey, + .releasekey = Binding_releasekey, + .tostring = Binding_tostring +}; + diff --git a/logic/Binding.h b/logic/Binding.h new file mode 100644 index 0000000..2d1a0b3 --- /dev/null +++ b/logic/Binding.h @@ -0,0 +1,23 @@ +#ifndef Binding_H +#define Binding_H + +#include + +/** + * \brief A Binding is an association of a (var*) name with a (void*) + * value. + * + * A \b (void*)0 value marks an "unbound" value. + */ +typedef struct Binding { + char *name; + void *value; +} Binding; + +/** + * \brief This is the applicable ItemKeyFun callbacks for a Binding + * item. + */ +extern ItemKeyFun Bindingitem; + +#endif diff --git a/logic/BindingTable.c b/logic/BindingTable.c new file mode 100644 index 0000000..2728516 --- /dev/null +++ b/logic/BindingTable.c @@ -0,0 +1,72 @@ +#include +#include +#include + +BindingTable *BindingTable_create() { + BindingTable *this = (BindingTable*) malloc( sizeof( BindingTable ) ); + (*this) = (HashVector) { + .table = (Vector) { + .variant = Nibble_index_levels, .size = 16, .entries = 0 + }, .fill = 0, .holes = 0, .type = &Bindingitem + }; + return this; +} + +void BindingTable_release(BindingTable *bt) { + if ( bt ) { + Vector_resize( &bt->table, 0, Vector_free_any, 0 ); + free( bt ); + } +} + +void BindingTable_set(BindingTable *bt,char *name,void *value) { + Binding *b = (Binding*) HashVector_find( bt, name ); + if ( b == 0 ) { + b = (Binding*) malloc( sizeof( Binding ) ); + b->name = name; + HashVector_add( bt, b ); + } + b->value = value; +} + +void *BindingTable_get(BindingTable *bt,char *name) { + Binding *b = (Binding*) HashVector_find( bt, name ); + return b? b->value : 0; +} + +void BindingTable_deref(BindingTable *bt,Tuple *t) { + unsigned long i; + for ( i = 0; i < t->size; i++ ) { + if ( t->elements[i] ) { + t->elements[i] = BindingTable_get( bt, t->elements[i] ); + } + } +} + +#if 0 +int BindingTable_unify( + BindingTable *bt,char *n1,char *n2,int (*eq)(void*,void*)) { + void *v1 = BindingTable_get( bt, n1 ); + void *v2 = BindingTable_get( bt, n2 ); + if ( v2 && v1 == 0 ) { + BindingTable_set( bt, n1, v2 ); + } + if ( v1 && v2 == 0 ) { + BindingTable_set( bt, n2, v1 ); + } + return ( v1 && v2 )? ( eq? ( eq( v1, v2 ) == 0 ) : ( v1 == v2 ) ) : 1; +} +#endif + +Tuple *BindingTable_get_all(BindingTable *bt,Tuple *t) { + Tuple *vt = Tuple_clone( t ); + BindingTable_deref( bt, vt ); + return vt; +} + +void BindingTable_set_all(BindingTable *bt,Tuple *nm,Tuple *vs,int all) { + int i; + for ( i = 0; i < nm->size; i++ ) { + BindingTable_set( bt, nm->elements[i], vs->elements[i] ); + } +} diff --git a/logic/BindingTable.h b/logic/BindingTable.h new file mode 100644 index 0000000..aa4ee6e --- /dev/null +++ b/logic/BindingTable.h @@ -0,0 +1,97 @@ +#ifndef BindingTable_H +#define BindingTable_H + +#include +#include +#include + +/** + * A BindingTable is a chain of \ref HashVector "HashVectors" of + * Binding items that associate a (char*) name with a (void*) value. + */ +typedef HashVector/**/ BindingTable; + +/** + * \brief Allocate a new \ref BindingTable. + * + * \returns the allocated \ref bandingtable. + * + * \related BindingTable + */ +extern BindingTable *BindingTable_create(); + +/** + * \brief Reclaim a \ref BindingTable with all its bindings. + * + * \param bt is the \ref BindingTable to reclaim. + * + * \related BindingTable + */ +extern void BindingTable_release(BindingTable *bt); + +/** + * \brief Set a Binding in a \ref BindingTable. + * + * \param bt is the \ref BindingTable concerned. + * + * \param name is the Binding name. + * + * \param value is the Binding value. + * + * \note Binding names are equal or not by means of strcmp, and each + * name has a at most single Binding. + * + * The name and the value are held as given with all memory management + * the callers responsibility. This function will however create new + * Binding objects and reclaim the old ones as needed. + * + * A value of \b 0 indicates "unbound". + * + * \related BindingTable + */ +extern void BindingTable_set(BindingTable *bt,char *name,void *value); + +/** + * \brief Get a Binding from a BindingTable chain. + * + * \param bt is the first BindingTable concerned. + * + * \param name is the Binding variable name. + * + * \returns the value of the found Binding, or \b 0 if none is found. + * + * \note Binding names are equal or not by means of strcmp, and each + * name has a at most single Binding. + * + * \note Not also that a name can be made unbound on top of being + * bound by setting it to \b 0. + * + * \related BindingTable + */ +extern void *BindingTable_get(BindingTable *bt,char *name); + +/** + * \brief Replace all names with their values. + * + * \param bt is the BindingTable concerned. + * + * \param t is the tuple of (char*) names to dereference. + */ +extern void BindingTable_deref(BindingTable *bt,Tuple *t); + +/** + * \brief Set values for names, optionally unbinding names as well. + * + * \param bt is the bindingtable concerned. + * + * \param nm is the Tuple of names to bind. + * + * \param vs is the Tuple of values. + * + * \param all is a flag to assign all (1) or only non-zero (0) values. + * + * \note The values tuple must be as wide as the names tuple. + */ +extern void BindingTable_set_all(BindingTable *bt,Tuple *nm,Tuple *vs,int all); + +#endif diff --git a/logic/Makefile b/logic/Makefile new file mode 100644 index 0000000..e18ef6c --- /dev/null +++ b/logic/Makefile @@ -0,0 +1,13 @@ +LIBRARY = librrqlogic.a +LIBOBJS += Tuple.o TupleSchema.o Relation.o +LIBOBJS += Binding.o BindingTable.o +LIBOBJS += Query.o +LIBOBJS += AssignQuery.o RelationQuery.o +LIBOBJS += NotQuery.o AndQuery.o OrQuery.o +#LIBOBJS += View.o + +default: $(LIBRARY) + +all: default + +include ../common.mk diff --git a/logic/NotQuery.c b/logic/NotQuery.c new file mode 100644 index 0000000..8418715 --- /dev/null +++ b/logic/NotQuery.c @@ -0,0 +1,41 @@ +#include +#include + +static void NotQuery_reclaim(Query *this) { + NotQuery *q = (NotQuery*) this; + Query_reclaim( q->query ); + free( this ); +} + +static int NotQuery_next( + Query *this,BindingTable *bt,enum NextState state) +{ + NotQuery *q = (NotQuery*) this; + if ( state == initial ) { + if ( Query_next( q->query, bt, initial ) ) { + Query_next( q->query, bt, restore ); + return 0; + } + return 1; + } + return 0; +} + +static void NotQuery_variables(Query *this,HashVector *hv) { + Query_variables( ((NotQuery*) this)->query, hv ); +} + +static struct QueryCallbacks NotQuery_def = { + .reclaim = NotQuery_reclaim, + .next = NotQuery_next, + .variables = NotQuery_variables +}; + +Query *Query_not(Query *q) { + NotQuery *nq = (NotQuery*) malloc( sizeof( NotQuery ) ); + (*nq) = (NotQuery) { + .def = &NotQuery_def, + .query = q + }; + return (Query*) nq; +} diff --git a/logic/NotQuery.h b/logic/NotQuery.h new file mode 100644 index 0000000..f2bf7ed --- /dev/null +++ b/logic/NotQuery.h @@ -0,0 +1,16 @@ +#ifndef NotQuery_H +#define NotQuery_H + +#include + +/** + * NotQuery represents logical negation. + * + * \extends Query + */ +typedef struct NotQuery { + struct QueryCallbacks *def; + Query *query; +} NotQuery; + +#endif diff --git a/logic/OrQuery.c b/logic/OrQuery.c new file mode 100644 index 0000000..5a48e5e --- /dev/null +++ b/logic/OrQuery.c @@ -0,0 +1,76 @@ +#include +#include +#include + +static void OrQuery_reclaim(Query *this) { + OrQuery *q = (OrQuery*) this; + int i; + for ( i = 0; i < q->size; i++ ) { + Query_reclaim( q->disjuncts[i] ); + } + free( q->disjuncts ); + free( this ); +} + +static int OrQuery_next( Query *this,BindingTable *bt,enum NextState state) { + OrQuery *q = (OrQuery*) this; + int i = q->index; + enum NextState s = subsequent; + switch ( state ) { + case initial: + i = 0; + s = initial; + case subsequent: + for ( ; i < q->size; i++ ) { + if ( Query_next( q->disjuncts[i], bt, s ) ) { + q->index = i; + return 1; + } + Query_next( q->disjuncts[i], bt, restore ); + s = initial; + } + q->index = -1; + return 0; + case restore: + if ( i >= 0 ) { + Query_next( q->disjuncts[i], bt, restore ); + q->index = -1; + } + return 0; + } + return 0; +} + +static void OrQuery_variables(Query *this,HashVector *hv) { + OrQuery *q = (OrQuery*) this; + int i; + for ( i = 0; i < q->size; i++ ) { + Query_variables( q->disjuncts[i], hv ); + } +} + +static struct QueryCallbacks OrQuery_def = { + .reclaim = OrQuery_reclaim, + .next = OrQuery_next, + .variables = OrQuery_variables +}; + +Query *Query_or(int n,...) { + va_list args; + OrQuery *q = (OrQuery *) + malloc( sizeof( OrQuery ) ); + (*q) = (OrQuery) { + .def = &OrQuery_def, + .index = -1, + .size = n, + .disjuncts = (Query**) malloc( n * sizeof( Query* ) ), + }; + int i; + va_start( args, n ); + for ( i = 0; i < n; i++ ) { + q->disjuncts[i] = va_arg( args, Query* ); + } + va_end( args ); + return (Query*) q; +} + diff --git a/logic/OrQuery.h b/logic/OrQuery.h new file mode 100644 index 0000000..9528356 --- /dev/null +++ b/logic/OrQuery.h @@ -0,0 +1,18 @@ +#ifndef OrQuery_H +#define OrQuery_H + +#include + +/** + * OrQuery represents a disjunction of sub queries. + * + * \extends Query + */ +typedef struct { + struct QueryCallbacks *def; + int index; + int size; + Query **disjuncts; +} OrQuery; + +#endif diff --git a/logic/Query.c b/logic/Query.c new file mode 100644 index 0000000..94a39d7 --- /dev/null +++ b/logic/Query.c @@ -0,0 +1,88 @@ +#include + +/** + * \brief Trampoline for the callback function to reclaim the Query + * memory for a given Query. + * + * \param this is the specific \ref Query concerned. + * + * Ground queries recalim their own state memory. Composite + * queries first propagate the reclaim call to its components, and + * thereafter reclaim their local state memory. + */ +void Query_reclaim(Query *this) { + this->def->reclaim( this ); +} + +/** + * \brief Trampoline for the callback function to update the Binding + * table with a succession of alternative bindings. + * + * \param this is the specific \ref Query concerned. + * + * \param bt is the Binding table to set bindings in. + * + * \param s is the call "sub-command" for the function. + * + * \returns 1 if a new Binding is provided and 0 otherwise. + * + * This function is called repeatedly for successively obtaining + * the alternative bindings that satisfy the Query. The "initial" + * state sub-command tells the function to capture the incoming + * BindingTable state so that the function can later restore it + * upon the "restore" sub-command. Upon the "initial" command, the + * function also sets up the Binding table with its first Binding + * alternative. This is followed by a series of "subsequent" + * sub-command calls for the function to change the BindingTable + * for its succession of Binding alternatives. The function should + * return 1 after any successful Binding setup, and return 0 when + * it cannot setup any (more) Binding. + */ +int Query_next(Query *this,BindingTable *bt,enum NextState state) { + return this->def->next( this, bt, state ); +} + +/** + * \brief Trampoline for the callback function that adds its binding + * names to the hashvector. + */ +void Query_variables(Query *this,HashVector *hv) { + this->def->variables( this, hv ); +} + +void Query_eval( + Query *q,BindingTable *bt, + int (*consequent)(BindingTable *bt,void *data), + void *data ) +{ + if ( Query_next( q, bt, initial ) && consequent( bt, data ) ) { + while ( Query_next( q, bt, subsequent ) && consequent( bt, data ) ); + } + (void) Query_next( q, bt, restore ); +} + +/* ==================== Snapshotting a query ==================== */ + +/** + * The data used for bindings capture in snapshotting. + */ +struct Query_snapshot_data { + Tuple *names; + Vector *v; +}; + +static int Query_snapshot_capture(BindingTable *bt,void *data) { + struct Query_snapshot_data *d = (struct Query_snapshot_data*) data; + Tuple *values = Tuple_clone( d->names ); + BindingTable_deref( bt, values ); + Vector_append( d->v, values ); + return 1; +} + +int Query_snapshot(Query *q,Tuple *names,Vector *v) { + BindingTable *bt = BindingTable_create( 0 ); + struct Query_snapshot_data data = { .names = names, .v = v }; + Vector_resize( v, 0, Vector_free_any, 0 ); + Query_eval( q, bt, Query_snapshot_capture, &data ); + return v->size; +} diff --git a/logic/Query.h b/logic/Query.h new file mode 100644 index 0000000..4b2fa6a --- /dev/null +++ b/logic/Query.h @@ -0,0 +1,190 @@ +#ifndef Query_H +#define Query_H + +#include +#include +#include + +/** + * A Query is an implementation of a generic ABI over relations. It's + * more or less a "virtual Relation" representing a logical composite + * access of actual relations, and as such its active part is held in + * a separate \ref Querytype record similar to how \ref ItemKeyFun + * records define valye types. + */ +typedef struct Query { + struct QueryCallbacks *def; +} Query; + +/** + * \brief Trampoline for the callback function to reclaim the Query + * memory for a given Query. + * + * \param this is the specific \ref Query concerned. + * + * Ground queries recalim their own state memory. Composite + * queries first propagate the reclaim call to its components, and + * thereafter reclaim their local state memory. + */ +extern void Query_reclaim(Query *this); + +/** + * \brief Trampoline for the callback function to update the Binding + * table with a succession of alternative bindings. + * + * \param this is the specific \ref Query concerned. + * + * \param bt is the Binding table to set bindings in. + * + * \param s is the call "sub-command" for the function. + * + * \returns 1 if a new Binding is provided and 0 otherwise. + * + * This function is called repeatedly for successively obtaining + * the alternative bindings that satisfy the Query. The "initial" + * state sub-command tells the function to capture the incoming + * BindingTable state so that the function can later restore it + * upon the "restore" sub-command. Upon the "initial" command, the + * function also sets up the Binding table with its first Binding + * alternative. This is followed by a series of "subsequent" + * sub-command calls for the function to change the BindingTable + * for its succession of Binding alternatives. The function should + * return 1 after any successful Binding setup, and return 0 when + * it cannot setup any (more) Binding. + */ +extern int Query_next(Query *this,BindingTable *bt,enum NextState state); + +/** + * \brief Trampoline for the callback function that adds its binding + * names to the hashvector. + * + * \param this is the query concerned. + * + * \param hv is the HashVector for collating names. + */ +extern void Query_variables(Query *this,HashVector *hv); + + + + +/** + * \brief Creates an assignment Query. + * + * \param arity is the assignment tuple arity. + * + * \param names is the (char*) names to assign. + * + * \param values is the (void*) values to asign. + * + * The two tuples must have the same arity for assigning names[i] to + * values[i]. This Query makes the given names have the given values, + * once for each (*next) call following a (*reset) call. + * + * \related Query + */ +extern Query *Query_assign(int arity,Tuple *names,Tuple *values); + +/** + * \brief Create a Query record for lookup data in a Relation. + * + * \param r is the relation being queried. + * + * \param names is a same-arity tuple of binding names for the + * columns, using \b 0 for unnamed columns. + * + * \param values is a same--arity tuple of query values, using \b 0 for + * columns to enumerate. + * + * The names and values tuples identify bindings and values to use for + * the search query, and which bindings to set up from the successive + * results. Names that are bound beforhand identify constraining + * values, and the names that are unbound gain successive values from + * the matching tuples. + * + * \related Query + */ +extern Query *Query_Relation(Relation *r,Tuple *names,Tuple *values); + +/** + * \brief Create a Query record for a conjunction of queries. + * + * \param n is the number of sub queries. + * + * \param ... are the sub queries. + * + * The conjunction query processes the sub queries in order resulting + * in the sequence of their combined bindings. + * + * \related Query + */ +extern Query *Query_and(int n,...); + +/** + * \brief Create a Query record for a disjunction of queries. + * + * \param n is the number of sub queries. + * + * \param ... are the sub queries. + * + * The disjunction query processed the sub queries in order to find + * all their individual "true" binding combinations. It processes one + * sub query at a time to exhaustion and provide their individudal + * binding sequences separately. + * + * \related Query + */ +extern Query *Query_or(int n,...); + +/** + * \brief Invoke a consequent callback function for each successful + * binding of a query. + * + * \param q is the antecedent query to process. + * + * \param bt is the binding table to use. + * + * \param consequent is the callback function to invoke for each + * binding. + * \param data is the caller's context data. + * + * This function prrocesses the Query for establishing its binding + * sequence and inokes the consequent callback function for each + * binding as it is provided. + * + * \related Query + */ +extern void Query_eval( + Query *q,BindingTable *bt, + int (*consequent)(BindingTable *bt,void *data), + void *data ); + +/** + * \brief Collect all bindings of query. + * + * \param q is the query to enumerate. + * + * \param names is the binding names to track. + * + * \param results is the result store of bindings for the names. + * + * \returns the number of results. + * + * This function evaluates the query for one round of bindings, and + * stores their value \ref Tuple Tuples in the given vector. The given + * vector is first cleared, and any item is reclaimed with \b free. + * Correspondingly the binding \ref Tuple Tuples are allocated with \b + * malloc for the caller to reclaim (possibly via a successive call to + * this function). + */ +//extern int Query_snapshot(Query *q,Tuple *names,Vector *v); + +/** + * \brief Creates an NotQuery. + * + * \param query is the query to negate. + * + * \related Query + */ +extern Query *Query_not(Query *values); + +#endif diff --git a/logic/QueryCallbacks.h b/logic/QueryCallbacks.h new file mode 100644 index 0000000..5d36e0b --- /dev/null +++ b/logic/QueryCallbacks.h @@ -0,0 +1,78 @@ +#ifndef QueryCallbacks_H +#define QueryCallbacks_H + +#include + +typedef struct Query Query; // forward + +enum NextState { + /** + * This state tells the "next" function that it should capture the + * incoming BindingTable state and provide the initial Binding of + * a new sucession of bindings. + */ + initial, + /** + * This state tells the "next" function that it should update the + * bidning table with a subsequent Binding in the current + * succession of bindings. + */ + subsequent, + /** + * This state tells the "next" function that it should just + * restore the Binding table to its incoming state. + */ + restore +}; + +/** + * A struct Query_callbacks record defines the callbacks for a + * specific Query type. + */ +typedef struct QueryCallbacks { + /** + * \brief Callback function to reclaim the Query memory for a + * given Query. + * + * \param this is the specific \ref Query concerned. + * + * Ground queries recalim their own state memory. Composite + * queries first propagate the reclaim call to its components, and + * thereafter reclaim their local state memory. + */ + void (*reclaim)(Query *this); + + /** + * \brief Callback function to update the Binding table with a + * succession of alternative bindings. + * + * \param this is the specific \ref Query concerned. + * + * \param bt is the Binding table to set bindings in. + * + * \param s is the call "sub-command" for the function. + * + * \returns 1 if a new Binding is provided and 0 otherwise. + * + * This function is called repeatedly for successively obtaining + * the alternative bindings that satisfy the Query. The "initial" + * state sub-command tells the function to capture the incoming + * BindingTable state so that the function can later restore it + * upon the "restore" sub-command. Upon the "initial" command, the + * function also sets up the Binding table with its first Binding + * alternative. This is followed by a series of "subsequent" + * sub-command calls for the function to change the BindingTable + * for its succession of Binding alternatives. The function should + * return 1 after any successful Binding setup, and return 0 when + * it cannot setup any (more) Binding. + */ + int (*next)(Query *this,BindingTable *bt,enum NextState state); + + /** + * \brief This callback function adds its binding names to the + * hashvector. + */ + void (*variables)(Query *this,HashVector *hv); +} QueryCallbacks; + +#endif diff --git a/logic/Relation.c b/logic/Relation.c new file mode 100644 index 0000000..868a477 --- /dev/null +++ b/logic/Relation.c @@ -0,0 +1,162 @@ +#include +#include +#include + +Relation *Relation_create(TupleSchema *schema) { + Relation *r = (Relation *) malloc( sizeof( Relation ) ); + (*r) = (Relation) { + .content = (HashVector) { + .table = (Vector) { + .variant = Nibble_index_levels, + .size = 16, + .entries = 0 + }, + .fill = 0, .holes = 0, .type = (ItemKeyFun*) schema + }, + .constraints = (Vector) { + .variant = single_index_level, + .size = 0, + .entries = 0 + }, + }; + return r; +} + +#define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) ) +#define COPY(T,P) COPYA(T,P,1) + +// Add an indexing HashVector to the Relation using the given column +// flags with 1 indicating key column and 0 indicating value column. +int Relation_add_constraint(Relation *r,...) { + va_list ap; + TupleSchema *ts = (TupleSchema *) r->content.type; + Tuple *columns = Tuple_clone( ts->columns ); + int i = 0; + va_start( ap, r ); + for ( ; i < columns->size; i++ ) { + if ( va_arg( ap, int ) == 0 ) { + columns->elements[i] = 0; + } + } + va_end( ap ); + ts = TupleSchema_create( columns ); + i = (int) r->constraints.size; + Vector_append( + &r->constraints, + HashVector_create( Nibble_index_levels, (ItemKeyFun*) ts ) ); + return i; +} + +//============== Adding an item ============= +// Iteration context for adding or Querying a Relation +typedef struct { + Relation *rel; + HashVector knockouts; + void *item; +} Knockout; + +// Determine matches to ((Knockout*)data)->key in +// (HashVector*)item, optionally using ((Knockout*)data)->columns +// for ignoring full matches to the key tuple. +static int knockout_check(VectorIndex index,void *item,void *data) { + Knockout *kod = (Knockout*) data; + void *key = kod->item; + HashVector *hv = (HashVector*) item; + TupleSchema *type = (TupleSchema *)hv->type; + VectorIndex i = 0; + for ( ; i < hv->table.size; i++ ) { + void *old = HashVector_next( hv, &i ); + if ( old ) { + if ( key && type->base.haskey( type, old, key ) == 0 ) { + continue; + } + HashVector_add( &kod->knockouts, old ); + } + } + return 0; +} + +// delete the (tuple*)item from the (HashVector*)data +static int knockout_delete(VectorIndex index,void *item,void *data) { + HashVector_delete( (HashVector*) item, data ); + return 0; +} + +// add the (tuple*)data to the (HashVector*)item +static int knockout_add(VectorIndex index,void *item,void *data) { + HashVector_add( (HashVector*)item, data ); + return 0; +} + +// Find and remove all collisions for a Query, unless "add" is +// non-zero in which case the function aborts if there is any match in +// the main content. +static int knockout_clear(Knockout *this,Relation *r,Tuple *item,int add) { + (*this) = (Knockout) { + .rel = r, + .knockouts = { + .table = { + .variant = Nibble_index_levels, .size = 16, .entries = 0 + }, + .fill = 0, .holes = 0, .type = r->content.type, + }, + .item = item + }; + knockout_check( 0, &r->content, this ); + if ( add ) { + if ( this->knockouts.fill > 0 ) { + return 0; + } + // Find all constraint knockouts for addition + Vector_iterate( &r->constraints, 0, knockout_check, this ); + } + if ( this->knockouts.fill > 0 ) { + // Delete them from all tables + VectorIndex i; + for ( i = 0; i < this->knockouts.table.size; i++ ) { + void *t = HashVector_next( &this->knockouts, &i ); + if ( t ) { + HashVector_delete( &r->content, t ); + Vector_iterate( &r->constraints, 0, knockout_delete, t ); + } + } + } + return 1; +} + +// add a tuple to a Relation and return a Vector of knocked out +// tuples, if any, or 0 otherwise. +Vector *Relation_add(Relation *r,Tuple *item) { + Knockout data; + if ( knockout_clear( &data, r, item, 1 ) ) { + // Add the new tuple + HashVector_add( &r->content, item ); + Vector_iterate( &r->constraints, 0, knockout_add, item ); + return HashVector_contents( &data.knockouts, single_index_level, 0 ); + } + return 0; +} + +Vector *Relation_delete(Relation *r,Tuple *item) { + Knockout data; + (void) knockout_clear( &data, r, item, 0 ); + return HashVector_contents( &data.knockouts, single_index_level, 0 ); +} + +void *Relation_next(Relation *r,VectorIndex *index,Tuple *query) { + HashVector *hv = &r->content; + void *key = query; + TupleSchema *type = (TupleSchema *) hv->type; + for ( ; (*index) < hv->table.size; (*index)++ ) { + void *old = HashVector_next( hv, index ); + if ( old ) { + if ( key && type->base.haskey( type, old, key ) == 0 ) { + continue; + } + return old; + } + } + (*index) = hv->table.size; + return 0; +} + diff --git a/logic/Relation.h b/logic/Relation.h new file mode 100644 index 0000000..852101a --- /dev/null +++ b/logic/Relation.h @@ -0,0 +1,164 @@ +#ifndef Relation_H +#define Relation_H + +#include +#include + +/** + * A Relation is an implementation of a tuple set with (optional) key + * constraints. The store is a \ref HashVector whose \b type is a \ref + * TupleSchema that defines the columns. The key constraints are + * represented as additional \ref HashVector "HashVectors" whose \ref + * TupleSchema "TupleSchemas" are clones of the column schema with + * some columns excluded. + * + * \extends HashVector + */ +typedef struct { + /** + * This is the primary content store for the Relation. Its type + * should be a TupleSchema declaring the "item types" for the + * Relation columns. + */ + HashVector content; + + /** + * This is a collection of relational constraints, if any, which + * are represented as HashVectors whose TupleSchemas are clones of + * the content TupleSchema with some columns excluded. + */ + Vector constraints; +} Relation; + +/** + * \brief Create a Relation for the given TupleSchema. + * + * \param schema is the column schema + * + * \returns the allocated Relation record. + * + * The given TupleSchema is set up as the type of the content + * HashVector, which also is initialised as a Nibble_index_levels + * variant Vector. + * + * \related Relation + */ +extern Relation *Relation_create(TupleSchema *schema); + +/** + * \brief Add a key constraint to a \ref Relation. + * + * \param r is the Relation concerned. + * + * \param ... are the column flags indicating key (1) or value (0) + * column for all columns. + * + * \returns the index into the constraints \ref Vector for the added + * constraint. + * + * This function adds a \ref HashVector with a \ref TupleSchema as its + * item type cloned from the content type and then modified to + * represent the constraint. Namely that the key columns have their + * "column type" set while value columsn are reset. + * + * The \b constraint \ref HashVectors are used when \ref tuple + * "tuples" are added to the \ref Relation so as to identify the + * already contained \ref tuple "tuples" that contradict the addition + * by means of having the same constraint key. The already contained + * \ref tuple "tuples" are then "knocked out" from the Relation by the + * new addition. + * + * \see Relation_add + * \related Relation + */ +extern int Relation_add_constraint(Relation *r,...); + +/** + * \brief Add the tuple to the Relation. + * + * \param r is the \ref Relation concerned. + * + * \param t is the \ref tuple to add. + * + * \returns a Vector of all knocked out tuples. + * + * This function adds the \ref tuple \b t to the \ref Relation \b r, + * and it returns a \ref Vector (single_index_level variant) of all + * same-key constraint tuples. The returned Vector is malloc-ed and it + * must be free-ed by the caller. If the tuple is already contained or + * there are no other same-key tuples knocked out, then \b 0 is + * returned. + * + * \related Relation + */ +extern Vector *Relation_add(Relation *r,Tuple *t); + +/** + * \brief Delete all tuples matching to the Query \ref tuple fromt the + * \ref Relation. + * + * \param r is the \ref Relation concerned. + * + * \param t is the \ref tuple to delete. + * + * \returns a \Vector Vector of all knocked out tuples, i.e. the + * same-key tuples, if any, contained in the Relation + * + * Note that deletion uses a "Query" tuple, which means that some + * columns may be null to mark that them match to any value. + * + * \related Relation + */ +extern Vector *Relation_delete(Relation *r,Tuple *query); + +/** + * \brief Return the next \ref tuple in the \ref Relation that matches + * to the Query \ref tuple, at or after the index. + * + * \param r is the \ref Relation concerned. + * + * \param index is a pointer to the \ref Vector index to update. + * + * \param Query is a Query \tuple tuple for selection of certain + * column values. + * + * \returns any such matching \tuple tuple and an updateed *index. + * + * \related Relation + */ +extern void *Relation_next(Relation *r,VectorIndex *index,Tuple *query); + +/** + * \brief Lay out a dynamic \ref Relation initializer for a Relation + * wth the given column "types". + * + * This defines a \ref Relation intializer that creates the \ref + * TupleSchema for the given columns. + * + * \note The initializer cannot be used statically. + * + * The \b content \ref HashVector is a \ref Nibble_index_level variant + * with an initial size of 16 slots. + * + * The constraints \ref Vector is a \ref BitPair_index_level variant + * with initial size 0. + * + * The \b content \ref HashVector \b type is set up with an allocated + * \ref TupleSchema that has an allocated \ref tuple that declares the + * column "types" view the given \ref ItemKeyFun pointers. Any add + * constraints will need to clone that \ref TupleSchema and then clear + * the column slots for the constraint value columns, typically by + * using \ref TupleSchema_mask for this. + * + * \related Relation + */ +#define RELATION(...) (Relation) { \ + .content = { \ + .table = { .variant = Nibble_index_levels, .size=16, .entries=0 }, \ + .fill = 0, .holes = 0, \ + .type = (ItemKeyFun*) TUPLESCHEMA( __VA_ARGS__ ) \ + }, \ + .constraints = { .variant = BitPair_index_levels, .size=0, .entries=0 } \ +} + +#endif diff --git a/logic/RelationQuery.c b/logic/RelationQuery.c new file mode 100644 index 0000000..aa4bdbd --- /dev/null +++ b/logic/RelationQuery.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +// Release any memory. +static void RelationQuery_reclaim(Query *this) { + RelationQuery *q = (RelationQuery*) this; + free( q->saved ); + free( this ); +} + +// Make names have given values and return 1. If any name has a +// different value then return 0. Values are strings. +static int RelationQuery_next( + Query *this,BindingTable *bt,enum NextState state) { + RelationQuery *q = (RelationQuery*) this; + VectorIndex index = q->index + 1; + switch ( state ) { + case initial: + if ( q->saved == 0 ) { + q->saved = Tuple_clone( q->names ); + } else { + memcpy( q->saved, q->names, q->names->size * sizeof( void* ) ); + } + BindingTable_deref( bt, q->saved ); + index = 0; + // Fall through + case subsequent: + for ( ; index < q->rel->content.table.size; index++ ) { + Tuple *values = Relation_next( q->rel, &index, q->values ); + if ( values ) { + q->index = index; + BindingTable_set_all( bt, q->names, values, 1 ); + return 1; + } + } + case restore: + if ( q->saved ) { + BindingTable_set_all( bt, q->names, q->saved, 1 ); + free( q->saved ); + q->saved = 0; + } + return 0; + } + return 0; +} + +static void RelationQuery_variables(Query *this,HashVector *hv) { + RelationQuery *q = (RelationQuery*) this; + unsigned long i; + for ( i = 0; i < q->names->size; i++ ) { + HashVector_add( hv, q->names->elements[i] ); + } +} + +static struct QueryCallbacks RelationQuery_def = { + .reclaim = RelationQuery_reclaim, + .next = RelationQuery_next, + .variables = RelationQuery_variables +}; + +/** + * Return a Query object representing an Relation of one or more + * variables. + */ +Query *Query_relation(Relation *r,Tuple *names,Tuple *values) { + RelationQuery *q = (RelationQuery*) malloc( sizeof( RelationQuery ) ); + (*q) = (RelationQuery) { + .def = &RelationQuery_def, + .rel = r, + .names = names, + .values = values, + .saved = 0 + }; + return (Query*) q; +} + diff --git a/logic/RelationQuery.h b/logic/RelationQuery.h new file mode 100644 index 0000000..4c50a53 --- /dev/null +++ b/logic/RelationQuery.h @@ -0,0 +1,21 @@ +#ifndef RelationQuery_H +#define RelationQuery_H + +#include +#include + +/** + * RelationQuery represents a ground query on a relation. + * \extends Query + * \related Query + */ +typedef struct { + struct QueryCallbacks *def; + Relation *rel; + VectorIndex index; + Tuple *names; + Tuple *values; + Tuple *saved; +} RelationQuery; + +#endif diff --git a/logic/Tuple.c b/logic/Tuple.c new file mode 100644 index 0000000..2b5b04e --- /dev/null +++ b/logic/Tuple.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +// Allocate zero-ed +Tuple *Tuple_calloc(unsigned long arity) { + Tuple *t = (Tuple *) malloc( sizeof( Tuple ) + arity * sizeof( void* ) ); + t->size = arity; + memset( t->elements, 0, arity * sizeof( void* ) ); + return t; +} + +// Allocate with values +Tuple *Tuple_create(int arity,...) { + va_list ap; + int i; + Tuple *t = (Tuple *) malloc( sizeof( Tuple ) + arity * sizeof( void* ) ); + t->size = arity; + va_start( ap, arity ); + for ( i = 0; i < arity; i++ ) { + t->elements[i] = va_arg( ap, void* ); + } + va_end( ap ); + return t; +} + +// Duplicate +Tuple *Tuple_clone(Tuple *t) { + unsigned long size = t->size * sizeof( void* ); + Tuple *ct = (Tuple *) malloc( sizeof( Tuple ) + size ); + memcpy( ct, t, size ); + return ct; +} diff --git a/logic/Tuple.h b/logic/Tuple.h new file mode 100644 index 0000000..ea439c5 --- /dev/null +++ b/logic/Tuple.h @@ -0,0 +1,44 @@ +#ifndef Tuple_H +#define Tuple_H + +#include + +typedef struct TupleSchema TupleSchema; + +/** + * A Tuple is a "self typed" array of elements. + */ +typedef struct { + /** + * The number of elements. + */ + unsigned long size; + /** + * Base address for element pointers, which thus follow this + * struct in memory. + */ + void *elements[]; +} Tuple; + +/** + * \brief Create an untyped tuple with given values. + * + * \related Tuple + */ +extern Tuple *Tuple_create(int arity,...); + +/** + * \brief Create an untyped tuple with 0 values. + * + * \related Tuple + */ +extern Tuple *Tuple_calloc(unsigned long arity); + +/** + * \brief Create a tuple as a clone of a given tuple. + * + * \related Tuple + */ +extern Tuple *Tuple_clone(Tuple *t); + +#endif diff --git a/logic/TupleSchema.c b/logic/TupleSchema.c new file mode 100644 index 0000000..5a92b64 --- /dev/null +++ b/logic/TupleSchema.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include + +/** + * This callback function returns the hashcode of a key. + * + * \param this is a pointer to the ItemKeyFun record from where this + * callback got invoked + * + * \param key is the key to produce a hascode for + * + * \returns the hashcode which is a VectorIndex (i.e. unsigned long) + * + * The hashcode is used for indexing into the backing Vector for + * finding the an item via its key. The same key must map consistently + * to the same hashcode while the hashtable contains an item with that + * key. Different keys map map to the same hashcode, in which case the + * Vector placement is made at the first empty or hole slot following + * the hashcode index. + */ +static unsigned long TupleSchema_hashcode(void *this,void *key) { + TupleSchema *def = (TupleSchema *) this; + ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; + Tuple *kp = (Tuple*) key; + int i = 0; + unsigned long value = 5381; + for ( ; i < def->columns->size; i++ ) { + if ( columns[i] ) { + value <<= 3; + if ( kp->elements[i] ) { + value += columns[i]->hashcode( columns[i], kp->elements[i] ); + } + } + value += 17; + } + return value; +} + +/** + * This callback function determines whether an item has a + * given key or not. + */ +static int TupleSchema_haskey(void *this,void *item,void *key) { + TupleSchema *def = (TupleSchema *) this; + ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; + Tuple *kp = (Tuple*) key; + Tuple *tp = (Tuple*) item; + int i = 0; + for ( ; i < def->columns->size; i++ ) { + if ( columns[i] && kp->elements[i] ) { + if ( columns[i]->haskey( + columns[i], tp->elements[i], kp->elements[i] ) == 0 ) { + return 0; + } + } + } + return 1; +} + + +/** + * This callback function returns the key of an item by considering + * the arity and mask. + */ +static void *TupleSchema_itemkey(void *this,void *item) { + TupleSchema *def = (TupleSchema *) this; + ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; + Tuple *tp = (Tuple*) item; + Tuple *key = Tuple_clone( tp ); + int i; + for ( i = 0; i < def->columns->size; i++ ) { + if ( columns[i] ) { + key->elements[i] = columns[i]->itemkey( + columns[i], tp->elements[i] ); + } else { + key->elements[i] = 0; + } + } + return (void*) key; +} + +/** + * This callback function handles a key obtained from the itemkey + * callback function to reclaim temporary allocation. + */ +static void TupleSchema_releasekey(void *this,void *key) { + TupleSchema *def = (TupleSchema *) this; + ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; + Tuple *kp = (Tuple*) key; + int i; + for ( i = 0; i < def->columns->size; i++ ) { + if ( columns[i] ) { + columns[i]->releasekey( columns[i], kp->elements[i] ); + } + } + free( key ); +} + +#define OUT(X) a = X; if ( a > limit ) return 0; buffer += a; limit -= a + +/** + * This callback function writes a representation of an item into + * a character buffer. + */ +static int TupleSchema_tostring(void *this,void *item,char *buffer,int limit) { + TupleSchema *def = (TupleSchema *) this; + ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; + Tuple *t = (Tuple*) item; + char *x = "<"; + int a, i; + for ( i = 0; i < def->columns->size; i++ ) { + OUT( snprintf( buffer, limit, x ) ); + x = ","; + OUT( columns[i]->tostring( + columns[i], t->elements[i], buffer, limit ) ); + } + OUT( snprintf( buffer, limit, ">" ) ); + return a; +} + +ItemKeyFun TupleSchema_callbacks = { + .hashcode = TupleSchema_hashcode, + .haskey = TupleSchema_haskey, + .itemkey = TupleSchema_itemkey, + .releasekey = TupleSchema_releasekey, + .tostring = TupleSchema_tostring +}; + +TupleSchema *TupleSchema_create(Tuple *columns) { + TupleSchema *ts = (TupleSchema*) malloc( sizeof( TupleSchema ) ); + (*ts) = (TupleSchema) { + .base = TupleSchema_callbacks, + .columns = columns + }; + return ts; +} + +#define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) ) +#define COPY(T,P) COPYA(T,P,1) + +// Duplicate a TupleSchema with optionally some columns reset. +TupleSchema *TupleSchema_mask(TupleSchema *schema,...) { + TupleSchema *masked = COPY(TupleSchema,schema); + masked->columns = Tuple_clone( schema->columns ); + va_list ap; + int i; + va_start( ap, schema ); + for ( ;; ) { + i = va_arg( ap, int ); + if ( i < 0 || i >= schema->columns->size ) { + break; + } + masked->columns->elements[i] = 0; + }; + va_end( ap ); + return masked; +} diff --git a/logic/TupleSchema.h b/logic/TupleSchema.h new file mode 100644 index 0000000..664ddad --- /dev/null +++ b/logic/TupleSchema.h @@ -0,0 +1,84 @@ +#ifndef tupleitem_H +#define tupleitem_H + +#include + +/** + * A TupleSchema record declares the ItemKeyFun functions for tuple + * items together with applicable arity and domain combinations. + * Records are created dynamically via the \ref TupleSchema_create + * function or the \ref TUPLESCHEMA convenience macro. + * + * \extends ItemKeyFun + */ +typedef struct TupleSchema { + /** + * These are the ItemKeyFun callback functions to support + * HashVector use for tuple items. The functions expects their + * ItemKeyFun pointer to be within a TupleSchema record so as to + * provide the handling of the tuple columns. + */ + ItemKeyFun base; + + /** + * This points to tuple whose elements is array of pointers to the + * tuple item domains as represented by their associated + * ItemKeyFun records. + */ + Tuple *columns; +} TupleSchema; + +/** + * Create a tuples with given values. + * + * \related TupleSchema + */ +extern TupleSchema *TupleSchema_create(Tuple *columns); + +/** + * Copy the given TupleSchema into a new TupleSchema with some columns + * masked. This represents a sub-index type using the unmasked columns + * for indexing. + * + * \related TupleSchema + */ +extern TupleSchema *TupleSchema_mask(TupleSchema *schema,...); + +/** + * \brief Return 1/0 to indicate whether the Query matches the item. + * + * \related TupleSchema + */ +extern int TupleSchema_match(TupleSchema *def,Tuple *query,Tuple *item); + +/** + * \brief Generic macro to determine the number of expressions in a + * __VA_ARGS__ + * + * \related TupleSchema + */ +#define NUMARGS(...) (sizeof((void*[]){__VA_ARGS__})/sizeof(void*)) + +/** + * \brief Create a tuple with the given values. + * + * This invokes \ref tuple_create to allocate and assign a void* + * array with the given values. + * + * \related TupleSchema + */ +#define TUPLE(...) Tuple_create( NUMARGS(__VA_ARGS__), __VA_ARGS__ ) + +/** + * \brief Create a \ref TupleSchema with the given column "types". + * + * This invokes \ref TupleSchema_create to allocate and initialize a + * \ref TupleSchema for the given columns via the \ref TUPLE macro. + * + * \related TupleSchema + */ +#define TUPLESCHEMA(...) \ + TupleSchema_create( TUPLE( __VA_ARGS__ ) ) + + +#endif diff --git a/socket-sniff/Makefile b/socket-sniff/Makefile index d9d783f..e86108f 100644 --- a/socket-sniff/Makefile +++ b/socket-sniff/Makefile @@ -1,7 +1,11 @@ default: socket-sniff -CFLAGS = -Wall -I../vector -g -fmax-errors=1 -LDLIBS = -L../vector -lvector +RRQLIBS = typing vector +INCDIRS = $(addprefix -I../,$(RRQLIBS)) + +CFLAGS = -Wall $(INCDIRS) +CFLAGS += -g -fmax-errors=1 +LDLIBS = -L../vector -lvector -lm .INTERMEDIATE: socket-sniff.o diff --git a/socket-sniff/socket-sniff.c b/socket-sniff/socket-sniff.c index 545c9f7..4bcc8a2 100644 --- a/socket-sniff/socket-sniff.c +++ b/socket-sniff/socket-sniff.c @@ -12,7 +12,8 @@ #include #include -#include +#include +#include // Seconds between outputs static int DELAY = 5; @@ -47,29 +48,41 @@ static void die(char *m) { exit( 1 ); } -// Return pointer to the key for an item -static void *Countp_itemkey(void *item) { +// Returns the hashcode for a key +static unsigned long Countp_hashcode(void *this,void *key) { + return HashVector_hashcode( key, IPBUFMAX ); +} + +// Return pointer a key for an item (could be temporary allocation) +static void *Countp_itemkey(void *this,void *item) { return ((Count*) item)->ip; } // Return 1 if the item has the key, or 0 otherwise. -static int Countp_haskey(void *item,void *key) { - return memcmp( key, Countp_itemkey( item ), IPBUFMAX ) == 0; +static int Countp_haskey(void *this,void *item,void *key) { + return memcmp( key, Countp_itemkey( this, item ), IPBUFMAX ) == 0; } -// Returns the hashcode for a key -static unsigned long Countp_hashcode(void *key) { - return hashvector_hashcode( key, IPBUFMAX ); +#if 0 +// Releasing a key does nothing +static void Countp_releasekey(void *this,void *item) { } +#endif -// The hashvector of seen IP -static hashvector TBL = { - .table = { VECTOR_SLOTS, 0 }, +static ItemKeyFun Countp_itemkeyfun = { + .hashcode = Countp_hashcode, + .haskey = Countp_haskey, + .itemkey = Countp_itemkey, + //.releasekey = Countp_releasekey, + //.tostring = Countp_tostring +}; + +// The HashVector of seen IP +static HashVector TBL = { + .table = { Nibble_index_levels, 16, 0 }, .fill = 0, .holes = 0, - .keyhashcode = Countp_hashcode, - .itemkey = Countp_itemkey, - .haskey = Countp_haskey + .type = &Countp_itemkeyfun, }; // The Count records in time order @@ -84,28 +97,28 @@ static char buffer[ IPBUFMAX ]; /*============================================================ * Reading ignore lines. */ +#if 0 // Return pointer to the key for an item -static void *charp_itemkey(void *item) { +static void *charp_itemkey(void *this,void *item) { return item; } // Return 1 if the item has the key, or 0 otherwise. -static int charp_haskey(void *item,void *key) { +static int charp_haskey(void *this,void *item,void *key) { return strcmp( key, item ) == 0; } // Returns the hashcode for a key -static unsigned long charp_hashcode(void *key) { - return hashvector_hashcode( key, strlen( (const char *) key ) ); +static unsigned long charp_hashcode(void *this,void *key) { + return HashVector_hashcode( key, strlen( (const char *) key ) ); } +#endif -static hashvector IGN = { +static HashVector IGN = { .table = { 256, 0 }, .fill = 0, .holes = 0, - .keyhashcode = charp_hashcode, - .itemkey = charp_itemkey, - .haskey = charp_haskey + .type = &stringitem }; static void read_ignore_file(char *filename) { @@ -144,7 +157,7 @@ static void read_ignore_file(char *filename) { char *ip = calloc( 1, p - cur + 1 ); memcpy( ip, cur, p - cur ); cur = p + 1; - hashvector_add( &IGN, ip ); + HashVector_add( &IGN, ip ); } } @@ -166,7 +179,7 @@ static int Countp_compare(const void *ax, const void *bx) { return a->last - b->last; } -static int Countp_fade_and_print(unsigned long index,void *x,void *d) { +static int Countp_fade_and_print(VectorIndex index,void *x,void *d) { if ( x ) { Count *item = (Count *) x; item->last = item->accum; @@ -182,7 +195,7 @@ static int Countp_fade_and_print(unsigned long index,void *x,void *d) { return 0; } -static int Countp_reclaim(vector *pv,unsigned long ix,void *item,void *data) { +static int Countp_reclaim(Vector *pv,unsigned long ix,void *item,void *data) { return 0; } @@ -190,21 +203,21 @@ static int Countp_reclaim(vector *pv,unsigned long ix,void *item,void *data) { // ip points to [ IPBUFMAX ] of ip address in text static void add_show_table(char *ip,size_t length) { static time_t show = 0; - Count *item; - int i = hashvector_find( &TBL, ip, (void**) &item ); + Count *item = HashVector_find( &TBL, ip ); struct timeval now; if ( gettimeofday( &now, 0 ) ) { perror( "gettimeofday" ); exit( 1 ); } - if ( i == 0 ) { + if ( item == 0 ) { item = (Count *) calloc( 1, sizeof( Count ) ); memcpy( item->ip, ip, strlen( ip ) ); - hashvector_add( &TBL, item ); - item->ignore = hashvector_find( &IGN, ip, 0 ); + HashVector_add( &TBL, item ); + item->ignore = (HashVector_find( &IGN, ip ) != 0); + int i; for ( i = strlen( ip )-1; i > 1; i-- ) { if ( ip[i] == '.' || ip[i] == ':' ) { - item->ignore |= hashvector_find( &IGN, ip, 0 ); + item->ignore |= (HashVector_find( &IGN, ip ) != 0); } ip[i] = 0; } @@ -243,7 +256,7 @@ static void add_show_table(char *ip,size_t length) { trail.head->prev = 0; } fprintf( stdout, "drop %s\n", old->ip ); - hashvector_delete( &TBL, old ); + HashVector_delete( &TBL, old ); free( old ); } if ( now.tv_sec < show ) { @@ -253,11 +266,11 @@ static void add_show_table(char *ip,size_t length) { show = now.tv_sec; } show += DELAY; // Time for next output - vector ordered = { 0, 0 }; - hashvector_contents( &TBL, &ordered ); - vector_qsort( &ordered, Countp_compare ); - vector_iterate( &ordered, Countp_fade_and_print, 0 ); - vector_resize( &ordered, 0, Countp_reclaim, 0 ); + Vector ordered = { Nibble_index_levels, 0, 0 }; + HashVector_contents( &TBL, Nibble_index_levels, &ordered ); + Vector_qsort( &ordered, Countp_compare ); + Vector_iterate( &ordered, 0, Countp_fade_and_print, 0 ); + Vector_resize( &ordered, 0, Countp_reclaim, 0 ); fprintf( stdout, "==%ld/%ld/%ld\n", TBL.fill, TBL.holes, TBL.table.size ); } diff --git a/tests/Makefile b/tests/Makefile index 6da4dd7..ab5fcc2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,7 +4,11 @@ BIN = ${SRC:.c=} default: ${BIN} -CFLAGS = -Wall -I../vector -g -fmax-errors=1 +RRQLIBS = typing vector logic +INCDIRS = $(addprefix -I../,$(RRQLIBS)) + +CFLAGS = -Wall $(INCDIRS) +CFLAGS += -g -fmax-errors=1 LDLIBS = -L../vector -lvector -lm .INTERMEDIATE: ${OBJ} diff --git a/tests/example-relation.c b/tests/example-relation.c index 73b0dc0..a70714c 100644 --- a/tests/example-relation.c +++ b/tests/example-relation.c @@ -53,7 +53,7 @@ typedef union { int main(int argc,char **argv) { Relation *rltn2 = Relation_create( TupleSchema_create( - 2, Tuple_create( 2, (void*) &stringitem, (void*) &stringitem ) ) ); + Tuple_create( 2, (void*) &stringitem, (void*) &stringitem ) ) ); Relation_add_constraint( rltn2, 0, 1 ); add_Relation( rltn2, "benton", "holly" ); add_Relation( rltn2, "benton", "molly"); diff --git a/typing/ItemKeyFun.c b/typing/ItemKeyFun.c new file mode 100644 index 0000000..81e4b9a --- /dev/null +++ b/typing/ItemKeyFun.c @@ -0,0 +1,67 @@ +/** + * Trampoline functions for the ItemKeyFun callbacks. + */ + +#include +#include + +/** + * \brief Trampoline function for the ItemKeyFun.hashcode callback. + * + * The default is to use the pointer itself. + */ +unsigned long ItemKeyFun_hashcode(void *this,void *key) { + ItemKeyFun *type = (ItemKeyFun*) this; + assert( type ); + return type->hashcode? type->hashcode( type, key ) : (unsigned long) key; +} + +/** + * \brief Trampoline function for the ItemKeyFun.itemkey callback. + */ +void *ItemKeyFun_itemkey(void *this,void *item) { + ItemKeyFun *type = (ItemKeyFun*) this; + assert( type ); + return type->itemkey? type->itemkey( type, item ) : item; +} + +/** + * \brief Trampoline function for the ItemKeyFun.haskey callback. + */ +int ItemKeyFun_haskey(void *this,void *item,void *key) { + ItemKeyFun *type = (ItemKeyFun*) this; + assert( type ); + if ( type->haskey ) { + return type->haskey( this, item, key ); + } + void *ikey = ItemKeyFun_itemkey( this, item ); + int n = ikey == key; + ItemKeyFun_releasekey( this, ikey ); + return n; +} + +/** + * \brief Trampoline function for the ItemKeyFun.releasekey callback. + */ +void ItemKeyFun_releasekey(void *this,void *key) { + ItemKeyFun *type = (ItemKeyFun*) this; + assert( type ); + if ( type->releasekey ) { + type->releasekey( type, key ); + } +} + +/** + * \brief Trampoline function for the ItemKeyFun.tostring callback. + */ +int ItemKeyFun_tostring(void *this,void *item,char *buffer,int limit) { + ItemKeyFun *type = (ItemKeyFun*) this; + assert( type ); + if ( type->tostring ) { + return type->tostring( type, item, buffer, limit ); + } + void *key = ItemKeyFun_itemkey( this, item ); + int n = snprintf( buffer, limit, "{%p/%p@%p}", type, key, item ); + ItemKeyFun_releasekey( this, key ); + return n; +} diff --git a/typing/ItemKeyFun.h b/typing/ItemKeyFun.h new file mode 100644 index 0000000..8a83f9b --- /dev/null +++ b/typing/ItemKeyFun.h @@ -0,0 +1,87 @@ +#ifndef ItemKeyFun_H +#define ItemKeyFun_H + +#include + +/** + * \struct ItemKeyFun + * + * ItemKeyFun provides a meta-level representation for abstracting + * items as pairs of keys and payload, and having a hashcode mapping + * for keys. The key part is used for identifying items under the idea + * that all items that have the same key are the same; distinct + * representations of the same abstract entity. The hashcode scrambles + * the key part in a consistent way into an ideal table index for the + * items that have that key. Different keys may yield the same + * hashcode. + */ +typedef struct { + + /** + * This callback function should return the hashcode of a key. The + * hashcode is used for indexing into the backing Vector for + * finding the an item via its key. The same key must map + * consistently to the same hashcode while the hashtable contains + * an item with that key. Different keys map map to the same + * hashcode, in which case the Vector placement is made at the + * first empty or hole slot following the hashcode index. + */ + unsigned long (*hashcode)(void *this,void *key); + + /** + * This callback function should determine whether an item has a + * given key or not. + */ + int (*haskey)(void *this,void *item,void *key); + + /** + * This callback function should return a (possibly temporary) key + * of an item. It can be anything (i.e., with or without internal + * structure) as it is treated as an identifier that other + * callbacks recognize. It is merely understood as a value in an + * identity domain. + */ + void *(*itemkey)(void *this,void *item); + + /** + * This callback function should handle a key obtained from the + * itemkey callback function, e.g., reclaim temporary allocation. + */ + void (*releasekey)(void *this,void *key); + + /** + * This callback function writes a representation of an item into + * a character buffer. + */ + int (*tostring)(void *this,void *item,char *buffer,int limit); + +} ItemKeyFun; + +/** + * \brief Trampoline function for the ItemKeyFun.hashcode callback. + * + * The default is to use the pointer itself. + */ +extern unsigned long ItemKeyFun_hashcode(void *this,void *key); + +/** + * \brief Trampoline function for the ItemKeyFun.haskey callback. + */ +extern int ItemKeyFun_haskey(void *this,void *item,void *key); + +/** + * \brief Trampoline function for the ItemKeyFun.itemkey callback. + */ +extern void *ItemKeyFun_itemkey(void *this,void *item); + +/** + * \brief Trampoline function for the ItemKeyFun.releasekey callback. + */ +extern void ItemKeyFun_releasekey(void *this,void *key); + +/** + * \brief Trampoline function for the ItemKeyFun.tostring callback. + */ +extern int ItemKeyFun_tostring(void *this,void *item,char *buffer,int limit); + +#endif diff --git a/typing/Makefile b/typing/Makefile new file mode 100644 index 0000000..91272e5 --- /dev/null +++ b/typing/Makefile @@ -0,0 +1,8 @@ +LIBRARY = librrqtyping.a +LIBOBJS += ItemKeyFun.o integeritem.o stringitem.o + +default: $(LIBRARY) + +all: default + +include ../common.mk diff --git a/typing/integeritem.c b/typing/integeritem.c new file mode 100644 index 0000000..221fb70 --- /dev/null +++ b/typing/integeritem.c @@ -0,0 +1,53 @@ +#include + +/** + * This callback function returns the hashcode of a key. The hashcode + * is used for indexing into the backing Vector for finding the an + * item via its key. The same key must map consistently to the same + * hashcode while the hashtable contains an item with that key. + * Different keys map map to the same hashcode, in which case the + * Vector placement is made at the first empty or hole slot following + * the hashcode index. + */ +static unsigned long integeritem_hashcode(void *this,void *key) { + return (unsigned long) key; +} + +/** + * This callback function determines whether an item has a + * given key or not. + */ +static int integeritem_haskey(void *this,void *item,void *key) { + return item == key; +} + +/** + * This callback function returns the key of an item by considering + * the arity and schema. + */ +static void *integeritem_itemkey(void *this,void *item) { + return item; +} + +/** + * This callback function handles a key obtained from the itemkey + * callback function to reclaim temporary allocation. + */ +static void integeritem_releasekey(void *this,void *key) { +} + +/** + * This callback function writes a representation of an item into + * a character buffer. + */ +static int integeritem_tostring(void *this,void *item,char *buffer,int limit) { + return snprintf( buffer, limit, "%lld", (long long) item ); +} + +ItemKeyFun integeritem = { + .hashcode = integeritem_hashcode, + .haskey = integeritem_haskey, + .itemkey = integeritem_itemkey, + .releasekey = integeritem_releasekey, + .tostring = integeritem_tostring +}; diff --git a/typing/integeritem.h b/typing/integeritem.h new file mode 100644 index 0000000..efa5323 --- /dev/null +++ b/typing/integeritem.h @@ -0,0 +1,12 @@ +#ifndef integeritem_H +#define integeritem_H + +#include + +/** + * The stringitem record declares the ItemKeyFun functions for integer + * items. + */ +extern ItemKeyFun integeritem; + +#endif diff --git a/typing/stringitem.c b/typing/stringitem.c new file mode 100644 index 0000000..ee8f2cb --- /dev/null +++ b/typing/stringitem.c @@ -0,0 +1,58 @@ +#include +#include +#include + +/** + * This callback function returns the hashcode of a key. The hashcode + * is used for indexing into the backing Vector for finding the an + * item via its key. The same key must map consistently to the same + * hashcode while the hashtable contains an item with that key. + * Different keys map map to the same hashcode, in which case the + * Vector placement is made at the first empty or hole slot following + * the hashcode index. + */ +static unsigned long stringitem_hashcode(void *this,void *key) { + return HashVector_hashcode( (unsigned char*)key, strlen( (char*)key ) ); +} + +/** + * This callback function determines whether an item has a + * given key or not. + */ +static int stringitem_haskey(void *this,void *item,void *key) { + return strcmp( item, key ) == 0; +} + +/** + * This callback function returns the key of an item by considering + * the arity and schema. + */ +static void *stringitem_itemkey(void *this,void *item) { + return item; +} + +/** + * This callback function handles a key obtained from the itemkey + * callback function to reclaim temporary allocation. + */ +static void stringitem_releasekey(void *this,void *key) { +} + +/** + * This callback function writes a representation of an item into + * a character buffer. + */ +static int stringitem_tostring(void *this,void *item,char *buffer,int limit) { + if ( item ) { + return snprintf( buffer, limit, "\"%s\"", (char*) item ); + } + return snprintf( buffer, limit, "(null)" ); +} + +ItemKeyFun stringitem = { + .hashcode = stringitem_hashcode, + .haskey = stringitem_haskey, + .itemkey = stringitem_itemkey, + .releasekey = stringitem_releasekey, + .tostring = stringitem_tostring +}; diff --git a/typing/stringitem.h b/typing/stringitem.h new file mode 100644 index 0000000..5cc1a32 --- /dev/null +++ b/typing/stringitem.h @@ -0,0 +1,12 @@ +#ifndef stringitem_H +#define stringitem_H + +#include + +/** + * The stringitem record declares the ItemKeyFun functions for string + * items. + */ +extern ItemKeyFun stringitem; + +#endif diff --git a/vector/AndQuery.c b/vector/AndQuery.c deleted file mode 100644 index 169ff9e..0000000 --- a/vector/AndQuery.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include - -static void AndQuery_reclaim(Query *this) { - AndQuery *q = (AndQuery*) this; - int i; - for ( i = 0; i < q->size; i++ ) { - Query_reclaim( q->conjuncts[i] ); - } - free( q->conjuncts ); - free( this ); -} - -static int AndQuery_next( - Query *this,BindingTable *bt,enum NextState state) -{ - AndQuery *q = (AndQuery*) this; - int i = q->size - 1; - enum NextState s = subsequent; - switch ( state ) { - case initial: - q->active = 1; - i = 0; - s = initial; - // Fall through? - case subsequent: - while ( i < q->size ) { - if ( Query_next( q->conjuncts[i], bt, s ) ) { - continue; - } - Query_next( q->conjuncts[i], bt, restore ); - if ( i-- == 0 ) { - // The first conjunct now exhausted - q->active = 0; - return 0; - } - s = subsequent; - } - return 1; - case restore: - if ( q->active ) { - for ( ; i >= 0; i-- ) { - Query_next( q->conjuncts[i], bt, restore ); - } - } - q->active = 0; - return 0; - } - return 0; -} - -static void AndQuery_variables(Query *this,HashVector *hv) { - AndQuery *q = (AndQuery*) this; - int i; - for ( i = 0; i < q->size; i++ ) { - Query_variables( q->conjuncts[i], hv ); - } -} - -static struct QueryCallbacks AndQuery_def = { - .reclaim = AndQuery_reclaim, - .next = AndQuery_next, - .variables = AndQuery_variables -}; - -Query *Query_and(int n,...) { - va_list args; - AndQuery *q = (AndQuery *) - malloc( sizeof( AndQuery ) ); - (*q) = (AndQuery) { - .def = &AndQuery_def, - .active = 0, - .size = n, - .conjuncts = (Query**) malloc( n * sizeof( Query* ) ) - }; - int i; - va_start( args, n ); - for ( i = 0; i < n; i++ ) { - q->conjuncts[i] = va_arg( args, Query* ); - } - va_end( args ); - return (Query*) q; -} - diff --git a/vector/AndQuery.h b/vector/AndQuery.h deleted file mode 100644 index 7871eea..0000000 --- a/vector/AndQuery.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef AndQuery_H -#define AndQuery_H - -#include - -/** - * AndQuery represents a conjunction of sub queries. - * \extends Query - * \related Query - */ -typedef struct { - struct QueryCallbacks *def; - int active; - int size; - Query **conjuncts; -} AndQuery; - - -#endif diff --git a/vector/AssignQuery.c b/vector/AssignQuery.c deleted file mode 100644 index bb264aa..0000000 --- a/vector/AssignQuery.c +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include - -// Release any memory. -static void AssignQuery_reclaim(Query *this) { - AssignQuery *q = (AssignQuery*) this; - free( q->saved ); - free( this ); -} - -static int AssignQuery_check(Tuple *a,Tuple *b) { - int i; - for ( i = 0; i < a->size; i++ ) { - char *value = a->elements[i]; - char *current = b->elements[i]; - if ( value && current && current != value && - strcmp( current, value ) != 0 ) { - return 0; - } - } - return 1; -} - -// Make names have given values and return 1. If any name has a -// different value then return 0. Values are strings. -static int AssignQuery_next( - Query *this,BindingTable *bt,enum NextState state) { - AssignQuery *q = (AssignQuery*) this; - switch ( state ) { - case initial: - if ( q->saved == 0 ) { - q->saved = Tuple_clone( q->names ); - } else { - memcpy( q->saved, q->names, q->names->size * sizeof( void* ) ); - } - BindingTable_deref( bt, q->saved ); - // Check with new values - if ( AssignQuery_check( q->values, q->saved ) ) { - BindingTable_set_all( bt, q->names, q->values, 0 ); - return 1; - } - // Fall through - case subsequent: - case restore: - if ( q->saved ) { - BindingTable_set_all( bt, q->names, q->saved, 1 ); - free( q->saved ); - q->saved = 0; - } - return 0; - } - return 0; -} - -static void AssignQuery_variables(Query *this,HashVector *hv) { - AssignQuery *q = (AssignQuery*) this; - unsigned long i; - for ( i = 0; i < q->names->size; i++ ) { - HashVector_add( hv, q->names->elements[i] ); - } -} - -static struct QueryCallbacks AssignQuery_def = { - .reclaim = AssignQuery_reclaim, - .next = AssignQuery_next, - .variables = AssignQuery_variables -}; - -/** - * Return a Query object representing an assignment of one or more - * variables. - */ -Query *Query_assign(int arity,Tuple *names,Tuple *values) { - AssignQuery *q = (AssignQuery*) - malloc( sizeof( AssignQuery ) ); - (*q) = (AssignQuery) { - .def = &AssignQuery_def, - .names = names, - .values = values, - .saved = 0 - }; - return (Query*) q; -} diff --git a/vector/AssignQuery.h b/vector/AssignQuery.h deleted file mode 100644 index 2e38b87..0000000 --- a/vector/AssignQuery.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef AssignQuery_H -#define AssignQuery_H - -#include -#include - -/** - * AssignQuery represents an assignment of values to names. - * - * \extends Query - * \related Query - */ -typedef struct { - struct QueryCallbacks *def; - Tuple *names; - Tuple *values; - Tuple *saved; -} AssignQuery; - -void AssignQuery_assign( - BindingTable *bt,Tuple *names,Tuple *values,int all); - -#endif diff --git a/vector/Binding.c b/vector/Binding.c deleted file mode 100644 index 9bcc0c1..0000000 --- a/vector/Binding.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include - -/** - * This callback function returns the hashcode of a Binding key, which - * is its name string. - */ -static unsigned long Binding_hashcode(void *this,void *key) { - return HashVector_hashcode( - (unsigned char *) key, strlen( (char*) key ) ); -} - -/** - * This callback function determines whether a Binding item has a - * given key or not. - */ -static int Binding_haskey(void *this,void *item,void *key) { - Binding *b = (Binding*) item; - char *name = (char *) key; - return strcmp( name, (char*) b->name ) == 0; -} - -/** - * This callback function returns the key of a Binding itme, which is - * its name string. - */ -static void *Binding_itemkey(void *this,void *item) { - return ((Binding*) item)->name; -} - - -/** - * This callback function handles the "release" of a Binding key, - * which is to do nothing, since the name string memory is not managed - * here. - */ -static void Binding_releasekey(void *this,void *key) { -} - -/** - * This callback function writes a representation of a Binding item - * into a character buffer. - */ -static int Binding_tostring(void *this,void *item,char *buffer,int limit) { - Binding *b = (Binding*) item; - return snprintf( buffer, limit, "{%s,%p}", b->name, b->value ); -} - -/** - * This is the "item type" for Binding items. - */ -ItemKeyFun Bindingitem = { - .hashcode = Binding_hashcode, - .haskey = Binding_haskey, - .itemkey = Binding_itemkey, - .releasekey = Binding_releasekey, - .tostring = Binding_tostring -}; - diff --git a/vector/Binding.h b/vector/Binding.h deleted file mode 100644 index 2d1a0b3..0000000 --- a/vector/Binding.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef Binding_H -#define Binding_H - -#include - -/** - * \brief A Binding is an association of a (var*) name with a (void*) - * value. - * - * A \b (void*)0 value marks an "unbound" value. - */ -typedef struct Binding { - char *name; - void *value; -} Binding; - -/** - * \brief This is the applicable ItemKeyFun callbacks for a Binding - * item. - */ -extern ItemKeyFun Bindingitem; - -#endif diff --git a/vector/BindingTable.c b/vector/BindingTable.c deleted file mode 100644 index 2728516..0000000 --- a/vector/BindingTable.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include - -BindingTable *BindingTable_create() { - BindingTable *this = (BindingTable*) malloc( sizeof( BindingTable ) ); - (*this) = (HashVector) { - .table = (Vector) { - .variant = Nibble_index_levels, .size = 16, .entries = 0 - }, .fill = 0, .holes = 0, .type = &Bindingitem - }; - return this; -} - -void BindingTable_release(BindingTable *bt) { - if ( bt ) { - Vector_resize( &bt->table, 0, Vector_free_any, 0 ); - free( bt ); - } -} - -void BindingTable_set(BindingTable *bt,char *name,void *value) { - Binding *b = (Binding*) HashVector_find( bt, name ); - if ( b == 0 ) { - b = (Binding*) malloc( sizeof( Binding ) ); - b->name = name; - HashVector_add( bt, b ); - } - b->value = value; -} - -void *BindingTable_get(BindingTable *bt,char *name) { - Binding *b = (Binding*) HashVector_find( bt, name ); - return b? b->value : 0; -} - -void BindingTable_deref(BindingTable *bt,Tuple *t) { - unsigned long i; - for ( i = 0; i < t->size; i++ ) { - if ( t->elements[i] ) { - t->elements[i] = BindingTable_get( bt, t->elements[i] ); - } - } -} - -#if 0 -int BindingTable_unify( - BindingTable *bt,char *n1,char *n2,int (*eq)(void*,void*)) { - void *v1 = BindingTable_get( bt, n1 ); - void *v2 = BindingTable_get( bt, n2 ); - if ( v2 && v1 == 0 ) { - BindingTable_set( bt, n1, v2 ); - } - if ( v1 && v2 == 0 ) { - BindingTable_set( bt, n2, v1 ); - } - return ( v1 && v2 )? ( eq? ( eq( v1, v2 ) == 0 ) : ( v1 == v2 ) ) : 1; -} -#endif - -Tuple *BindingTable_get_all(BindingTable *bt,Tuple *t) { - Tuple *vt = Tuple_clone( t ); - BindingTable_deref( bt, vt ); - return vt; -} - -void BindingTable_set_all(BindingTable *bt,Tuple *nm,Tuple *vs,int all) { - int i; - for ( i = 0; i < nm->size; i++ ) { - BindingTable_set( bt, nm->elements[i], vs->elements[i] ); - } -} diff --git a/vector/BindingTable.h b/vector/BindingTable.h deleted file mode 100644 index aa4ee6e..0000000 --- a/vector/BindingTable.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef BindingTable_H -#define BindingTable_H - -#include -#include -#include - -/** - * A BindingTable is a chain of \ref HashVector "HashVectors" of - * Binding items that associate a (char*) name with a (void*) value. - */ -typedef HashVector/**/ BindingTable; - -/** - * \brief Allocate a new \ref BindingTable. - * - * \returns the allocated \ref bandingtable. - * - * \related BindingTable - */ -extern BindingTable *BindingTable_create(); - -/** - * \brief Reclaim a \ref BindingTable with all its bindings. - * - * \param bt is the \ref BindingTable to reclaim. - * - * \related BindingTable - */ -extern void BindingTable_release(BindingTable *bt); - -/** - * \brief Set a Binding in a \ref BindingTable. - * - * \param bt is the \ref BindingTable concerned. - * - * \param name is the Binding name. - * - * \param value is the Binding value. - * - * \note Binding names are equal or not by means of strcmp, and each - * name has a at most single Binding. - * - * The name and the value are held as given with all memory management - * the callers responsibility. This function will however create new - * Binding objects and reclaim the old ones as needed. - * - * A value of \b 0 indicates "unbound". - * - * \related BindingTable - */ -extern void BindingTable_set(BindingTable *bt,char *name,void *value); - -/** - * \brief Get a Binding from a BindingTable chain. - * - * \param bt is the first BindingTable concerned. - * - * \param name is the Binding variable name. - * - * \returns the value of the found Binding, or \b 0 if none is found. - * - * \note Binding names are equal or not by means of strcmp, and each - * name has a at most single Binding. - * - * \note Not also that a name can be made unbound on top of being - * bound by setting it to \b 0. - * - * \related BindingTable - */ -extern void *BindingTable_get(BindingTable *bt,char *name); - -/** - * \brief Replace all names with their values. - * - * \param bt is the BindingTable concerned. - * - * \param t is the tuple of (char*) names to dereference. - */ -extern void BindingTable_deref(BindingTable *bt,Tuple *t); - -/** - * \brief Set values for names, optionally unbinding names as well. - * - * \param bt is the bindingtable concerned. - * - * \param nm is the Tuple of names to bind. - * - * \param vs is the Tuple of values. - * - * \param all is a flag to assign all (1) or only non-zero (0) values. - * - * \note The values tuple must be as wide as the names tuple. - */ -extern void BindingTable_set_all(BindingTable *bt,Tuple *nm,Tuple *vs,int all); - -#endif diff --git a/vector/HashVector.c b/vector/HashVector.c index ec0e0b4..a07a916 100644 --- a/vector/HashVector.c +++ b/vector/HashVector.c @@ -1,8 +1,6 @@ #include #include -#define SELF hv->type - // Find the slot for the keyed element, and return pointer to it, or // to the first of holes encountered while considering collisions. // Returns a pointer to the place for the item, or 0 in case of OOM or @@ -14,9 +12,9 @@ static void **HashVector_find_slot( { if ( itemkey ) { // Get actual key from keying item - key = hv->type->itemkey( SELF, key ); + key = ItemKeyFun_itemkey( hv->type, key ); } - unsigned long index = hv->type->hashcode( SELF, key ) % hv->table.size; + unsigned long index = ItemKeyFun_hashcode( hv->type, key ) % hv->table.size; *i = index; void **hole = 0; void **p = 0; @@ -24,13 +22,13 @@ static void **HashVector_find_slot( p = Vector_entry( &hv->table, (*i) ); if ( p == 0 ) { if ( itemkey ) { - hv->type->releasekey( SELF, key ); + ItemKeyFun_releasekey( hv->type, key ); } return 0; // This basically means OOM, and is a failure condition. } if ( (*p) == 0 ) { if ( itemkey ) { - hv->type->releasekey( SELF, key ); + ItemKeyFun_releasekey( hv->type, key ); } return ( hole )? hole : p; // Not found; it's place is here. } @@ -38,9 +36,9 @@ static void **HashVector_find_slot( if ( hole == 0 ) { hole = p; // Remember the first hole } - } else if ( hv->type->haskey( SELF, (*p), key ) ) { + } else if ( ItemKeyFun_haskey( hv->type, (*p), key ) ) { if ( itemkey ) { - hv->type->releasekey( SELF, key ); + ItemKeyFun_releasekey( hv->type, key ); } return p; // Found } @@ -49,7 +47,7 @@ static void **HashVector_find_slot( } if ( (*i) == index ) { if ( itemkey ) { - hv->type->releasekey( SELF, key ); + ItemKeyFun_releasekey( hv->type, key ); } return 0; // Overfull HashVector! } diff --git a/vector/ItemKeyFun.h b/vector/ItemKeyFun.h deleted file mode 100644 index 93c0200..0000000 --- a/vector/ItemKeyFun.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef ItemKeyFun_H -#define ItemKeyFun_H - -#include - -/** - * \struct ItemKeyFun - * - * ItemKeyFun provides a meta-level representation for abstracting - * items as pairs of keys and payload, and having a hashcode mapping - * for keys. The key part is used for identifying items under the idea - * that all items that have the same key are the same; distinct - * representations of the same abstract entity. The hashcode scrambles - * the key part in a consistent way into an ideal table index for the - * items that have that key. Different keys may yield the same - * hashcode. - */ -typedef struct { - -#define SELF void *this - /** - * This callback function should return the hashcode of a key. The - * hashcode is used for indexing into the backing Vector for - * finding the an item via its key. The same key must map - * consistently to the same hashcode while the hashtable contains - * an item with that key. Different keys map map to the same - * hashcode, in which case the Vector placement is made at the - * first empty or hole slot following the hashcode index. - */ - unsigned long (*hashcode)(SELF,void *key); - - /** - * This callback function should determine whether an item has a - * given key or not. - */ - int (*haskey)(SELF,void *item,void *key); - - /** - * This callback function should return a (possibly temporary) key - * of an item. It can be anything (i.e., with or without internal - * structure) as it is treated as an identifier that other - * callbacks recognize. It is merely understood as a value in an - * identity domain. - */ - void *(*itemkey)(SELF,void *item); - - /** - * This callback function should handle a key obtained from the - * itemkey callback function, e.g., reclaim temporary allocation. - */ - void (*releasekey)(SELF,void *key); - - /** - * This callback function writes a representation of an item into - * a character buffer. - */ - int (*tostring)(SELF,void *item,char *buffer,int limit); - -#undef SELF -} ItemKeyFun; - -#endif diff --git a/vector/Makefile b/vector/Makefile index a3feec4..ea8e8de 100644 --- a/vector/Makefile +++ b/vector/Makefile @@ -1,34 +1,7 @@ -LIBRARY = libvector.a +LIBRARY = librrqvector.a LIBOBJS = Vector.o HashVector.o -LIBOBJS += Tuple.o TupleSchema.o integeritem.o stringitem.o Binding.o -LIBOBJS += BindingTable.o Relation.o Query.o -LIBOBJS += AssignQuery.o RelationQuery.o -LIBOBJS += NotQuery.o AndQuery.o OrQuery.o -#LIBOBJS += View.o default: $(LIBRARY) - -all: default - -CFLAGS = -Wall -g -fmax-errors=1 -I. -LDLIBS = -lm - -%.h: - echo "#ifndef ${@:.h=_H}" > $@ - echo "#define ${@:.h=_H}" >> $@ - echo "#endif" >> $@ - -define STDCC -.INTERMEDIATE: $1.o -CLEANRM += $1.o -$1.o: $1.c | $1.h -endef - -$(foreach OBJ,$(LIBOBJS:.o=),$(eval $(call STDCC,$(OBJ)))) - CLEANRM += $(LIBRARY) -$(LIBRARY): $(LIBOBJS) - $(AR) r $@ $^ -clean: - rm -f $(CLEANRM) +include ../common.mk diff --git a/vector/NotQuery.c b/vector/NotQuery.c deleted file mode 100644 index 8418715..0000000 --- a/vector/NotQuery.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -static void NotQuery_reclaim(Query *this) { - NotQuery *q = (NotQuery*) this; - Query_reclaim( q->query ); - free( this ); -} - -static int NotQuery_next( - Query *this,BindingTable *bt,enum NextState state) -{ - NotQuery *q = (NotQuery*) this; - if ( state == initial ) { - if ( Query_next( q->query, bt, initial ) ) { - Query_next( q->query, bt, restore ); - return 0; - } - return 1; - } - return 0; -} - -static void NotQuery_variables(Query *this,HashVector *hv) { - Query_variables( ((NotQuery*) this)->query, hv ); -} - -static struct QueryCallbacks NotQuery_def = { - .reclaim = NotQuery_reclaim, - .next = NotQuery_next, - .variables = NotQuery_variables -}; - -Query *Query_not(Query *q) { - NotQuery *nq = (NotQuery*) malloc( sizeof( NotQuery ) ); - (*nq) = (NotQuery) { - .def = &NotQuery_def, - .query = q - }; - return (Query*) nq; -} diff --git a/vector/NotQuery.h b/vector/NotQuery.h deleted file mode 100644 index f2bf7ed..0000000 --- a/vector/NotQuery.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef NotQuery_H -#define NotQuery_H - -#include - -/** - * NotQuery represents logical negation. - * - * \extends Query - */ -typedef struct NotQuery { - struct QueryCallbacks *def; - Query *query; -} NotQuery; - -#endif diff --git a/vector/OrQuery.c b/vector/OrQuery.c deleted file mode 100644 index 5a48e5e..0000000 --- a/vector/OrQuery.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include - -static void OrQuery_reclaim(Query *this) { - OrQuery *q = (OrQuery*) this; - int i; - for ( i = 0; i < q->size; i++ ) { - Query_reclaim( q->disjuncts[i] ); - } - free( q->disjuncts ); - free( this ); -} - -static int OrQuery_next( Query *this,BindingTable *bt,enum NextState state) { - OrQuery *q = (OrQuery*) this; - int i = q->index; - enum NextState s = subsequent; - switch ( state ) { - case initial: - i = 0; - s = initial; - case subsequent: - for ( ; i < q->size; i++ ) { - if ( Query_next( q->disjuncts[i], bt, s ) ) { - q->index = i; - return 1; - } - Query_next( q->disjuncts[i], bt, restore ); - s = initial; - } - q->index = -1; - return 0; - case restore: - if ( i >= 0 ) { - Query_next( q->disjuncts[i], bt, restore ); - q->index = -1; - } - return 0; - } - return 0; -} - -static void OrQuery_variables(Query *this,HashVector *hv) { - OrQuery *q = (OrQuery*) this; - int i; - for ( i = 0; i < q->size; i++ ) { - Query_variables( q->disjuncts[i], hv ); - } -} - -static struct QueryCallbacks OrQuery_def = { - .reclaim = OrQuery_reclaim, - .next = OrQuery_next, - .variables = OrQuery_variables -}; - -Query *Query_or(int n,...) { - va_list args; - OrQuery *q = (OrQuery *) - malloc( sizeof( OrQuery ) ); - (*q) = (OrQuery) { - .def = &OrQuery_def, - .index = -1, - .size = n, - .disjuncts = (Query**) malloc( n * sizeof( Query* ) ), - }; - int i; - va_start( args, n ); - for ( i = 0; i < n; i++ ) { - q->disjuncts[i] = va_arg( args, Query* ); - } - va_end( args ); - return (Query*) q; -} - diff --git a/vector/OrQuery.h b/vector/OrQuery.h deleted file mode 100644 index 9528356..0000000 --- a/vector/OrQuery.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef OrQuery_H -#define OrQuery_H - -#include - -/** - * OrQuery represents a disjunction of sub queries. - * - * \extends Query - */ -typedef struct { - struct QueryCallbacks *def; - int index; - int size; - Query **disjuncts; -} OrQuery; - -#endif diff --git a/vector/Query.c b/vector/Query.c deleted file mode 100644 index 94a39d7..0000000 --- a/vector/Query.c +++ /dev/null @@ -1,88 +0,0 @@ -#include - -/** - * \brief Trampoline for the callback function to reclaim the Query - * memory for a given Query. - * - * \param this is the specific \ref Query concerned. - * - * Ground queries recalim their own state memory. Composite - * queries first propagate the reclaim call to its components, and - * thereafter reclaim their local state memory. - */ -void Query_reclaim(Query *this) { - this->def->reclaim( this ); -} - -/** - * \brief Trampoline for the callback function to update the Binding - * table with a succession of alternative bindings. - * - * \param this is the specific \ref Query concerned. - * - * \param bt is the Binding table to set bindings in. - * - * \param s is the call "sub-command" for the function. - * - * \returns 1 if a new Binding is provided and 0 otherwise. - * - * This function is called repeatedly for successively obtaining - * the alternative bindings that satisfy the Query. The "initial" - * state sub-command tells the function to capture the incoming - * BindingTable state so that the function can later restore it - * upon the "restore" sub-command. Upon the "initial" command, the - * function also sets up the Binding table with its first Binding - * alternative. This is followed by a series of "subsequent" - * sub-command calls for the function to change the BindingTable - * for its succession of Binding alternatives. The function should - * return 1 after any successful Binding setup, and return 0 when - * it cannot setup any (more) Binding. - */ -int Query_next(Query *this,BindingTable *bt,enum NextState state) { - return this->def->next( this, bt, state ); -} - -/** - * \brief Trampoline for the callback function that adds its binding - * names to the hashvector. - */ -void Query_variables(Query *this,HashVector *hv) { - this->def->variables( this, hv ); -} - -void Query_eval( - Query *q,BindingTable *bt, - int (*consequent)(BindingTable *bt,void *data), - void *data ) -{ - if ( Query_next( q, bt, initial ) && consequent( bt, data ) ) { - while ( Query_next( q, bt, subsequent ) && consequent( bt, data ) ); - } - (void) Query_next( q, bt, restore ); -} - -/* ==================== Snapshotting a query ==================== */ - -/** - * The data used for bindings capture in snapshotting. - */ -struct Query_snapshot_data { - Tuple *names; - Vector *v; -}; - -static int Query_snapshot_capture(BindingTable *bt,void *data) { - struct Query_snapshot_data *d = (struct Query_snapshot_data*) data; - Tuple *values = Tuple_clone( d->names ); - BindingTable_deref( bt, values ); - Vector_append( d->v, values ); - return 1; -} - -int Query_snapshot(Query *q,Tuple *names,Vector *v) { - BindingTable *bt = BindingTable_create( 0 ); - struct Query_snapshot_data data = { .names = names, .v = v }; - Vector_resize( v, 0, Vector_free_any, 0 ); - Query_eval( q, bt, Query_snapshot_capture, &data ); - return v->size; -} diff --git a/vector/Query.h b/vector/Query.h deleted file mode 100644 index 4b2fa6a..0000000 --- a/vector/Query.h +++ /dev/null @@ -1,190 +0,0 @@ -#ifndef Query_H -#define Query_H - -#include -#include -#include - -/** - * A Query is an implementation of a generic ABI over relations. It's - * more or less a "virtual Relation" representing a logical composite - * access of actual relations, and as such its active part is held in - * a separate \ref Querytype record similar to how \ref ItemKeyFun - * records define valye types. - */ -typedef struct Query { - struct QueryCallbacks *def; -} Query; - -/** - * \brief Trampoline for the callback function to reclaim the Query - * memory for a given Query. - * - * \param this is the specific \ref Query concerned. - * - * Ground queries recalim their own state memory. Composite - * queries first propagate the reclaim call to its components, and - * thereafter reclaim their local state memory. - */ -extern void Query_reclaim(Query *this); - -/** - * \brief Trampoline for the callback function to update the Binding - * table with a succession of alternative bindings. - * - * \param this is the specific \ref Query concerned. - * - * \param bt is the Binding table to set bindings in. - * - * \param s is the call "sub-command" for the function. - * - * \returns 1 if a new Binding is provided and 0 otherwise. - * - * This function is called repeatedly for successively obtaining - * the alternative bindings that satisfy the Query. The "initial" - * state sub-command tells the function to capture the incoming - * BindingTable state so that the function can later restore it - * upon the "restore" sub-command. Upon the "initial" command, the - * function also sets up the Binding table with its first Binding - * alternative. This is followed by a series of "subsequent" - * sub-command calls for the function to change the BindingTable - * for its succession of Binding alternatives. The function should - * return 1 after any successful Binding setup, and return 0 when - * it cannot setup any (more) Binding. - */ -extern int Query_next(Query *this,BindingTable *bt,enum NextState state); - -/** - * \brief Trampoline for the callback function that adds its binding - * names to the hashvector. - * - * \param this is the query concerned. - * - * \param hv is the HashVector for collating names. - */ -extern void Query_variables(Query *this,HashVector *hv); - - - - -/** - * \brief Creates an assignment Query. - * - * \param arity is the assignment tuple arity. - * - * \param names is the (char*) names to assign. - * - * \param values is the (void*) values to asign. - * - * The two tuples must have the same arity for assigning names[i] to - * values[i]. This Query makes the given names have the given values, - * once for each (*next) call following a (*reset) call. - * - * \related Query - */ -extern Query *Query_assign(int arity,Tuple *names,Tuple *values); - -/** - * \brief Create a Query record for lookup data in a Relation. - * - * \param r is the relation being queried. - * - * \param names is a same-arity tuple of binding names for the - * columns, using \b 0 for unnamed columns. - * - * \param values is a same--arity tuple of query values, using \b 0 for - * columns to enumerate. - * - * The names and values tuples identify bindings and values to use for - * the search query, and which bindings to set up from the successive - * results. Names that are bound beforhand identify constraining - * values, and the names that are unbound gain successive values from - * the matching tuples. - * - * \related Query - */ -extern Query *Query_Relation(Relation *r,Tuple *names,Tuple *values); - -/** - * \brief Create a Query record for a conjunction of queries. - * - * \param n is the number of sub queries. - * - * \param ... are the sub queries. - * - * The conjunction query processes the sub queries in order resulting - * in the sequence of their combined bindings. - * - * \related Query - */ -extern Query *Query_and(int n,...); - -/** - * \brief Create a Query record for a disjunction of queries. - * - * \param n is the number of sub queries. - * - * \param ... are the sub queries. - * - * The disjunction query processed the sub queries in order to find - * all their individual "true" binding combinations. It processes one - * sub query at a time to exhaustion and provide their individudal - * binding sequences separately. - * - * \related Query - */ -extern Query *Query_or(int n,...); - -/** - * \brief Invoke a consequent callback function for each successful - * binding of a query. - * - * \param q is the antecedent query to process. - * - * \param bt is the binding table to use. - * - * \param consequent is the callback function to invoke for each - * binding. - * \param data is the caller's context data. - * - * This function prrocesses the Query for establishing its binding - * sequence and inokes the consequent callback function for each - * binding as it is provided. - * - * \related Query - */ -extern void Query_eval( - Query *q,BindingTable *bt, - int (*consequent)(BindingTable *bt,void *data), - void *data ); - -/** - * \brief Collect all bindings of query. - * - * \param q is the query to enumerate. - * - * \param names is the binding names to track. - * - * \param results is the result store of bindings for the names. - * - * \returns the number of results. - * - * This function evaluates the query for one round of bindings, and - * stores their value \ref Tuple Tuples in the given vector. The given - * vector is first cleared, and any item is reclaimed with \b free. - * Correspondingly the binding \ref Tuple Tuples are allocated with \b - * malloc for the caller to reclaim (possibly via a successive call to - * this function). - */ -//extern int Query_snapshot(Query *q,Tuple *names,Vector *v); - -/** - * \brief Creates an NotQuery. - * - * \param query is the query to negate. - * - * \related Query - */ -extern Query *Query_not(Query *values); - -#endif diff --git a/vector/QueryCallbacks.h b/vector/QueryCallbacks.h deleted file mode 100644 index 5d36e0b..0000000 --- a/vector/QueryCallbacks.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef QueryCallbacks_H -#define QueryCallbacks_H - -#include - -typedef struct Query Query; // forward - -enum NextState { - /** - * This state tells the "next" function that it should capture the - * incoming BindingTable state and provide the initial Binding of - * a new sucession of bindings. - */ - initial, - /** - * This state tells the "next" function that it should update the - * bidning table with a subsequent Binding in the current - * succession of bindings. - */ - subsequent, - /** - * This state tells the "next" function that it should just - * restore the Binding table to its incoming state. - */ - restore -}; - -/** - * A struct Query_callbacks record defines the callbacks for a - * specific Query type. - */ -typedef struct QueryCallbacks { - /** - * \brief Callback function to reclaim the Query memory for a - * given Query. - * - * \param this is the specific \ref Query concerned. - * - * Ground queries recalim their own state memory. Composite - * queries first propagate the reclaim call to its components, and - * thereafter reclaim their local state memory. - */ - void (*reclaim)(Query *this); - - /** - * \brief Callback function to update the Binding table with a - * succession of alternative bindings. - * - * \param this is the specific \ref Query concerned. - * - * \param bt is the Binding table to set bindings in. - * - * \param s is the call "sub-command" for the function. - * - * \returns 1 if a new Binding is provided and 0 otherwise. - * - * This function is called repeatedly for successively obtaining - * the alternative bindings that satisfy the Query. The "initial" - * state sub-command tells the function to capture the incoming - * BindingTable state so that the function can later restore it - * upon the "restore" sub-command. Upon the "initial" command, the - * function also sets up the Binding table with its first Binding - * alternative. This is followed by a series of "subsequent" - * sub-command calls for the function to change the BindingTable - * for its succession of Binding alternatives. The function should - * return 1 after any successful Binding setup, and return 0 when - * it cannot setup any (more) Binding. - */ - int (*next)(Query *this,BindingTable *bt,enum NextState state); - - /** - * \brief This callback function adds its binding names to the - * hashvector. - */ - void (*variables)(Query *this,HashVector *hv); -} QueryCallbacks; - -#endif diff --git a/vector/Relation.c b/vector/Relation.c deleted file mode 100644 index 868a477..0000000 --- a/vector/Relation.c +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include - -Relation *Relation_create(TupleSchema *schema) { - Relation *r = (Relation *) malloc( sizeof( Relation ) ); - (*r) = (Relation) { - .content = (HashVector) { - .table = (Vector) { - .variant = Nibble_index_levels, - .size = 16, - .entries = 0 - }, - .fill = 0, .holes = 0, .type = (ItemKeyFun*) schema - }, - .constraints = (Vector) { - .variant = single_index_level, - .size = 0, - .entries = 0 - }, - }; - return r; -} - -#define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) ) -#define COPY(T,P) COPYA(T,P,1) - -// Add an indexing HashVector to the Relation using the given column -// flags with 1 indicating key column and 0 indicating value column. -int Relation_add_constraint(Relation *r,...) { - va_list ap; - TupleSchema *ts = (TupleSchema *) r->content.type; - Tuple *columns = Tuple_clone( ts->columns ); - int i = 0; - va_start( ap, r ); - for ( ; i < columns->size; i++ ) { - if ( va_arg( ap, int ) == 0 ) { - columns->elements[i] = 0; - } - } - va_end( ap ); - ts = TupleSchema_create( columns ); - i = (int) r->constraints.size; - Vector_append( - &r->constraints, - HashVector_create( Nibble_index_levels, (ItemKeyFun*) ts ) ); - return i; -} - -//============== Adding an item ============= -// Iteration context for adding or Querying a Relation -typedef struct { - Relation *rel; - HashVector knockouts; - void *item; -} Knockout; - -// Determine matches to ((Knockout*)data)->key in -// (HashVector*)item, optionally using ((Knockout*)data)->columns -// for ignoring full matches to the key tuple. -static int knockout_check(VectorIndex index,void *item,void *data) { - Knockout *kod = (Knockout*) data; - void *key = kod->item; - HashVector *hv = (HashVector*) item; - TupleSchema *type = (TupleSchema *)hv->type; - VectorIndex i = 0; - for ( ; i < hv->table.size; i++ ) { - void *old = HashVector_next( hv, &i ); - if ( old ) { - if ( key && type->base.haskey( type, old, key ) == 0 ) { - continue; - } - HashVector_add( &kod->knockouts, old ); - } - } - return 0; -} - -// delete the (tuple*)item from the (HashVector*)data -static int knockout_delete(VectorIndex index,void *item,void *data) { - HashVector_delete( (HashVector*) item, data ); - return 0; -} - -// add the (tuple*)data to the (HashVector*)item -static int knockout_add(VectorIndex index,void *item,void *data) { - HashVector_add( (HashVector*)item, data ); - return 0; -} - -// Find and remove all collisions for a Query, unless "add" is -// non-zero in which case the function aborts if there is any match in -// the main content. -static int knockout_clear(Knockout *this,Relation *r,Tuple *item,int add) { - (*this) = (Knockout) { - .rel = r, - .knockouts = { - .table = { - .variant = Nibble_index_levels, .size = 16, .entries = 0 - }, - .fill = 0, .holes = 0, .type = r->content.type, - }, - .item = item - }; - knockout_check( 0, &r->content, this ); - if ( add ) { - if ( this->knockouts.fill > 0 ) { - return 0; - } - // Find all constraint knockouts for addition - Vector_iterate( &r->constraints, 0, knockout_check, this ); - } - if ( this->knockouts.fill > 0 ) { - // Delete them from all tables - VectorIndex i; - for ( i = 0; i < this->knockouts.table.size; i++ ) { - void *t = HashVector_next( &this->knockouts, &i ); - if ( t ) { - HashVector_delete( &r->content, t ); - Vector_iterate( &r->constraints, 0, knockout_delete, t ); - } - } - } - return 1; -} - -// add a tuple to a Relation and return a Vector of knocked out -// tuples, if any, or 0 otherwise. -Vector *Relation_add(Relation *r,Tuple *item) { - Knockout data; - if ( knockout_clear( &data, r, item, 1 ) ) { - // Add the new tuple - HashVector_add( &r->content, item ); - Vector_iterate( &r->constraints, 0, knockout_add, item ); - return HashVector_contents( &data.knockouts, single_index_level, 0 ); - } - return 0; -} - -Vector *Relation_delete(Relation *r,Tuple *item) { - Knockout data; - (void) knockout_clear( &data, r, item, 0 ); - return HashVector_contents( &data.knockouts, single_index_level, 0 ); -} - -void *Relation_next(Relation *r,VectorIndex *index,Tuple *query) { - HashVector *hv = &r->content; - void *key = query; - TupleSchema *type = (TupleSchema *) hv->type; - for ( ; (*index) < hv->table.size; (*index)++ ) { - void *old = HashVector_next( hv, index ); - if ( old ) { - if ( key && type->base.haskey( type, old, key ) == 0 ) { - continue; - } - return old; - } - } - (*index) = hv->table.size; - return 0; -} - diff --git a/vector/Relation.h b/vector/Relation.h deleted file mode 100644 index 852101a..0000000 --- a/vector/Relation.h +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef Relation_H -#define Relation_H - -#include -#include - -/** - * A Relation is an implementation of a tuple set with (optional) key - * constraints. The store is a \ref HashVector whose \b type is a \ref - * TupleSchema that defines the columns. The key constraints are - * represented as additional \ref HashVector "HashVectors" whose \ref - * TupleSchema "TupleSchemas" are clones of the column schema with - * some columns excluded. - * - * \extends HashVector - */ -typedef struct { - /** - * This is the primary content store for the Relation. Its type - * should be a TupleSchema declaring the "item types" for the - * Relation columns. - */ - HashVector content; - - /** - * This is a collection of relational constraints, if any, which - * are represented as HashVectors whose TupleSchemas are clones of - * the content TupleSchema with some columns excluded. - */ - Vector constraints; -} Relation; - -/** - * \brief Create a Relation for the given TupleSchema. - * - * \param schema is the column schema - * - * \returns the allocated Relation record. - * - * The given TupleSchema is set up as the type of the content - * HashVector, which also is initialised as a Nibble_index_levels - * variant Vector. - * - * \related Relation - */ -extern Relation *Relation_create(TupleSchema *schema); - -/** - * \brief Add a key constraint to a \ref Relation. - * - * \param r is the Relation concerned. - * - * \param ... are the column flags indicating key (1) or value (0) - * column for all columns. - * - * \returns the index into the constraints \ref Vector for the added - * constraint. - * - * This function adds a \ref HashVector with a \ref TupleSchema as its - * item type cloned from the content type and then modified to - * represent the constraint. Namely that the key columns have their - * "column type" set while value columsn are reset. - * - * The \b constraint \ref HashVectors are used when \ref tuple - * "tuples" are added to the \ref Relation so as to identify the - * already contained \ref tuple "tuples" that contradict the addition - * by means of having the same constraint key. The already contained - * \ref tuple "tuples" are then "knocked out" from the Relation by the - * new addition. - * - * \see Relation_add - * \related Relation - */ -extern int Relation_add_constraint(Relation *r,...); - -/** - * \brief Add the tuple to the Relation. - * - * \param r is the \ref Relation concerned. - * - * \param t is the \ref tuple to add. - * - * \returns a Vector of all knocked out tuples. - * - * This function adds the \ref tuple \b t to the \ref Relation \b r, - * and it returns a \ref Vector (single_index_level variant) of all - * same-key constraint tuples. The returned Vector is malloc-ed and it - * must be free-ed by the caller. If the tuple is already contained or - * there are no other same-key tuples knocked out, then \b 0 is - * returned. - * - * \related Relation - */ -extern Vector *Relation_add(Relation *r,Tuple *t); - -/** - * \brief Delete all tuples matching to the Query \ref tuple fromt the - * \ref Relation. - * - * \param r is the \ref Relation concerned. - * - * \param t is the \ref tuple to delete. - * - * \returns a \Vector Vector of all knocked out tuples, i.e. the - * same-key tuples, if any, contained in the Relation - * - * Note that deletion uses a "Query" tuple, which means that some - * columns may be null to mark that them match to any value. - * - * \related Relation - */ -extern Vector *Relation_delete(Relation *r,Tuple *query); - -/** - * \brief Return the next \ref tuple in the \ref Relation that matches - * to the Query \ref tuple, at or after the index. - * - * \param r is the \ref Relation concerned. - * - * \param index is a pointer to the \ref Vector index to update. - * - * \param Query is a Query \tuple tuple for selection of certain - * column values. - * - * \returns any such matching \tuple tuple and an updateed *index. - * - * \related Relation - */ -extern void *Relation_next(Relation *r,VectorIndex *index,Tuple *query); - -/** - * \brief Lay out a dynamic \ref Relation initializer for a Relation - * wth the given column "types". - * - * This defines a \ref Relation intializer that creates the \ref - * TupleSchema for the given columns. - * - * \note The initializer cannot be used statically. - * - * The \b content \ref HashVector is a \ref Nibble_index_level variant - * with an initial size of 16 slots. - * - * The constraints \ref Vector is a \ref BitPair_index_level variant - * with initial size 0. - * - * The \b content \ref HashVector \b type is set up with an allocated - * \ref TupleSchema that has an allocated \ref tuple that declares the - * column "types" view the given \ref ItemKeyFun pointers. Any add - * constraints will need to clone that \ref TupleSchema and then clear - * the column slots for the constraint value columns, typically by - * using \ref TupleSchema_mask for this. - * - * \related Relation - */ -#define RELATION(...) (Relation) { \ - .content = { \ - .table = { .variant = Nibble_index_levels, .size=16, .entries=0 }, \ - .fill = 0, .holes = 0, \ - .type = (ItemKeyFun*) TUPLESCHEMA( __VA_ARGS__ ) \ - }, \ - .constraints = { .variant = BitPair_index_levels, .size=0, .entries=0 } \ -} - -#endif diff --git a/vector/RelationQuery.c b/vector/RelationQuery.c deleted file mode 100644 index aa4bdbd..0000000 --- a/vector/RelationQuery.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include - -// Release any memory. -static void RelationQuery_reclaim(Query *this) { - RelationQuery *q = (RelationQuery*) this; - free( q->saved ); - free( this ); -} - -// Make names have given values and return 1. If any name has a -// different value then return 0. Values are strings. -static int RelationQuery_next( - Query *this,BindingTable *bt,enum NextState state) { - RelationQuery *q = (RelationQuery*) this; - VectorIndex index = q->index + 1; - switch ( state ) { - case initial: - if ( q->saved == 0 ) { - q->saved = Tuple_clone( q->names ); - } else { - memcpy( q->saved, q->names, q->names->size * sizeof( void* ) ); - } - BindingTable_deref( bt, q->saved ); - index = 0; - // Fall through - case subsequent: - for ( ; index < q->rel->content.table.size; index++ ) { - Tuple *values = Relation_next( q->rel, &index, q->values ); - if ( values ) { - q->index = index; - BindingTable_set_all( bt, q->names, values, 1 ); - return 1; - } - } - case restore: - if ( q->saved ) { - BindingTable_set_all( bt, q->names, q->saved, 1 ); - free( q->saved ); - q->saved = 0; - } - return 0; - } - return 0; -} - -static void RelationQuery_variables(Query *this,HashVector *hv) { - RelationQuery *q = (RelationQuery*) this; - unsigned long i; - for ( i = 0; i < q->names->size; i++ ) { - HashVector_add( hv, q->names->elements[i] ); - } -} - -static struct QueryCallbacks RelationQuery_def = { - .reclaim = RelationQuery_reclaim, - .next = RelationQuery_next, - .variables = RelationQuery_variables -}; - -/** - * Return a Query object representing an Relation of one or more - * variables. - */ -Query *Query_relation(Relation *r,Tuple *names,Tuple *values) { - RelationQuery *q = (RelationQuery*) malloc( sizeof( RelationQuery ) ); - (*q) = (RelationQuery) { - .def = &RelationQuery_def, - .rel = r, - .names = names, - .values = values, - .saved = 0 - }; - return (Query*) q; -} - diff --git a/vector/RelationQuery.h b/vector/RelationQuery.h deleted file mode 100644 index 4c50a53..0000000 --- a/vector/RelationQuery.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef RelationQuery_H -#define RelationQuery_H - -#include -#include - -/** - * RelationQuery represents a ground query on a relation. - * \extends Query - * \related Query - */ -typedef struct { - struct QueryCallbacks *def; - Relation *rel; - VectorIndex index; - Tuple *names; - Tuple *values; - Tuple *saved; -} RelationQuery; - -#endif diff --git a/vector/Tuple.c b/vector/Tuple.c deleted file mode 100644 index 2b5b04e..0000000 --- a/vector/Tuple.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include - -// Allocate zero-ed -Tuple *Tuple_calloc(unsigned long arity) { - Tuple *t = (Tuple *) malloc( sizeof( Tuple ) + arity * sizeof( void* ) ); - t->size = arity; - memset( t->elements, 0, arity * sizeof( void* ) ); - return t; -} - -// Allocate with values -Tuple *Tuple_create(int arity,...) { - va_list ap; - int i; - Tuple *t = (Tuple *) malloc( sizeof( Tuple ) + arity * sizeof( void* ) ); - t->size = arity; - va_start( ap, arity ); - for ( i = 0; i < arity; i++ ) { - t->elements[i] = va_arg( ap, void* ); - } - va_end( ap ); - return t; -} - -// Duplicate -Tuple *Tuple_clone(Tuple *t) { - unsigned long size = t->size * sizeof( void* ); - Tuple *ct = (Tuple *) malloc( sizeof( Tuple ) + size ); - memcpy( ct, t, size ); - return ct; -} diff --git a/vector/Tuple.h b/vector/Tuple.h deleted file mode 100644 index ea439c5..0000000 --- a/vector/Tuple.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef Tuple_H -#define Tuple_H - -#include - -typedef struct TupleSchema TupleSchema; - -/** - * A Tuple is a "self typed" array of elements. - */ -typedef struct { - /** - * The number of elements. - */ - unsigned long size; - /** - * Base address for element pointers, which thus follow this - * struct in memory. - */ - void *elements[]; -} Tuple; - -/** - * \brief Create an untyped tuple with given values. - * - * \related Tuple - */ -extern Tuple *Tuple_create(int arity,...); - -/** - * \brief Create an untyped tuple with 0 values. - * - * \related Tuple - */ -extern Tuple *Tuple_calloc(unsigned long arity); - -/** - * \brief Create a tuple as a clone of a given tuple. - * - * \related Tuple - */ -extern Tuple *Tuple_clone(Tuple *t); - -#endif diff --git a/vector/TupleSchema.c b/vector/TupleSchema.c deleted file mode 100644 index 5a92b64..0000000 --- a/vector/TupleSchema.c +++ /dev/null @@ -1,159 +0,0 @@ -#include -#include -#include -#include - -/** - * This callback function returns the hashcode of a key. - * - * \param this is a pointer to the ItemKeyFun record from where this - * callback got invoked - * - * \param key is the key to produce a hascode for - * - * \returns the hashcode which is a VectorIndex (i.e. unsigned long) - * - * The hashcode is used for indexing into the backing Vector for - * finding the an item via its key. The same key must map consistently - * to the same hashcode while the hashtable contains an item with that - * key. Different keys map map to the same hashcode, in which case the - * Vector placement is made at the first empty or hole slot following - * the hashcode index. - */ -static unsigned long TupleSchema_hashcode(void *this,void *key) { - TupleSchema *def = (TupleSchema *) this; - ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; - Tuple *kp = (Tuple*) key; - int i = 0; - unsigned long value = 5381; - for ( ; i < def->columns->size; i++ ) { - if ( columns[i] ) { - value <<= 3; - if ( kp->elements[i] ) { - value += columns[i]->hashcode( columns[i], kp->elements[i] ); - } - } - value += 17; - } - return value; -} - -/** - * This callback function determines whether an item has a - * given key or not. - */ -static int TupleSchema_haskey(void *this,void *item,void *key) { - TupleSchema *def = (TupleSchema *) this; - ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; - Tuple *kp = (Tuple*) key; - Tuple *tp = (Tuple*) item; - int i = 0; - for ( ; i < def->columns->size; i++ ) { - if ( columns[i] && kp->elements[i] ) { - if ( columns[i]->haskey( - columns[i], tp->elements[i], kp->elements[i] ) == 0 ) { - return 0; - } - } - } - return 1; -} - - -/** - * This callback function returns the key of an item by considering - * the arity and mask. - */ -static void *TupleSchema_itemkey(void *this,void *item) { - TupleSchema *def = (TupleSchema *) this; - ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; - Tuple *tp = (Tuple*) item; - Tuple *key = Tuple_clone( tp ); - int i; - for ( i = 0; i < def->columns->size; i++ ) { - if ( columns[i] ) { - key->elements[i] = columns[i]->itemkey( - columns[i], tp->elements[i] ); - } else { - key->elements[i] = 0; - } - } - return (void*) key; -} - -/** - * This callback function handles a key obtained from the itemkey - * callback function to reclaim temporary allocation. - */ -static void TupleSchema_releasekey(void *this,void *key) { - TupleSchema *def = (TupleSchema *) this; - ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; - Tuple *kp = (Tuple*) key; - int i; - for ( i = 0; i < def->columns->size; i++ ) { - if ( columns[i] ) { - columns[i]->releasekey( columns[i], kp->elements[i] ); - } - } - free( key ); -} - -#define OUT(X) a = X; if ( a > limit ) return 0; buffer += a; limit -= a - -/** - * This callback function writes a representation of an item into - * a character buffer. - */ -static int TupleSchema_tostring(void *this,void *item,char *buffer,int limit) { - TupleSchema *def = (TupleSchema *) this; - ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements; - Tuple *t = (Tuple*) item; - char *x = "<"; - int a, i; - for ( i = 0; i < def->columns->size; i++ ) { - OUT( snprintf( buffer, limit, x ) ); - x = ","; - OUT( columns[i]->tostring( - columns[i], t->elements[i], buffer, limit ) ); - } - OUT( snprintf( buffer, limit, ">" ) ); - return a; -} - -ItemKeyFun TupleSchema_callbacks = { - .hashcode = TupleSchema_hashcode, - .haskey = TupleSchema_haskey, - .itemkey = TupleSchema_itemkey, - .releasekey = TupleSchema_releasekey, - .tostring = TupleSchema_tostring -}; - -TupleSchema *TupleSchema_create(Tuple *columns) { - TupleSchema *ts = (TupleSchema*) malloc( sizeof( TupleSchema ) ); - (*ts) = (TupleSchema) { - .base = TupleSchema_callbacks, - .columns = columns - }; - return ts; -} - -#define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) ) -#define COPY(T,P) COPYA(T,P,1) - -// Duplicate a TupleSchema with optionally some columns reset. -TupleSchema *TupleSchema_mask(TupleSchema *schema,...) { - TupleSchema *masked = COPY(TupleSchema,schema); - masked->columns = Tuple_clone( schema->columns ); - va_list ap; - int i; - va_start( ap, schema ); - for ( ;; ) { - i = va_arg( ap, int ); - if ( i < 0 || i >= schema->columns->size ) { - break; - } - masked->columns->elements[i] = 0; - }; - va_end( ap ); - return masked; -} diff --git a/vector/TupleSchema.h b/vector/TupleSchema.h deleted file mode 100644 index 7bfb67d..0000000 --- a/vector/TupleSchema.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef tupleitem_H -#define tupleitem_H - -#include - -/** - * A TupleSchema record declares the ItemKeyFun functions for tuple - * items together with applicable arity and domain combinations. - * Records are created dynamically via the \ref TupleSchema_create - * function or the \ref TUPLESCHEMA convenience macro. - * - * \extends ItemKeyFun - */ -typedef struct TupleSchema { - /** - * These are the ItemKeyFun callback functions to support - * HashVector use for tuple items. The functions expects their - * ItemKeyFun pointer to be within a TupleSchema record so as to - * provide the handling of the tuple columns. - */ - ItemKeyFun base; - - /** - * This points to tuple whose elements is array of pointers to the - * tuple item domains as represented by their associated - * ItemKeyFun records. - */ - Tuple *columns; -} TupleSchema; - -/** - * Create a tuples with given values. - * - * \related TupleSchema - */ -extern TupleSchema *TupleSchema_create(Tuple *columns); - -/** - * Copy the given TupleSchema into a new TupleSchema with some columns - * masked. This represents a sub-index type using the unmasked columns - * for indexing. - * - * \related TupleSchema - */ -extern TupleSchema *TupleSchema_mask(TupleSchema *schema,...); - -/** - * \brief Return 1/0 to indicate whether the Query matches the item. - * - * \related TupleSchema - */ -extern int TupleSchema_match(TupleSchema *def,Tuple *query,Tuple *item); - -/** - * \brief Generic macro to determine the number of expressions in a - * __VA_ARGS__ - * - * \related TupleSchema - */ -#define NUMARGS(...) (sizeof((void*[]){__VA_ARGS__})/sizeof(void*)) - -/** - * \brief Create a tuple with the given values. - * - * This invokes \ref tuple_create to allocate and assign a void* - * array with the given values. - * - * \related TupleSchema - */ -#define TUPLE(...) Tuple_create( NUMARGS(__VA_ARGS__), __VA_ARGS__ ) - -/** - * \brief Create a \ref TupleSchema with the given column "types". - * - * This invokes \ref TupleSchema_create to allocate and initialize a - * \ref TupleSchema for the given columns via the \ref TUPLE macro. - * - * \related TupleSchema - */ -#define TUPLESCHEMA(...) \ - TupleSchema_create( NUMARGS( __VA_ARGS__ ), TUPLE( __VA_ARGS__ ) ) - - -#endif diff --git a/vector/integeritem.c b/vector/integeritem.c deleted file mode 100644 index 221fb70..0000000 --- a/vector/integeritem.c +++ /dev/null @@ -1,53 +0,0 @@ -#include - -/** - * This callback function returns the hashcode of a key. The hashcode - * is used for indexing into the backing Vector for finding the an - * item via its key. The same key must map consistently to the same - * hashcode while the hashtable contains an item with that key. - * Different keys map map to the same hashcode, in which case the - * Vector placement is made at the first empty or hole slot following - * the hashcode index. - */ -static unsigned long integeritem_hashcode(void *this,void *key) { - return (unsigned long) key; -} - -/** - * This callback function determines whether an item has a - * given key or not. - */ -static int integeritem_haskey(void *this,void *item,void *key) { - return item == key; -} - -/** - * This callback function returns the key of an item by considering - * the arity and schema. - */ -static void *integeritem_itemkey(void *this,void *item) { - return item; -} - -/** - * This callback function handles a key obtained from the itemkey - * callback function to reclaim temporary allocation. - */ -static void integeritem_releasekey(void *this,void *key) { -} - -/** - * This callback function writes a representation of an item into - * a character buffer. - */ -static int integeritem_tostring(void *this,void *item,char *buffer,int limit) { - return snprintf( buffer, limit, "%lld", (long long) item ); -} - -ItemKeyFun integeritem = { - .hashcode = integeritem_hashcode, - .haskey = integeritem_haskey, - .itemkey = integeritem_itemkey, - .releasekey = integeritem_releasekey, - .tostring = integeritem_tostring -}; diff --git a/vector/integeritem.h b/vector/integeritem.h deleted file mode 100644 index efa5323..0000000 --- a/vector/integeritem.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef integeritem_H -#define integeritem_H - -#include - -/** - * The stringitem record declares the ItemKeyFun functions for integer - * items. - */ -extern ItemKeyFun integeritem; - -#endif diff --git a/vector/mainpage.dox b/vector/mainpage.dox deleted file mode 100644 index 2a4c45e..0000000 --- a/vector/mainpage.dox +++ /dev/null @@ -1,46 +0,0 @@ -/** \mainpage rrq C hacks: Vectors And Such - -This software is an implementation platform for vector based -representations. - -- \ref itemkeyfun is an implementation of an abstracted type notion - that is focussed on what the \ref hashvector needs in terms of - callbacks for its separation between the hash management - functionality and the contained items. This name is choosen so as to - avoid some connotations common to the term "type". - -- \ref array and \ref vector are two implementations of an abstract - array notion using an indexing tree of 16 and 256 pointers - respectively. The indexing tree is balanced into levels matching to - the needs of the vector size. When the size of a vector is changed - then indexing levels are added or removed as needed. However - indexing pages are allocated on a needs basis. The level 0 indexing - pages hold the actual data as void* pointers - -- \ref hasharray and \ref hashvector are two implementations of an - abstract hashtable using \ref array and \ref vector as backing store - respectively. It also uses the \ref itemkeyfn type abstraction of - items being compunded as pairs of key and payload. This separates - the hashing code from the type abstraction. - -\subsubsection s0 itemkeyfun actualizations - -- \ref integeritem is an plain actualization of an itemkeyfun record - for integer items other than 0 and 1, i.e. where the void* slots of - a tuple are reinterpreted as 64-bit integers rather than "item - pointers". - -\note Note that a hashvector cannot directly contain integer items - since 0 and 1 are treated as indicating unused and holed slots. - -- stringitem is a plain actualization of an itemkeyfun record for - null-terminated character strings. - -- tupleschema is a specialization of the itemkeyfun type scheme - intended for tuple items that are arrays of items that are further - type via itemkeyfun records. A tupleschema actualization includes - the applicable arity and an array of pointers to itemkeyfun - actualizations that declare the itemkeyfun types of the parts - (columns). - -*/ diff --git a/vector/stringitem.c b/vector/stringitem.c deleted file mode 100644 index ee8f2cb..0000000 --- a/vector/stringitem.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include - -/** - * This callback function returns the hashcode of a key. The hashcode - * is used for indexing into the backing Vector for finding the an - * item via its key. The same key must map consistently to the same - * hashcode while the hashtable contains an item with that key. - * Different keys map map to the same hashcode, in which case the - * Vector placement is made at the first empty or hole slot following - * the hashcode index. - */ -static unsigned long stringitem_hashcode(void *this,void *key) { - return HashVector_hashcode( (unsigned char*)key, strlen( (char*)key ) ); -} - -/** - * This callback function determines whether an item has a - * given key or not. - */ -static int stringitem_haskey(void *this,void *item,void *key) { - return strcmp( item, key ) == 0; -} - -/** - * This callback function returns the key of an item by considering - * the arity and schema. - */ -static void *stringitem_itemkey(void *this,void *item) { - return item; -} - -/** - * This callback function handles a key obtained from the itemkey - * callback function to reclaim temporary allocation. - */ -static void stringitem_releasekey(void *this,void *key) { -} - -/** - * This callback function writes a representation of an item into - * a character buffer. - */ -static int stringitem_tostring(void *this,void *item,char *buffer,int limit) { - if ( item ) { - return snprintf( buffer, limit, "\"%s\"", (char*) item ); - } - return snprintf( buffer, limit, "(null)" ); -} - -ItemKeyFun stringitem = { - .hashcode = stringitem_hashcode, - .haskey = stringitem_haskey, - .itemkey = stringitem_itemkey, - .releasekey = stringitem_releasekey, - .tostring = stringitem_tostring -}; diff --git a/vector/stringitem.h b/vector/stringitem.h deleted file mode 100644 index 5cc1a32..0000000 --- a/vector/stringitem.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef stringitem_H -#define stringitem_H - -#include - -/** - * The stringitem record declares the ItemKeyFun functions for string - * items. - */ -extern ItemKeyFun stringitem; - -#endif