/* Copyright (c) 2000-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file dksto.c Storage module. */ #include "dk.h" #include "dktypes.h" #include "dkmem.h" #include "dkstr.h" /** Inside the dksto module. */ #define DK_STO_C 1 #include "dksto.h" #if DK_HAVE_STDLIB_H #include #endif $(trace-include) /** Allocate memory. */ #if DK_MEM_NOTRACK #define STO_ALLOC(t,s) (t *)dkmem_alloc(sizeof(t),(size_t)(s)) #else #define STO_ALLOC(t,s) (t *)dkmem_alloc_tracked(sizeof(t),(size_t)(s)) #endif /** Release memory. */ #define STO_FREE(p) { if(p) { dkmem_free((void *)(p)); } } /** Comparison criteria: Do not compare the objects (unsorted storage). */ #define COMPARE_NONE 0 /** Comparison criteria: Use comparison function. */ #define COMPARE_FCT 1 /** Comparison criteria: Evaluate objects to char values. */ #define COMPARE_CHAR 2 /** Comparison criteria: Evaluate objects to unsigned char values. */ #define COMPARE_UCHAR 3 /** Comparison criteria: Evaluate objects to short values. */ #define COMPARE_SHORT 4 /** Comparison criteria: Evaluate objects to unsigned short values. */ #define COMPARE_USHORT 5 /** Comparison criteria: Evaluate objects to int values. */ #define COMPARE_INT 6 /** Comparison criteria: Evaluate objects to unsigned int values. */ #define COMPARE_UINT 7 /** Comparison criteria: Evaluate objects to long values. */ #define COMPARE_LONG 8 /** Comparison criteria: Evaluate objects to unsigned long values. */ #define COMPARE_ULONG 9 /** Comparison criteria: Evaluate objects to float values. */ #define COMPARE_FLOAT 10 /** Comparison criteria: Evaluate objects to double values. */ #define COMPARE_DOUBLE 11 /** Flag: AVL trees can be used. */ static int use_trees = 1; /** Flag: AVLTREE variable is checked. */ static int is_configured = 0; /** Check whether the use of AVL trees for sorted storages is allowed. */ static void configure_use_trees DK_P0() { if(!is_configured) { char *ptr; ptr = getenv("AVLTREE"); if(ptr) { if(dkstr_is_on(ptr)) { use_trees = 1; } else { use_trees = 0; } } is_configured = 1; } } /* GENERAL STATIC FUNCTIONS */ /** Initialize storage node for object. @param n Storage node. @param o Object. @param s Storage. @param crit Comparison/evaluation criteria used by storage. */ static void node_init_for_object DK_P4(dk_storage_node_t *, n, void *, o, dk_storage_t *,s, int, crit) { $? "+ node_init_for_object %s %s %s %d", TR_PTR(n), TR_PTR(o), TR_PTR(s), crit n->p = n->l = n->r = NULL; n->b = n->w = 0; n->o = o; switch(s->h) { case COMPARE_CHAR: (n->v).c = (*((s->e).c))(o,crit); break; case COMPARE_UCHAR: (n->v).uc = (*((s->e).uc))(o,crit); break; case COMPARE_SHORT: (n->v).s = (*((s->e).s))(o,crit); break; case COMPARE_USHORT: (n->v).us = (*((s->e).us))(o,crit); break; case COMPARE_INT: (n->v).i = (*((s->e).i))(o,crit); break; case COMPARE_UINT: (n->v).ui = (*((s->e).ui))(o,crit); break; case COMPARE_LONG: (n->v).l = (*((s->e).l))(o,crit); break; case COMPARE_ULONG: (n->v).ul = (*((s->e).ul))(o,crit); break; case COMPARE_FLOAT: (n->v).f = (*((s->e).f))(o,crit); break; case COMPARE_DOUBLE: (n->v).d = (*((s->e).d))(o,crit); break; } $? "- node_init_for_object" } /** Copy data from one storage node to another. @param d Destination node. @param s Source node. @param st Storage. */ static void node_data_copy DK_P3(dk_storage_node_t *, d, dk_storage_node_t *, s, dk_storage_t *, st) { $? "+ node_data_copy %s %s", TR_PTR(d), TR_PTR(s) d->o = s->o; switch(st->h) { case COMPARE_CHAR: (d->v).c = (s->v).c ; break; case COMPARE_UCHAR: (d->v).uc = (s->v).uc ; break; case COMPARE_SHORT: (d->v).s = (s->v).s ; break; case COMPARE_USHORT: (d->v).us = (s->v).us ; break; case COMPARE_INT: (d->v).i = (s->v).i ; break; case COMPARE_UINT: (d->v).ui = (s->v).ui ; break; case COMPARE_LONG: (d->v).l = (s->v).l ; break; case COMPARE_ULONG: (d->v).ul = (s->v).ul ; break; case COMPARE_FLOAT: (d->v).f = (s->v).f ; break; case COMPARE_DOUBLE: (d->v).d = (s->v).d ; break; } $? "- node_data_copy" } /** Compare two storage nodes. @param l Left node. @param r Right node. @param s Storage. @param c Comparison criteria. @return Comparison result. */ static int node_compare DK_P4(dk_storage_node_t *, l, dk_storage_node_t *, r, dk_storage_t *, s, int, c) { int back = 0; $? "+ node_compare %s %s %s %d", TR_PTR(l), TR_PTR(r), TR_PTR(s), c switch(s->h) { case COMPARE_FCT: { $? ". compare by function" back = (*((s->e).comp))((void *)(l->o),(void *)(r->o),c); if(back < 0) back = -1; if(back > 0) back = 1; } break; case COMPARE_CHAR: { $? ". compare character" if(((l->v).c) > ((r->v).c)) { back = 1; } else { if(((l->v).c) < ((r->v).c)) { back = -1; } } } break; case COMPARE_UCHAR: { $? ". compare unsigned character" if(((l->v).uc) > ((r->v).uc)) { back = 1; } else { if(((l->v).uc) < ((r->v).uc)) { back = -1; } } } break; case COMPARE_SHORT: { $? ". compare short" if(((l->v).s) > ((r->v).s)) { back = 1; } else { if(((l->v).s) < ((r->v).s)) { back = -1; } } } break; case COMPARE_USHORT: { $? ". compare unsigned short" if(((l->v).us) > ((r->v).us)) { back = 1; } else { if(((l->v).us) < ((r->v).us)) { back = -1; } } } break; case COMPARE_INT: { $? ". compare int" if(((l->v).i) > ((r->v).i)) { back = 1; } else { if(((l->v).i) < ((r->v).i)) { back = -1; } } } break; case COMPARE_UINT: { $? ". compare unsigned int" if(((l->v).ui) > ((r->v).ui)) { back = 1; } else { if(((l->v).ui) < ((r->v).ui)) { back = -1; } } } break; case COMPARE_LONG: { $? ". compare long" if(((l->v).l) > ((r->v).l)) { back = 1; } else { if(((l->v).l) < ((r->v).l)) { back = -1; } } } break; case COMPARE_ULONG: { $? ". compare unsigned long" if(((l->v).ul) > ((r->v).ul)) { back = 1; } else { if(((l->v).ul) < ((r->v).ul)) { back = -1; } } } break; case COMPARE_FLOAT: { $? ". compare float" if(((l->v).f) > ((r->v).f)) { back = 1; } else { if(((l->v).f) < ((r->v).f)) { back = -1; } } } break; case COMPARE_DOUBLE: { $? ". compare double" if(((l->v).d) > ((r->v).d)) { back = 1; } else { if(((l->v).d) < ((r->v).d)) { back = -1; } } } break; } $? "- node_compare %d", back return back; } /* UNSORTED DATA HANDLING */ /** Remove node from an unsorted storage. @param ro Root node. @param n Node to remove. @return New root node. */ static dk_storage_node_t * unsorted_remove DK_P2(dk_storage_node_t *, ro, dk_storage_node_t *, n) { dk_storage_node_t *back; dk_storage_node_t *l, *r; $? "+ unsorted_remove %s %s", TR_PTR(ro), TR_PTR(n) back = ro; l = n->l; r = n->r; if(r) { r->l = l; } if(l) { l->r = r; } else { back = r; } $? "- unsorted_remove %s", TR_PTR(back) return back; } /** Add node to an unsorted storage. @param r Old root node. @param n Node to add. @return New root node or NULL. */ static dk_storage_node_t * unsorted_add DK_P2(dk_storage_node_t *, r, dk_storage_node_t *, n) { dk_storage_node_t *back; $? "+ unsorted_add %s %s", TR_PTR(r), TR_PTR(n) back = n; n->r = r; if(r) { r->l = n; } $? "- unsorted_add %s", TR_PTR(back) return back; } /** Release all nodes in an unsorted storage. @param r Root node. */ static void unsorted_release_all_nodes DK_P1(dk_storage_node_t *, r) { dk_storage_node_t *c, *n; $? "+ unsorted_release_all_nodes %s", TR_PTR(r) c = r; while(c) { n = c->r; c->p = c->l = c->r = NULL; c->o = NULL; c->b = c->w = 0; STO_FREE(c) ; c = n; } $? "- unsorted_release_all_nodes" } /** Find next node in an unsorted storage. @param n Current node. @param r Root node. @return Pointer to next node or NULL. */ static dk_storage_node_t * unsorted_find_next_node DK_P2(dk_storage_node_t *, n, dk_storage_node_t *, r) { dk_storage_node_t *back = NULL; $? "+ unsorted_find_next_node %s %s", TR_PTR(n), TR_PTR(r) if(n) { back = n->r; } else { back = r; } $? "- unsorted_find_next_node %s", TR_PTR(back) return back; } /** Find last node in an unsorted storage. @param n Root node. @return Last node or NULL. */ static dk_storage_node_t * unsorted_find_last_node DK_P1(dk_storage_node_t *, n) { dk_storage_node_t *back = NULL; $? "+ unsorted_find_last_node %s", TR_PTR(n) if(n) back = n->l; $? "- unsorted_find_last_node %s", TR_PTR(back) return back; } /** Find node for an object in an unsorted storage. @param r Root node. @param o Object. @return Node for object or NULL. */ static dk_storage_node_t * unsorted_find_exact DK_P2(dk_storage_node_t *, r, void *, o) { dk_storage_node_t *back = NULL; dk_storage_node_t *c; $? "+ unsorted_find_exact %s %s", TR_PTR(r), TR_PTR(o) c = r; while(c && (!back)) { if((c->o) == o) { back = c; } c = c->r; } $? "- unsorted_find_exact %s", TR_PTR(back) return back; } /* SORTED DATA HANDLING */ /** Go to direction: Left. */ #define WALK_LEFT 1 /** Go to direction: Right. */ #define WALK_RIGHT 2 /* USE AVL-TREE */ /** Perform left rotation at node. @param p Subpath to modify. @return New subpath root node or NULL. */ static dk_storage_node_t * left_rotation DK_P1(dk_storage_node_t *, p) { dk_storage_node_t *p1; $? "+ left_rotation %s", TR_PTR(p) p1 = p->r; p->r = p1->l; if(p->r) (p->r)->p = p; p1->l = p; if(p) p->p = p1; $? "- left_rotation %s", TR_PTR(p1) return p1; } /** Perform right rotation at node. @param p Subpath to modify. @return New subpath root node or NULL. */ static dk_storage_node_t * right_rotation DK_P1(dk_storage_node_t *, p) { dk_storage_node_t *p1; $? "+ right_rotation %s", TR_PTR(p) p1 = p->l; p->l = p1->r; if(p->l) (p->l)->p = p; p1->r = p; if(p) p->p = p1; $? "- right_rotation %s", TR_PTR(p1) return p1; } /** Increment balance field of a storage node. @param p Node to modify. */ static void inc_balance DK_P1(dk_storage_node_t *, p) { short x; $? "+ inc_balance %s", TR_PTR(p) x = p->b; $? ". old balance %d", x x++; if(x > 3) x = 0; p->b = x; $? "- inc_balance %d", p->b } /** Decrement balance field of a storage node. @param p Storage node. */ static void dec_balance DK_P1(dk_storage_node_t *, p) { short x; $? "+ dec_balance %s", TR_PTR(p) x = p->b; $? ". old balance %d", x x--; if(x < 0) x = 3; p->b = x; $? "- dec_balance %d", p->b } /** Set mark for "left node deleted". @param p Node. @param h Pointer to balance variable. @return New root node for path behind \a p. */ static dk_storage_node_t * left_deleted DK_P2(dk_storage_node_t *, p, short *, h) { $? "+ left_deleted %s %s", TR_PTR(p), TR_PTR(h) switch(p->b) { case 0: *h = - *h; case 3: $? ". going to increment balance field of %s", TR_PTR(p) inc_balance(p); $? ". balance field incremented" break; case 1: { switch((p->r)->b) { case 0: (p->r)->b = 3; *h = - *h; p = left_rotation(p); break; case 1: (p->r)->b = 0; p->b = 0; p = left_rotation(p); break; case 3: p->b = (((((p->r)->l)->b) == 1) ? 3 : 0); (p->r)->b = (((((p->r)->l)->b) == 3) ? 1 : 0); p->r = right_rotation(p->r); if(p->r) (p->r)->p = p; p = left_rotation(p); p->b = 0; } } } $? "- left_deleted %s", TR_PTR(p) return p; } /** Set mark for "right node deleted". @param p Node. @param h Pointer to balance variable. @return New root node for path behind \a p. */ static dk_storage_node_t * right_deleted DK_P2(dk_storage_node_t *, p, short *, h) { $? "+ right_deleted %s %s", TR_PTR(p), TR_PTR(h) switch(p->b) { case 0: *h = - *h; case 1: dec_balance(p); break; case 3: { switch((p->l)->b) { case 0: (p->l)->b = 1; *h = - *h; p = right_rotation(p); break; case 3: (p->l)->b = 0; p->b = 0; p = right_rotation(p); break; case 1: p->b = (((((p->l)->r)->b) == 3) ? 1 : 0); (p->l)->b = (((((p->l)->r)->b) == 1) ? 3 : 0); p->l = left_rotation(p->l); if(p->l) (p->l)->p = p; p = right_rotation(p); p->b = 0; } } } $? "- right_deleted %s", TR_PTR(p) return p; } /** Add node to tree storage. @param root Root node. @param newnode Node to add. @param st Storage. @return New root node or NULL. */ static dk_storage_node_t * avlt_add DK_P3(dk_storage_node_t *, root, dk_storage_node_t *, newnode, dk_storage_t *, st) { /* p ... the current node q ... father of p r ... critical node s ... father of r */ dk_storage_node_t *back, *p, *q, *r, *s; $? "+ avlt_add %s %s %s", TR_PTR(root), TR_PTR(newnode), TR_PTR(st) back = root; p = r = root; q = s = NULL; /* Search place for insertion, write direction into the "w" field in each node. The final new node has an empty "w" field. */ while(p) { if(p->b) { s = q; r = p; } q = p; if(node_compare(p,newnode,st,st->c) > 0) { p->w = WALK_LEFT; p = p->l; } else { p->w = WALK_RIGHT; p = p->r; } } p = newnode; if(!back) { /* When inserting into an empty tree we are done here. */ back = p; } else { /* The tree is not empty. The new node p is concatenated to the parent q. */ if(node_compare(q,newnode,st,st->c) > 0) { q->l = p; q->w = WALK_LEFT; } else { q->r = p; q->w = WALK_RIGHT; } /* Now we must balance the tree again if necessary. */ p->p = q; if(r) { /* There is a critical node. */ p = r; /* Modify balance fields from critial node until we find our new node. */ while(p->w) { if(p->w == WALK_LEFT) { dec_balance(p); p = p->l; } else { inc_balance(p); p = p->r; } } p = r; /* Now look whether we are dis-balanced, correct if necessary. */ if((p->b) == 2) { /* We must balance */ if(p->w == WALK_LEFT) { if((p->l)->b == 3) { p->b = 0; p = right_rotation(p); } else { p->b = (((((p->l)->r)->b) == 3) ? 1 : 0); (p->l)->b = (((((p->l)->r)->b) == 1) ? 3 : 0); p->l = left_rotation(p->l); if(p->l) (p->l)->p = p; p = right_rotation(p); } } else { if((p->r)->b == 1) { p->b = 0; p = left_rotation(p); } else { p->b = (((((p->r)->l)->b) == 1) ? 3 : 0); (p->r)->b = (((((p->r)->l)->b) == 3) ? 1 : 0); p->r = right_rotation(p->r); if(p->r) (p->r)->p = p; p = left_rotation(p); } } p->b = 0; /* Balance at the critical nodes father (if there is one) or create new root. */ if(s) { if(s->w == WALK_LEFT) { s->l = p; } else { s->r = p; } if(p) p->p = s; } else { back = p; } } } } if(back) { back->p = NULL; } $? "- avlt_add %s", TR_PTR(back) return back; } /** Remove storage node from tree storage. @param root Root object. @param node Node to remove. @param delpath Deletion path (used for tree balancing). @param st Storage. @param success_indicator Pointer to success variable. @param toremove Node to remove. @return New root object. */ static dk_storage_node_t * avlt_delete DK_P6(dk_storage_node_t *, root, dk_storage_node_t *, node, dk_storage_node_t **, delpath, dk_storage_t *, st, int *, success_indicator, dk_storage_node_t **, toremove) { dk_storage_node_t *back, *p, *q, *r, *todel; short x1 = 0; short delroot = 0; int can_continue; back = root; todel = node; $? "+ avlt_delete %s %s %s %s %s", TR_PTR(root), TR_PTR(node), TR_PTR(delpath), TR_PTR(st), TR_PTR(success_indicator) /* Make sure the node to delete has max. 1 subtree. */ if((todel->l) && (todel->r)) { $? ". finding another node to delete" todel = todel->l; while(todel->r) todel = todel->r; node_data_copy(node,todel,st); } if(!(todel->p)) { $? ". deleting the root node" delroot = 1; } /* Mark the way in the "w" fields. */ *toremove = todel; todel->w = 0; while(todel->p) { if((todel->p)->l == todel) { $? ". walk left" (todel->p)->w = WALK_LEFT; } else { $? ". walk right" (todel->p)->w = WALK_RIGHT; } todel = todel->p; } p = back; q = r = NULL; x1 = 0; can_continue = 1; while(can_continue) { $? ". new while loop 1 %d", x1 if(p) { $? ". have current node" if(p->w) { $? ". not final node" if(p->b == 0) { x1 = 0; $? ". current node is balanced" } delpath[x1++] = p; $? ". adding current node to delpath %d", (x1 - 1) if(x1 >= st->l) { $? ". x1 out of range" /* x1 too large */ *success_indicator = 0; goto error_mark; } if(p->w == WALK_LEFT) { $? ". going down left" p = p->l; } else { $? ". going down right" p = p->r; } } else { can_continue = 0; $? ". we are at the final point" } } else { can_continue = 0; $? ". no more nodes to visit" } } r = p; if(p->l) q = p->l; else q = p->r; if(x1 == 0) { if(delroot) { $? ". setting new root node" back = q; } } while(x1 > 0) { $? ". begin of while loop 2 %d", (x1 - 1) x1--; p = delpath[x1]; if(p->w == WALK_LEFT) { $? ". going to left" p->l = q; if(q) q->p = p; q = left_deleted(p, &x1); $? ". after left_deleted" } else { $? ". going to right" p->r = q; if(q) q->p = p; q = right_deleted(p, &x1); $? ". after right_deleted" } if(x1 == 0) { $? ". final node" if(delpath[x1] == back) { $? ". setting new root node" back = q; } } if(x1 < 0) { $? ". finished" p = delpath[0 - x1 - 1]; if(p->w == WALK_LEFT) { p->l = q; } else { p->r = q; } if(q) q->p = p; } } error_mark: if(back) { back->p = NULL; } $? "- avlt_delete %s", TR_PTR(back) return back; } /** Find storage node for an object evaluated like a given object. @param root Root node. @param testnode Node of the given object. @param st Storage. @param crit Comparison criteria. @param cand Pointer for candidate. @return Pointer to storage node or NULL. */ static dk_storage_node_t * tree_find_like DK_P5(dk_storage_node_t *, root, dk_storage_node_t *, testnode, dk_storage_t *, st, int, crit, dk_storage_node_t **, cand) { dk_storage_node_t *back = NULL; dk_storage_node_t *c; int testval; $? "+ sorted_find_like %s %s %s %d", TR_PTR(root), TR_PTR(testnode), TR_PTR(st), crit c = root; while(c) { testval = node_compare(c,testnode,st,crit); switch(testval) { case -1: { if(cand) *cand = c; c = c->r; } break; case 0: { back = c; c = c->l; } break; default: { c = c->l; } break; } } $? "- sorted_find_like %s", TR_PTR(back) return back; } /** Add node to a tree. @param r Root node. @param n New node to add. @param s Storage. @return Node pointer on success, NULL on error. */ static dk_storage_node_t * tree_add DK_P3(dk_storage_node_t *, r, dk_storage_node_t *, n, dk_storage_t *, s) { dk_storage_node_t *back; back = avlt_add(r,n,s); return back; } /** Release all nodes in a tree storage. @param r Root node. */ static void tree_release_all_nodes DK_P1(dk_storage_node_t *, r) { $? "+ sorted_release_all_nodes %s", TR_PTR(r) if(r) { tree_release_all_nodes(r->l); tree_release_all_nodes(r->r); r->l = r->r = r->p = NULL; r->o = NULL; r->b = 0; r->w = 0; STO_FREE(r) ; } $? "- sorted_release_all_nodes" } /** Find next node in a tree storage. @param n Current node. @param r Root node. @return Pointer to next node or NULL. */ static dk_storage_node_t * tree_find_next_node DK_P2(dk_storage_node_t *, n, dk_storage_node_t *, r) { dk_storage_node_t *back = NULL; dk_storage_node_t *c, *p; $? "+ sorted_find_next_node %s %s", TR_PTR(n), TR_PTR(r) /* if(n) { if(n->r) { back = n->r; while(back->l) { back = back->l; } } else { c = n; p = c->p; while(p && (!back)) { if((p->l) == c) { back = p; } else { c = p; p = c->p; } } } } else { back = r; if(back) { while(back->l) { back = back->l; } } } */ if(n) { if(n->r) { back = n->r; while(back->l) back = back->l; } else { c = n; p = c->p; while(p && (!back)) { if(p->l == c) { back = p; } else { c = p; p = c->p; } } } } else { back = r; if(back) { while(back->l) back = back->l; } } $? "- sorted_find_next_node %s", TR_PTR(back) return back; } /** Find last node in a tree storage. @param n Node to start search from. @return Last node or NULL. */ static dk_storage_node_t * tree_find_last_node DK_P1(dk_storage_node_t *, n) { dk_storage_node_t *back = NULL; dk_storage_node_t *c, *p; $? "+ sorted_find_last_node %s", TR_PTR(n) if(n->l) { back = n->l; while(back->r) { back = back->r; } } else { c = n; p = c->p; while(p && (!back)) { if((p->r) == c) { back = p; } else { c = p; p = c->p; } } } $? "- sorted_find_last_node %s", TR_PTR(back) return back; } /** Find node for object in tree storage (exact search). @param r Root node. @param o Object to find node for. @param s Storage. @return Pointer to node or NULL. */ static dk_storage_node_t * tree_find_exact DK_P3(dk_storage_node_t *, r, void *, o, dk_storage_t *, s) { dk_storage_node_t *back = NULL; dk_storage_node_t testnode, *c, *candidate; int testval; $? "+ sorted_find_exact %s %s %s", TR_PTR(r), TR_PTR(o), TR_PTR(s) node_init_for_object(&testnode, o, s, s->c); c = tree_find_like(r, &testnode, s, s->c, &candidate); while(c && (!back)) { testval = node_compare(c, &testnode, s, s->c); if(testval == 0) { if((c->o) == o) { back = c; } else { c = tree_find_next_node(c, r); } } else { c = NULL; } } $? "- sorted_find_exact %s", TR_PTR(back) return back; } /** Remove storage node from tree storage. @param ro Root node. @param n Node to delete. @param st Storage. @param sci ??? @param toremove Node to remove. @return New root node. */ static dk_storage_node_t * tree_remove DK_P5(dk_storage_node_t *, ro, dk_storage_node_t *, n, dk_storage_t *, st, int *, sci, dk_storage_node_t **, toremove) { dk_storage_node_t *back; back = avlt_delete(ro,n,st->d,st,sci,toremove); return back; } /* USE DOUBLE LINKED LIST */ /** Find node for an object evaluated like a given object in a list storage. @param root Root node. @param testnode Node with object for comparison. @param st Storage. @param crit Comparison criteria. @param cand Test candidate. @return Pointer to storage node or NULL. */ static dk_storage_node_t * list_find_like DK_P5(dk_storage_node_t *, root, dk_storage_node_t *, testnode, dk_storage_t *, st, int, crit, dk_storage_node_t **, cand) { dk_storage_node_t *back = NULL; dk_storage_node_t *c; int testval; $? "+ sorted_find_like %s %s %s %d", TR_PTR(root), TR_PTR(testnode), TR_PTR(st), crit c = root; while(c && (!back)) { testval = node_compare(c,testnode,st,crit); switch(testval) { case -1: { if(cand) *cand = c; c = c->r; } break; case 0: { back = c; c = NULL; } break; default : { c = NULL; } break; } } $? "- sorted_find_like %s", TR_PTR(back) return back; } /** Find node for an object (exact search). @param r Root node. @param o Object. @param s Storage. @return Pointer to the objects node or NULL. */ static dk_storage_node_t * list_find_exact DK_P3(dk_storage_node_t *, r, void *, o, dk_storage_t *, s) { dk_storage_node_t *back; $? "+ sorted_find_exact %s %s %s", TR_PTR(r), TR_PTR(o), TR_PTR(s) back = unsorted_find_exact(r,o); $? "- sorted_find_exact %s", TR_PTR(back) return back; } /** Add node to list storage. @param r Root node. @param n New node. @param s Storage. @return Pointer on success, NULL on error. */ static dk_storage_node_t * list_add DK_P3(dk_storage_node_t *, r, dk_storage_node_t *, n, dk_storage_t *, s) { dk_storage_node_t *back; dk_storage_node_t *larger, *current, *smaller; int ende; $? "+ sorted_add %s %s %s", TR_PTR(r), TR_PTR(n), TR_PTR(s) back = r; if(r) { larger = smaller = NULL; current = r; ende = 0; while(!ende) { if(node_compare(current,n,s,s->c) >= 0) { larger = current; ende = 1; } else { smaller = current; } if(current->r) { current = current->r; } else { ende = 1; } } if(larger) { n->r = larger; larger->l = n; if(smaller) { smaller->r = n; n->l = smaller; } else { back = n; } } else { if(smaller) { smaller->r = n; n->l = smaller; } } } else { back = n; } $? "- sorted_add %s", TR_PTR(back) return back; } /** Release all nodes of a list storage. @param r Root node. */ static void list_release_all_nodes DK_P1(dk_storage_node_t *, r) { $? "+ sorted_release_all_nodes" unsorted_release_all_nodes(r); $? "- sorted_release_all_nodes" } /** Find next node. @param n Current node. @param r Root node. @return Pointer to next node or NULL. */ static dk_storage_node_t * list_find_next_node DK_P2(dk_storage_node_t *, n, dk_storage_node_t *, r) { dk_storage_node_t *back; $? "+ sorted_find_next_node %s %s", TR_PTR(n), TR_PTR(r) back = unsorted_find_next_node(n,r); $? "- sorted_find_next_node %s", TR_PTR(back) return back; } /** Find last (previous) node. @param n Current node. @return Pointer to previous node or NULL. */ static dk_storage_node_t * list_find_last_node DK_P1(dk_storage_node_t *, n) { dk_storage_node_t *back; $? "+ sorted_find_last_node %s", TR_PTR(n) back = unsorted_find_last_node(n); $? "- sorted_find_last_node %s", TR_PTR(back) return back; } /** Remove storage node from list storage. @param ro Root node. @param n Node to remove. @param st Storage. @param sci ??? @param toremove ??? */ static dk_storage_node_t * list_remove DK_P5(dk_storage_node_t *, ro, dk_storage_node_t *, n, dk_storage_t *, st, int *, sci, dk_storage_node_t **, toremove) { dk_storage_node_t *back; $? "+ sorted_remove %s %s %s", TR_PTR(ro), TR_PTR(n), TR_PTR(st) back = unsorted_remove(ro,n); $? "- sorted_remove %s", TR_PTR(back) return back; } /* COMMON STATIC FUNCTIONS */ /** Get object node (traverse storage). @param it Storage iterator. @param o Object to find storage node. @return Pointer to node or NULL. */ static dk_storage_node_t * traverse_iterators_for DK_P2(void *, it, void *, o) { dk_storage_node_t *back = NULL; dk_storage_iterator_t *c; $? "+ traverse_iterators_for %s %s", TR_PTR(it), TR_PTR(o) if(it) { c = (dk_storage_iterator_t *)it; while(c && (!back)) { if(c->c) { if(((c->c)->o) == o) { back = c->c; } } if(!back) c = c->r; } } $? "- traverse_iterators_for %s", TR_PTR(back) return back; } /** Find last storage node. @param n Current storage node. @param st Storage. @return Pointer to last node or NULL. */ static dk_storage_node_t * find_last_node DK_P2(dk_storage_node_t *, n, dk_storage_t *, st) { dk_storage_node_t *back = NULL; $? "+ find_last_node %s %s", TR_PTR(n), TR_PTR(st) if(st->h) { if(st->t) { back = tree_find_last_node(n); } else { back = list_find_last_node(n); } } else { back = unsorted_find_last_node(n); } $? "- find_last_node %s", TR_PTR(back) return back; } /** Initialize storage. @param st Storage to initialize. @param sz Indicator for return path buffer length. @return 1 on success, 0 on error. */ static int storage_init DK_P2(dk_storage_t *, st, int, sz) { int back = 0; short l; $? "+ storage_init %s %d", TR_PTR(st), sz /* delpath begin address and length */ st->d = NULL; st->l = 0; /* root node */ st->r = NULL; /* comparison method */ st->h = 0; /* comparison criteria */ st->c = 0; /* iterators list */ st->i = NULL; st->l = l = 1536; switch(sz) { case DK_STO_SIZE_LARGE : { st->l = l = 1024; } break; case DK_STO_SIZE_MEDIUM : { st->l = l = 512; } break; case DK_STO_SIZE_SMALL : { st->l = l = 128; } break; case DK_STO_SIZE_TINY : { st->l = l = 64; } break; } st->d = STO_ALLOC(dk_storage_node_p,l); st->t = use_trees; if(st->d) { back = 1; } $? "- storage_init %d", back return back; } /** Close the storage, release memory. @param st Storage to close. */ static void storage_end DK_P1(dk_storage_t *, st) { $? "+ storage_end %s", TR_PTR(st) /* release iterators */ { dk_storage_iterator_t *c, *n; c = (dk_storage_iterator_t *)(st->i); st->i = NULL; while(c) { $? ". going to release iterator %s", TR_PTR(c) n = c->r; c->r = NULL; c->l = NULL; c->c = NULL; c->s = NULL; STO_FREE(c) ; c = n; } st->i = NULL; } $? ". iterators released" /* release nodes */ { if(st->h) { if(st->t) { tree_release_all_nodes(st->r); } else { list_release_all_nodes(st->r); } } else { unsorted_release_all_nodes(st->r); } st->r = NULL; } $? ". nodes released" /* release delpath */ { dk_storage_node_p *p; p = st->d; if(p) { STO_FREE(p); } st->d = NULL; st->l = 0; } $? ". delpath released" /* set pointers to NULL */ { st->h = 0; st->c = 0; } $? ". function pointers resetted" $? "- storage_end" } /* PUBLIC INTERFACES */ dk_storage_t *dksto_open DK_P1(int, sz) { dk_storage_t *back = NULL; $? "+ dksto_open %d", sz configure_use_trees(); back = STO_ALLOC(dk_storage_t,1) ; if(back) { if(!storage_init(back,sz)) { STO_FREE(back) ; back = NULL; } } $? "- dksto_open %s", TR_PTR(back) return back; } void dksto_close DK_P1(dk_storage_t *, st) { $? "+ dksto_close %s", TR_PTR(st) if(st) { storage_end(st); STO_FREE(st) ; } $? "- dksto_close" } void dksto_remove_all DK_P1(dk_storage_t *,st) { if(st) { /* reset all iterators */ dk_storage_iterator_t *c, *n; c = (dk_storage_iterator_t *)(st->i); while(c) { n = c->r; c->c = NULL; c = n; } /* remove all nodes */ if(st->r) { if(st->h) { if(st->t) { tree_release_all_nodes(st->r); } else { list_release_all_nodes(st->r); } } else { unsorted_release_all_nodes(st->r); } } st->r = NULL; } } int dksto_remove DK_P2(dk_storage_t *, st, void *, o) { int back = 0; dk_storage_node_t *node_to_remove, *ln; dk_storage_iterator_t *iterator; $? "+ dksto_remove %s %s", TR_PTR(st), TR_PTR(o) if(st && o) { node_to_remove = traverse_iterators_for(st->i, o); if(!node_to_remove) { if(st->h) { if(st->t) { node_to_remove = tree_find_exact(st->r,o,st); } else { node_to_remove = list_find_exact(st->r,o,st); } } else { node_to_remove = unsorted_find_exact(st->r,o); } } if(node_to_remove) { back = 1; ln = find_last_node(node_to_remove,st); iterator = (dk_storage_iterator_t *)(st->i); while(iterator) { if((iterator->c) == node_to_remove) { iterator->c = ln; } iterator = iterator->r; } if(st->h) { if(st->t) { st->r = tree_remove(st->r,node_to_remove,st,&back,&node_to_remove); } else { st->r = list_remove(st->r,node_to_remove,st,&back,&node_to_remove); } } else { st->r = unsorted_remove(st->r,node_to_remove); } node_to_remove->l = node_to_remove->r = node_to_remove->p = NULL; node_to_remove->o = NULL; STO_FREE(node_to_remove); } } $? "- dksto_remove %d", back return back; } int dksto_add DK_P2(dk_storage_t *, st, void *, o) { int back = 0; dk_storage_node_t *n; $? "+ dksto_add %s %s", TR_PTR(st), TR_PTR(o) if(st && o) { n = STO_ALLOC(dk_storage_node_t,1) ; if(n) { node_init_for_object(n,o,st,st->c); if(st->h) { if(st->t) { st->r = tree_add(st->r, n, st); } else { st->r = list_add(st->r, n, st); } } else { st->r = unsorted_add(st->r, n); } back = 1; } } $? "- dksto_add %d", back return back; } dk_storage_iterator_t * dksto_it_open DK_P1(dk_storage_t *, st) { dk_storage_iterator_t *back = NULL; $? "+ dksto_it_open %s", TR_PTR(st) if(st) { back = STO_ALLOC(dk_storage_iterator_t,1) ; if(back) { back->s = st; back->l = NULL; back->r = (dk_storage_iterator_t *)(st->i); back->c = NULL; st->i = (void *)back; } } $? "- dksto_it_open %s", TR_PTR(back) return back; } void dksto_it_close DK_P1(dk_storage_iterator_t *, it) { dk_storage_iterator_t *l, *r; dk_storage_t *s; $? "+ dksto_it_close %s", TR_PTR(it) if(it) { s = it->s; l = it->l; r = it->r; if(l) { l->r = r; } else { s->i = (void *)(r); } if(r) { r->l = l; } it->s = NULL; it->l = it->r = NULL; it->c = NULL; STO_FREE(it) ; } $? "- dksto_it_close" } void dksto_it_reset DK_P1(dk_storage_iterator_t *, it) { $? "+ dksto_it_reset %s", TR_PTR(it) if(it) { it->c = NULL; } $? "- dksto_it_reset" } void * dksto_it_next DK_P1(dk_storage_iterator_t *, it) { void *back = NULL; $? "+ dksto_it_next %s", TR_PTR(it) if(it) { if(it->s) { if((it->s)->h) { if((it->s)->t) { it->c = tree_find_next_node(it->c, (it->s)->r); } else { it->c = list_find_next_node(it->c, (it->s)->r); } } else { it->c = unsorted_find_next_node(it->c, (it->s)->r); } if(it->c) { back = (it->c)->o; } } } $? "- dksto_it_next %s", TR_PTR(back) return back; } void * dksto_it_find_exact DK_P2(dk_storage_iterator_t *, i, void *, o) { void *back = NULL; $? "+ dksto_it_find_exact %s %s", TR_PTR(i), TR_PTR(o) if(i && o) { if(i->s) { if((i->s)->h) { if((i->s)->t) { i->c = tree_find_exact((i->s)->r, o, i->s); } else { i->c = list_find_exact((i->s)->r, o, i->s); } } else { i->c = unsorted_find_exact((i->s)->r, o); } } if(i->c) { back = (i->c)->o; } } $? "- dksto_it_find_exact %s", TR_PTR(back) return back; } void * dksto_it_find_like DK_P3(dk_storage_iterator_t *, i, void *, o, int, cr) { void *back = NULL; dk_storage_node_t testnode, *candidate; $? "+ dksto_it_find_like %s %s %d", TR_PTR(i), TR_PTR(o), cr if(i && o) { if(i->s) { candidate = NULL; if((i->s)->h) { node_init_for_object(&testnode, o, (i->s), cr); if((i->s)->t) { i->c = tree_find_like((i->s)->r, &testnode, i->s, cr, &candidate); } else { i->c = list_find_like((i->s)->r, &testnode, i->s, cr, &candidate); } } else { i->c = unsorted_find_exact((i->s)->r, o); } if(i->c) { back = (i->c)->o; } else { i->c = candidate; } } } $? "- dksto_it_find_like %s", TR_PTR(back) return back; } int dksto_set_eval_c DK_P3(dk_storage_t *, st, dk_fct_eval_c_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_c %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).c = f; st->c = cr; st->h = COMPARE_CHAR; } } $? "- dksto_set_eval_c %d", back return back; } int dksto_set_eval_uc DK_P3(dk_storage_t *, st, dk_fct_eval_uc_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_uc %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).uc = f; st->c = cr; st->h = COMPARE_UCHAR; } } $? "- dksto_set_eval_uc %d", back return back; } int dksto_set_eval_s DK_P3(dk_storage_t *, st, dk_fct_eval_s_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_s %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).s = f; st->c = cr; st->h = COMPARE_SHORT; } } $? "- dksto_set_eval_s %d", back return back; } int dksto_set_eval_us DK_P3(dk_storage_t *, st, dk_fct_eval_us_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_us %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).us = f; st->c = cr; st->h = COMPARE_USHORT; } } $? "- dksto_set_eval_us %d", back return back; } int dksto_set_eval_i DK_P3(dk_storage_t *, st, dk_fct_eval_i_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_i %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).i = f; st->c = cr; st->h = COMPARE_INT; } } $? "- dksto_set_eval_i %d", back return back; } int dksto_set_eval_ui DK_P3(dk_storage_t *, st, dk_fct_eval_ui_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_ui %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).ui = f; st->c = cr; st->h = COMPARE_UINT; } } $? "- dksto_set_eval_ui %d", back return back; } int dksto_set_eval_l DK_P3(dk_storage_t *, st, dk_fct_eval_l_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_l %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).l = f; st->c = cr; st->h = COMPARE_LONG; } } $? "- dksto_set_eval_l %d", back return back; } int dksto_set_eval_ul DK_P3(dk_storage_t *, st, dk_fct_eval_ul_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_ul %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).ul = f; st->c = cr; st->h = COMPARE_ULONG; } } $? "- dksto_set_eval_ul %d", back return back; } int dksto_set_eval_f DK_P3(dk_storage_t *, st, dk_fct_eval_f_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_f %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).f = f; st->c = cr; st->h = COMPARE_FLOAT; } } $? "- dksto_set_eval_f %d", back return back; } int dksto_set_eval_d DK_P3(dk_storage_t *, st, dk_fct_eval_d_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_d %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).d = f; st->c = cr; st->h = COMPARE_DOUBLE; } } $? "- dksto_set_eval_d %d", back return back; } int dksto_set_comp DK_P3(dk_storage_t *, st, dk_fct_comp_t *, f, int, cr) { int back = 0; $? "+ dksto_set_eval_d %s %s %d", TR_PTR(st), TR_PTR(f), cr if(st) { if(!(st->r)) { back = 1; (st->e).comp = f; st->c = cr; st->h = COMPARE_FCT; } } $? "- dksto_set_eval_d %d", back return back; } int dksto_use_trees DK_P2(dk_storage_t *,st,int,ok) { int back = 0; if(st) { if(!(st->r)) { st->t = (ok ? 1 : 0); back = 1; } } else { use_trees = (ok ? 1 : 0); is_configured = 1; } return back; } void * dksto_find_root DK_P1(dk_storage_t *,s) { void *back = NULL; if(s) { if(s->r) { back = (s->r)->o; } } return back; } void * dksto_it_find_parent DK_P1(dk_storage_iterator_t *,i) { void *back = NULL; if(i) { if(i->c) { if((i->c)->p) { back = ((i->c)->p)->o; } } } return back; } void * dksto_it_find_left DK_P1(dk_storage_iterator_t *,i) { void *back = NULL; if(i) { if(i->c) { if((i->c)->l) { back = ((i->c)->l)->o; } } } return back; } void * dksto_it_find_right DK_P1(dk_storage_iterator_t *,i) { void *back = NULL; if(i) { if(i->c) { if((i->c)->r) { back = ((i->c)->r)->o; } } } return back; } void * dksto_it_find_root DK_P1(dk_storage_iterator_t *,i) { void *back = NULL; if(i) { if(i->s) { back = dksto_find_root(i->s); } } return back; }