6 #include "libcitadel.h"
9 typedef struct Payload Payload
;
13 * \brief Hash Payload storage Structure; filled in linear.
15 void *Data
; /**< the Data belonging to this storage */
16 DeleteHashDataFunc Destructor
; /**< if we want to destroy Data, do it with this function. */
21 * \brief Hash key element; sorted by key
23 long Key
; /**< Numeric Hashkey comperator for hash sorting */
24 long Position
; /**< Pointer to a Payload struct in the Payload Aray */
25 char *HashKey
; /**< the Plaintext Hashkey */
26 long HKLen
; /**< length of the Plaintext Hashkey */
27 Payload
*PL
; /**< pointer to our payload for sorting */
32 * \brief Hash structure; holds arrays of Hashkey and Payload.
34 Payload
**Members
; /**< Our Payload members. This fills up linear */
35 HashKey
**LookupTable
; /**< Hash Lookup table. Elements point to members, and are sorted by their hashvalue */
36 char **MyKeys
; /**< this keeps the members for a call of GetHashKeys */
37 HashFunc Algorithm
; /**< should we use an alternating algorithm to calc the hash values? */
38 long nMembersUsed
; /**< how many pointers inside of the array are used? */
39 long MemberSize
; /**< how big is Members and LookupTable? */
40 long tainted
; /**< if 0, we're hashed, else s.b. else sorted us in his own way. */
41 long uniq
; /**< are the keys going to be uniq? */
46 * \brief Anonymous Hash Iterator Object. used for traversing the whole array from outside
54 * \brief Iterate over the hash and call PrintEntry.
55 * \param Hash your Hashlist structure
56 * \param Trans is called so you could for example print 'A:' if the next entries are like that.
57 * Must be aware to receive NULL in both pointers.
58 * \param PrintEntry print entry one by one
59 * \returns the number of items printed
61 int PrintHash(HashList
*Hash
, TransitionFunc Trans
, PrintHashDataFunc PrintEntry
)
71 for (i
=0; i
< Hash
->nMembersUsed
; i
++) {
76 if (Hash
->LookupTable
[i
- 1] == NULL
)
79 Previous
= Hash
->Members
[Hash
->LookupTable
[i
-1]->Position
]->Data
;
81 if (Hash
->LookupTable
[i
] == NULL
) {
86 Next
= Hash
->Members
[Hash
->LookupTable
[i
]->Position
]->Data
;
87 KeyStr
= Hash
->LookupTable
[i
]->HashKey
;
90 Trans(Previous
, Next
, i
% 2);
91 PrintEntry(KeyStr
, Next
, i
% 2);
98 * \brief verify the contents of a hash list; here for debugging purposes.
99 * \param Hash your Hashlist structure
100 * \param First Functionpointer to allow you to print your payload
101 * \param Second Functionpointer to allow you to print your payload
104 int dbg_PrintHash(HashList
*Hash
, PrintHashContent First
, PrintHashContent Second
)
108 const char *bla
= "";
115 if (Hash
->MyKeys
!= NULL
)
118 Hash
->MyKeys
= (char**) malloc(sizeof(char*) * Hash
->nMembersUsed
);
120 printf("----------------------------------\n");
122 for (i
=0; i
< Hash
->nMembersUsed
; i
++) {
124 if (Hash
->LookupTable
[i
] == NULL
)
132 key
= Hash
->LookupTable
[i
]->Key
;
133 foo
= Hash
->LookupTable
[i
]->HashKey
;
135 bar
= First(Hash
->Members
[Hash
->LookupTable
[i
]->Position
]->Data
);
139 bla
= Second(Hash
->Members
[Hash
->LookupTable
[i
]->Position
]->Data
);
144 printf (" ---- Hashkey[%ld][%ld]: '%s' Value: '%s' ; %s\n", i
, key
, foo
, bar
, bla
);
148 printf("----------------------------------\n");
155 * \brief instanciate a new hashlist
156 * \returns the newly allocated list.
158 HashList
*NewHash(int Uniq
, HashFunc F
)
161 NewList
= malloc (sizeof(HashList
));
162 memset(NewList
, 0, sizeof(HashList
));
164 NewList
->Members
= malloc(sizeof(Payload
*) * 100);
165 memset(NewList
->Members
, 0, sizeof(Payload
*) * 100);
167 NewList
->LookupTable
= malloc(sizeof(HashKey
*) * 100);
168 memset(NewList
->LookupTable
, 0, sizeof(HashKey
*) * 100);
170 NewList
->MemberSize
= 100;
171 NewList
->tainted
= 0;
172 NewList
->uniq
= Uniq
;
173 NewList
->Algorithm
= F
;
178 int GetCount(HashList
*Hash
)
180 if(Hash
==NULL
) return 0;
181 return Hash
->nMembersUsed
;
186 * \brief private destructor for one hash element.
187 * Crashing? go one frame up and do 'print *FreeMe->LookupTable[i]'
188 * \param Data an element to free using the user provided destructor, or just plain free
190 static void DeleteHashPayload (Payload
*Data
)
192 /** do we have a destructor for our payload? */
193 if (Data
->Destructor
)
194 Data
->Destructor(Data
->Data
);
200 * \brief Destructor for nested hashes
202 void HDeleteHash(void *vHash
)
204 HashList
*FreeMe
= (HashList
*)vHash
;
209 * \brief destroy a hashlist and all of its members
210 * Crashing? do 'print *FreeMe->LookupTable[i]'
211 * \param Hash Hash to destroy. Is NULL'ed so you are shure its done.
213 void DeleteHash(HashList
**Hash
)
221 for (i
=0; i
< FreeMe
->nMembersUsed
; i
++)
223 /** get rid of our payload */
224 if (FreeMe
->Members
[i
] != NULL
)
226 DeleteHashPayload(FreeMe
->Members
[i
]);
227 free(FreeMe
->Members
[i
]);
229 /** delete our hashing data */
230 if (FreeMe
->LookupTable
[i
] != NULL
)
232 free(FreeMe
->LookupTable
[i
]->HashKey
);
233 free(FreeMe
->LookupTable
[i
]);
236 /** now, free our arrays... */
237 free(FreeMe
->LookupTable
);
238 free(FreeMe
->Members
);
239 /** did s.b. want an array of our keys? free them. */
240 if (FreeMe
->MyKeys
!= NULL
)
241 free(FreeMe
->MyKeys
);
242 /** buye bye cruel world. */
248 * \brief Private function to increase the hash size.
249 * \param Hash the Hasharray to increase
251 static void IncreaseHashSize(HashList
*Hash
)
253 /* Ok, Our space is used up. Double the available space. */
254 Payload
**NewPayloadArea
;
260 /** double our payload area */
261 NewPayloadArea
= (Payload
**) malloc(sizeof(Payload
*) * Hash
->MemberSize
* 2);
262 memset(&NewPayloadArea
[Hash
->MemberSize
], 0, sizeof(Payload
*) * Hash
->MemberSize
);
263 memcpy(NewPayloadArea
, Hash
->Members
, sizeof(Payload
*) * Hash
->MemberSize
);
265 Hash
->Members
= NewPayloadArea
;
267 /** double our hashtable area */
268 NewTable
= malloc(sizeof(HashKey
*) * Hash
->MemberSize
* 2);
269 memset(&NewTable
[Hash
->MemberSize
], 0, sizeof(HashKey
*) * Hash
->MemberSize
);
270 memcpy(NewTable
, Hash
->LookupTable
, sizeof(HashKey
*) * Hash
->MemberSize
);
271 free(Hash
->LookupTable
);
272 Hash
->LookupTable
= NewTable
;
274 Hash
->MemberSize
*= 2;
279 * \brief private function to add a new item to / replace an existing in - the hashlist
280 * if the hash list is full, its re-alloced with double size.
281 * \parame Hash our hashlist to manipulate
282 * \param HashPos where should we insert / replace?
283 * \param HashKeyStr the Hash-String
284 * \param HKLen length of HashKeyStr
285 * \param Data your Payload to add
286 * \param Destructor Functionpointer to free Data. if NULL, default free() is used.
288 static void InsertHashItem(HashList
*Hash
,
291 const char *HashKeyStr
,
294 DeleteHashDataFunc Destructor
)
296 Payload
*NewPayloadItem
;
302 if (Hash
->nMembersUsed
>= Hash
->MemberSize
)
303 IncreaseHashSize (Hash
);
305 /** Arrange the payload */
306 NewPayloadItem
= (Payload
*) malloc (sizeof(Payload
));
307 NewPayloadItem
->Data
= Data
;
308 NewPayloadItem
->Destructor
= Destructor
;
309 /** Arrange the hashkey */
310 NewHashKey
= (HashKey
*) malloc (sizeof(HashKey
));
311 NewHashKey
->HashKey
= (char *) malloc (HKLen
+ 1);
312 NewHashKey
->HKLen
= HKLen
;
313 memcpy (NewHashKey
->HashKey
, HashKeyStr
, HKLen
+ 1);
314 NewHashKey
->Key
= HashBinKey
;
315 NewHashKey
->PL
= NewPayloadItem
;
316 /** our payload is queued at the end... */
317 NewHashKey
->Position
= Hash
->nMembersUsed
;
318 /** but if we should be sorted into a specific place... */
319 if ((Hash
->nMembersUsed
!= 0) &&
320 (HashPos
!= Hash
->nMembersUsed
) ) {
323 ItemsAfter
= Hash
->nMembersUsed
- HashPos
;
324 /** make space were we can fill us in */
327 memmove(&Hash
->LookupTable
[HashPos
+ 1],
328 &Hash
->LookupTable
[HashPos
],
329 ItemsAfter
* sizeof(HashKey
*));
333 Hash
->Members
[Hash
->nMembersUsed
] = NewPayloadItem
;
334 Hash
->LookupTable
[HashPos
] = NewHashKey
;
335 Hash
->nMembersUsed
++;
339 * \brief if the user has tainted the hash, but wants to insert / search items by their key
340 * we need to search linear through the array. You have been warned that this will take more time!
341 * \param Hash Our Hash to manipulate
342 * \param HashBinKey the Hash-Number to lookup.
343 * \returns the position (most closely) matching HashBinKey (-> Caller needs to compare! )
345 static long FindInTaintedHash(HashList
*Hash
, long HashBinKey
)
352 for (SearchPos
= 0; SearchPos
< Hash
->nMembersUsed
; SearchPos
++) {
353 if (Hash
->LookupTable
[SearchPos
]->Key
== HashBinKey
){
361 * \brief Private function to lookup the Item / the closest position to put it in
362 * \param Hash Our Hash to manipulate
363 * \param HashBinKey the Hash-Number to lookup.
364 * \returns the position (most closely) matching HashBinKey (-> Caller needs to compare! )
366 static long FindInHash(HashList
*Hash
, long HashBinKey
)
375 return FindInTaintedHash(Hash
, HashBinKey
);
377 SearchPos
= Hash
->nMembersUsed
/ 2;
378 StepWidth
= SearchPos
/ 2;
379 while ((SearchPos
> 0) &&
380 (SearchPos
< Hash
->nMembersUsed
))
382 /** Did we find it? */
383 if (Hash
->LookupTable
[SearchPos
]->Key
== HashBinKey
){
386 /** are we Aproximating in big steps? */
388 if (Hash
->LookupTable
[SearchPos
]->Key
> HashBinKey
)
389 SearchPos
-= StepWidth
;
391 SearchPos
+= StepWidth
;
394 else { /** We are right next to our target, within 4 positions */
395 if (Hash
->LookupTable
[SearchPos
]->Key
> HashBinKey
) {
396 if ((SearchPos
> 0) &&
397 (Hash
->LookupTable
[SearchPos
- 1]->Key
< HashBinKey
))
402 if ((SearchPos
+ 1 < Hash
->nMembersUsed
) &&
403 (Hash
->LookupTable
[SearchPos
+ 1]->Key
> HashBinKey
))
415 * \brief another hashing algorithm; treat it as just a pointer to long.
416 * \param str Our pointer to the long value
417 * \param len the length of the data pointed to; needs to be sizeof int, else we won't use it!
418 * \returns the calculated hash value
420 int Flathash(const char *str
, long len
)
422 if (len
!= sizeof (int))
424 else return *(int*)str
;
428 * \brief private abstract wrapper around the hashing algorithm
429 * \param HKey the hash string
430 * \param HKLen length of HKey
431 * \returns the calculated hash value
433 inline static long CalcHashKey (HashList
*Hash
, const char *HKey
, long HKLen
)
438 if (Hash
->Algorithm
== NULL
)
439 return hashlittle(HKey
, HKLen
, 9283457);
441 return Hash
->Algorithm(HKey
, HKLen
);
446 * \brief Add a new / Replace an existing item in the Hash
447 * \param HashList the list to manipulate
448 * \param HKey the hash-string to store Data under
449 * \param HKeyLen Length of HKey
450 * \param Data the payload you want to associate with HKey
451 * \param DeleteIt if not free() should be used to delete Data set to NULL, else DeleteIt is used.
453 void Put(HashList
*Hash
, const char *HKey
, long HKLen
, void *Data
, DeleteHashDataFunc DeleteIt
)
461 /** first, find out were we could fit in... */
462 HashBinKey
= CalcHashKey(Hash
, HKey
, HKLen
);
463 HashAt
= FindInHash(Hash
, HashBinKey
);
465 if (HashAt
>= Hash
->MemberSize
)
466 IncreaseHashSize (Hash
);
468 /** oh, we're brand new... */
469 if (Hash
->LookupTable
[HashAt
] == NULL
) {
470 InsertHashItem(Hash
, HashAt
, HashBinKey
, HKey
, HKLen
, Data
, DeleteIt
);
471 }/** Insert After? */
472 else if (Hash
->LookupTable
[HashAt
]->Key
> HashBinKey
) {
473 InsertHashItem(Hash
, HashAt
, HashBinKey
, HKey
, HKLen
, Data
, DeleteIt
);
474 }/** Insert before? */
475 else if (Hash
->LookupTable
[HashAt
]->Key
< HashBinKey
) {
476 InsertHashItem(Hash
, HashAt
+ 1, HashBinKey
, HKey
, HKLen
, Data
, DeleteIt
);
478 else { /** Ok, we have a colision. replace it. */
482 PayloadPos
= Hash
->LookupTable
[HashAt
]->Position
;
483 DeleteHashPayload(Hash
->Members
[PayloadPos
]);
484 Hash
->Members
[PayloadPos
]->Data
= Data
;
485 Hash
->Members
[PayloadPos
]->Destructor
= DeleteIt
;
488 InsertHashItem(Hash
, HashAt
+ 1, HashBinKey
, HKey
, HKLen
, Data
, DeleteIt
);
494 * \brief Lookup the Data associated with HKey
495 * \param Hash the Hashlist to search in
496 * \param HKey the hashkey to look up
497 * \param HKLen length of HKey
498 * \param Data returns the Data associated with HKey
499 * \returns 0 if not found, 1 if.
501 int GetHash(HashList
*Hash
, const char *HKey
, long HKLen
, void **Data
)
513 /** first, find out were we could be... */
514 HashBinKey
= CalcHashKey(Hash
, HKey
, HKLen
);
515 HashAt
= FindInHash(Hash
, HashBinKey
);
516 if ((HashAt
< 0) || /**< Not found at the lower edge? */
517 (HashAt
>= Hash
->nMembersUsed
) || /**< Not found at the upper edge? */
518 (Hash
->LookupTable
[HashAt
]->Key
!= HashBinKey
)) { /**< somewhere inbetween but no match? */
522 else { /** GOTCHA! */
525 MemberPosition
= Hash
->LookupTable
[HashAt
]->Position
;
526 *Data
= Hash
->Members
[MemberPosition
]->Data
;
532 int GetKey(HashList
*Hash
, char *HKey
, long HKLen
, void **Payload
)
538 * \brief get the Keys present in this hash, simila to array_keys() in PHP
539 * Attention: List remains to Hash! don't modify or free it!
540 * \param Hash Your Hashlist to extract the keys from
541 * \param List returns the list of hashkeys stored in Hash
543 int GetHashKeys(HashList
*Hash
, char ***List
)
548 if (Hash
->MyKeys
!= NULL
)
551 Hash
->MyKeys
= (char**) malloc(sizeof(char*) * Hash
->nMembersUsed
);
552 for (i
=0; i
< Hash
->nMembersUsed
; i
++) {
554 Hash
->MyKeys
[i
] = Hash
->LookupTable
[i
]->HashKey
;
556 *List
= (char**)Hash
->MyKeys
;
557 return Hash
->nMembersUsed
;
561 * \brief creates a hash-linear iterator object
562 * \param Hash the list we reference
563 * \param in which step width should we iterate?
564 * If negative, the last position matching the
565 * step-raster is provided.
566 * \returns the hash iterator
568 HashPos
*GetNewHashPos(HashList
*Hash
, int StepWidth
)
572 Ret
= (HashPos
*)malloc(sizeof(HashPos
));
574 Ret
->StepWidth
= StepWidth
;
577 if (Ret
->StepWidth
< 0) {
578 Ret
->Position
= Hash
->nMembersUsed
- 1;
587 * \brief retrieve the counter from the itteratoor
588 * \param the Iterator to analyze
589 * \returns the n'th hashposition we point at
591 int GetHashPosCounter(HashPos
*At
)
597 * \brief frees a linear hash iterator
599 void DeleteHashPos(HashPos
**DelMe
)
610 * \brief Get the data located where HashPos Iterator points at, and Move HashPos one forward
611 * \param Hash your Hashlist to follow
612 * \param HKLen returns Length of Hashkey Returned
613 * \param HashKey returns the Hashkey corrosponding to HashPos
614 * \param Data returns the Data found at HashPos
615 * \returns whether the item was found or not.
617 int GetNextHashPos(HashList
*Hash
, HashPos
*At
, long *HKLen
, const char **HashKey
, void **Data
)
622 if ((Hash
== NULL
) || (At
->Position
>= Hash
->nMembersUsed
) || (At
->Position
< 0))
624 *HKLen
= Hash
->LookupTable
[At
->Position
]->HKLen
;
625 *HashKey
= Hash
->LookupTable
[At
->Position
]->HashKey
;
626 PayloadPos
= Hash
->LookupTable
[At
->Position
]->Position
;
627 *Data
= Hash
->Members
[PayloadPos
]->Data
;
628 /* Position is NULL-Based, while Stepwidth is not... */
629 if (At
->StepWidth
< 0)
631 if ((At
->Position
% abs(At
->StepWidth
)) == 0)
632 At
->Position
+= At
->StepWidth
;
634 At
->Position
+= ((At
->Position
) % abs(At
->StepWidth
)) *
635 (At
->StepWidth
/ abs(At
->StepWidth
));
637 if (At
->Position
> Hash
->nMembersUsed
) {
638 At
->Position
= Hash
->nMembersUsed
- 1;
640 } else if (At
->Position
<= 0) {
648 * \brief Get the data located where At points to
649 * note: you should prefer iterator operations instead of using me.
650 * \param Hash your Hashlist peek from
651 * \param HKLen returns Length of Hashkey Returned
652 * \param HashKey returns the Hashkey corrosponding to HashPos
653 * \param Data returns the Data found at HashPos
654 * \returns whether the item was found or not.
656 int GetHashAt(HashList
*Hash
,long At
, long *HKLen
, const char **HashKey
, void **Data
)
660 if ((Hash
== NULL
) || (At
>= Hash
->nMembersUsed
))
662 *HKLen
= Hash
->LookupTable
[At
]->HKLen
;
663 *HashKey
= Hash
->LookupTable
[At
]->HashKey
;
664 PayloadPos
= Hash
->LookupTable
[At
]->Position
;
665 *Data
= Hash
->Members
[PayloadPos
]->Data
;
670 * \brief sorting function for sorting the Hash alphabeticaly by their strings
671 * \param Key1 first item
672 * \param Key2 second item
674 static int SortByKeys(const void *Key1
, const void* Key2
)
676 HashKey
*HKey1
, *HKey2
;
677 HKey1
= *(HashKey
**) Key1
;
678 HKey2
= *(HashKey
**) Key2
;
680 return strcasecmp(HKey1
->HashKey
, HKey2
->HashKey
);
684 * \brief sorting function for sorting the Hash alphabeticaly reverse by their strings
685 * \param Key1 first item
686 * \param Key2 second item
688 static int SortByKeysRev(const void *Key1
, const void* Key2
)
690 HashKey
*HKey1
, *HKey2
;
691 HKey1
= *(HashKey
**) Key1
;
692 HKey2
= *(HashKey
**) Key2
;
694 return strcasecmp(HKey2
->HashKey
, HKey1
->HashKey
);
698 * \brief sorting function to regain hash-sequence and revert tainted status
699 * \param Key1 first item
700 * \param Key2 second item
702 static int SortByHashKeys(const void *Key1
, const void* Key2
)
704 HashKey
*HKey1
, *HKey2
;
705 HKey1
= *(HashKey
**) Key1
;
706 HKey2
= *(HashKey
**) Key2
;
708 return HKey1
->Key
> HKey2
->Key
;
713 * \brief sort the hash alphabeticaly by their keys.
714 * Caution: This taints the hashlist, so accessing it later
715 * will be significantly slower! You can un-taint it by SortByHashKeyStr
716 * \param Hash the list to sort
717 * \param Order 0/1 Forward/Backward
719 void SortByHashKey(HashList
*Hash
, int Order
)
721 if (Hash
->nMembersUsed
< 2)
723 qsort(Hash
->LookupTable
, Hash
->nMembersUsed
, sizeof(HashKey
*),
724 (Order
)?SortByKeys
:SortByKeysRev
);
729 * \brief sort the hash by their keys (so it regains untainted state).
730 * this will result in the sequence the hashing allgorithm produces it by default.
731 * \param Hash the list to sort
733 void SortByHashKeyStr(HashList
*Hash
)
736 if (Hash
->nMembersUsed
< 2)
738 qsort(Hash
->LookupTable
, Hash
->nMembersUsed
, sizeof(HashKey
*), SortByHashKeys
);
743 * \brief gives user sort routines access to the hash payload
744 * \param Searchentry to retrieve Data to
745 * \returns Data belonging to HashVoid
747 const void *GetSearchPayload(const void *HashVoid
)
749 return (*(HashKey
**)HashVoid
)->PL
->Data
;
753 * \brief sort the hash by your sort function. see the following sample.
754 * this will result in the sequence the hashing allgorithm produces it by default.
755 * \param Hash the list to sort
756 * \param SortBy Sortfunction; see below how to implement this
758 void SortByPayload(HashList
*Hash
, CompareFunc SortBy
)
760 if (Hash
->nMembersUsed
< 2)
762 qsort(Hash
->LookupTable
, Hash
->nMembersUsed
, sizeof(HashKey
*), SortBy
);
770 * given you've put char * into your hash as a payload, a sort function might
772 * int SortByChar(const void* First, const void* Second)
775 * a = (char*) GetSearchPayload(First);
776 * b = (char*) GetSearchPayload(Second);
777 * return strcmp (a, b);
783 * Generic function to free a pointer. This can be used as a callback with the
784 * hash table, even on systems where free() is defined as a macro or has had other
785 * horrible things done to it.
787 void generic_free_handler(void *ptr
) {
792 * Generic function to free a reference.
793 * since a reference actualy isn't needed to be freed, do nothing.
795 void reference_free_handler(void *ptr
)