[ICS] IVORY CITY STSTEMS
mod_perlservice  
ds (data structures) api  


DS API REFERENCE

DQUESTIONS/PROBLEMS/BUGS: michaelcollins@ivorycity.com

DOWNLOAD:
xwspc-0.0.2.tar.gz

FUNCTION SUMMARY:

dsany general functions



dsany dsmake( const char* dsdescription )
int dsquery( dsany toquery, const char* query, dsany* returnval )
void dsdump( dsany dumpme )
void dsdelete( dsany delme, int freechildren )
void dsglobal_setdestructor( int usertype, dsdestructor fcn )

Setting dsany variable type


Longhand Shorthand
void dsany_setchar( dsany ds, char chr) dsetC( dsany ds, char chr)
void dsany_setuchar( dsany ds, unsigned char uchr ) dsetUC( dsany ds, unsigned char uchr )
void dsany_setuint( dsany ds, unsigned int uint ) dsetUI( dsany ds, unsigned int uint )
void dsany_setint( dsany ds, int sint ) dsetI( dsany ds, int sint )
void dsany_setfloat( dsany ds, float flt ) dsetF( dsany ds, float flt )
void dsany_setstring( dsany ds, char* string ) dsetS( dsany ds, char* string )
void dsany_setvoid( dsany ds, void* vptr ) dsetV( dsany ds, void* vptr )
void dsany_setdsarray( dsany ds, dsarray* lowlevelarr ) *Low Level dsetLLA( dsany ds, dsarray* lowlevelarr )
void dsany_setdsanyarray( dsany ds, dsanyarray* dsanyarr ) dsetA( dsany ds, dsanyarray* dsanyarr )
void dsany_setdstable( dsany ds, dstable* dsanytable ) dsetT( dsany ds, dstable* dsanytable )

Testing dsany variable type


Longhand Shorthand
int dsany_ischar( dsany ds ) dsisC( dsany ds )
int dsany_isuchar( dsany ds ) dsisUC( dsany ds )
int dsany_isuint( dsany ds ) dsisUI( dsany ds )
int dsany_isint( dsany ds ) dsisI( dsany ds )
int dsany_isfloat( dsany ds ) dsisF( dsany ds )
int dsany_isstring( dsany ds ) dsisS( dsany ds )
int dsany_isvoid( dsany ds ) dsisV( dsany ds )
int dsany_isdsarray( dsany ds ) dsisLLA( dsany ds )
int dsany_isdsanyarray( dsany ds ) dsisA( dsany ds )
int dsany_isdstable( dsany ds ) dsisT( dsany ds )

Getting the value of a dsany variable



char dsC(dsany ds)
unsigned char dsUC(dsany ds)
int dsI(dsany ds)
unsigned int dsUI(dsany ds)
float dsF(dsany ds)
char* dsS(dsany ds)
void* dsV(dsany ds)
dsarray* dsLLA(dsany ds)
dsanyarray* dsA(dsany ds)
dstable* dsA(dsany ds)

dsarray functions (Low Level -- You usually won't use this.)



dsarray* dsarray_new( int nElements, int elementSize )
int dsarray_delete( dsarray* a )
int dsarray_clear ( dsarray* a )
int dsarray_push ( dsarray* a, void* ptrToYourData )
int dsarray_pop ( dsarray* a, void* ptrToYourContainer )
int dsarray_unshift ( dsarray* a, void* ptrToYourData )
int dsarray_set ( dsarray* a, int index, void* ptrToYourData )
int dsarray_get ( dsarray* a, int index, void* ptrToYourContainer )
int dsarray_getIndexOf ( dsarray* a, void* pDataToMatch, int startAtIndex )
int dsarray_spliceOut ( dsarray* a, int startIndex, int endIndex )
int dsarray_foreachcall ( dsarray* a, dsarray_callback yourCallback, void* yourUserData )
int dsarray_getLength ( dsarray* a )

dsanyarray functions (Arrays of dsany type variables)



dsanyarray* dsanyarray_new ( int nElements)
int dsanyarray_delete ( dsanyarray* a, int deleteChildren )
int dsanyarray_clear ( dsanyarray* a )
int dsanyarray_push ( dsanyarray* a, dsany ds )
int dsanyarray_pop ( dsanyarray* a, dsany* ds )
int dsanyarray_unshift ( dsanyarray* a, dsany* ds )
int dsanyarray_set ( dsanyarray* a, int index, dsany ds )
int dsanyarray_get ( dsanyarray* a, int index, dsany* ds )
int dsanyarray_getIndexOf ( dsanyarray* a, dsany dsanyToMatch, int startAtIndex )
int dsanyarray_spliceOut ( dsanyarray* a, int startIndex, int endIndex )
int dsanyarray_foreachcall ( dsanyarray* a, dsarray_callback yourCallback, void* yourUserData )
int dsanyarray_getLength ( dsanyarray* a )

dstable functions (key-value storage for dsany type variables)



dstable* dstable_new (int approxEntries )
int dstable_add ( dstable* t, const char* key, dsany value )
int dstable_set ( dstable* t, const char* key, dsany value )
int dstable_get ( dstable* t, const char* key, dsany* value )
int dstable_foreach ( dstable* t, dstable_iterator* iter, char** key, dsany* value )
int dstable_clear ( dstable* t, int destroy_values )
int dstable_delete ( dstable* t, int destroy_values )
int dstable_getlength ( dstable* t )
void dstable_setnocasekeys ( dstable* t )
void dstable_iterator_init ( struct dstable_iterator* iterator )


OVERVIEW:

So what do we have here? The data structures (ds) API is the C interface for dynamic data structures used with mod_perlservice. Of course Perl uses dynamic data structures, so we need to have a way to deal with them in C too!

The dsany type can be any type of data structure, makes sense huh? A dsany type is basically equivalent to the Perl scalar type, ie a number, string, or reference (arrays, and tables).

FUNCTION DOCUMENTATION

dsany dsmake( const char* dsdescription )

Allocate and populate a new dsany variable. This is the highest level way to create a very complex data structure. It basically compiles the description into the corresponding entry. Don't be fooled though, this is very fast at runtime! It's hardly slower than constructing it by hand and much more readable!

USAGE
dsany mydsany = dsmake("[ { mystr:'hello',myint:8192},123.321,'some text']");

ARGUMENTS

char* dsdescription The dsdescription argument constructs, allocates, and populates a dynamic data structure described by the string. For instance, the above example creates a dsanyarray with 3 elements: a table, a number, and a string. The table has 2 key value pairs. mystr keys for the string 'hello' and myint keys for 8192.

You may nest these data structures as deeply as you wish. The below table describes the format for each type.

Integer Type 8192
Float Type 11.110
String Type 'hello' (enclosed by single quote )
Array Type [item1, item2, item3,...]
Table Type { key1: item1, key2: item2, key3, item3, ...}

Of course table and array items may be any of the above types including arrays and tables for nesting.

int dsquery( dsany toquery, const char* query, dsany* returnval )

This function is the highest level and easiest way to retrieve a variable in a complex data structure. It is dsmake's counterpart. Again, it's very fast, so to you performance mongers: don't be afraid to use it! It queries the 'dsany toquery' and stores the return value in retval. It returns 1 if the variable can be found, 0 if it cannot.

USAGE

dsany myvar;
dsany myds = dsmake( "{ keyofmyarray: [ { keyofmyvar:200, blah:'hi' } ], someotherkey:18 }" );

if( dsquery( myds, "thekeyofmyarray.[0].thekeyofmyvar", &myvar) ){ printf("myvar %d",dsI(myvar); }
else { printf("not found\n"); }

if( dsquery( myds, "thekeyofmyarray.[0].blah", &myvar) ){ printf("myvar %s",dsS(myvar); }
else { printf("not found\n"); }

if( dsquery( myds, "someotherkey", &myvar) ){ printf("myvar %d",dsI(myvar); }
else { printf("not found\n"); }

dsdelete(myds,1);

ARGUMENTS
dsany toquery The dsany you want to query. This will be a dstable or dsanyarray, otherwise why would you want to query it?
const char* query The query string. Queries are constructed as follows:
Arrays are queried using [elementid]
Tables are queried using the key of your element
Put them all with '.' (period) in between and you've got yourself a query.
So, "[8].foo" queries a dsanyarray's element 8, which is a table with a key 'foo'. We've stored foo's value in returnval.
dsany* returnval The variable where the result is stored. Remember to pass the POINTER to it!

void dsdump( dsany dumpme )

Recursively prints the data structure to stdout. Good for debugging.

USAGE
dsdump( mydsany );

ARGUMENTS
dsany dumpme The dsany you want to print out.

void dsdelete( dsany delme, int freechildren )

dstables, dsanyarrays and dsarrays are all dynamically allocated. This function frees their dynamic memory and sends them to that heap in data structure heaven. Optionally, you may request it to delete the children recursively. You will usually want to do that. Be careful though not to delete variables that are not dynamically allocated! Don't attempt to delete constant strings for instance, that would be bad.

USAGE
dsdelete( myComplexDs, 1);
dsdelete( mySingleLevelDs, 0);

ARGUMENTS
dsany delme The datastructure you want scrubbed from existence.
int freechildren Whether it should recursively commit datastructuricide.

void dsglobal_setdestructor( int usertype, dsdestructor fcn )

This is an advanced user feature. You can extend the dsapi to your own data structures by defining your own user types. Internally the dsapi uses negative numbers for dsany types, so you can use positive numbers greater than 0 for your custom types. However, you may need to have automatic destruction of your custom type via dsdelete. Here just tell the dsapi what number your custom type is and pass your destructor function and it will automatically call your custom destructor when it's elimination time.

USAGE

#define MYTYPE 1

struct mytype
{
   int a;
   int b;
};

void mytype_destroy(void* yourtype, int freechildren )
{
   free(yourtype);
}

int main()
{
   struct mytype* m = malloc( sizeof(struct mytype) );
   dsany d;
   dsglobal_setdestructor( MYTYPE, mytype_destroy);

   d.type = MYTYPE;
   d.any.voidptr = m;

   dsdelete( d, 0 ); /* your destructor will be called */
}

ARGUMENTS

int usertype Your user defined type number. Anything > 0 is okay to define.
dsdestructor fcn
Your custom destructor function in the format: void mydestructor( void* mytype, int destroy children )

void dsany_set*(dsany d, X ) or dset*(dsany d, X )

Set the dsany to a specified value and type. This does not allocate anything! What you pass is what you store. The dsany is not passed by pointer.

USAGE
dsany myds;
char* str = "Hello!";

dsetI(myds, 1492 ); /*myds is an int type and contains 1492 */
dsetS(myds, str );   /*myds is a string type and contains the pointer to "Hello!". Beware, 'str' is local!! */

ARGUMENTS
dsany d
The dsany you want to set.
X
The thing you want to set it to.

int dsany_is*( dsany ds )

Test to see if a dsany is of a specific type. Returns true if so, false if not.

USAGE

if( dsisI(myds) ) {.. do int stuff ..}
else if ( dsisS( myds ) ) { .. do string stuff .. }
else if ( dsisT( myds ) ) { .. do table stuff  .. }


ARGUMENTS

dsany ds  The dsany you want to test

<yourtype> ds*(dsany ds)

This set of functions retrieves the value stored in the dsany.

USAGE

int myi = dsI(mydsany1);
dstable* t = dsT(mydsany2);


dsarray* dsarray_new( int nElements, int elementSize )

The dsarray* functions are a lower lever array interface, you will usually want to use dsanyarray* which are arrays of dsany type variables. dsarray's can be arrays of any SIZE object as opposed to dsany arrays which can be any type of object. They grow automatically to any size, though they are not implemented using linked lists -- an actual data buffer is used.  This means that your array will indeed be the size of your greatest element index. This approach makes setting and getting elements very fast, but operations like splicing are slow.  Initialize the size of your array to the number elements you think you're going to need to maximize performance.

When you set an element in the array, you're actually copying all the data into the array, not a pointer. So the array allocates all the storage you need.

USAGE

dsarray* a1 = dsarray_new( 1024, sizeof(int) ); /* creates a 1024 element dynamic array of integers */
dsarray* a2 = dsarray_new( 24, sizeof( struct mystruct ) ); /* creates a 24 element dynamic array of 'struct mystruct' */

ARGUMENTS

int nElements
The number of initial elements you want in your array. The array can grow beyond this, but its your best guess.
int elementSize
The size of each element in your array. So if you wanted to store structures that are 18 bites wide, you would say 18 here. An array with 10 elements would take up 180 bytes of space.

int dsarray_delete( dsarray* a )

Free all resources associated with this array. Returns 1 if okay, else 0.

int dsarray_clear ( dsarray* a )

This function zero's out the array, deleting all the data. It's a born again array. The array length is set to 0. Returns 1 if okay else 0.

int dsarray_push ( dsarray* a, void* ptrToYourData )

Append and copy the data pointed to by ptrToYourData to the end of the array.  Remember,  the size of your data must be the element SIZE you specified when you created the array. Returns 1 if okay else 0.

USAGE
dsarray* a1 = dsarray_new( 8, sizeof(int) );
int myint = 3;

dsarray_push( a1, &myint );

int dsarray_pop ( dsarray* a, void* ptrToYourContainer )

Pop the last element off the end of the array and copy it into ptrToYourContainer. Make sure your container is big enough! Returns 1 if okay else 0.

USAGE

dsarray* a1 = dsarray_new( 8, sizeof(double) );
double d1 = 3.14;
double d2;
dsarray_push( a1, &d1 );
dsarray_pop( a1,  &d2 );

int dsarray_unshift ( dsarray* a, void* ptrToYourData )

Remove the first element from the front of the array and copy it into ptrToYourData. This operation may be slow on large arrays. Returns 1 if okay else 0.

int dsarray_set ( dsarray* a, int index, void* ptrToYourData )

Copies the contents of ptrToYourData to the 'index' in array 'a'. Returns 1 if okay else 0.

USAGE
double md = 32.221
dsarray_set(mya, 32, &md ); /* sets element 32 to 32.221 */

int dsarray_get ( dsarray* a, int index, void* ptrToYourContainer )

Copies the contents of the data at the specified index into ptrToYourContainer. Returns 1 if okay else 0.

USAGE
double md;
dsarray_get(mya, 32, &md ); /* sets md to the double at index 32 */

int dsarray_getIndexOf ( dsarray* a, void* pDataToMatch, int startAtIndex )

Returns the index in the array of first intstance of the contents pointed to by pDataToMatch. It starts searching at index 'startAtIndex'. If there is no match, it returns -1.

int dsarray_spliceOut ( dsarray* a, int startIndex, int endIndex )

Removes elements from the array. It removes elements from startIndex up to and including endIndex, and the remaining elements are spliced together.
Returns 1 if okay else 0.

int dsarray_foreachcall ( dsarray* a, dsarray_callback yourCallback, void* yourUserData )

This is one way to interate through every element of the array.
For every element in the array, 'yourCallback' function of the form:

  int mycallback(dsarray* a, void* yourUserData, void* ptrToElement)

is called. Your callback must return either DSARRAY_CALLBACK_STOP, if you want to stop iterating or DSARRAY_CALLBACK_CONTINUE if otherwise. You may place anything you want in 'yourUserData' for use in the callback.

USAGE

int
onElement
(dsarray* a, void* yourUserData, void* ptrToElement)
{
  int* pcount = (int*) yourUserData;
  char** ppchar = (char**) ptrToElement;

  if( *pcount>10 )  return DSARRAY_CALLBACK_STOP;

  printf(*ppchar);
  *pcount++;

  return DSARRAY_CALLBACK_CONTINUE;
}
.
.
.
int count = 0;
dsarray_foreachcall( mycharptrarr, onElement, &count );

int dsarray_getLength ( dsarray* a )

Returns the number of items in the array.

dsanyarray* dsanyarray_new ( int nElements )

The dsany array is an array of dsany type variables. This is the array you will most often use. For instance, the arrays created with dsmake are dsany arrays. This function creates a new dsany array with a projected length of nElements. This entire set of functions inherits from dsarray, but provides a convenience interface for specifically dealing with dsany's.

int dsanyarray_delete ( dsanyarray* a, int deleteChildren )

Free all resources associated with this array. If deleteChildren is 1, call the appropriate dsany destructor recursively for all the child dsany's. Returns 1 if okay, else 0.

int dsanyarray_clear ( dsarray* a )

This function zero's out the array, deleting all the data. It's a born again array. The array length is reset to 0. Returns 1 if okay else 0.

int dsanyarray_push ( dsanyarray* a, dsany ds )

Append and copy the dsany to the end of the array. Notice the dsany is passed by VALUE. Returns 1 if okay else 0.

USAGE
dsany da = dsmake("[1,2,3,4]");
dsany di;
dsetI(di,5 );
dsanyarray_push( dsA(a1), di ); /* the array now reads [1,2,3,4,5] */

int dsanyarray_pop ( dsanyarray* a, dsany* ds )

Pop the last element off the end of the array and copy it into ds. Make sure you pass the pointer to the dsany! Returns 1 if okay else 0.

USAGE

dsany  da = dsmake("[ 1.1, 2.2, 3.3, 4.4 ]");
dsany f1;
dsanyarray_pop( a1,  &f1 ); /* f1 is a float of value 4.4 and the array is [ 1.1, 2.2, 3.3 ] */

int dsanyarray_unshift ( dsanyarray* a, dsany* ds )

Remove the first element from the front of the array and copy it into ds. Its just like the pop, but from the front of the array. This operation may be slow on large arrays. Returns 1 if okay else 0.

int dsanyarray_set ( dsanyarray* a, int index, dsany ds )

Sets the index 'index' to ds. Pass by VALUE! Returns 1 if okay else 0.

USAGE
dsany md;
dsany mi;

dsetF(md, 32.221 );
dsetI( mi, 200 );

dsanyarray_set(mya, 32, md ); /* sets element 32 to md */
dsanyarray_set(mya, 33, mi );  /* sets element 32 to mi  */

int dsanyarray_get ( dsanyarray* a, int index, dsany* ds )

Copies the dsany at 'index' into 'ds' . Returns 1 if okay else 0.

USAGE
dsany md;
dsetF(32.221);

dsanyarray_set(mya, 32, &md ); /* sets element 32 to 32.221 */

int dsanyarray_getIndexOf ( dsanyarray* a, dsany dsanyToMatch, int startAtIndex )

Returns the index in the array of first intstance of the contents that match 'dsanyToMatch'. It starts searching at index 'startAtIndex'. If there is no match, it returns -1.

int dsanyarray_spliceOut ( dsanyarray* a, int startIndex, int endIndex )

Removes elements from the array. It removes elements from startIndex up to and including endIndex, and the remaining elements are spliced together.
Returns 1 if okay else 0.

int dsanyarray_foreachcall ( dsanyarray* a, dsarray_callback yourCallback, void* yourUserData )

This is one way to interate through every element of the array.
For every element in the array, 'yourCallback' function of the form:

  int mycallback(dsarray* a, void* yourUserData, void* ptrToElement)

is called. Your callback must return either DSARRAY_CALLBACK_STOP, if you want to stop iterating or DSARRAY_CALLBACK_CONTINUE if otherwise. You may place anything you want in 'yourUserData' for use in the callback.

USAGE

int
onElement
(dsarray* a, void* yourUserData, dsany* ptrToDsAny)
{
  int* pcount = (int*) yourUserData;
  dsany d     = *ptrToDsAny;

  if( *pcount>10 )  return DSARRAY_CALLBACK_STOP;

  printf( dsS(d) );
  *pcount++;

  return DSARRAY_CALLBACK_CONTINUE;
}
.
.
.
int count = 0;
dsanyarray_foreachcall( mycharptrarr, onElement, &count );


int dsanyarray_getLength ( dsanyarray* a )

Returns the number of items in the array.

dstable* dstable_new (int approxEntries )

Creates a new key-value table of dsany type variables. 'approxEntries' is your best guess of how many key-value pairs you will need in your table: this is done for efficiency reasons and you may safely have more or fewer pairs.

int dstable_add ( dstable* t, const char* key, dsany value )

Adds a new key-value pair to the table. Returns 1 if OK, else 0.

USAGE:

dstable* t = dstable_new(8);
dsany n;
dsetI( 12 );

dstable_add( t, "mykey", n ); /* adds a dsany value 12 to the table */

int dstable_set ( dstable* t, const char* key, dsany value )

This changes the value of the dsany. If the key-value pair doesn't exist, this behaves exactly as dstable_add. Returns 1 if OK, else 0.

int dstable_get ( dstable* t, const char* key, dsany* value )

Gets the dsany value associated with 'key'. Returns 0 if key doesn't exist, else 1 means OK.

USAGE:

dsany dt  = dsmake("{ hello: 'world' , foo: 'bar', left: 'right' }");
dsany dstr;
dstable_get( dsT(dt), "foo", &dstr );
printf( dsS(dstr) ); /* prints bar */

int dstable_foreach ( dstable* t, dstable_iterator* iter, char** key, dsany* value )

This function iterates through each key-value pair in the table one by one. Once you initialize the dstable_iterator, call this function each time you want to get a new key-value pair. It returns 0 if you have seen all the pairs, 1 if some remain.

USAGE:

dsany dt = dsmake("{ hello: 'world' , foo: 'bar', left: 'right' }");

char* key;
dsany value;
dstable_iterator it;

dstable_iterator_init ( &it );

while( dstable_foreach( dsT(dt), &it, &key, &value  ) )
{
  printf("key %s value %s\n", key, dsS(value) );
}

int dstable_clear ( dstable* t, int destroy_children )

This function clears out all the existing key-value pairs in the table though it does not free the table itself. You would want to use this when you want a clean, fresh table but don't want to deallocate an old table and reallocate a new one. Pass a '1' for destroy values if you want to call the destructors (if any) of the values in the table.

int dstable_delete ( dstable* t, int destroy_children )

Deletes and frees the table's resources. Optionally recursively calls the destructors of the dsany values contained in the table.

int dstable_getlength ( dstable* t )

Returns the number of key-value pairs stored in the table.

int dstable_setnocasekeys ( dstable* t )

Tells the dstable to ignore case in the table keys. 

USAGE:

dsany dstr;
dsany dt  = dsmake("{ hello: 'world' , foo: 'bar', left: 'right' }");

dstable_setnocasekeys( dsT( dt ) );

dstable_get( dsT(dt), "foo", &dstr );
printf( dsS(dstr) ); /* prints bar */

dstable_get( dsT(dt), "FOo", &dstr );
printf( dsS(dstr) ); /* prints bar */




Copyright 2004. Michael W. Collins, Ivory City Systems.
You have permission to copy and redistribute this document so long as it retains this original copyright.