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