utf8ify_rfc822_string() is in libcitadel now
[citadel.git] / libcitadel / lib / hash.c
1 /*
2  * Copyright (c) 1987-2011 by the citadel.org team
3  *
4 // This program is open source software.  Use, duplication, or disclosure
5 // is subject to the terms of the GNU General Public License, version 3.
6  */
7
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <limits.h>
12 //dbg
13 #include <stdio.h>
14 #include "libcitadel.h"
15 #include "lookup3.h"
16
17 typedef struct Payload Payload;
18
19 /**
20  * @defgroup HashList Hashlist Key Value list implementation; 
21  * Hashlist is a simple implementation of key value pairs. It doesn't implement collision handling.
22  * the Hashingalgorythm is pluggeable on creation. 
23  * items are added with a functionpointer destructs them; that way complex structures can be added.
24  * if no pointer is given, simply free is used. Use @ref reference_free_handler if you don't want us to free you rmemory.
25  */
26
27 /**
28  * @defgroup HashListData Datastructures used for the internals of HashList
29  * @ingroup HashList
30  */
31
32 /**
33  * @defgroup HashListDebug Hashlist debugging functions
34  * @ingroup HashList
35  */
36
37 /**
38  * @defgroup HashListPrivate Hashlist internal functions
39  * @ingroup HashList
40  */
41
42 /**
43  * @defgroup HashListSort Hashlist sorting functions
44  * @ingroup HashList
45  */
46
47 /**
48  * @defgroup HashListAccess Hashlist functions to access / put / delete items in(to) the list
49  * @ingroup HashList
50  */
51
52 /**
53  * @defgroup HashListAlgorithm functions to condense Key to an integer.
54  * @ingroup HashList
55  */
56
57 /**
58  * @defgroup HashListMset MSet is sort of a derived hashlist, its special for treating Messagesets as Citadel uses them to store access rangesx
59  * @ingroup HashList
60  */
61
62 /**
63  * @ingroup HashListData
64  * @brief Hash Payload storage Structure; filled in linear.
65  */
66 struct Payload {
67         void *Data; /**< the Data belonging to this storage */
68         DeleteHashDataFunc Destructor; /**< if we want to destroy Data, do it with this function. */
69 };
70
71
72 /**
73  * @ingroup HashListData
74  * @brief Hash key element; sorted by key
75  */
76 struct HashKey {
77         long Key;         /**< Numeric Hashkey comperator for hash sorting */
78         long Position;    /**< Pointer to a Payload struct in the Payload Aray */
79         char *HashKey;    /**< the Plaintext Hashkey */
80         long HKLen;       /**< length of the Plaintext Hashkey */
81         Payload *PL;      /**< pointer to our payload for sorting */
82 };
83
84 /**
85  * @ingroup HashListData
86  * @brief Hash structure; holds arrays of Hashkey and Payload. 
87  */
88 struct HashList {
89         Payload **Members;     /**< Our Payload members. This fills up linear */
90         HashKey **LookupTable; /**< Hash Lookup table. Elements point to members, and are sorted by their hashvalue */
91         char **MyKeys;         /**< this keeps the members for a call of GetHashKeys */
92         HashFunc Algorithm;    /**< should we use an alternating algorithm to calc the hash values? */
93         long nMembersUsed;     /**< how many pointers inside of the array are used? */
94         long nLookupTableItems; /**< how many items of the lookup table are used? */
95         long MemberSize;       /**< how big is Members and LookupTable? */
96         long tainted;          /**< if 0, we're hashed, else s.b. else sorted us in his own way. */
97         long uniq;             /**< are the keys going to be uniq? */
98 };
99
100 /**
101  * @ingroup HashListData
102  * @brief Anonymous Hash Iterator Object. used for traversing the whole array from outside 
103  */
104 struct HashPos {
105         long Position;        /**< Position inside of the hash */
106         int StepWidth;        /**< small? big? forward? backward? */
107 };
108
109
110 /**
111  * @ingroup HashListDebug
112  * @brief Iterate over the hash and call PrintEntry. 
113  * @param Hash your Hashlist structure
114  * @param Trans is called so you could for example print 'A:' if the next entries are like that.
115  *        Must be aware to receive NULL in both pointers.
116  * @param PrintEntry print entry one by one
117  * @return the number of items printed
118  */
119 int PrintHash(HashList *Hash, TransitionFunc Trans, PrintHashDataFunc PrintEntry)
120 {
121         int i;
122         void *Previous;
123         void *Next;
124         const char* KeyStr;
125
126         if (Hash == NULL)
127                 return 0;
128
129         for (i=0; i < Hash->nLookupTableItems; i++) {
130                 if (i==0) {
131                         Previous = NULL;
132                 }
133                 else {
134                         if (Hash->LookupTable[i - 1] == NULL)
135                                 Previous = NULL;
136                         else
137                                 Previous = Hash->Members[Hash->LookupTable[i-1]->Position]->Data;
138                 }
139                 if (Hash->LookupTable[i] == NULL) {
140                         KeyStr = "";
141                         Next = NULL;
142                 }
143                 else {
144                         Next = Hash->Members[Hash->LookupTable[i]->Position]->Data;
145                         KeyStr = Hash->LookupTable[i]->HashKey;
146                 }
147
148                 Trans(Previous, Next, i % 2);
149                 PrintEntry(KeyStr, Next, i % 2);
150         }
151         return i;
152 }
153
154 const char *dbg_PrintStrBufPayload(const char *Key, void *Item, int Odd)
155 {
156         return ChrPtr((StrBuf*)Item);
157 }
158
159 /**
160  * @ingroup HashListDebug
161  * @brief verify the contents of a hash list; here for debugging purposes.
162  * @param Hash your Hashlist structure
163  * @param First Functionpointer to allow you to print your payload
164  * @param Second Functionpointer to allow you to print your payload
165  * @return 0
166  */
167 int dbg_PrintHash(HashList *Hash, PrintHashContent First, PrintHashContent Second)
168 {
169 #ifdef DEBUG
170         const char *foo;
171         const char *bar;
172         const char *bla = "";
173         long key;
174 #endif
175         long i;
176
177         if (Hash == NULL)
178                 return 0;
179
180         if (Hash->MyKeys != NULL)
181                 free (Hash->MyKeys);
182
183         Hash->MyKeys = (char**) malloc(sizeof(char*) * Hash->nLookupTableItems);
184 #ifdef DEBUG
185         printf("----------------------------------\n");
186 #endif
187         for (i=0; i < Hash->nLookupTableItems; i++) {
188                 
189                 if (Hash->LookupTable[i] == NULL)
190                 {
191 #ifdef DEBUG
192                         foo = "";
193                         bar = "";
194                         key = 0;
195 #endif
196                 }
197                 else 
198                 {
199 #ifdef DEBUG
200                         key = Hash->LookupTable[i]->Key;
201                         foo = Hash->LookupTable[i]->HashKey;
202 #endif
203                         if (First != NULL)
204 #ifdef DEBUG
205                                 bar =
206 #endif
207                                         First(Hash->Members[Hash->LookupTable[i]->Position]->Data);
208 #ifdef DEBUG
209                         else 
210                                 bar = "";
211 #endif
212
213                         if (Second != NULL)
214 #ifdef DEBUG
215                                 bla = 
216 #endif 
217                                         Second(Hash->Members[Hash->LookupTable[i]->Position]->Data);
218 #ifdef DEBUG
219
220                         else
221                                 bla = "";
222 #endif
223
224                 }
225 #ifdef DEBUG
226                 if ((Hash->Algorithm == lFlathash) || (Hash->Algorithm == Flathash)) {
227                         printf (" ---- Hashkey[%ld][%ld]: %ld '%s' Value: '%s' ; %s\n", i, key, *(long*) foo, foo, bar, bla);
228                 }
229                 else {
230                         printf (" ---- Hashkey[%ld][%ld]: '%s' Value: '%s' ; %s\n", i, key, foo, bar, bla);
231                 }
232 #endif
233         }
234 #ifdef DEBUG
235         printf("----------------------------------\n");
236 #endif
237         return 0;
238 }
239
240
241 int TestValidateHash(HashList *TestHash)
242 {
243         long i;
244
245         if (TestHash->nMembersUsed != TestHash->nLookupTableItems)
246                 return 1;
247
248         if (TestHash->nMembersUsed > TestHash->MemberSize)
249                 return 2;
250
251         for (i=0; i < TestHash->nMembersUsed; i++)
252         {
253
254                 if (TestHash->LookupTable[i]->Position > TestHash->nMembersUsed)
255                         return 3;
256                 
257                 if (TestHash->Members[TestHash->LookupTable[i]->Position] == NULL)
258                         return 4;
259                 if (TestHash->Members[TestHash->LookupTable[i]->Position]->Data == NULL)
260                         return 5;
261         }
262         return 0;
263 }
264
265 /**
266  * @ingroup HashListAccess
267  * @brief instanciate a new hashlist
268  * @return the newly allocated list. 
269  */
270 HashList *NewHash(int Uniq, HashFunc F)
271 {
272         HashList *NewList;
273         NewList = malloc (sizeof(HashList));
274         if (NewList == NULL)
275                 return NULL;
276         memset(NewList, 0, sizeof(HashList));
277
278         NewList->Members = malloc(sizeof(Payload*) * 100);
279         if (NewList->Members == NULL)
280         {
281                 free(NewList);
282                 return NULL;
283         }
284         memset(NewList->Members, 0, sizeof(Payload*) * 100);
285
286         NewList->LookupTable = malloc(sizeof(HashKey*) * 100);
287         if (NewList->LookupTable == NULL)
288         {
289                 free(NewList->Members);
290                 free(NewList);
291                 return NULL;
292         }
293         memset(NewList->LookupTable, 0, sizeof(HashKey*) * 100);
294
295         NewList->MemberSize = 100;
296         NewList->tainted = 0;
297         NewList->uniq = Uniq;
298         NewList->Algorithm = F;
299
300         return NewList;
301 }
302
303 int GetCount(HashList *Hash)
304 {
305         if(Hash==NULL) return 0;
306         return Hash->nLookupTableItems;
307 }
308
309
310 /**
311  * @ingroup HashListPrivate
312  * @brief private destructor for one hash element.
313  * Crashing? go one frame up and do 'print *FreeMe->LookupTable[i]'
314  * @param Data an element to free using the user provided destructor, or just plain free
315  */
316 static void DeleteHashPayload (Payload *Data)
317 {
318         /** do we have a destructor for our payload? */
319         if (Data->Destructor)
320                 Data->Destructor(Data->Data);
321         else
322                 free(Data->Data);
323 }
324
325 /**
326  * @ingroup HashListPrivate
327  * @brief Destructor for nested hashes
328  */
329 void HDeleteHash(void *vHash)
330 {
331         HashList *FreeMe = (HashList*)vHash;
332         DeleteHash(&FreeMe);
333 }
334
335 /**
336  * @ingroup HashListAccess
337  * @brief flush the members of a hashlist 
338  * Crashing? do 'print *FreeMe->LookupTable[i]'
339  * @param Hash Hash to destroy. Is NULL'ed so you are shure its done.
340  */
341 void DeleteHashContent(HashList **Hash)
342 {
343         int i;
344         HashList *FreeMe;
345
346         FreeMe = *Hash;
347         if (FreeMe == NULL)
348                 return;
349         /* even if there are sparse members already deleted... */
350         for (i=0; i < FreeMe->nMembersUsed; i++)
351         {
352                 /** get rid of our payload */
353                 if (FreeMe->Members[i] != NULL)
354                 {
355                         DeleteHashPayload(FreeMe->Members[i]);
356                         free(FreeMe->Members[i]);
357                 }
358                 /** delete our hashing data */
359                 if (FreeMe->LookupTable[i] != NULL)
360                 {
361                         free(FreeMe->LookupTable[i]->HashKey);
362                         free(FreeMe->LookupTable[i]);
363                 }
364         }
365         FreeMe->nMembersUsed = 0;
366         FreeMe->tainted = 0;
367         FreeMe->nLookupTableItems = 0;
368         memset(FreeMe->Members, 0, sizeof(Payload*) * FreeMe->MemberSize);
369         memset(FreeMe->LookupTable, 0, sizeof(HashKey*) * FreeMe->MemberSize);
370
371         /** did s.b. want an array of our keys? free them. */
372         if (FreeMe->MyKeys != NULL)
373                 free(FreeMe->MyKeys);
374 }
375
376 /**
377  * @ingroup HashListAccess
378  * @brief destroy a hashlist and all of its members
379  * Crashing? do 'print *FreeMe->LookupTable[i]'
380  * @param Hash Hash to destroy. Is NULL'ed so you are shure its done.
381  */
382 void DeleteHash(HashList **Hash)
383 {
384         HashList *FreeMe;
385
386         FreeMe = *Hash;
387         if (FreeMe == NULL)
388                 return;
389         DeleteHashContent(Hash);
390         /** now, free our arrays... */
391         free(FreeMe->LookupTable);
392         free(FreeMe->Members);
393
394         /** buye bye cruel world. */    
395         free (FreeMe);
396         *Hash = NULL;
397 }
398
399 /**
400  * @ingroup HashListPrivate
401  * @brief Private function to increase the hash size.
402  * @param Hash the Hasharray to increase
403  */
404 static int IncreaseHashSize(HashList *Hash)
405 {
406         /* Ok, Our space is used up. Double the available space. */
407         Payload **NewPayloadArea;
408         HashKey **NewTable;
409         
410         if (Hash == NULL)
411                 return 0;
412
413         /** If we grew to much, this might be the place to rehash and shrink again.
414         if ((Hash->NMembersUsed > Hash->nLookupTableItems) && 
415             ((Hash->NMembersUsed - Hash->nLookupTableItems) > 
416              (Hash->nLookupTableItems / 10)))
417         {
418
419
420         }
421         */
422
423         NewPayloadArea = (Payload**) malloc(sizeof(Payload*) * Hash->MemberSize * 2);
424         if (NewPayloadArea == NULL)
425                 return 0;
426         NewTable = malloc(sizeof(HashKey*) * Hash->MemberSize * 2);
427         if (NewTable == NULL)
428         {
429                 free(NewPayloadArea);
430                 return 0;
431         }
432
433         /** double our payload area */
434         memset(&NewPayloadArea[Hash->MemberSize], 0, sizeof(Payload*) * Hash->MemberSize);
435         memcpy(NewPayloadArea, Hash->Members, sizeof(Payload*) * Hash->MemberSize);
436         free(Hash->Members);
437         Hash->Members = NewPayloadArea;
438         
439         /** double our hashtable area */
440         memset(&NewTable[Hash->MemberSize], 0, sizeof(HashKey*) * Hash->MemberSize);
441         memcpy(NewTable, Hash->LookupTable, sizeof(HashKey*) * Hash->MemberSize);
442         free(Hash->LookupTable);
443         Hash->LookupTable = NewTable;
444         
445         Hash->MemberSize *= 2;
446         return 1;
447 }
448
449
450 /**
451  * @ingroup HashListPrivate
452  * @brief private function to add a new item to / replace an existing in -  the hashlist
453  * if the hash list is full, its re-alloced with double size.
454  * @param Hash our hashlist to manipulate
455  * @param HashPos where should we insert / replace?
456  * @param HashKeyStr the Hash-String
457  * @param HKLen length of HashKeyStr
458  * @param Data your Payload to add
459  * @param Destructor Functionpointer to free Data. if NULL, default free() is used.
460  */
461 static int InsertHashItem(HashList *Hash, 
462                           long HashPos, 
463                           long HashBinKey, 
464                           const char *HashKeyStr, 
465                           long HKLen, 
466                           void *Data,
467                           DeleteHashDataFunc Destructor)
468 {
469         Payload *NewPayloadItem;
470         HashKey *NewHashKey;
471         char *HashKeyOrgVal;
472
473         if (Hash == NULL)
474                 return 0;
475
476         if ((Hash->nMembersUsed >= Hash->MemberSize) &&
477             (!IncreaseHashSize (Hash)))
478             return 0;
479
480         NewPayloadItem = (Payload*) malloc (sizeof(Payload));
481         if (NewPayloadItem == NULL)
482                 return 0;
483         NewHashKey = (HashKey*) malloc (sizeof(HashKey));
484         if (NewHashKey == NULL)
485         {
486                 free(NewPayloadItem);
487                 return 0;
488         }
489         HashKeyOrgVal = (char *) malloc (HKLen + 1);
490         if (HashKeyOrgVal == NULL)
491         {
492                 free(NewHashKey);
493                 free(NewPayloadItem);
494                 return 0;
495         }
496
497
498         /** Arrange the payload */
499         NewPayloadItem->Data = Data;
500         NewPayloadItem->Destructor = Destructor;
501         /** Arrange the hashkey */
502         NewHashKey->HKLen = HKLen;
503         NewHashKey->HashKey = HashKeyOrgVal;
504         memcpy (NewHashKey->HashKey, HashKeyStr, HKLen + 1);
505         NewHashKey->Key = HashBinKey;
506         NewHashKey->PL = NewPayloadItem;
507         /** our payload is queued at the end... */
508         NewHashKey->Position = Hash->nMembersUsed;
509         /** but if we should be sorted into a specific place... */
510         if ((Hash->nLookupTableItems != 0) && 
511             (HashPos != Hash->nLookupTableItems) ) {
512                 long ItemsAfter;
513
514                 ItemsAfter = Hash->nLookupTableItems - HashPos;
515                 /** make space were we can fill us in */
516                 if (ItemsAfter > 0)
517                 {
518                         memmove(&Hash->LookupTable[HashPos + 1],
519                                 &Hash->LookupTable[HashPos],
520                                 ItemsAfter * sizeof(HashKey*));
521                 } 
522         }
523         
524         Hash->Members[Hash->nMembersUsed] = NewPayloadItem;
525         Hash->LookupTable[HashPos] = NewHashKey;
526         Hash->nMembersUsed++;
527         Hash->nLookupTableItems++;
528         return 1;
529 }
530
531 /**
532  * @ingroup HashListSort
533  * @brief if the user has tainted the hash, but wants to insert / search items by their key
534  *  we need to search linear through the array. You have been warned that this will take more time!
535  * @param Hash Our Hash to manipulate
536  * @param HashBinKey the Hash-Number to lookup. 
537  * @return the position (most closely) matching HashBinKey (-> Caller needs to compare! )
538  */
539 static long FindInTaintedHash(HashList *Hash, long HashBinKey)
540 {
541         long SearchPos;
542
543         if (Hash == NULL)
544                 return 0;
545
546         for (SearchPos = 0; SearchPos < Hash->nLookupTableItems; SearchPos ++) {
547                 if (Hash->LookupTable[SearchPos]->Key == HashBinKey){
548                         return SearchPos;
549                 }
550         }
551         return SearchPos;
552 }
553
554 /**
555  * @ingroup HashListPrivate
556  * @brief Private function to lookup the Item / the closest position to put it in
557  * @param Hash Our Hash to manipulate
558  * @param HashBinKey the Hash-Number to lookup. 
559  * @return the position (most closely) matching HashBinKey (-> Caller needs to compare! )
560  */
561 static long FindInHash(HashList *Hash, long HashBinKey)
562 {
563         long SearchPos;
564         long StepWidth;
565
566         if (Hash == NULL)
567                 return 0;
568
569         if (Hash->tainted)
570                 return FindInTaintedHash(Hash, HashBinKey);
571
572         SearchPos = Hash->nLookupTableItems / 2;
573         StepWidth = SearchPos / 2;
574         while ((SearchPos > 0) && 
575                (SearchPos < Hash->nLookupTableItems)) 
576         {
577                 /** Did we find it? */
578                 if (Hash->LookupTable[SearchPos]->Key == HashBinKey){
579                         return SearchPos;
580                 }
581                 /** are we Aproximating in big steps? */
582                 if (StepWidth > 1){
583                         if (Hash->LookupTable[SearchPos]->Key > HashBinKey)
584                                 SearchPos -= StepWidth;
585                         else
586                                 SearchPos += StepWidth;
587                         StepWidth /= 2;                 
588                 }
589                 else { /** We are right next to our target, within 4 positions */
590                         if (Hash->LookupTable[SearchPos]->Key > HashBinKey) {
591                                 if ((SearchPos > 0) && 
592                                     (Hash->LookupTable[SearchPos - 1]->Key < HashBinKey))
593                                         return SearchPos;
594                                 SearchPos --;
595                         }
596                         else {
597                                 if ((SearchPos + 1 < Hash->nLookupTableItems) && 
598                                     (Hash->LookupTable[SearchPos + 1]->Key > HashBinKey))
599                                         return SearchPos;
600                                 SearchPos ++;
601                         }
602                         StepWidth--;
603                 }
604         }
605         return SearchPos;
606 }
607
608
609 /**
610  * @ingroup HashListAlgorithm
611  * @brief another hashing algorithm; treat it as just a pointer to int.
612  * @param str Our pointer to the int value
613  * @param len the length of the data pointed to; needs to be sizeof int, else we won't use it!
614  * @return the calculated hash value
615  */
616 long Flathash(const char *str, long len)
617 {
618         if (len != sizeof (int))
619         {
620 #ifdef DEBUG
621                 int *crash = NULL;
622                 *crash = 1;
623 #endif
624                 return 0;
625         }
626         else return *(int*)str;
627 }
628
629 /**
630  * @ingroup HashListAlgorithm
631  * @brief another hashing algorithm; treat it as just a pointer to long.
632  * @param str Our pointer to the long value
633  * @param len the length of the data pointed to; needs to be sizeof long, else we won't use it!
634  * @return the calculated hash value
635  */
636 long lFlathash(const char *str, long len)
637 {
638         if (len != sizeof (long))
639         {
640 #ifdef DEBUG
641                 int *crash = NULL;
642                 *crash = 1;
643 #endif
644                 return 0;
645         }
646         else return *(long*)str;
647 }
648
649 /**
650  * @ingroup HashListAlgorithm
651  * @brief another hashing algorithm; accepts exactly 4 characters, convert it to a hash key.
652  * @param str Our pointer to the long value
653  * @param len the length of the data pointed to; needs to be sizeof long, else we won't use it!
654  * @return the calculated hash value
655  */
656 long FourHash(const char *key, long length) 
657 {
658         int i;
659         int ret = 0;
660         const unsigned char *ptr = (const unsigned char*)key;
661
662         for (i = 0; i < 4; i++, ptr ++) 
663                 ret = (ret << 8) | 
664                         ( ((*ptr >= 'a') &&
665                            (*ptr <= 'z'))? 
666                           *ptr - 'a' + 'A': 
667                           *ptr);
668
669         return ret;
670 }
671
672 /**
673  * @ingroup HashListPrivate
674  * @brief private abstract wrapper around the hashing algorithm
675  * @param HKey the hash string
676  * @param HKLen length of HKey
677  * @return the calculated hash value
678  */
679 inline static long CalcHashKey (HashList *Hash, const char *HKey, long HKLen)
680 {
681         if (Hash == NULL)
682                 return 0;
683
684         if (Hash->Algorithm == NULL)
685                 return hashlittle(HKey, HKLen, 9283457);
686         else
687                 return Hash->Algorithm(HKey, HKLen);
688 }
689
690
691 /**
692  * @ingroup HashListAccess
693  * @brief Add a new / Replace an existing item in the Hash
694  * @param Hash the list to manipulate
695  * @param HKey the hash-string to store Data under
696  * @param HKLen Length of HKey
697  * @param Data the payload you want to associate with HKey
698  * @param DeleteIt if not free() should be used to delete Data set to NULL, else DeleteIt is used.
699  */
700 void Put(HashList *Hash, const char *HKey, long HKLen, void *Data, DeleteHashDataFunc DeleteIt)
701 {
702         long HashBinKey;
703         long HashAt;
704
705         if (Hash == NULL)
706                 return;
707
708         /** first, find out were we could fit in... */
709         HashBinKey = CalcHashKey(Hash, HKey, HKLen);
710         HashAt = FindInHash(Hash, HashBinKey);
711
712         if ((HashAt >= Hash->MemberSize) &&
713             (!IncreaseHashSize (Hash)))
714                 return;
715
716         /** oh, we're brand new... */
717         if (Hash->LookupTable[HashAt] == NULL) {
718                 InsertHashItem(Hash, HashAt, HashBinKey, HKey, HKLen, Data, DeleteIt);
719         }/** Insert Before? */
720         else if (Hash->LookupTable[HashAt]->Key > HashBinKey) {
721                 InsertHashItem(Hash, HashAt, HashBinKey, HKey, HKLen, Data, DeleteIt);
722         }/** Insert After? */
723         else if (Hash->LookupTable[HashAt]->Key < HashBinKey) {
724                 InsertHashItem(Hash, HashAt + 1, HashBinKey, HKey, HKLen, Data, DeleteIt);
725         }
726         else { /** Ok, we have a colision. replace it. */
727                 if (Hash->uniq) {
728                         long PayloadPos;
729                         
730                         PayloadPos = Hash->LookupTable[HashAt]->Position;
731                         DeleteHashPayload(Hash->Members[PayloadPos]);
732                         Hash->Members[PayloadPos]->Data = Data;
733                         Hash->Members[PayloadPos]->Destructor = DeleteIt;
734                 }
735                 else {
736                         InsertHashItem(Hash, HashAt + 1, HashBinKey, HKey, HKLen, Data, DeleteIt);
737                 }
738         }
739 }
740
741 /**
742  * @ingroup HashListAccess
743  * @brief Lookup the Data associated with HKey
744  * @param Hash the Hashlist to search in
745  * @param HKey the hashkey to look up
746  * @param HKLen length of HKey
747  * @param Data returns the Data associated with HKey
748  * @return 0 if not found, 1 if.
749  */
750 int GetHash(HashList *Hash, const char *HKey, long HKLen, void **Data)
751 {
752         long HashBinKey;
753         long HashAt;
754
755         if (Hash == NULL)
756                 return 0;
757
758         if (HKLen <= 0) {
759                 *Data = NULL;
760                 return  0;
761         }
762         /** first, find out were we could be... */
763         HashBinKey = CalcHashKey(Hash, HKey, HKLen);
764         HashAt = FindInHash(Hash, HashBinKey);
765         if ((HashAt < 0) || /**< Not found at the lower edge? */
766             (HashAt >= Hash->nLookupTableItems) || /**< Not found at the upper edge? */
767             (Hash->LookupTable[HashAt]->Key != HashBinKey)) { /**< somewhere inbetween but no match? */
768                 *Data = NULL;
769                 return 0;
770         }
771         else { /** GOTCHA! */
772                 long MemberPosition;
773
774                 MemberPosition = Hash->LookupTable[HashAt]->Position;
775                 *Data = Hash->Members[MemberPosition]->Data;
776                 return 1;
777         }
778 }
779
780 /* TODO? */
781 int GetKey(HashList *Hash, char *HKey, long HKLen, void **Payload)
782 {
783         return 0;
784 }
785
786 /**
787  * @ingroup HashListAccess
788  * @brief get the Keys present in this hash, similar to array_keys() in PHP
789  *  Attention: List remains to Hash! don't modify or free it!
790  * @param Hash Your Hashlist to extract the keys from
791  * @param List returns the list of hashkeys stored in Hash
792  */
793 int GetHashKeys(HashList *Hash, char ***List)
794 {
795         long i;
796
797         *List = NULL;
798         if (Hash == NULL)
799                 return 0;
800         if (Hash->MyKeys != NULL)
801                 free (Hash->MyKeys);
802
803         Hash->MyKeys = (char**) malloc(sizeof(char*) * Hash->nLookupTableItems);
804         if (Hash->MyKeys == NULL)
805                 return 0;
806
807         for (i=0; i < Hash->nLookupTableItems; i++)
808         {
809                 Hash->MyKeys[i] = Hash->LookupTable[i]->HashKey;
810         }
811         *List = (char**)Hash->MyKeys;
812         return Hash->nLookupTableItems;
813 }
814
815 /**
816  * @ingroup HashListAccess
817  * @brief creates a hash-linear iterator object
818  * @param Hash the list we reference
819  * @param StepWidth in which step width should we iterate?
820  *  If negative, the last position matching the 
821  *  step-raster is provided.
822  * @return the hash iterator
823  */
824 HashPos *GetNewHashPos(const HashList *Hash, int StepWidth)
825 {
826         HashPos *Ret;
827         
828         Ret = (HashPos*)malloc(sizeof(HashPos));
829         if (Ret == NULL)
830                 return NULL;
831
832         if (StepWidth != 0)
833                 Ret->StepWidth = StepWidth;
834         else
835                 Ret->StepWidth = 1;
836         if (Ret->StepWidth <  0) {
837                 Ret->Position = Hash->nLookupTableItems - 1;
838         }
839         else {
840                 Ret->Position = 0;
841         }
842         return Ret;
843 }
844
845 /**
846  * @ingroup HashListAccess
847  * @brief resets a hash-linear iterator object
848  * @param Hash the list we reference
849  * @param StepWidth in which step width should we iterate?
850  * @param it the iterator object to manipulate
851  *  If negative, the last position matching the 
852  *  step-raster is provided.
853  * @return the hash iterator
854  */
855 void RewindHashPos(const HashList *Hash, HashPos *it, int StepWidth)
856 {
857         if (StepWidth != 0)
858                 it->StepWidth = StepWidth;
859         else
860                 it->StepWidth = 1;
861         if (it->StepWidth <  0) {
862                 it->Position = Hash->nLookupTableItems - 1;
863         }
864         else {
865                 it->Position = 0;
866         }
867 }
868
869 /**
870  * @ingroup HashListAccess
871  * @brief Set iterator object to point to key. If not found, don't change iterator
872  * @param Hash the list we reference
873  * @param HKey key to search for
874  * @param HKLen length of key
875  * @param At HashPos to update
876  * @return 0 if not found
877  */
878 int GetHashPosFromKey(HashList *Hash, const char *HKey, long HKLen, HashPos *At)
879 {
880         long HashBinKey;
881         long HashAt;
882
883         if (Hash == NULL)
884                 return 0;
885
886         if (HKLen <= 0) {
887                 return  0;
888         }
889         /** first, find out were we could be... */
890         HashBinKey = CalcHashKey(Hash, HKey, HKLen);
891         HashAt = FindInHash(Hash, HashBinKey);
892         if ((HashAt < 0) || /**< Not found at the lower edge? */
893             (HashAt >= Hash->nLookupTableItems) || /**< Not found at the upper edge? */
894             (Hash->LookupTable[HashAt]->Key != HashBinKey)) { /**< somewhere inbetween but no match? */
895                 return 0;
896         }
897         /** GOTCHA! */
898         At->Position = HashAt;
899         return 1;
900 }
901
902 /**
903  * @ingroup HashListAccess
904  * @brief Delete from the Hash the entry at Position
905  * @param Hash the list we reference
906  * @param At the position within the Hash
907  * @return 0 if not found
908  */
909 int DeleteEntryFromHash(HashList *Hash, HashPos *At)
910 {
911         Payload *FreeMe;
912         if (Hash == NULL)
913                 return 0;
914
915         /* if lockable, lock here */
916         if ((Hash == NULL) || 
917             (At->Position >= Hash->nLookupTableItems) || 
918             (At->Position < 0) ||
919             (At->Position > Hash->nLookupTableItems))
920         {
921                 /* unlock... */
922                 return 0;
923         }
924
925         FreeMe = Hash->Members[Hash->LookupTable[At->Position]->Position];
926         Hash->Members[Hash->LookupTable[At->Position]->Position] = NULL;
927
928
929         /** delete our hashing data */
930         if (Hash->LookupTable[At->Position] != NULL)
931         {
932                 free(Hash->LookupTable[At->Position]->HashKey);
933                 free(Hash->LookupTable[At->Position]);
934                 if (At->Position < Hash->nLookupTableItems)
935                 {
936                         memmove(&Hash->LookupTable[At->Position],
937                                 &Hash->LookupTable[At->Position + 1],
938                                 (Hash->nLookupTableItems - At->Position - 1) * 
939                                 sizeof(HashKey*));
940
941                         Hash->LookupTable[Hash->nLookupTableItems - 1] = NULL;
942                 }
943                 else 
944                         Hash->LookupTable[At->Position] = NULL;
945                 Hash->nLookupTableItems--;
946         }
947         /* unlock... */
948
949
950         /** get rid of our payload */
951         if (FreeMe != NULL)
952         {
953                 DeleteHashPayload(FreeMe);
954                 free(FreeMe);
955         }
956         return 1;
957 }
958
959 /**
960  * @ingroup HashListAccess
961  * @brief retrieve the counter from the itteratoor
962  * @param Hash which 
963  * @param At the Iterator to analyze
964  * @return the n'th hashposition we point at
965  */
966 int GetHashPosCounter(HashList *Hash, HashPos *At)
967 {
968         if ((Hash == NULL) || 
969             (At->Position >= Hash->nLookupTableItems) || 
970             (At->Position < 0) ||
971             (At->Position > Hash->nLookupTableItems))
972                 return 0;
973         return At->Position;
974 }
975
976 /**
977  * @ingroup HashListAccess
978  * @brief frees a linear hash iterator
979  */
980 void DeleteHashPos(HashPos **DelMe)
981 {
982         if (*DelMe != NULL)
983         {
984                 free(*DelMe);
985                 *DelMe = NULL;
986         }
987 }
988
989
990 /**
991  * @ingroup HashListAccess
992  * @brief Get the data located where HashPos Iterator points at, and Move HashPos one forward
993  * @param Hash your Hashlist to follow
994  * @param At the position to retrieve the Item from and move forward afterwards
995  * @param HKLen returns Length of Hashkey Returned
996  * @param HashKey returns the Hashkey corrosponding to HashPos
997  * @param Data returns the Data found at HashPos
998  * @return whether the item was found or not.
999  */
1000 int GetNextHashPos(const HashList *Hash, HashPos *At, long *HKLen, const char **HashKey, void **Data)
1001 {
1002         long PayloadPos;
1003
1004         if ((Hash == NULL) || 
1005             (At->Position >= Hash->nLookupTableItems) || 
1006             (At->Position < 0) ||
1007             (At->Position > Hash->nLookupTableItems))
1008                 return 0;
1009         *HKLen = Hash->LookupTable[At->Position]->HKLen;
1010         *HashKey = Hash->LookupTable[At->Position]->HashKey;
1011         PayloadPos = Hash->LookupTable[At->Position]->Position;
1012         *Data = Hash->Members[PayloadPos]->Data;
1013
1014         /* Position is NULL-Based, while Stepwidth is not... */
1015         if ((At->Position % abs(At->StepWidth)) == 0)
1016                 At->Position += At->StepWidth;
1017         else 
1018                 At->Position += ((At->Position) % abs(At->StepWidth)) * 
1019                         (At->StepWidth / abs(At->StepWidth));
1020         return 1;
1021 }
1022
1023 /**
1024  * @ingroup HashListAccess
1025  * @brief Get the data located where HashPos Iterator points at
1026  * @param Hash your Hashlist to follow
1027  * @param At the position retrieve the data from
1028  * @param HKLen returns Length of Hashkey Returned
1029  * @param HashKey returns the Hashkey corrosponding to HashPos
1030  * @param Data returns the Data found at HashPos
1031  * @return whether the item was found or not.
1032  */
1033 int GetHashPos(HashList *Hash, HashPos *At, long *HKLen, const char **HashKey, void **Data)
1034 {
1035         long PayloadPos;
1036
1037         if ((Hash == NULL) || 
1038             (At->Position >= Hash->nLookupTableItems) || 
1039             (At->Position < 0) ||
1040             (At->Position > Hash->nLookupTableItems))
1041                 return 0;
1042         *HKLen = Hash->LookupTable[At->Position]->HKLen;
1043         *HashKey = Hash->LookupTable[At->Position]->HashKey;
1044         PayloadPos = Hash->LookupTable[At->Position]->Position;
1045         *Data = Hash->Members[PayloadPos]->Data;
1046
1047         return 1;
1048 }
1049
1050 /**
1051  * @ingroup HashListAccess
1052  * @brief Move HashPos one forward
1053  * @param Hash your Hashlist to follow
1054  * @param At the position to move forward
1055  * @return whether there is a next item or not.
1056  */
1057 int NextHashPos(HashList *Hash, HashPos *At)
1058 {
1059         if ((Hash == NULL) || 
1060             (At->Position >= Hash->nLookupTableItems) || 
1061             (At->Position < 0) ||
1062             (At->Position > Hash->nLookupTableItems))
1063                 return 0;
1064
1065         /* Position is NULL-Based, while Stepwidth is not... */
1066         if ((At->Position % abs(At->StepWidth)) == 0)
1067                 At->Position += At->StepWidth;
1068         else 
1069                 At->Position += ((At->Position) % abs(At->StepWidth)) * 
1070                         (At->StepWidth / abs(At->StepWidth));
1071         return !((At->Position >= Hash->nLookupTableItems) || 
1072                  (At->Position < 0) ||
1073                  (At->Position > Hash->nLookupTableItems));
1074 }
1075
1076 /**
1077  * @ingroup HashListAccess
1078  * @brief Get the data located where At points to
1079  * note: you should prefer iterator operations instead of using me.
1080  * @param Hash your Hashlist peek from
1081  * @param At get the item in the position At.
1082  * @param HKLen returns Length of Hashkey Returned
1083  * @param HashKey returns the Hashkey corrosponding to HashPos
1084  * @param Data returns the Data found at HashPos
1085  * @return whether the item was found or not.
1086  */
1087 int GetHashAt(HashList *Hash,long At, long *HKLen, const char **HashKey, void **Data)
1088 {
1089         long PayloadPos;
1090
1091         if ((Hash == NULL) || 
1092             (At < 0) || 
1093             (At >= Hash->nLookupTableItems))
1094                 return 0;
1095         *HKLen = Hash->LookupTable[At]->HKLen;
1096         *HashKey = Hash->LookupTable[At]->HashKey;
1097         PayloadPos = Hash->LookupTable[At]->Position;
1098         *Data = Hash->Members[PayloadPos]->Data;
1099         return 1;
1100 }
1101
1102 /**
1103  * @ingroup HashListSort
1104  * @brief Get the data located where At points to
1105  * note: you should prefer iterator operations instead of using me.
1106  * @param Hash your Hashlist peek from
1107  * @param HKLen returns Length of Hashkey Returned
1108  * @param HashKey returns the Hashkey corrosponding to HashPos
1109  * @param Data returns the Data found at HashPos
1110  * @return whether the item was found or not.
1111  */
1112 /*
1113 long GetHashIDAt(HashList *Hash,long At)
1114 {
1115         if ((Hash == NULL) || 
1116             (At < 0) || 
1117             (At > Hash->nLookupTableItems))
1118                 return 0;
1119
1120         return Hash->LookupTable[At]->Key;
1121 }
1122 */
1123
1124
1125 /**
1126  * @ingroup HashListSort
1127  * @brief sorting function for sorting the Hash alphabeticaly by their strings
1128  * @param Key1 first item
1129  * @param Key2 second item
1130  */
1131 static int SortByKeys(const void *Key1, const void* Key2)
1132 {
1133         HashKey *HKey1, *HKey2;
1134         HKey1 = *(HashKey**) Key1;
1135         HKey2 = *(HashKey**) Key2;
1136
1137         return strcasecmp(HKey1->HashKey, HKey2->HashKey);
1138 }
1139
1140 /**
1141  * @ingroup HashListSort
1142  * @brief sorting function for sorting the Hash alphabeticaly reverse by their strings
1143  * @param Key1 first item
1144  * @param Key2 second item
1145  */
1146 static int SortByKeysRev(const void *Key1, const void* Key2)
1147 {
1148         HashKey *HKey1, *HKey2;
1149         HKey1 = *(HashKey**) Key1;
1150         HKey2 = *(HashKey**) Key2;
1151
1152         return strcasecmp(HKey2->HashKey, HKey1->HashKey);
1153 }
1154
1155 /**
1156  * @ingroup HashListSort
1157  * @brief sorting function to regain hash-sequence and revert tainted status
1158  * @param Key1 first item
1159  * @param Key2 second item
1160  */
1161 static int SortByHashKeys(const void *Key1, const void* Key2)
1162 {
1163         HashKey *HKey1, *HKey2;
1164         HKey1 = *(HashKey**) Key1;
1165         HKey2 = *(HashKey**) Key2;
1166
1167         return HKey1->Key > HKey2->Key;
1168 }
1169
1170
1171 /**
1172  * @ingroup HashListSort
1173  * @brief sort the hash alphabeticaly by their keys.
1174  * Caution: This taints the hashlist, so accessing it later 
1175  * will be significantly slower! You can un-taint it by SortByHashKeyStr
1176  * @param Hash the list to sort
1177  * @param Order 0/1 Forward/Backward
1178  */
1179 void SortByHashKey(HashList *Hash, int Order)
1180 {
1181         if (Hash->nLookupTableItems < 2)
1182                 return;
1183         qsort(Hash->LookupTable, Hash->nLookupTableItems, sizeof(HashKey*), 
1184               (Order)?SortByKeys:SortByKeysRev);
1185         Hash->tainted = 1;
1186 }
1187
1188 /**
1189  * @ingroup HashListSort
1190  * @brief sort the hash by their keys (so it regains untainted state).
1191  * this will result in the sequence the hashing allgorithm produces it by default.
1192  * @param Hash the list to sort
1193  */
1194 void SortByHashKeyStr(HashList *Hash)
1195 {
1196         Hash->tainted = 0;
1197         if (Hash->nLookupTableItems < 2)
1198                 return;
1199         qsort(Hash->LookupTable, Hash->nLookupTableItems, sizeof(HashKey*), SortByHashKeys);
1200 }
1201
1202
1203 /**
1204  * @ingroup HashListSort
1205  * @brief gives user sort routines access to the hash payload
1206  * @param HashVoid to retrieve Data to
1207  * @return Data belonging to HashVoid
1208  */
1209 const void *GetSearchPayload(const void *HashVoid)
1210 {
1211         return (*(HashKey**)HashVoid)->PL->Data;
1212 }
1213
1214 /**
1215  * @ingroup HashListSort
1216  * @brief sort the hash by your sort function. see the following sample.
1217  * this will result in the sequence the hashing allgorithm produces it by default.
1218  * @param Hash the list to sort
1219  * @param SortBy Sortfunction; see below how to implement this
1220  */
1221 void SortByPayload(HashList *Hash, CompareFunc SortBy)
1222 {
1223         if (Hash->nLookupTableItems < 2)
1224                 return;
1225         qsort(Hash->LookupTable, Hash->nLookupTableItems, sizeof(HashKey*), SortBy);
1226         Hash->tainted = 1;
1227 }
1228
1229
1230
1231
1232 /**
1233  * given you've put char * into your hash as a payload, a sort function might
1234  * look like this:
1235  * int SortByChar(const void* First, const void* Second)
1236  * {
1237  *      char *a, *b;
1238  *      a = (char*) GetSearchPayload(First);
1239  *      b = (char*) GetSearchPayload(Second);
1240  *      return strcmp (a, b);
1241  * }
1242  */
1243
1244
1245 /**
1246  * @ingroup HashListAccess
1247  * @brief Generic function to free a reference.  
1248  * since a reference actualy isn't needed to be freed, do nothing.
1249  */
1250 void reference_free_handler(void *ptr) 
1251 {
1252         return;
1253 }
1254
1255
1256 /**
1257  * @ingroup HashListAlgorithm
1258  * This exposes the hashlittle() function to consumers.
1259  */
1260 int HashLittle(const void *key, size_t length) {
1261         return (int)hashlittle(key, length, 1);
1262 }
1263
1264
1265 /**
1266  * @ingroup HashListMset
1267  * @brief parses an MSet string into a list for later use
1268  * @param MSetList List to be read from MSetStr
1269  * @param MSetStr String containing the list
1270  */
1271 int ParseMSet(MSet **MSetList, StrBuf *MSetStr)
1272 {
1273         const char *POS = NULL, *SetPOS = NULL;
1274         StrBuf *OneSet;
1275         HashList *ThisMSet;
1276         long StartSet, EndSet;
1277         long *pEndSet;
1278         
1279         *MSetList = NULL;
1280         if ((MSetStr == NULL) || (StrLength(MSetStr) == 0))
1281             return 0;
1282             
1283         OneSet = NewStrBufPlain(NULL, StrLength(MSetStr));
1284         if (OneSet == NULL)
1285                 return 0;
1286
1287         ThisMSet = NewHash(0, lFlathash);
1288         if (ThisMSet == NULL)
1289         {
1290                 FreeStrBuf(&OneSet);
1291                 return 0;
1292         }
1293
1294         *MSetList = (MSet*) ThisMSet;
1295
1296         /* an MSet is a coma separated value list. */
1297         StrBufExtract_NextToken(OneSet, MSetStr, &POS, ',');
1298         do {
1299                 SetPOS = NULL;
1300
1301                 /* One set may consist of two Numbers: Start + optional End */
1302                 StartSet = StrBufExtractNext_long(OneSet, &SetPOS, ':');
1303                 EndSet = 0; /* no range is our default. */
1304                 /* do we have an end (aka range?) */
1305                 if ((SetPOS != NULL) && (SetPOS != StrBufNOTNULL))
1306                 {
1307                         if (*(SetPOS) == '*')
1308                                 EndSet = LONG_MAX; /* ranges with '*' go until infinity */
1309                         else 
1310                                 /* in other cases, get the EndPoint */
1311                                 EndSet = StrBufExtractNext_long(OneSet, &SetPOS, ':');
1312                 }
1313
1314                 pEndSet = (long*) malloc (sizeof(long));
1315                 if (pEndSet == NULL)
1316                 {
1317                         FreeStrBuf(&OneSet);
1318                         DeleteHash(&ThisMSet);
1319                         return 0;
1320                 }
1321                 *pEndSet = EndSet;
1322
1323                 Put(ThisMSet, LKEY(StartSet), pEndSet, NULL);
1324                 /* if we don't have another, we're done. */
1325                 if (POS == StrBufNOTNULL)
1326                         break;
1327                 StrBufExtract_NextToken(OneSet, MSetStr, &POS, ',');
1328         } while (1);
1329         FreeStrBuf(&OneSet);
1330
1331         return 1;
1332 }
1333
1334 /**
1335  * @ingroup HashListMset
1336  * @brief checks whether a message is inside a mset
1337  * @param MSetList List to search for MsgNo
1338  * @param MsgNo number to search in mset
1339  */
1340 int IsInMSetList(MSet *MSetList, long MsgNo)
1341 {
1342         /* basicaly we are a ... */
1343         long MemberPosition;
1344         HashList *Hash = (HashList*) MSetList;
1345         long HashAt;
1346         long EndAt;
1347         long StartAt;
1348
1349         if (Hash == NULL)
1350                 return 0;
1351         if (Hash->MemberSize == 0)
1352                 return 0;
1353         /** first, find out were we could fit in... */
1354         HashAt = FindInHash(Hash, MsgNo);
1355         
1356         /* we're below the first entry, so not found. */
1357         if (HashAt < 0)
1358                 return 0;
1359         /* upper edge? move to last item */
1360         if (HashAt >= Hash->nMembersUsed)
1361                 HashAt = Hash->nMembersUsed - 1;
1362         /* Match? then we got it. */
1363         else if (Hash->LookupTable[HashAt]->Key == MsgNo)
1364                 return 1;
1365         /* One above possible range start? we need to move to the lower one. */ 
1366         else if ((HashAt > 0) && 
1367                  (Hash->LookupTable[HashAt]->Key > MsgNo))
1368                 HashAt -=1;
1369
1370         /* Fetch the actual data */
1371         StartAt = Hash->LookupTable[HashAt]->Key;
1372         MemberPosition = Hash->LookupTable[HashAt]->Position;
1373         EndAt = *(long*) Hash->Members[MemberPosition]->Data;
1374         if ((MsgNo >= StartAt) && (EndAt == LONG_MAX))
1375                 return 1;
1376         /* no range? */
1377         if (EndAt == 0)
1378                 return 0;
1379         /* inside of range? */
1380         if ((StartAt <= MsgNo) && (EndAt >= MsgNo))
1381                 return 1;
1382         return 0;
1383 }
1384
1385
1386 /**
1387  * @ingroup HashListMset
1388  * @brief frees a mset [redirects to @ref DeleteHash
1389  * @param FreeMe to be free'd
1390  */
1391 void DeleteMSet(MSet **FreeMe)
1392 {
1393         DeleteHash((HashList**) FreeMe);
1394 }