123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- .fp 5 CW
- .TH LIBCDT 3
- .SH NAME
- \fBCdt\fR \- container data types
- .SH SYNOPSIS
- .de Tp
- .fl
- .ne 2
- .TP
- ..
- .de Ss
- .fl
- .ne 2
- .SS "\\$1"
- ..
- .de Cs
- .nf
- .ft 5
- ..
- .de Ce
- .ft 1
- .fi
- ..
- .ta 1.0i 2.0i 3.0i 4.0i 5.0i
- .Cs
- #include <cdt.h>
- .Ce
- .Ss "DICTIONARY TYPES"
- .Cs
- Dt_t;
- Dtdisc_t;
- Dtmethod_t;
- Dtlink_t;
- Dtstat_t;
- .Ce
- .Ss "DICTIONARY CONTROL"
- .Cs
- Dt_t* dtopen(const Dtdisc_t* disc, const Dtmethod_t* meth);
- int dtclose(Dt_t* dt);
- void dtclear(dt);
- Dtmethod_t* dtmethod(Dt_t* dt, const Dtmethod_t* meth);
- Dtdisc_t* dtdisc(Dt_t* dt, const Dtdisc_t* disc);
- Dt_t* dtview(Dt_t* dt, Dt_t* view);
- .Ce
- .Ss "STORAGE METHODS"
- .Cs
- Dtmethod_t* Dtset;
- Dtmethod_t* Dtoset;
- Dtmethod_t* Dtobag;
- Dtmethod_t* Dtqueue;
- .Ce
- .Ss "DISCIPLINE"
- .Cs
- #define DTDISC(disc,key,size,link,makef,freef,comparf)
- typedef void* (*Dtmake_f)(void*, Dtdisc_t*);
- typedef void (*Dtfree_f)(void*, Dtdisc_t*);
- typedef int (*Dtcompar_f)(Dt_t*, void*, void*, Dtdisc_t*);
- .Ce
- .Ss "OBJECT OPERATIONS"
- .Cs
- void* dtinsert(Dt_t* dt, void* obj);
- void* dtdelete(Dt_t* dt, void* obj);
- void* dtdetach(Dt_t* dt, void* obj);
- void* dtsearch(Dt_t* dt, void* obj);
- void* dtmatch(Dt_t* dt, void* key);
- void* dtfirst(Dt_t* dt);
- void* dtnext(Dt_t* dt, void* obj);
- void* dtlast(Dt_t* dt);
- void* dtprev(Dt_t* dt, void* obj);
- void* dtfinger(Dt_t* dt);
- void* dtrenew(Dt_t* dt, void* obj);
- int dtwalk(Dt_t* dt, int (*userf)(void*, void*), void*);
- Dtlink_t* dtflatten(Dt_t* dt);
- Dtlink_t* dtlink(Dt_t*, Dtlink_t* link);
- void* dtobj(Dt_t* dt, Dtlink_t* link);
- Dtlink_t* dtextract(Dt_t* dt);
- int dtrestore(Dt_t* dt, Dtlink_t* link);
- .Ce
- .Ss "DICTIONARY STATUS"
- .Cs
- int dtsize(Dt_t* dt);
- int dtstat(Dt_t* dt, Dtstat_t*, int all);
- .Ce
- .Ss "HASH FUNCTIONS"
- .Cs
- unsigned int dtstrhash(void *str, int n);
- .Ce
- .SH DESCRIPTION
- .PP
- \fICdt\fP manages run-time dictionaries using standard container data types:
- unordered set/multiset, ordered set/multiset, list, stack, and queue.
- .PP
- .Ss "DICTIONARY TYPES"
- .PP
- .Ss " Dt_t"
- This is the type of a dictionary handle.
- .PP
- .Ss " Dtdisc_t"
- This defines the type of a discipline structure which describes
- object lay-out and manipulation functions.
- .PP
- .Ss " Dtmethod_t"
- This defines the type of a container method.
- .PP
- .Ss " Dtlink_t"
- This is the type of a dictionary object holder (see \f5dtdisc()\fP.)
- .PP
- .Ss " Dtstat_t"
- This is the type of a structure to return dictionary statistics (see \f5dtstat()\fP.)
- .PP
- .Ss "DICTIONARY CONTROL"
- .PP
- .Ss " Dt_t* dtopen(const Dtdisc_t* disc, const Dtmethod_t* meth)"
- This creates a new dictionary.
- \f5disc\fP is a discipline structure to describe object format.
- \f5meth\fP specifies a manipulation method.
- \f5dtopen()\fP returns the new dictionary or \f5NULL\fP on error.
- .PP
- .Ss " int dtclose(Dt_t* dt)"
- This deletes \f5dt\fP and its objects.
- Note that \f5dtclose()\fP fails if \f5dt\fP is being viewed by
- some other dictionaries (see \f5dtview()\fP).
- \f5dtclose()\fP returns \f50\fP on success and \f5-1\fP on error.
- .PP
- .Ss " void dtclear(Dt_t* dt)"
- This deletes all objects in \f5dt\fP without closing \f5dt\fP.
- .PP
- .Ss " Dtmethod_t dtmethod(Dt_t* dt, const Dtmethod_t* meth)"
- If \f5meth\fP is \f5NULL\fP, \f5dtmethod()\fP returns the current method.
- Otherwise, it changes the storage method of \f5dt\fP to \f5meth\fP.
- Object order remains the same during a
- method switch for \f5Dtqueue\fP.
- Switching to and from \f5Dtset\fP and \f5Dtoset/Dtobag\fP may cause
- objects to be rehashed, reordered, or removed as the case requires.
- \f5dtmethod()\fP returns the previous method or \f5NULL\fP on error.
- .PP
- .Ss " Dtdisc_t* dtdisc(Dt_t* dt, const Dtdisc_t* disc)"
- If \f5disc\fP is \f5NULL\fP, \f5dtdisc()\fP returns the current discipline.
- Otherwise, it changes the discipline of \f5dt\fP to \f5disc\fP.
- Objects may be rehashed, reordered, or removed as appropriate.
- \f5dtdisc()\fP returns the previous discipline on success
- and \f5NULL\fP on error.
- .PP
- .Ss " Dt_t* dtview(Dt_t* dt, Dt_t* view)"
- A viewpath allows a search or walk starting from a dictionary to continue to another.
- \f5dtview()\fP first terminates any current view from \f5dt\fP to another dictionary.
- Then, if \f5view\fP is \f5NULL\fP, \f5dtview\fP returns the terminated view dictionary.
- If \f5view\fP is not \f5NULL\fP, a viewpath from \f5dt\fP to \f5view\fP is established.
- \f5dtview()\fP returns \f5dt\fP on success and \f5NULL\fP on error.
- .PP
- It is an error to have dictionaries on a viewpath with different storage methods.
- In addition, dictionaries on the same view path should
- treat objects in a consistent manner with respect to comparison or hashing.
- If not, undefined behaviors may result.
- .PP
- .Ss "STORAGE METHODS"
- .PP
- Storage methods are of type \f5Dtmethod_t*\fP.
- \fICdt\fP supports the following methods:
- .PP
- .Ss " Dtoset"
- .Ss " Dtobag"
- Objects are ordered by comparisons.
- \f5Dtoset\fP keeps unique objects.
- \f5Dtobag\fP allows repeatable objects.
- .PP
- .Ss " Dtset"
- Objects are unordered.
- \f5Dtset\fP keeps unique objects.
- This method uses a hash table with chaining to manage the objects.
- .PP
- .Ss " Dtqueue"
- Objects are kept in a queue, i.e., in order of insertion.
- Thus, the first object inserted is at queue head
- and will be the first to be deleted.
- .PP
- .Ss "DISCIPLINE"
- .PP
- Object format and associated management functions are
- defined in the type \f5Dtdisc_t\fP:
- .Cs
- typedef struct
- { int key, size;
- int link;
- Dtmake_f makef;
- Dtfree_f freef;
- Dtcompar_f comparf;
- } Dtdisc_t;
- .Ce
- .Ss " int key, size"
- Each object \f5obj\fP is identified by a key used for object comparison or hashing.
- \f5key\fP should be non-negative and defines an offset into \f5obj\fP.
- If \f5size\fP is negative, the key is a null-terminated
- string with starting address \f5*(void**)((char*)obj+key)\fP.
- If \f5size\fP is zero, the key is a null-terminated string with starting address
- \f5(void*)((char*)obj+key)\fP.
- Finally, if \f5size\fP is positive, the key is a byte array of length \f5size\fP
- starting at \f5(void*)((char*)obj+key)\fP.
- .PP
- .Ss " int link"
- Let \f5obj\fP be an object to be inserted into \f5dt\fP as discussed below.
- If \f5link\fP is negative, an internally allocated object holder is used
- to hold \f5obj\fP. Otherwise, \f5obj\fP should have
- a \f5Dtlink_t\fP structure embedded \f5link\fP bytes into it,
- i.e., at address \f5(Dtlink_t*)((char*)obj+link)\fP.
- .PP
- .Ss " void* (*makef)(void* obj, Dtdisc_t* disc)"
- If \f5makef\fP is not \f5NULL\fP,
- \f5dtinsert(dt,obj)\fP will call it
- to make a copy of \f5obj\fP suitable for insertion into \f5dt\fP.
- If \f5makef\fP is \f5NULL\fP, \f5obj\fP itself will be inserted into \f5dt\fP.
- .PP
- .Ss " void (*freef)(void* obj, Dtdisc_t* disc)"
- If not \f5NULL\fP,
- \f5freef\fP is used to destroy data associated with \f5obj\fP.
- .PP
- .Ss "int (*comparf)(Dt_t* dt, void* key1, void* key2, Dtdisc_t* disc)"
- If not \f5NULL\fP, \f5comparf\fP is used to compare two keys.
- Its return value should be \f5<0\fP, \f5=0\fP, or \f5>0\fP to indicate
- whether \f5key1\fP is smaller, equal to, or larger than \f5key2\fP.
- All three values are significant for method \f5Dtoset\fP and \f5Dtobag\fP.
- For other methods, a zero value
- indicates equality and a non-zero value indicates inequality.
- If \f5(*comparf)()\fP is \f5NULL\fP, an internal function is used
- to compare the keys as defined by the \f5Dtdisc_t.size\fP field.
- .PP
- .Ss "#define DTDISC(disc,key,size,link,makef,freef,comparf)"
- This macro function initializes the discipline pointed to by \f5disc\fP
- with the given values.
- .PP
- .Ss "OBJECT OPERATIONS"
- .PP
- .Ss " void* dtinsert(Dt_t* dt, void* obj)"
- This function adds an object prototyped by \f5obj\fP into \f5dt\fP.
- \f5dtinsert()\fP performs the same function
- for all methods.
- If there is an existing object in \f5dt\fP matching \f5obj\fP
- and the storage method is \f5Dtset\fP or \f5Dtoset\fP,
- \f5dtinsert()\fP will simply return the matching object.
- Otherwise, a new object is inserted according to the method in use.
- See \f5Dtdisc_t.makef\fP for object construction.
- The new object or a matching object as noted will be returned on success
- while \f5NULL\fP is returned on error.
- .PP
- .Ss " void* dtdelete(Dt_t* dt, void* obj)"
- If \f5obj\fP is \f5NULL\fP, method \f5Dtqueue\fP
- deletes queue head while other methods do nothing.
- If \f5obj\fP is not \f5NULL\fP, there are two cases.
- If the method in use is not \f5Dtobag\fP,
- the first object matching \f5obj\fP is deleted.
- On the other hand, if the method in use is or \f5Dtobag\fP,
- the library check to see if \f5obj\fP is in the dictionary and delete it.
- If \f5obj\fP is not in the dictionary, some object matching it will be deleted.
- See \f5Dtdisc_t.freef\fP for object destruction.
- \f5dtdelete()\fP returns the deleted object (even if it was deallocated)
- or \f5NULL\fP on error.
- .PP
- .Ss " void* dtdetach(Dt_t* dt, void* obj)"
- This function is similar to \f5dtdelete()\fP but the object to be deleted
- from \f5dt\fP will not be freed (via the discipline \f5freef\fP function).
- .PP
- .Ss " void* dtsearch(Dt_t* dt, void* obj)"
- .Ss " void* dtmatch(Dt_t* dt, void* key)"
- These functions find an object matching \f5obj\fP or \f5key\fP either from \f5dt\fP or
- from some dictionary accessible from \f5dt\fP via a viewpath (see \f5dtview()\fP.)
- \f5dtsearch()\fP and \f5dtmatch()\fP return the matching object or
- \f5NULL\fP on failure.
- .PP
- .Ss " void* dtfirst(Dt_t* dt)"
- .Ss " void* dtnext(Dt_t* dt, void* obj)"
- \f5dtfirst()\fP returns the first object in \f5dt\fP.
- \f5dtnext()\fP returns the object following \f5obj\fP.
- Objects are ordered based on the storage method in use.
- For \f5Dtoset\fP and \f5Dtobag\fP, objects are ordered by object comparisons.
- For \f5Dtqueue\fP, objects are ordered in order of insertion.
- For \f5Dtset\fP,
- objects are ordered by some internal order (more below).
- Thus, objects in a dictionary or a viewpath can be walked using
- a \f5for(;;)\fP loop as below.
- .Cs
- for(obj = dtfirst(dt); obj; obj = dtnext(dt,obj))
- .Ce
- When a dictionary uses \f5Dtset\fP,
- the object order is determined upon a call to \f5dtfirst()\fP/\f5dtlast()\fP.
- This order is frozen until a call \f5dtnext()\fP/\f5dtprev()\fP returns \f5NULL\fP
- or when these same functions are called with a \f5NULL\fP object argument.
- It is important that a \f5dtfirst()/dtlast()\fP call be
- balanced by a \f5dtnext()/dtprev()\fP call as described.
- Nested loops will require multiple balancing, once per loop.
- If loop balancing is not done carefully, either performance is degraded
- or unexpected behaviors may result.
- .Ss " void* dtlast(Dt_t* dt)"
- .Ss " void* dtprev(Dt_t* dt, void* obj)"
- \f5dtlast()\fP and \f5dtprev()\fP are like \f5dtfirst()\fP and \f5dtnext()\fP
- but work in reverse order.
- Note that dictionaries on a viewpath are still walked in order
- but objects in each dictionary are walked in reverse order.
- .PP
- .Ss " void* dtfinger(Dt_t* dt)"
- This function returns the \fIcurrent object\fP of \f5dt\fP, if any.
- The current object is defined after a successful call to one of
- \f5dtsearch()\fP, \f5dtmatch()\fP, \f5dtinsert()\fP,
- \f5dtfirst()\fP, \f5dtnext()\fP, \f5dtlast()\fP, or \f5dtprev()\fP.
- As a side effect of this implementation of \fICdt\fP,
- when a dictionary is based on \f5Dtoset\fP and \f5Dtobag\fP,
- the current object is always defined and is the root of the tree.
- .PP
- .Ss " void* dtrenew(Dt_t* dt, void* obj)"
- This function repositions and perhaps rehashes
- an object \f5obj\fP after its key has been changed.
- \f5dtrenew()\fP only works if \f5obj\fP is the current object (see \f5dtfinger()\fP).
- .PP
- .Ss " dtwalk(Dt_t* dt, int (*userf)(void*, void*), void* data)"
- This function calls \f5(*userf)(obj,data)\fP on each object in \f5dt\fP and
- other dictionaries viewable from it.
- If \f5userf()\fP returns a \f5<0\fP value,
- \f5dtwalk()\fP terminates and returns the same value.
- \f5dtwalk()\fP returns \f50\fP on completion.
- .PP
- .Ss " Dtlink_t* dtflatten(Dt_t* dt)"
- .Ss " Dtlink_t* dtlink(Dt_t* dt, Dtlink_t* link)"
- .Ss " void* dtobj(Dt_t* dt, Dtlink_t* link)"
- Using \f5dtfirst()/dtnext()\fP or \f5dtlast()/dtprev()\fP
- to walk a single dictionary can incur significant cost due to function calls.
- For efficient walking of a single directory (i.e., no viewpathing),
- \f5dtflatten()\fP and \f5dtlink()\fP can be used.
- Objects in \f5dt\fP are made into a linked list and walked as follows:
- .Cs
- for(link = dtflatten(dt); link; link = dtlink(dt,link) )
- .Ce
- .PP
- Note that \f5dtflatten()\fP returns a list of type \f5Dtlink_t*\fP,
- not \f5void*\fP. That is, it returns a dictionary holder pointer,
- not a user object pointer
- (although both are the same if the discipline field \f5link\fP is zero.)
- The macro function \f5dtlink()\fP
- returns the dictionary holder object following \f5link\fP.
- The macro function \f5dtobj(dt,link)\fP
- returns the user object associated with \f5link\fP,
- Beware that the flattened object list is unflattened on any
- dictionary operations other than \f5dtlink()\fP.
- .PP
- .Ss " Dtlink_t* dtextract(Dt_t* dt)"
- .Ss " int dtrestore(Dt_t* dt, Dtlink_t* link)"
- \f5dtextract()\fP extracts all objects from \f5dt\fP and makes it appear empty.
- \f5dtrestore()\fP repopulates \f5dt\fP with
- objects previously obtained via \f5dtextract()\fP.
- \f5dtrestore()\fP will fail if \f5dt\fP is not empty.
- These functions can be used
- to share a same \f5dt\fP handle among many sets of objects.
- They are useful to reduce dictionary overhead
- in an application that creates many concurrent dictionaries.
- It is important that the same discipline and method are in use at both
- extraction and restoration. Otherwise, undefined behaviors may result.
- .PP
- .Ss "DICTIONARY INFORMATION"
- .PP
- .Ss " int dtsize(Dt_t* dt)"
- This function returns the number of objects stored in \f5dt\fP.
- .PP
- .Ss " int dtstat(Dt_t *dt, Dtstat_t* st, int all)"
- This function reports dictionary statistics.
- If \f5all\fP is non-zero, all fields of \f5st\fP are filled.
- Otherwise, only the \f5dt_type\fP and \f5dt_size\fP fields are filled.
- It returns \f50\fP on success and \f5-1\fP on error.
- .PP
- \f5Dtstat_t\fP contains the below fields:
- .Tp
- \f5int dt_type\fP:
- This is one of \f5DT_SET\fP, \f5DT_OSET\fP, \f5DT_OBAG\fP,
- and \f5DT_QUEUE\fP.
- .Tp
- \f5int dt_size\fP:
- This contains the number of objects in the dictionary.
- .Tp
- \f5int dt_n\fP:
- For \f5Dtset\fP,
- this is the number of non-empty chains in the hash table.
- For \f5Dtoset\fP and \f5Dtobag\fP,
- this is the deepest level in the tree (counting from zero.)
- Each level in the tree contains all nodes of equal distance from the root node.
- \f5dt_n\fP and the below two fields are undefined for other methods.
- .Tp
- \f5int dt_max\fP:
- For \f5Dtset\fP, this is the size of a largest chain.
- For \f5Dtoset\fP and \f5Dtobag\fP, this is the size of a largest level.
- .Tp
- \f5int* dt_count\fP:
- For \f5Dtset\fP,
- this is the list of counts for chains of particular sizes.
- For example, \f5dt_count[1]\fP is the number of chains of size \f51\fP.
- For \f5Dtoset\fP and \f5Dtobag\fP, this is the list of sizes of the levels.
- For example, \f5dt_count[1]\fP is the size of level \f51\fP.
- .PP
- .Ss "HASH FUNCTIONS"
- .PP
- .Ss " unsigned int dtstrhash(void *str, int n)"
- This function computes hash values from bytes or strings.
- \f5dtstrhash()\fP computes a new hash value from string \f5str\fP.
- If \f5n\fP is positive, \f5str\fP is a byte array of length \f5n\fP;
- otherwise, \f5str\fP is a null-terminated string.
- .PP
- .SH IMPLEMENTATION NOTES
- \f5Dtset\fP are based on hash tables with
- move-to-front collision chains.
- \f5Dtoset\fP and \f5Dtobag\fP are based on top-down splay trees.
- \f5Dtqueue\fP is based on doubly linked list.
- .PP
- .SH AUTHOR
- Kiem-Phong Vo, [email protected]
|