hlst.c
上传用户:tjescc
上传日期:2021-02-23
资源大小:419k
文件大小:23k
源码类别:

Telnet服务器

开发平台:

Unix_Linux

  1. /*
  2.  *          Copyright (c) mjh-EDV Beratung, 1996-1999
  3.  *     mjh-EDV Beratung - 63263 Neu-Isenburg - Rosenstrasse 12
  4.  *          Tel +49 6102 328279 - Fax +49 6102 328278
  5.  *                Email info@mjh.teddy-net.com
  6.  *
  7.  *       Author: Jordan Hrycaj <jordan@mjh.teddy-net.com>
  8.  *
  9.  *    $Id: hlst.c,v 1.33 2003/02/27 10:09:57 renaud Exp $ 
  10.  *
  11.  *   This library is free software; you can redistribute it and/or
  12.  *   modify it under the terms of the GNU Library General Public
  13.  *   License as published by the Free Software Foundation; either
  14.  *   version 2 of the License, or (at your option) any later version.
  15.  *
  16.  *   This library is distributed in the hope that it will be useful,
  17.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  19.  *   Library General Public License for more details.
  20.  *
  21.  *   You should have received a copy of the GNU Library General Public
  22.  *   License along with this library; if not, write to the Free
  23.  *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  *
  25.  *   HLST - a simple hash list manager
  26.  */
  27. #include <includes.h>
  28. #define __HLST_INTERNAL__
  29. #include "hlst.h"
  30. #ifdef HAVE_ERRNO_H
  31. #include <errno.h>
  32. #endif
  33. /* ------------------------------------------------------------------------- *
  34.  *                      private definitions                                  *
  35.  * ------------------------------------------------------------------------- */
  36. /* XMALLOC returns memory initialized to zero */
  37. #define XMALLOC(x) emalloc(x)
  38. #define XFREE(x)   efree(&(x))
  39. /* default number of hash buckets per list */
  40. #define DEFAULT_BUCKETS 53
  41. /* global factor to be applied internally to any estimated_size_hint */
  42. #define DEFAULT_PERCENTAGE_COMPRESSOR 80
  43. typedef
  44. struct _sorter {
  45.   int                  dirty ;
  46.   unsigned              size ;
  47.   struct _hashqueue *inx [1] ;
  48.   /* varable length, pointer aligned  */
  49. } sorter ;
  50. typedef 
  51. struct _hashqueue { /* linked list of bucket entries */
  52.   void           *contents;
  53.   struct _hashqueue  *next;
  54.   unsigned          keylen; /* length of current key */
  55.   int               locked; /* currently visited my some hash walk */
  56.   struct _sorter *backlink; /* there might be an index on that list */
  57. # ifdef ENABLE_RHLST
  58.   int               tranum; /* transaction id, used for caching */
  59. # endif /* ENABLE_RHLST */
  60.   char             key [1]; /* varable size key */
  61.   /* varable length, pointer aligned  */
  62. } hashqueue ;
  63. /* get the byte offset of a field */
  64. #define STRUCT_OFFSET(type, field) 
  65. ((char*)(&((type*)0)->field)-(char*)0)
  66. /* given a pointer to field "field" from a structure of type "type",
  67.    get the structure pointer */
  68. #define REVERT_FIELD_PTR(p, type, field)  
  69. ((type*)(((char*)p) - STRUCT_OFFSET (type,field)))
  70. /* ------------------------------------------------------------------------- *
  71.  *                      private variables                                    *
  72.  * ------------------------------------------------------------------------- */
  73. /* non-empty, 0-terminated sorted list in the first entry:
  74.    possible table sizes and hash factors (rel prime to the table size) */
  75. /* FIXME: parameters need to be double ckecked, here */
  76. static const hash_defs hints [] = {
  77.   { 11,   7},
  78.   { 23,  11},
  79.   { 53,  31},
  80.   { 73,  37},
  81.   {101,  40},
  82.   {151,  43},
  83.   {269,  97},
  84.   {509, 101},
  85.   {577, 107},
  86.   {0,0}
  87. };
  88. static unsigned 
  89. size_hint_percentage_compressor = DEFAULT_PERCENTAGE_COMPRESSOR ;
  90. /* used for custum sorting -- not thread safe */
  91. static void *sorter_desc ;
  92. static int (*sorter_fn)(void*,const char*,unsigned,const char*,unsigned);
  93. #ifdef USE_PTHREADS
  94. /* some global sync for logging etc  -- make custum sorting thread safe */
  95. static int glob_mutex_initialized = 0;
  96. static pthread_mutex_t glob_mutex; 
  97. #endif
  98. /* ------------------------------------------------------------------------- *
  99.  *               hashed index ank key length calculation                     *
  100.  * ------------------------------------------------------------------------- */
  101. /* make a hash from the argument key string */
  102. #define _GET_HASH_AND_STRLEN( h, H, L, key) {
  103.   const char *s = (key) ;
  104.   (H) = *s ;
  105.   (L) =  1 ;
  106.   goto S; do {                 
  107.       (H) *= (h)->z.fac ; /* shift accu  */
  108.       (H) +=        * s ; /* get char    */
  109.       (L) ++            ; /* word length */
  110.     S:(H) %= (h)->z.mod ; /* module size */
  111.   } while (*s ++) ; }
  112. /* make a hash from a generic argument key of given length */
  113. #define _GET_HASH( h, H, len, key) {
  114.   const char *s = (key) ;
  115.   int l = (len) ;
  116.   (H)   =    *s ;
  117.   goto T; do {
  118.       (H) *= (h)->z.fac ; /* shift accu  */
  119.       (H) +=     * ++ s ; /* get char    */
  120.     T:(H) %= (h)->z.mod ; /* module size */
  121.   } while (-- l); }
  122. /* combine that methods, above */
  123. #define GET_HASH_AND_LEN( h, H, len, key)
  124.   { if (len) _GET_HASH (h,H,len,key) else _GET_HASH_AND_STRLEN(h,H,len,key) }
  125. #ifdef USE_PTHREADS
  126. # define mutex_init(x)     pthread_mutex_init    (x, 0)
  127. # define mutex_destroy(x)  pthread_mutex_destroy (x)
  128. # define mutex_lock(x)     pthread_mutex_lock    (x)
  129. # define mutex_unlock(x)   pthread_mutex_unlock  (x)
  130. # define globally_lock()   _glob_lock ()
  131. # define globally_unlock() mutex_unlock (&glob_mutex)
  132. static void
  133. _glob_lock
  134.   (void)
  135. {
  136.   if (glob_mutex_initialized == 0) {
  137.     mutex_init (&glob_mutex);
  138.     glob_mutex_initialized = 1;
  139.   }
  140.   mutex_lock (&glob_mutex);
  141. }
  142.  #else
  143. # define mutex_init(x)     /* empty */
  144. # define mutex_lock(x)     /* empty */
  145. # define mutex_unlock(x)   /* empty */
  146. # define mutex_destroy(x)  /* empty */
  147. # define globally_lock()   /* empty */
  148. # define globally_unlock() /* empty */
  149. #endif
  150. /* ------------------------------------------------------------------------- *
  151.  *                      private functions                                    *
  152.  * ------------------------------------------------------------------------- */
  153. #define XMCOPY(p,len) memcpy (XMALLOC (len), (p), (len))
  154. static hashqueue **
  155. find_bucket_ptr
  156.   (hashqueue   **Q,
  157.    const char *key,
  158.    unsigned    len)
  159. {
  160.   hashqueue *q ;
  161.   while (q = *Q, q != 0) {
  162.     if (len == q->keylen && memcmp (q->key, key, len) == 0)
  163.       return Q ;
  164.     Q = &q->next ;
  165.   }
  166.   errno = ENOENT ;
  167.   return 0;
  168. }
  169. /* qsort call back functions */
  170. static int 
  171. __comp
  172.   (hashqueue**  left, 
  173.    hashqueue** right)
  174. {
  175.   int n, min ;
  176.   
  177.   if ((min = (*left)->keylen) > (*right)->keylen)
  178.     min = (*right)->keylen ;
  179.   
  180.   if ((n = memcmp ((*left)->key, (*right)->key, min)) != 0)
  181.     return n;
  182.   
  183.   return (*left)->keylen - (*right)->keylen ;
  184. }
  185. static int 
  186. __comp_custom
  187.   (hashqueue**  left, 
  188.    hashqueue** right)
  189. {
  190.   return (*sorter_fn) (sorter_desc,
  191.        (*left)->key,
  192.        (*left)->keylen,
  193.        (*right)->key,
  194.        (*right)->keylen);
  195. }
  196. /* ------------------------------------------------------------------------- *
  197.  *                 public functions: open/close management                   *
  198.  * ------------------------------------------------------------------------- */
  199. hlst *
  200. create_hlst
  201.   (unsigned estimated_size_hint,
  202.    void (*clup)(void*,void*,char*,unsigned),
  203.    void *state)
  204. {
  205.   const hash_defs *hd = hints ;
  206.   hlst *h ;
  207.   if (estimated_size_hint == 0)
  208.     estimated_size_hint = DEFAULT_BUCKETS ;
  209.   
  210.   /* adjust accoording to policy */
  211.   estimated_size_hint *= size_hint_percentage_compressor ;
  212.   estimated_size_hint /= 100 ;
  213.   /* find appropriate list size, will stop at the last entry */
  214. # ifdef _WIN32
  215.   for (;;) {
  216.     const hash_defs *hd1 = hd+1 ;
  217.     if (hd1->mod == 0 || hd1->mod > estimated_size_hint)
  218.       break ;
  219.     ++ hd ;
  220.   }
  221. # else
  222.   while (hd [1].mod != 0 && hd [1].mod <= estimated_size_hint)
  223.     ++ hd ;
  224. # endif
  225.   h = XMALLOC (sizeof (hlst) + (hd->mod - 1) * sizeof (void*));
  226.   h->z          =   *hd ;
  227.   h->clup       =  clup ;
  228.   h->clup_state = state ;
  229.   return h;
  230. }
  231. hlst *
  232. copy_hlst
  233.   (hlst                     *h,
  234.    unsigned estimated_size_hint,
  235.    void *(*copy)(void*,void*,char*,unsigned),
  236.    void           *cpstate,
  237.    void (*clup)(void*,void*,char*,unsigned),
  238.    void             *state)
  239. {
  240.   const hash_defs *hd = hints ;
  241.   hlst *new ;
  242.   unsigned i, copy_only ;
  243.   
  244.   /* sanity check */
  245.   if (h == 0) {
  246.     errno = EINVAL;
  247.     return 0 ;
  248.   }
  249.   if (estimated_size_hint == 0)
  250.     /* get default from list to copy */
  251.     hd = &h->z ;
  252.   else {
  253.     /* adjust accoording to policy */
  254.     estimated_size_hint *= size_hint_percentage_compressor ;
  255.     estimated_size_hint /= 100 ;
  256.     if (estimated_size_hint != h->z.mod) {
  257.       /* find appropriate list size, will stop at the last entry */
  258. #     ifdef _WIN32
  259.       for (;;) {
  260. const hash_defs *hd1 = hd+1 ;
  261. if (hd1->mod == 0 || hd1->mod > estimated_size_hint)
  262.   break ;
  263. ++ hd ;
  264.       }
  265. #     else
  266.       while (hd [1].mod != 0 && hd [1].mod <= estimated_size_hint)
  267. ++ hd ;
  268. #     endif
  269.     }
  270.   }
  271.   new = (copy_only = (hd->mod == h->z.mod && copy == 0)) == 0
  272.     /* create a new list */
  273.     ? XMALLOC (sizeof (hlst) + (hd->mod - 1) * sizeof (void*))
  274.     /* in this case, we can simply copy blocks, later on */
  275.     : XMCOPY (h, sizeof (hlst) + (h->z.mod - 1) * sizeof (void*))
  276.     ;
  277.   
  278.   new->walk          =                0 ;
  279.   new->clup          =             clup ;
  280.   new->clup_state    =            state ;
  281.   new->total_entries = h->total_entries ;
  282.   
  283.   /* we organize the new list while looping over the old one */
  284.   for (i = 0; i < h->z.mod; i ++) {
  285.     
  286.     /* get hash queue */
  287.     hashqueue *p = h->bucket [i] ;
  288.     new->bucket [i] = 0 ;
  289.  
  290.     while (p != 0) {
  291.       hashqueue *q ;
  292.       void **Q ;
  293.       
  294.       if (copy_only) {
  295. /* copy entry */
  296. q = XMCOPY (p, sizeof (hashqueue) + p->keylen-1) ;
  297. q->locked = 0 ;
  298. /* link to bucket queue */
  299. q->next         = ((hashqueue*)new->bucket [i]) ;
  300. new->bucket [i] = q ;
  301. /* get contents pointer */
  302. Q = &q->contents ;
  303.       } else {
  304. /* create new entry */
  305. if ((Q = make_hlst ((hlst*)new, p->key, p->keylen)) == 0) {
  306.   fprintf (stderr, __FILE__ 
  307.    "(%d): [make_hlst() == 0] serious bug, "
  308.    "corrupt target list -- please report, aborting.n",
  309.    __LINE__);
  310.   exit (2) ;
  311. }
  312.       }
  313.       if (copy != 0 && /* duplicate user contents */
  314.   (*Q = (*copy) (cpstate, p->contents, p->key, p->keylen)) == 0 &&
  315.   errno != 0) {
  316. int e = errno ;
  317. /* stop: clean up */
  318. destroy_hlst (new);
  319. errno = e ;
  320. return 0;
  321.       }
  322.       
  323.       /* get next template */
  324.       p = p->next ;
  325.     }
  326.   }
  327.   
  328.   return new ;
  329. }
  330. void 
  331. flush_hlst 
  332.   (hlst *h,
  333.    void (*clup)(void*desc,void*,char*,unsigned),
  334.    void*             desc)
  335. {
  336.   unsigned i;
  337.   hsrch *s ;
  338.   /* sanity check */
  339.   if (h == 0) return ;
  340.   if (clup == 0) {
  341.     clup = h->clup ;
  342.     desc = h->clup_state ;
  343.   }
  344.   /* remove sorter */
  345.   if (h->access != 0) {
  346.     XFREE (h->access);
  347.     h->access = 0;
  348.   }
  349.   for (i = 0; i < h->z.mod; i ++) {
  350.     /* do with this bucket */
  351.     hashqueue *p, **P = (hashqueue**)h->bucket + i ;
  352.     while ((p = *P) != 0) {
  353.       /* unlink that node, so even circular sublists would not loop */
  354.       *P = p->next ;
  355.       if (clup != 0 && p->contents != 0)
  356. (*clup) (desc, p->contents, p->key, p->keylen);
  357.       XFREE (p);
  358.     }
  359.   } /* for */
  360.   /* cannot visit any node, anymore */
  361.   for (s = h->walk; s != 0; s = s->next) {
  362.     s->hlist = 0 ;      /* next_hlst_search() will stop, that way */
  363. #   ifdef ENABLE_RHLST
  364.     if (s->clup != 0) { /* clean up by call back as early as possible */
  365.       (*s->clup)(s->clup_state);
  366.       s->clup = 0 ;
  367.     }
  368. #   endif
  369.   }
  370.   /* statistics */
  371.   h->total_entries = 0 ;
  372. }
  373. void 
  374. destroy_hlst 
  375.   (hlst *h)
  376. {
  377.   /* sanity check */
  378.   if (h == 0) return ;
  379.   flush_hlst (h, 0, 0);
  380.   if (h->clup != 0) 
  381.     (*h->clup) (h->clup_state,0,0,0);
  382.   XFREE (h);
  383. }
  384. /* ------------------------------------------------------------------------- *
  385.  *                 public functions: manipulate slots                        *
  386.  * ------------------------------------------------------------------------- */
  387. void** 
  388. find_hlst
  389.   (hlst         *h,
  390.    const char *key,
  391.    unsigned    len)
  392. {
  393.   hashqueue **Q;
  394.   int inx ;
  395.   /* sanity check */
  396.   if (h == 0 || key == 0) {
  397.     errno = EINVAL;
  398.     return 0;
  399.   }
  400.   
  401.   GET_HASH_AND_LEN (h, inx, len, key);
  402.   if ((Q = find_bucket_ptr ((hashqueue**)h->bucket + inx, key, len)) != 0)
  403.     return &(*Q)->contents ;
  404.   errno = ENOENT;
  405.   return 0;
  406. }
  407. void** 
  408. make_hlst
  409.   (hlst        *_h,
  410.    const char *key,
  411.    unsigned    len)
  412. {
  413.   hlst *h = (hlst *)_h ;
  414.   hashqueue *q ;
  415.   int inx ;
  416.   /* sanity check */
  417.   if (h == 0 || key == 0) {
  418.     errno = EINVAL;
  419.     return 0;
  420.   }
  421.   
  422.   GET_HASH_AND_LEN (h, inx, len, key);
  423.   /* cannot have duplicate entries */
  424.   if (find_bucket_ptr ((hashqueue**)h->bucket + inx, key, len) != 0) {
  425.     errno = EEXIST;
  426.     return 0;
  427.   }
  428.   /* make entry */
  429.   q = XMALLOC (sizeof (hashqueue) + len - 1);
  430.   memcpy (q->key, key, q->keylen = len);
  431.   /* link entry */
  432.   q->next = h->bucket [inx] ;
  433.   h->bucket [inx] = q ;
  434.   /* statistics */
  435.   h->total_entries ++ ;
  436.   if (h->access != 0)
  437.     /* mark the sorter ready for rebuilt */
  438.     h->access->dirty = 1 ;
  439.   /* always returns some pointer */
  440.   return &q->contents ;
  441. }
  442. int
  443. delete_hlst
  444.   (hlst         *h, 
  445.    const char *key,
  446.    unsigned    len)
  447. {
  448.   hashqueue *q, **Q;
  449.   hsrch *s;
  450.   unsigned inx ;
  451.   /* sanity check */
  452.   if (h == 0 || key == 0) {
  453.     errno = EINVAL;
  454.     return -1;
  455.   }
  456.   
  457.   GET_HASH_AND_LEN (h, inx, len, key);
  458.   if ((Q = find_bucket_ptr ((hashqueue**)h->bucket + inx, key, len)) == 0) {
  459.     errno = ENOENT ;
  460.     return -1 ;
  461.   }
  462.   q = *Q ;
  463.   if (q->locked) 
  464.     /* cannot visit that node, anymore so update the walk decriptors */
  465.     for (s = h->walk; s != 0; s = s->next)  
  466.       if (s->ntry == q) /* find find descriptor */
  467. s->ntry = q->next ; /* visit successor, instead */
  468.   /* set that index link idle */
  469.   if (h->access != 0)
  470.     if (q->backlink != 0) {
  471.       *q->backlink->inx = 0 ;
  472.       h->access->dirty = 1;
  473.     }
  474.   /* unlink */
  475.   *Q = q->next ;
  476.   /* statistics */
  477.   h->total_entries -- ;
  478.   if (h->clup != 0 && q->contents != 0) /* clean up call back */
  479.     (*h->clup) (h->clup_state, q->contents, q->key, q->keylen);
  480.   
  481.   XFREE (q);
  482.   return 0;
  483. }
  484. /* ------------------------------------------------------------------------- *
  485.  *                 public functions: misc                                    *
  486.  * ------------------------------------------------------------------------- */
  487. /* global factor to be applied internally to any estimated_size_hint */
  488. unsigned 
  489. compress_hlst_index 
  490.   (unsigned size_hint_percentage)
  491. {
  492.   unsigned o_index = size_hint_percentage_compressor ;
  493.   if ((size_hint_percentage_compressor = size_hint_percentage) > 100)
  494.     size_hint_percentage_compressor = 100 ;
  495.   return o_index;
  496. }
  497. char *
  498. query_key_hlst
  499.   (void **t)
  500. {
  501.   if (t == 0) {
  502.     errno = EINVAL;
  503.     return 0;
  504.   }
  505.   return REVERT_FIELD_PTR (t, hashqueue, contents)->key ;
  506. }
  507. unsigned
  508. query_keylen_hlst
  509.   (void **t)
  510. {
  511.   if (t == 0) {
  512.     errno = EINVAL;
  513.     return 0;
  514.   }
  515.   return  REVERT_FIELD_PTR (t, hashqueue, contents)->keylen ;
  516. }
  517. #ifdef ENABLE_RHLST
  518. int
  519. query_tranum_hlst
  520.   (void **t)
  521. {
  522.   if (t == 0) {
  523.     errno = EINVAL;
  524.     return 0;
  525.   }
  526.   errno = 0 ;
  527.   return REVERT_FIELD_PTR (t, hashqueue, contents)->tranum ;
  528. }
  529. int
  530. set_tranum_hlst
  531.   (void **t,
  532.    int    n)
  533. {
  534.   int last ;
  535.   if (t == 0) {
  536.     errno = EINVAL;
  537.     return 0;
  538.   }
  539.   errno = 0 ;
  540.   last = REVERT_FIELD_PTR (t, hashqueue, contents)->tranum ;
  541.   REVERT_FIELD_PTR (t, hashqueue, contents)->tranum = n ;
  542.   return last;
  543. }
  544. #endif /* ENABLE_RHLST */
  545. unsigned
  546. query_hlst_size
  547.   (hlst *h)
  548. {
  549.   if (h == 0) {
  550.     errno = EINVAL;
  551.     return 0;
  552.   }
  553.   errno = 0 ;
  554.   return h->total_entries ;
  555. }
  556. /* ------------------------------------------------------------------------- *
  557.  *                 public functions: search that list, itemwise              *
  558.  * ------------------------------------------------------------------------- */
  559. hsrch* 
  560. open_hlst_search 
  561.  (hlst *h)
  562. {
  563.   hsrch *s ;
  564.   /* sanity check */
  565.   if (h == 0) {
  566.     errno = EINVAL;
  567.     return 0;
  568.   }
  569.   
  570.   s = XMALLOC (sizeof (hsrch));
  571.   s->hlist     =       h ; /* current hash list, to walk on */
  572.   s->bucket_id =      -1 ;
  573.   s->ntry      =       0 ; /* before the first entry */
  574.   s->next      = h->walk ; /* more such entries */
  575.   h->walk = s ;
  576.   return s;
  577. }
  578. void **
  579. next_hlst_search 
  580.   (hsrch *s)
  581. {
  582.   hlst *h ;
  583.   void ** V ;
  584.   /* sanity check */
  585.   if (s == 0) {
  586.     errno = EINVAL;
  587.     return 0;
  588.   }
  589.   /* get the hash list */
  590.   if ((h = s->hlist) == 0) {
  591.     /* list has been flushed */
  592.     errno = ENOENT ;
  593.     return 0;
  594.   }
  595.   if (s->ntry != 0)
  596.     s->ntry->locked -- ; /* release that node */
  597.   else
  598.     do { /* find next node */
  599.       /* get bucket, check for end-of-list */
  600.       if (++ s->bucket_id >= h->z.mod) {
  601. errno = 0;
  602. return 0 ;
  603.       }
  604.       s->ntry = h->bucket [s->bucket_id] ;
  605.     } while (s->ntry == 0) ;
  606.   
  607.   /* get node contents as return value */
  608.   V = &s->ntry->contents ;
  609.   /* set to next value */
  610.   if ((s->ntry = s->ntry->next) != 0)
  611.     s->ntry->locked ++ ;    /* mark it visited */
  612.   return V;
  613. }
  614. void
  615. close_hlst_search
  616.   (hsrch *s)
  617. {
  618.   hsrch **U, *u;
  619.   /* sanity check */
  620.   if (s == 0) return ;
  621.   /* is this a stale walk descriptor? */
  622.   if (s->hlist == 0) {
  623.     XFREE (s);
  624.     return ;
  625.   }
  626.   /* unlink current walk descriptor */
  627.   U = &s->hlist->walk ;
  628.   while (u = *U, u != 0) {
  629.     if (u == s) {        /* find the link pointer for that record */
  630.       if (u->ntry != 0)  /* release that particular node */
  631. u->ntry->locked -- ;
  632.       *U = u->next ;     /* unlink the walk descriptor */
  633. #     ifdef ENABLE_RHLST
  634.       if (u->clup != 0)  /* clean up peripheral my call back fn */
  635. (*u->clup)(u->clup_state);
  636. #     endif
  637.       XFREE (u);         /* done */
  638.       return ;
  639.     }
  640.     if (u->next == u) { /* XXXXXXXXXXXXX Grrr - should not happen (jh) */
  641.       fprintf (stderr, 
  642.        "%s (%d): [u->next == u] serious bug -- please reportn",
  643.        __FILE__,
  644.        __LINE__);
  645.       u->next = 0 ;
  646.       return;
  647.     }
  648.     U = &u->next; /* set to successor link */
  649.   }
  650. }
  651. int
  652. for_hlst_do
  653.   (hlst     *h,
  654.    int (*fn)(void*,void*,char*,unsigned),
  655.    void *state)
  656. {
  657.   unsigned i ;
  658.   int n ;
  659.   
  660.   /* sanity check */
  661.   if (h == 0 || fn == 0) {
  662.     errno = EINVAL;
  663.     return -1;
  664.   }
  665.   /* looping over the buckets */
  666.   for (i = 0; i < h->z.mod; i ++) {
  667.     
  668.     /* get hash queue */
  669.     hashqueue *p = h->bucket [i] ;
  670.     
  671.     while (p != 0) {
  672.       /* get next, the cb function could delete the current entry */
  673.       hashqueue *q = p->next ;
  674.       if ((n = (*fn) (state, p->contents, p->key, p->keylen)) < 0)
  675. return -1;
  676.       if (n)
  677. return n;
  678.       p = q ;
  679.     }
  680.   }
  681.   
  682.   return 0;
  683. }
  684. /* ------------------------------------------------------------------------- *
  685.  *                 public functions: sorter index                            *
  686.  * ------------------------------------------------------------------------- */
  687. void
  688. sort_hlst
  689.   (hlst *h)
  690. {
  691.   unsigned i;
  692.   hashqueue **ix ;
  693.   int (*sorter_cb)(const void*,const void*);
  694.   if (h == 0) return ;
  695.   /* create an access array with entry pointers */
  696.   if (h->access != 0) {
  697.     /* nothing has changed, yet */
  698.     if (h->access->dirty == 0)
  699.       return ;
  700.     XFREE (h->access) ;
  701.   }
  702.   h->access = XMALLOC 
  703.     (sizeof (sorter) + (h->total_entries - 1) * sizeof (hashqueue*));
  704.   h->access->size = h->total_entries ;
  705.   /* link that array somehow to the list entries */
  706.   ix = h->access->inx ;
  707.   /* looping over the buckets */
  708.   for (i = 0; i < h->z.mod; i ++) {
  709.     
  710.     /* looping over the hash queue */
  711.     hashqueue *p = h->bucket [i] ;
  712.     while (p != 0) {
  713.       /* link into access array */
  714.       * ix ++ = p ;
  715.       /* get next */
  716.       p = p->next ;
  717.     }
  718.   }
  719.   /* check comparison function */
  720.   if (h->sorter_fn != 0) {
  721.     globally_lock () ; /* not thread safe, otherwise */
  722.     sorter_fn   = h->sorter_fn   ;
  723.     sorter_desc = h->sorter_desc ;
  724.     sorter_cb   = (int(*)(const void*, const void*))__comp_custom ;
  725.   } else {
  726.     sorter_cb   = (int(*)(const void*, const void*))__comp ;
  727.   }
  728.   /* sort that access array */
  729.   qsort (h->access->inx, h->total_entries, sizeof (hashqueue*), sorter_cb);
  730.   
  731.   if (h->sorter_fn != 0)
  732.   {
  733.     globally_unlock () ;
  734.   }
  735. }
  736. int
  737. csort_hlst
  738.   (hlst *h,
  739.    int (*fn)(void*,const char*,unsigned,const char*,unsigned),
  740.    void *fn_desc)
  741. {
  742.   if (h == 0) {
  743.     errno = EINVAL;
  744.     return 0;
  745.   }
  746.   h->sorter_fn   = fn;
  747.   h->sorter_desc = fn_desc;
  748.   return 0;
  749. }
  750. void **
  751. inx_hlst
  752.   (hlst    *h,
  753.    unsigned n)
  754. {
  755.   hashqueue *p ;
  756.   if (h == 0) {
  757.     errno = EINVAL;
  758.     return 0;
  759.   }
  760.   if (h->access == 0) {
  761.     errno = ESRCH;
  762.     return 0;
  763.   }
  764.   if (n < h->access->size && (p = h->access->inx [n]) != 0)
  765.     return &p->contents ;
  766.   errno = ENOENT;
  767.   return 0 ;
  768. }
  769. void
  770. unsort_hlst
  771.   (hlst *h)
  772. {
  773.   if (h == 0 || h->access == 0) return ;
  774.   XFREE (h->access) ;
  775.   h->access = 0;
  776. }
  777. /* ------------------------------------------------------------------------- *
  778.  *                 public functions: statistics                              *
  779.  * ------------------------------------------------------------------------- */
  780. int
  781. hlst_buckets
  782.   (hlst *h)
  783. {
  784.   if (h == 0) {
  785.     errno = EINVAL;
  786.     return -1;
  787.   }
  788.   return h->z.mod;
  789. }
  790. typedef
  791. struct _hstatistics {
  792.   struct {
  793.     unsigned busy, idle ;
  794.     struct { unsigned entries, squares ; } sum ;
  795.   } buckets ;
  796.   struct {
  797.     unsigned min, max ;
  798.   } fill ;
  799. } hstatistics ;
  800. static void
  801. __hstatistics_fn
  802.  (hstatistics *state, 
  803.   unsigned      fill)
  804. {
  805.   if (fill == 0) {
  806.     state->buckets.idle ++ ;
  807.     return ;
  808.   }
  809.   state->buckets.busy ++ ;  
  810.   state->buckets.sum.entries += fill ;
  811.   fill *= fill ;
  812.   state->buckets.sum.squares += fill ;
  813.   if (fill > state->fill.max)
  814.     state->fill.max = fill ;
  815.   if (fill < state->fill.min)
  816.     state->fill.min = fill ;
  817. }
  818. void
  819. hlst_statistics
  820.   (hlst *h,
  821.    void (*fn) (void*, unsigned),
  822.    void *state)
  823. {
  824.   unsigned i, sum;
  825.   float var, mu ;
  826.   hstatistics hs;  
  827.   /* sanity check */
  828.   if (h == 0) return ;
  829.   if (fn == 0) {
  830.     fn = (void(*)(void*,unsigned))__hstatistics_fn ;
  831.     hs.fill.min = -1 ;
  832.     state = &hs ;
  833.   }
  834.   
  835.   /* looping over the buckets */
  836.   for (i = 0; i < h->z.mod; i ++) {
  837.     
  838.     /* get hash queue */
  839.     hashqueue *p = h->bucket [i] ;
  840.     unsigned   n = 0;
  841.     
  842.     while (p != 0)
  843.       ++ n, p = p->next ;
  844.     (*fn) (state, n);
  845.   }
  846.   if (fn != (void(*)(void*,unsigned))__hstatistics_fn) 
  847.     return ;
  848.  
  849.   sum = hs.buckets.idle + hs.buckets.busy ;  
  850.   if (hs.buckets.busy <= 1) {
  851.     return ;
  852.   }
  853.   sum = hs.buckets.sum.entries + hs.buckets.idle ;
  854.   fprintf (stderr, "Buckets: %u out of %u are busy, min/max fill: %u/%un",  
  855.    hs.buckets.busy, sum, hs.fill.min, hs.fill.max);
  856.   
  857.   /* busy buckets */
  858.   mu  = hs.buckets.sum.entries / hs.buckets.busy ;
  859.   var = hs.buckets.sum.squares / hs.buckets.busy  - mu * mu ;
  860.   fprintf (stderr, "Busy statistics (mean/stddev): %f/%fn", mu, var);
  861.   /* all buckets */
  862.   mu  = hs.buckets.sum.entries / sum ;
  863.   var = hs.buckets.sum.squares / sum  - mu * mu ;
  864.   fprintf (stderr, "Total statistics (mean/stddev): %f/%fn", mu, var);
  865. }