wolffd@0: .TH LIBCDT 3 wolffd@0: .SH NAME wolffd@0: \fBCdt\fR \- container data types wolffd@0: .SH SYNOPSIS wolffd@0: .de Tp wolffd@0: .fl wolffd@0: .ne 2 wolffd@0: .TP wolffd@0: .. wolffd@0: .de Ss wolffd@0: .fl wolffd@0: .ne 2 wolffd@0: .SS "\\$1" wolffd@0: .. wolffd@0: .de Cs wolffd@0: .nf wolffd@0: .ft 5 wolffd@0: .. wolffd@0: .de Ce wolffd@0: .ft 1 wolffd@0: .fi wolffd@0: .. wolffd@0: .ta 1.0i 2.0i 3.0i 4.0i 5.0i wolffd@0: .Cs wolffd@0: #include wolffd@0: .Ce wolffd@0: .Ss "DICTIONARY TYPES" wolffd@0: .Cs wolffd@0: Void_t*; wolffd@0: Dt_t; wolffd@0: Dtdisc_t; wolffd@0: Dtmethod_t; wolffd@0: Dtlink_t; wolffd@0: Dtstat_t; wolffd@0: .Ce wolffd@0: .Ss "DICTIONARY CONTROL" wolffd@0: .Cs wolffd@0: Dt_t* dtopen(Dtdisc_t* disc, Dtmethod_t* meth); wolffd@0: int dtclose(Dt_t* dt); wolffd@0: void dtclear(dt); wolffd@0: Dtmethod_t* dtmethod(Dt_t* dt, Dtmethod_t* meth); wolffd@0: Dtdisc_t* dtdisc(Dt_t* dt, Dtdisc_t* disc, int type); wolffd@0: Dt_t* dtview(Dt_t* dt, Dt_t* view); wolffd@0: .Ce wolffd@0: .Ss "STORAGE METHODS" wolffd@0: .Cs wolffd@0: Dtmethod_t* Dtset; wolffd@0: Dtmethod_t* Dtbag; wolffd@0: Dtmethod_t* Dtoset; wolffd@0: Dtmethod_t* Dtobag; wolffd@0: Dtmethod_t* Dtlist; wolffd@0: Dtmethod_t* Dtstack; wolffd@0: Dtmethod_t* Dtqueue; wolffd@0: .Ce wolffd@0: .Ss "DISCIPLINE" wolffd@0: .Cs wolffd@0: typedef Void_t* (*Dtmake_f)(Dt_t*, Void_t*, Dtdisc_t*); wolffd@0: typedef void (*Dtfree_f)(Dt_t*, Void_t*, Dtdisc_t*); wolffd@0: typedef int (*Dtcompar_f)(Dt_t*, Void_t*, Void_t*, Dtdisc_t*); wolffd@0: typedef unsigned int (*Dthash_f)(Dt_t*, Void_t*, Dtdisc_t*); wolffd@0: typedef Void_t* (*Dtmemory_f)(Dt_t*, Void_t*, size_t, Dtdisc_t*); wolffd@0: typedef int (*Dtevent_f)(Dt_t*, int, Void_t*, Dtdisc_t*); wolffd@0: .Ce wolffd@0: .Ss "OBJECT OPERATIONS" wolffd@0: .Cs wolffd@0: Void_t* dtinsert(Dt_t* dt, Void_t* obj); wolffd@0: Void_t* dtdelete(Dt_t* dt, Void_t* obj); wolffd@0: Void_t* dtsearch(Dt_t* dt, Void_t* obj); wolffd@0: Void_t* dtmatch(Dt_t* dt, Void_t* key); wolffd@0: Void_t* dtfirst(Dt_t* dt); wolffd@0: Void_t* dtnext(Dt_t* dt, Void_t* obj); wolffd@0: Void_t* dtlast(Dt_t* dt); wolffd@0: Void_t* dtprev(Dt_t* dt, Void_t* obj); wolffd@0: Void_t* dtfinger(Dt_t* dt); wolffd@0: Void_t* dtrenew(Dt_t* dt, Void_t* obj); wolffd@0: int dtwalk(Dt_t* dt, int (*userf)(Dt_t*, Void_t*, Void_t*), Void_t*); wolffd@0: Dtlink_t* dtflatten(Dt_t* dt); wolffd@0: Dtlink_t* dtlink(Dt_t*, Dtlink_t* link); wolffd@0: Void_t* dtobj(Dt_t* dt, Dtlink_t* link); wolffd@0: Dtlink_t* dtextract(Dt_t* dt); wolffd@0: int dtrestore(Dt_t* dt, Dtlink_t* link); wolffd@0: .Ce wolffd@0: .Ss "DICTIONARY STATUS" wolffd@0: .Cs wolffd@0: Dt_t* dtvnext(Dt_t* dt); wolffd@0: int dtvcount(Dt_t* dt); wolffd@0: Dt_t* dtvhere(Dt_t* dt); wolffd@0: int dtsize(Dt_t* dt); wolffd@0: int dtstat(Dt_t* dt, Dtstat_t*, int all); wolffd@0: .Ce wolffd@0: .Ss "HASH FUNCTIONS" wolffd@0: .Cs wolffd@0: unsigned int dtstrhash(unsigned int h, char* str, int n); wolffd@0: unsigned int dtcharhash(unsigned int h, unsigned char c); wolffd@0: .Ce wolffd@0: .SH DESCRIPTION wolffd@0: .PP wolffd@0: \fICdt\fP manages run-time dictionaries using standard container data types: wolffd@0: unordered set/multiset, ordered set/multiset, list, stack, and queue. wolffd@0: .PP wolffd@0: .Ss "DICTIONARY TYPES" wolffd@0: .PP wolffd@0: .Ss " Void_t*" wolffd@0: This type is used to pass objects between \fICdt\fP and application code. wolffd@0: \f5Void_t\fP is defined as \f5void\fP for ANSI-C and C++ wolffd@0: and \f5char\fP for other compilation environments. wolffd@0: .PP wolffd@0: .Ss " Dt_t" wolffd@0: This is the type of a dictionary handle. wolffd@0: .PP wolffd@0: .Ss " Dtdisc_t" wolffd@0: This defines the type of a discipline structure which describes wolffd@0: object lay-out and manipulation functions. wolffd@0: .PP wolffd@0: .Ss " Dtmethod_t" wolffd@0: This defines the type of a container method. wolffd@0: .PP wolffd@0: .Ss " Dtlink_t" wolffd@0: This is the type of a dictionary object holder (see \f5dtdisc()\fP.) wolffd@0: .PP wolffd@0: .Ss " Dtstat_t" wolffd@0: This is the type of a structure to return dictionary statistics (see \f5dtstat()\fP.) wolffd@0: .PP wolffd@0: .Ss "DICTIONARY CONTROL" wolffd@0: .PP wolffd@0: .Ss " Dt_t* dtopen(Dtdisc_t* disc, Dtmethod_t* meth)" wolffd@0: This creates a new dictionary. wolffd@0: \f5disc\fP is a discipline structure to describe object format. wolffd@0: \f5meth\fP specifies a manipulation method. wolffd@0: \f5dtopen()\fP returns the new dictionary or \f5NULL\fP on error. wolffd@0: .PP wolffd@0: .Ss " int dtclose(Dt_t* dt)" wolffd@0: This deletes \f5dt\fP and its objects. wolffd@0: Note that \f5dtclose()\fP fails if \f5dt\fP is being viewed by wolffd@0: some other dictionaries (see \f5dtview()\fP). wolffd@0: \f5dtclose()\fP returns \f50\fP on success and \f5-1\fP on error. wolffd@0: .PP wolffd@0: .Ss " void dtclear(Dt_t* dt)" wolffd@0: This deletes all objects in \f5dt\fP without closing \f5dt\fP. wolffd@0: .PP wolffd@0: .Ss " Dtmethod_t dtmethod(Dt_t* dt, Dtmethod_t* meth)" wolffd@0: If \f5meth\fP is \f5NULL\fP, \f5dtmethod()\fP returns the current method. wolffd@0: Otherwise, it changes the storage method of \f5dt\fP to \f5meth\fP. wolffd@0: Object order remains the same during a wolffd@0: method switch among \f5Dtlist\fP, \f5Dtstack\fP and \f5Dtqueue\fP. wolffd@0: Switching to and from \f5Dtset/Dtbag\fP and \f5Dtoset/Dtobag\fP may cause wolffd@0: objects to be rehashed, reordered, or removed as the case requires. wolffd@0: \f5dtmethod()\fP returns the previous method or \f5NULL\fP on error. wolffd@0: .PP wolffd@0: .Ss " Dtdisc_t* dtdisc(Dt_t* dt, Dtdisc_t* disc, int type)" wolffd@0: If \f5disc\fP is \f5NULL\fP, \f5dtdisc()\fP returns the current discipline. wolffd@0: Otherwise, it changes the discipline of \f5dt\fP to \f5disc\fP. wolffd@0: Objects may be rehashed, reordered, or removed as appropriate. wolffd@0: \f5type\fP can be any bit combination of \f5DT_SAMECMP\fP and \f5DT_SAMEHASH\fP. wolffd@0: \f5DT_SAMECMP\fP means that objects will compare exactly the same as before wolffd@0: thus obviating the need for reordering or removing new duplicates. wolffd@0: \f5DT_SAMEHASH\fP means that hash values of objects remain the same wolffd@0: thus obviating the need to rehash. wolffd@0: \f5dtdisc()\fP returns the previous discipline on success wolffd@0: and \f5NULL\fP on error. wolffd@0: .PP wolffd@0: .Ss " Dt_t* dtview(Dt_t* dt, Dt_t* view)" wolffd@0: A viewpath allows a search or walk starting from a dictionary to continue to another. wolffd@0: \f5dtview()\fP first terminates any current view from \f5dt\fP to another dictionary. wolffd@0: Then, if \f5view\fP is \f5NULL\fP, \f5dtview\fP returns the terminated view dictionary. wolffd@0: If \f5view\fP is not \f5NULL\fP, a viewpath from \f5dt\fP to \f5view\fP is established. wolffd@0: \f5dtview()\fP returns \f5dt\fP on success and \f5NULL\fP on error. wolffd@0: .PP wolffd@0: If two dictionaries on the same viewpath have the same values for the discipline fields wolffd@0: \f5Dtdisc_t.link\fP, \f5Dtdisc_t.key\fP, \f5Dtdisc_t.size\fP, and \f5Dtdisc_t.hashf\fP, wolffd@0: it is expected that key hashing will be the same. wolffd@0: If not, undefined behaviors may result during a search or a walk. wolffd@0: .PP wolffd@0: .Ss "STORAGE METHODS" wolffd@0: .PP wolffd@0: Storage methods are of type \f5Dtmethod_t*\fP. wolffd@0: \fICdt\fP supports the following methods: wolffd@0: .PP wolffd@0: .Ss " Dtoset" wolffd@0: .Ss " Dtobag" wolffd@0: Objects are ordered by comparisons. wolffd@0: \f5Dtoset\fP keeps unique objects. wolffd@0: \f5Dtobag\fP allows repeatable objects. wolffd@0: .PP wolffd@0: .Ss " Dtset" wolffd@0: .Ss " Dtbag" wolffd@0: Objects are unordered. wolffd@0: \f5Dtset\fP keeps unique objects. wolffd@0: \f5Dtbag\fP allows repeatable objects and always keeps them together wolffd@0: (note the effect on dictionary walking.) wolffd@0: .PP wolffd@0: .Ss " Dtlist" wolffd@0: Objects are kept in a list. wolffd@0: New objects are inserted either wolffd@0: in front of \fIcurrent object\fP (see \f5dtfinger()\fP) if this is defined wolffd@0: or at list front if there is no current object. wolffd@0: .PP wolffd@0: .Ss " Dtstack" wolffd@0: Objects are kept in a stack, i.e., in reverse order of insertion. wolffd@0: Thus, the last object inserted is at stack top wolffd@0: and will be the first to be deleted. wolffd@0: .PP wolffd@0: .Ss " Dtqueue" wolffd@0: Objects are kept in a queue, i.e., in order of insertion. wolffd@0: Thus, the first object inserted is at queue head wolffd@0: and will be the first to be deleted. wolffd@0: .PP wolffd@0: .Ss "DISCIPLINE" wolffd@0: .PP wolffd@0: Object format and associated management functions are wolffd@0: defined in the type \f5Dtdisc_t\fP: wolffd@0: .Cs wolffd@0: typedef struct wolffd@0: { int key, size; wolffd@0: int link; wolffd@0: Dtmake_f makef; wolffd@0: Dtfree_f freef; wolffd@0: Dtcompar_f comparf; wolffd@0: Dthash_f hashf; wolffd@0: Dtmemory_f memoryf; wolffd@0: Dtevent_f eventf; wolffd@0: } Dtdisc_t; wolffd@0: .Ce wolffd@0: .Ss " int key, size" wolffd@0: Each object \f5obj\fP is identified by a key used for object comparison or hashing. wolffd@0: \f5key\fP should be non-negative and defines an offset into \f5obj\fP. wolffd@0: If \f5size\fP is negative, the key is a null-terminated wolffd@0: string with starting address \f5*(Void_t**)((char*)obj+key)\fP. wolffd@0: If \f5size\fP is zero, the key is a null-terminated string with starting address wolffd@0: \f5(Void_t*)((char*)obj+key)\fP. wolffd@0: Finally, if \f5size\fP is positive, the key is a byte array of length \f5size\fP wolffd@0: starting at \f5(Void_t*)((char*)obj+key)\fP. wolffd@0: .PP wolffd@0: .Ss " int link" wolffd@0: Let \f5obj\fP be an object to be inserted into \f5dt\fP as discussed below. wolffd@0: If \f5link\fP is negative, an internally allocated object holder is used wolffd@0: to hold \f5obj\fP. Otherwise, \f5obj\fP should have wolffd@0: a \f5Dtlink_t\fP structure embedded \f5link\fP bytes into it, wolffd@0: i.e., at address \f5(Dtlink_t*)((char*)obj+link)\fP. wolffd@0: .PP wolffd@0: .Ss " Void_t* (*makef)(Dt_t* dt, Void_t* obj, Dtdisc_t* disc)" wolffd@0: If \f5makef\fP is not \f5NULL\fP, wolffd@0: \f5dtinsert(dt,obj)\fP will call it wolffd@0: to make a copy of \f5obj\fP suitable for insertion into \f5dt\fP. wolffd@0: If \f5makef\fP is \f5NULL\fP, \f5obj\fP itself will be inserted into \f5dt\fP. wolffd@0: .PP wolffd@0: .Ss " void (*freef)(Dt_t* dt, Void_t* obj, Dtdisc_t* disc)" wolffd@0: If not \f5NULL\fP, wolffd@0: \f5freef\fP is used to destroy data associated with \f5obj\fP. wolffd@0: .PP wolffd@0: .Ss "int (*comparf)(Dt_t* dt, Void_t* key1, Void_t* key2, Dtdisc_t* disc)" wolffd@0: If not \f5NULL\fP, \f5comparf\fP is used to compare two keys. wolffd@0: Its return value should be \f5<0\fP, \f5=0\fP, or \f5>0\fP to indicate wolffd@0: whether \f5key1\fP is smaller, equal to, or larger than \f5key2\fP. wolffd@0: All three values are significant for method \f5Dtoset\fP and \f5Dtobag\fP. wolffd@0: For other methods, a zero value wolffd@0: indicates equality and a non-zero value indicates inequality. wolffd@0: If \f5(*comparf)()\fP is \f5NULL\fP, an internal function is used wolffd@0: to compare the keys as defined by the \f5Dtdisc_t.size\fP field. wolffd@0: .PP wolffd@0: .Ss " unsigned int (*hashf)(Dt_t* dt, Void_t* key, Dtdisc_t* disc)" wolffd@0: If not \f5NULL\fP, wolffd@0: \f5hashf\fP is used to compute the hash value of \f5key\fP. wolffd@0: It is required that keys compared equal will also have same hash values. wolffd@0: If \f5hashf\fP is \f5NULL\fP, an internal function is used to hash wolffd@0: the key as defined by the \f5Dtdisc_t.size\fP field. wolffd@0: .PP wolffd@0: .Ss " Void_t* (*memoryf)(Dt_t* dt, Void_t* addr, size_t size, Dtdisc_t* disc)" wolffd@0: If not \f5NULL\fP, \f5memoryf\fP is used to allocate and free memory. wolffd@0: When \f5addr\fP is \f5NULL\fP, a memory segment of size \f5size\fP is requested. wolffd@0: If \f5addr\fP is not \f5NULL\fP and \f5size\fP is zero, \f5addr\fP is to be freed. wolffd@0: If \f5addr\fP is not \f5NULL\fP and \f5size\fP is positive, wolffd@0: \f5addr\fP is to be resized to the given size. wolffd@0: If \f5memoryf\fP is \f5NULL\fP, \fImalloc(3)\fP is used. wolffd@0: When dictionaries share memory, wolffd@0: a record of the first allocated memory segment should be kept wolffd@0: so that it can be used to initialize new dictionaries (see below.) wolffd@0: .PP wolffd@0: .Ss " int (*eventf)(Dt_t* dt, int type, Void_t* data, Dtdisc_t* disc)" wolffd@0: If not \f5NULL\fP, \f5eventf\fP announces various events. wolffd@0: If it returns a negative value, the calling operation will terminate with failure. wolffd@0: Unless noted otherwise, a non-negative return value let the wolffd@0: calling function proceed normally. Following are the events: wolffd@0: .Tp wolffd@0: \f5DT_OPEN\fP: wolffd@0: \f5dt\fP is being opened. wolffd@0: If \f5eventf\fP returns zero, the opening process proceeds normally. wolffd@0: A positive return value indicates that \f5dt\fP wolffd@0: uses memory already initialized by a different dictionary. wolffd@0: In that case, \f5*(Void_t**)data\fP should be set to wolffd@0: the first allocated memory segment as discussed in \f5memoryf\fP. wolffd@0: \f5dtopen()\fP may fail if this segment is not returned or wolffd@0: if it has not been properly initialized. wolffd@0: .Tp wolffd@0: \f5DT_CLOSE\fP: wolffd@0: \f5dt\fP is being closed. wolffd@0: .Tp wolffd@0: \f5DT_DISC\fP: wolffd@0: The discipline of \f5dt\fP is being changed to a new one given in wolffd@0: \f5(Dtdisc_t*)data\fP. wolffd@0: .Tp wolffd@0: \f5DT_METH\fP: wolffd@0: The method of \f5dt\fP is being changed to a new one given in wolffd@0: \f5(Dtmethod_t*)data\fP. wolffd@0: .PP wolffd@0: .Ss "OBJECT OPERATIONS" wolffd@0: .PP wolffd@0: .Ss " Void_t* dtinsert(Dt_t* dt, Void_t* obj)" wolffd@0: This inserts an object prototyped by \f5obj\fP into \f5dt\fP. wolffd@0: If there is an existing object in \f5dt\fP matching \f5obj\fP wolffd@0: and the storage method is \f5Dtset\fP or \f5Dtoset\fP, wolffd@0: \f5dtinsert()\fP will simply return the matching object. wolffd@0: Otherwise, a new object is inserted according to the method in use. wolffd@0: See \f5Dtdisc_t.makef\fP for object construction. wolffd@0: \f5dtinsert()\fP returns the new object, a matching object as noted, wolffd@0: or \f5NULL\fP on error. wolffd@0: .PP wolffd@0: .Ss " Void_t* dtdelete(Dt_t* dt, Void_t* obj)" wolffd@0: If \f5obj\fP is not \f5NULL\fP, the first object matching it is deleted. wolffd@0: If \f5obj\fP is \f5NULL\fP, methods \f5Dtstack\fP and \f5Dtqueue\fP wolffd@0: delete respectively stack top or queue head while other methods do nothing. wolffd@0: See \f5Dtdisc_t.freef\fP for object destruction. wolffd@0: \f5dtdelete()\fP returns the deleted object (even if it was deallocated) wolffd@0: or \f5NULL\fP on error. wolffd@0: .PP wolffd@0: .Ss " Void_t* dtsearch(Dt_t* dt, Void_t* obj)" wolffd@0: .Ss " Void_t* dtmatch(Dt_t* dt, Void_t* key)" wolffd@0: These functions find an object matching \f5obj\fP or \f5key\fP either from \f5dt\fP or wolffd@0: from some dictionary accessible from \f5dt\fP via a viewpath (see \f5dtview()\fP.) wolffd@0: \f5dtsearch()\fP and \f5dtmatch()\fP return the matching object or wolffd@0: \f5NULL\fP on failure. wolffd@0: .PP wolffd@0: .Ss " Void_t* dtfirst(Dt_t* dt)" wolffd@0: .Ss " Void_t* dtnext(Dt_t* dt, Void_t* obj)" wolffd@0: \f5dtfirst()\fP returns the first object in \f5dt\fP. wolffd@0: \f5dtnext()\fP returns the object following \f5obj\fP. wolffd@0: Objects are ordered based on the storage method in use. wolffd@0: For \f5Dtoset\fP and \f5Dtobag\fP, objects are ordered by object comparisons. wolffd@0: For \f5Dtstack\fP, objects are ordered in reverse order of insertion. wolffd@0: For \f5Dtqueue\fP, objects are ordered in order of insertion. wolffd@0: For \f5Dtlist\fP, objects are ordered by list position. wolffd@0: For \f5Dtset\fP and \f5Dtbag\fP, wolffd@0: objects use some internal ordering which wolffd@0: may change on any search, insert, or delete operations. wolffd@0: Therefore, these operations should not be used wolffd@0: during a walk on a dictionary using either \f5Dtset\fP or \f5Dtbag\fP. wolffd@0: .PP wolffd@0: Objects in a dictionary or a viewpath can be walked using wolffd@0: a \f5for(;;)\fP loop as below. wolffd@0: Note that only one loop can be used at a time per dictionary. wolffd@0: Concurrent or nested loops may result in unexpected behaviors. wolffd@0: .Cs wolffd@0: for(obj = dtfirst(dt); obj; obj = dtnext(dt,obj)) wolffd@0: .Ce wolffd@0: .Ss " Void_t* dtlast(Dt_t* dt)" wolffd@0: .Ss " Void_t* dtprev(Dt_t* dt, Void_t* obj)" wolffd@0: \f5dtlast()\fP and \f5dtprev()\fP are like \f5dtfirst()\fP and \f5dtnext()\fP wolffd@0: but work in reverse order. wolffd@0: Note that dictionaries on a viewpath are still walked in order wolffd@0: but objects in each dictionary are walked in reverse order. wolffd@0: .PP wolffd@0: .Ss " Void_t* dtfinger(Dt_t* dt)" wolffd@0: This function returns the \fIcurrent object\fP of \f5dt\fP, if any. wolffd@0: The current object is defined after a successful call to one of wolffd@0: \f5dtsearch()\fP, \f5dtmatch()\fP, \f5dtinsert()\fP, wolffd@0: \f5dtfirst()\fP, \f5dtnext()\fP, \f5dtlast()\fP, or \f5dtprev()\fP. wolffd@0: As a side effect of this implementation of \fICdt\fP, wolffd@0: when a dictionary is based on \f5Dtoset\fP and \f5Dtobag\fP, wolffd@0: the current object is always defined and is the root of the tree. wolffd@0: .PP wolffd@0: .Ss " Void_t* dtrenew(Dt_t* dt, Void_t* obj)" wolffd@0: This function repositions and perhaps rehashes wolffd@0: an object \f5obj\fP after its key has been changed. wolffd@0: \f5dtrenew()\fP only works if \f5obj\fP is the current object (see \f5dtfinger()\fP). wolffd@0: .PP wolffd@0: .Ss " dtwalk(Dt_t* dt, int (*userf)(Dt_t*, Void_t*, Void_t*), Void_t* data)" wolffd@0: This function calls \f5(*userf)(walk,obj,data)\fP on each object in \f5dt\fP and wolffd@0: other dictionaries viewable from it. wolffd@0: \f5walk\fP is the dictionary containing \f5obj\fP. wolffd@0: If \f5userf()\fP returns a \f5<0\fP value, wolffd@0: \f5dtwalk()\fP terminates and returns the same value. wolffd@0: \f5dtwalk()\fP returns \f50\fP on completion. wolffd@0: .PP wolffd@0: .Ss " Dtlink_t* dtflatten(Dt_t* dt)" wolffd@0: .Ss " Dtlink_t* dtlink(Dt_t* dt, Dtlink_t* link)" wolffd@0: .Ss " Void_t* dtobj(Dt_t* dt, Dtlink_t* link)" wolffd@0: Using \f5dtfirst()/dtnext()\fP or \f5dtlast()/dtprev()\fP wolffd@0: to walk a single dictionary can incur significant cost due to function calls. wolffd@0: For efficient walking of a single directory (i.e., no viewpathing), wolffd@0: \f5dtflatten()\fP and \f5dtlink()\fP can be used. wolffd@0: Objects in \f5dt\fP are made into a linked list and walked as follows: wolffd@0: .Cs wolffd@0: for(link = dtflatten(dt); link; link = dtlink(dt,link) ) wolffd@0: .Ce wolffd@0: .PP wolffd@0: Note that \f5dtflatten()\fP returns a list of type \f5Dtlink_t*\fP, wolffd@0: not \f5Void_t*\fP. That is, it returns a dictionary holder pointer, wolffd@0: not a user object pointer wolffd@0: (although both are the same if the discipline field \f5link\fP is non-negative.) wolffd@0: The macro function \f5dtlink()\fP wolffd@0: returns the dictionary holder object following \f5link\fP. wolffd@0: The macro function \f5dtobj(dt,link)\fP wolffd@0: returns the user object associated with \f5link\fP, wolffd@0: Beware that the flattened object list is unflattened on any wolffd@0: dictionary operations other than \f5dtlink()\fP. wolffd@0: .PP wolffd@0: .Ss " Dtlink_t* dtextract(Dt_t* dt)" wolffd@0: .Ss " int dtrestore(Dt_t* dt, Dtlink_t* link)" wolffd@0: \f5dtextract()\fP extracts all objects from \f5dt\fP and makes it appear empty. wolffd@0: \f5dtrestore()\fP repopulates \f5dt\fP with wolffd@0: objects previously obtained via \f5dtextract()\fP. wolffd@0: \f5dtrestore()\fP will fail if \f5dt\fP is not empty. wolffd@0: These functions can be used wolffd@0: to share a same \f5dt\fP handle among many sets of objects. wolffd@0: They are useful to reduce dictionary overhead wolffd@0: in an application that creates concurrently many dictionaries. wolffd@0: It is important that the same discipline and method are in use at both wolffd@0: extraction and restoration. Otherwise, undefined behaviors may result. wolffd@0: .PP wolffd@0: .Ss "DICTIONARY INFORMATION" wolffd@0: .PP wolffd@0: .Ss " Dt_t* dtvnext(Dt_t* dt)" wolffd@0: This returns the dictionary that \f5dt\fP is viewing, if any. wolffd@0: .Ss " int dtvcount(Dt_t* dt)" wolffd@0: This returns the number of dictionaries that view \f5dt\fP. wolffd@0: .Ss " Dt_t* dtvhere(Dt_t* dt)" wolffd@0: This returns the dictionary \f5v\fP viewable from \f5dt\fP wolffd@0: where an object was found from the most recent search or walk operation. wolffd@0: .Ss " int dtsize(Dt_t* dt)" wolffd@0: This function returns the number of objects stored in \f5dt\fP. wolffd@0: .PP wolffd@0: .Ss " int dtstat(Dt_t *dt, Dtstat_t* st, int all)" wolffd@0: This function reports dictionary statistics. wolffd@0: If \f5all\fP is non-zero, all fields of \f5st\fP are filled. wolffd@0: Otherwise, only the \f5dt_type\fP and \f5dt_size\fP fields are filled. wolffd@0: It returns \f50\fP on success and \f5-1\fP on error. wolffd@0: .PP wolffd@0: \f5Dtstat_t\fP contains the below fields: wolffd@0: .Tp wolffd@0: \f5int dt_type\fP: wolffd@0: This is one of \f5DT_SET\fP, \f5DT_BAG\fP, \f5DT_OSET\fP, \f5DT_OBAG\fP, wolffd@0: \f5DT_LIST\fP, \f5DT_STACK\fP, and \f5DT_QUEUE\fP. wolffd@0: .Tp wolffd@0: \f5int dt_size\fP: wolffd@0: This contains the number of objects in the dictionary. wolffd@0: .Tp wolffd@0: \f5int dt_n\fP: wolffd@0: For \f5Dtset\fP and \f5Dtbag\fP, wolffd@0: this is the number of non-empty chains in the hash table. wolffd@0: For \f5Dtoset\fP and \f5Dtobag\fP, wolffd@0: this is the deepest level in the tree (counting from zero.) wolffd@0: Each level in the tree contains all nodes of equal distance from the root node. wolffd@0: \f5dt_n\fP and the below two fields are undefined for other methods. wolffd@0: .Tp wolffd@0: \f5int dt_max\fP: wolffd@0: For \f5Dtbag\fP and \f5Dtset\fP, this is the size of a largest chain. wolffd@0: For \f5Dtoset\fP and \f5Dtobag\fP, this is the size of a largest level. wolffd@0: .Tp wolffd@0: \f5int* dt_count\fP: wolffd@0: For \f5Dtset\fP and \f5Dtbag\fP, wolffd@0: this is the list of counts for chains of particular sizes. wolffd@0: For example, \f5dt_count[1]\fP is the number of chains of size \f51\fP. wolffd@0: For \f5Dtoset\fP and \f5Dtobag\fP, this is the list of sizes of the levels. wolffd@0: For example, \f5dt_count[1]\fP is the size of level \f51\fP. wolffd@0: .PP wolffd@0: .Ss "HASH FUNCTIONS" wolffd@0: .PP wolffd@0: .Ss " unsigned int dtcharhash(unsigned int h, char c)" wolffd@0: .Ss " unsigned int dtstrhash(unsigned int h, char* str, int n)" wolffd@0: These functions compute hash values from bytes or strings. wolffd@0: \f5dtcharhash()\fP computes a new hash value from byte \f5c\fP and seed value \f5h\fP. wolffd@0: \f5dtstrhash()\fP computes a new hash value from string \f5str\fP and seed value \f5h\fP. wolffd@0: If \f5n\fP is positive, \f5str\fP is a byte array of length \f5n\fP; wolffd@0: otherwise, \f5str\fP is a null-terminated string. wolffd@0: .PP wolffd@0: .SH IMPLEMENTATION NOTES wolffd@0: \f5Dtset\fP and \f5Dtbag\fP are based on hash tables with wolffd@0: move-to-front collision chains. wolffd@0: \f5Dtoset\fP and \f5Dtobag\fP are based on top-down splay trees. wolffd@0: \f5Dtlist\fP, \f5Dtstack\fP and \f5Dtqueue\fP are based on doubly linked list. wolffd@0: .PP wolffd@0: .SH AUTHOR wolffd@0: Kiem-Phong Vo, kpv@research.att.com