source: trunk/src/kernel32/ccollection.cpp@ 7010

Last change on this file since 7010 was 7010, checked in by phaller, 24 years ago

added ODIN perfview profiler

File size: 19.6 KB
Line 
1/* $Id: ccollection.cpp,v 1.7 2001-10-11 00:59:43 phaller Exp $ */
2
3/*
4 * Collection class:
5 * provides very fast object lookup by index number
6 *
7 * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
8 *
9 *
10 * Project Odin Software License can be found in LICENSE.TXT
11 *
12 *
13 */
14
15#include <ccollection.h>
16
17
18
19static inline unsigned long _Optlink hashcode(int iRing, char *pszName)
20{
21 unsigned long h;
22 unsigned char *t = (unsigned char*)pszName;
23
24 for (h = 0;
25 *t;
26 t++)
27 {
28 h = (h << 6);
29 h += *t;
30 h %= iRing;
31 }
32
33 return h;
34}
35
36
37
38CCollection::CCollection(int iInitialSize)
39{
40 iSize = iInitialSize;
41}
42
43CCollection::~CCollection()
44{
45}
46
47
48CIndexLookup::CIndexLookup(int iInitialSize)
49{
50 // iSize is expected to be set by parent constructor
51
52 // allocate array with given size
53 if (iInitialSize > 0)
54 {
55 pEntries = (PINDEXLOOKUPENTRY)malloc(iInitialSize * sizeof(INDEXLOOKUPENTRY));
56 memset(pEntries,
57 0,
58 iInitialSize * sizeof(INDEXLOOKUPENTRY));
59 }
60 else
61 // no initial array provided
62 pEntries = NULL;
63
64 iOffset = INT_MAX;
65 iHigh = INT_MIN;
66 iUsedOffset = INT_MAX;
67 iUsedHigh = INT_MIN;
68}
69
70
71CIndexLookup::~CIndexLookup()
72{
73 free(pEntries);
74}
75
76
77int CIndexLookup::isValidIndex(int iIndex)
78{
79 /* determine if the given index is within bounds */
80 return ( (iIndex >= iOffset) &&
81 (iIndex <= iHigh) );
82}
83
84
85int CIndexLookup::shrink()
86{
87 // shrink the index array to the absolutely necessary size only
88
89 if (iInitialized == 0)
90 {
91 // if no elements were allocated, simple remove all data memory
92 if (pEntries)
93 {
94 free(pEntries);
95 pEntries = NULL;
96 }
97
98 return 1;
99 }
100
101 // verify if shrink can do anything good
102 if ( (iOffset < iUsedOffset) ||
103 (iHigh > iUsedHigh) )
104 {
105 // OK, reallocate the beast
106 int iRequiredSize = iUsedHigh - iUsedOffset + 1;
107
108 PINDEXLOOKUPENTRY pNewData = (PINDEXLOOKUPENTRY)malloc(iRequiredSize * sizeof(INDEXLOOKUPENTRY));
109 if (NULL == pNewData)
110 {
111 /* signal failure */
112 return 0;
113 }
114
115 // copy old data and write new boundary info
116 if (NULL != pEntries)
117 {
118 int iRelative = iUsedOffset - iOffset;
119
120 // Note: due to C++ pointer arithmetic,
121 // (pEntries + iRelative) should automatically
122 // increase pEntries by iRelative-times the
123 // sizeof(INDEXLOOKUPENTRY) byte positions!
124 memmove(pNewData,
125 pEntries + iRelative,
126 iRequiredSize * sizeof(INDEXLOOKUPENTRY));
127
128 // deferred delete ensures we've always got a valid array
129 PINDEXLOOKUPENTRY pTemp = pEntries;
130 pEntries = pNewData;
131 free(pTemp);
132 }
133 else
134 {
135 // set new pointer and we're done
136 memset(pNewData,
137 0,
138 iRequiredSize * sizeof(INDEXLOOKUPENTRY));
139
140 pEntries = pNewData;
141 }
142
143 iSize = iRequiredSize;
144 iOffset = iUsedOffset;
145 iHigh = iUsedHigh;
146
147 return 1; // shrunk
148 }
149 else
150 return 0; // didn't do anything
151}
152
153
154PINDEXLOOKUPENTRY CIndexLookup::reallocateData(int iShift, int iRequiredSize)
155{
156 // Note: assumption is iRequiredSize is > iSize!
157 // if (iRequiredSize < iSize)
158 // return NULL;
159
160 PINDEXLOOKUPENTRY pNewData = (PINDEXLOOKUPENTRY)malloc(iRequiredSize * sizeof(INDEXLOOKUPENTRY));
161 if (NULL == pNewData)
162 {
163 /* signal failure */
164 return NULL;
165 }
166
167 // copy current data
168 if (pEntries != NULL)
169 {
170 // copy the data, array cannot overlap
171 memmove(pNewData + (iShift * sizeof(INDEXLOOKUPENTRY)),
172 pEntries,
173 iSize * sizeof(INDEXLOOKUPENTRY));
174
175 // zero out at the beginning or at the end
176 if (iShift == 0)
177 {
178 // clear trailing
179 memset(pNewData + sizeof(INDEXLOOKUPENTRY) * iSize,
180 0,
181 sizeof(INDEXLOOKUPENTRY) * (iRequiredSize - iSize));
182 }
183 else
184 {
185 // clear leading
186 memset(pNewData,
187 0,
188 sizeof(INDEXLOOKUPENTRY) * (iShift));
189 }
190
191 }
192 else
193 // clear brand-new array
194 memset(pNewData,
195 0,
196 iRequiredSize * sizeof(INDEXLOOKUPENTRY));
197
198
199 return pNewData;
200}
201
202
203int CIndexLookup::ensureCapacity(int iIndex)
204{
205 // we need to resize if the index is not valid
206 if (!isValidIndex(iIndex))
207 {
208 // calculate new array bounds,
209 // copy original table,
210 // swap table pointers
211 // and free original pointer
212
213 // check if new base offset is lower
214 if (iIndex < iOffset)
215 {
216 // insert space at the front
217
218 // calculate new size
219 int iRequiredSize = iHigh - iIndex + 1;
220
221 // reallocate the array
222 int iShift = iOffset - iIndex; // for how many elements to shift left
223
224 PINDEXLOOKUPENTRY pNewData = reallocateData(iShift, iRequiredSize);
225 if (NULL == pNewData)
226 // signal failure
227 return 0;
228
229 // deferred delete ensures we've always got a valid array
230 PINDEXLOOKUPENTRY pTemp = pEntries;
231 pEntries = pNewData;
232 if (pTemp)
233 free(pTemp);
234
235 iSize = iRequiredSize;
236 iOffset = iIndex;
237 }
238 else
239 if (iIndex > iHigh)
240 {
241 // lengthen the array
242
243 // calculate new size
244 int iRequiredSize = iIndex - iOffset + 1;
245
246 // reallocate the array
247 PINDEXLOOKUPENTRY pNewData = reallocateData(0, iRequiredSize);
248 if (NULL == pNewData)
249 // signal failure
250 return 0;
251
252 // deferred delete ensures we've always got a valid array
253 PINDEXLOOKUPENTRY pTemp = pEntries;
254 pEntries = pNewData;
255 if (pTemp)
256 free(pTemp);
257
258 iSize = iRequiredSize;
259 iHigh = iIndex;
260 }
261 else
262 // internal logic flaw!
263 return 0;
264 }
265
266 return 1; /* OK */
267}
268
269
270void* CIndexLookup::addElement(int iIndex, void* pObject)
271{
272 // ensure the array can hold the entry
273 if (ensureCapacity(iIndex))
274 {
275 // update the used low and high marks
276 if (iIndex < iUsedOffset)
277 iUsedOffset = iIndex;
278
279 if (iIndex > iUsedHigh)
280 iUsedHigh = iIndex;
281
282 // used limits are not initialized
283 iInitialized = 1;
284
285 // insert entry and quit
286 pEntries[iIndex - iOffset].pObject = pObject;
287 return pObject;
288 }
289 else
290 {
291 // signal failure
292 return NULL;
293 }
294}
295
296
297void* CIndexLookup::removeElement(int iIndex)
298{
299 // check if index is within table
300 if (isValidIndex(iIndex))
301 {
302 // remove specified element from the array
303 void *pOld = pEntries[iIndex - iOffset].pObject;
304 pEntries[iIndex - iOffset].pObject = NULL;
305
306 return pOld;
307 }
308 else
309 {
310 // signal failure
311 return NULL;
312 }
313}
314
315
316void CIndexLookup::clear(void)
317{
318 free(pEntries);
319 pEntries = NULL;
320 iOffset = INT_MAX;
321 iHigh = INT_MIN;
322 iSize = 0;
323 iUsedOffset = INT_MAX;
324 iUsedHigh = INT_MIN;
325 iInitialized = 0;
326}
327
328
329void* CIndexLookup::getElement(int iIndex)
330{
331 if (isValidIndex(iIndex))
332 {
333 // OK, return entry
334 return pEntries[iIndex - iOffset].pObject;
335 }
336 else
337 {
338 // signal: not found
339 return NULL;
340 }
341}
342
343
344CIndexLookupLimit::CIndexLookupLimit(int iHardLimitLow,
345 int iHardLimitHigh)
346 : CIndexLookup(0)
347{
348 // iSize is expected to be set by parent constructor
349
350 // enable hard limits
351 iLimitLow = min(iHardLimitLow, iHardLimitHigh);
352 iLimitHigh = max (iHardLimitLow, iHardLimitHigh);
353
354 // size the array
355 iOffset = iLimitLow;
356 iHigh = iLimitHigh;
357 iSize = iHigh - iOffset + 1;
358 pEntries = reallocateData(0, iSize);
359}
360
361
362CIndexLookupLimit::~CIndexLookupLimit()
363{
364}
365
366
367int CIndexLookupLimit::ensureCapacity(int iIndex)
368{
369 // verify against hard limits if enabled
370 // prevent growing under the low limit
371 if (iIndex < iLimitLow)
372 return 0;
373
374 // prevent growing above the high limit
375 if (iIndex > iLimitHigh)
376 return 0;
377
378
379 return CIndexLookup::ensureCapacity(iIndex);
380}
381
382
383CLinearList::CLinearList()
384 : CCollection(0)
385{
386 pFirst = NULL;
387 pLast = NULL;
388}
389
390
391CLinearList::~CLinearList()
392{
393 clear();
394}
395
396
397PLINEARLISTENTRY CLinearList::addFirst(void* pObject)
398{
399 if (pFirst)
400 return addBefore(pFirst, pObject);
401 else
402 return add0(pObject);
403}
404
405
406PLINEARLISTENTRY CLinearList::addLast(void* pObject)
407{
408 if (pLast)
409 return addAfter(pLast, pObject);
410 else
411 return add0(pObject);
412}
413
414
415// special internal entry to add the 1st element
416PLINEARLISTENTRY CLinearList::add0 (void *pObject)
417{
418 // allocate new entry
419 PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
420 if (NULL == pLLENew)
421 // signal failure
422 return NULL;
423
424 // setup object
425 pLLENew->pObject = pObject;
426 pLLENew->pPrev = NULL;
427 pLLENew->pNext = NULL;
428
429 pFirst = pLLENew;
430 pLast = pLLENew;
431
432 // count items
433 iSize = 1;
434
435 return pLLENew;
436}
437
438
439
440PLINEARLISTENTRY CLinearList::addBefore (PLINEARLISTENTRY pLLE, void *pObject)
441{
442 // allocate new entry
443 PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
444 if (NULL == pLLENew)
445 // signal failure
446 return NULL;
447
448 // setup object
449 pLLENew->pObject = pObject;
450 pLLENew->pPrev = pLLE->pPrev;
451 pLLENew->pNext = pLLE;
452 pLLE->pPrev = pLLENew;
453
454 // establish linking, append to end of list
455 if (pFirst == pLLE)
456 pFirst = pLLENew;
457
458 // count items
459 iSize++;
460
461 return pLLENew;
462}
463
464
465PLINEARLISTENTRY CLinearList::addAfter(PLINEARLISTENTRY pLLE, void *pObject)
466{
467 // allocate new entry
468 PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
469 if (NULL == pLLENew)
470 // signal failure
471 return NULL;
472
473 // setup object
474 pLLENew->pObject = pObject;
475 pLLENew->pNext = pLLE->pNext;
476 pLLENew->pPrev = pLLE;
477 pLLE->pNext = pLLENew;
478
479 // establish linking, append to end of list
480 if (pLast == pLLE)
481 pLast = pLLENew;
482
483 // count items
484 iSize++;
485
486 return pLLENew;
487}
488
489
490
491PLINEARLISTENTRY CLinearList::findForward(void *pObject)
492{
493 return findForward(pFirst, pObject);
494}
495
496
497PLINEARLISTENTRY CLinearList::findBackward(void *pObject)
498{
499 return findBackward(pLast, pObject);
500}
501
502
503PLINEARLISTENTRY CLinearList::findForward(PLINEARLISTENTRY pLLECurrent,
504 void *pObject)
505{
506 while(pLLECurrent)
507 {
508 // check current item
509 if (pLLECurrent->pObject == pObject)
510 return pLLECurrent;
511
512 pLLECurrent = pLLECurrent->pNext;
513 }
514
515 // signal not found
516 return NULL;
517}
518
519
520PLINEARLISTENTRY CLinearList::findBackward(PLINEARLISTENTRY pLLECurrent,
521 void *pObject)
522{
523 while(pLLECurrent)
524 {
525 // check current item
526 if (pLLECurrent->pObject == pObject)
527 return pLLECurrent;
528
529 pLLECurrent = pLLECurrent->pPrev;
530 }
531
532 // signal not found
533 return NULL;
534}
535
536
537void CLinearList::removeElement(PLINEARLISTENTRY pLLE)
538{
539 // check if we're removing from the head
540 if (pLLE == pFirst)
541 pFirst = pLLE->pNext;
542
543 // check if we're removing from the tail
544 if (pLLE == pLast)
545 pLast = pLLE->pPrev;
546
547 // remove from chain
548 if (pLLE->pPrev != NULL)
549 pLLE->pPrev->pNext = pLLE->pNext;
550
551 if (pLLE->pNext != NULL)
552 pLLE->pNext->pPrev = pLLE->pPrev;
553
554 // free memory of the pLLE
555 free(pLLE);
556
557 // count elements
558 iSize--;
559}
560
561
562int CLinearList::removeElement(void* pObject)
563{
564 PLINEARLISTENTRY pLLE = findForward(pObject);
565 if (pLLE == NULL)
566 // signal not found
567 return 0;
568 else
569 {
570 removeElement(pLLE);
571 return 1;
572 }
573}
574
575
576void CLinearList::clear()
577{
578 PLINEARLISTENTRY pLLE = pFirst;
579 PLINEARLISTENTRY pLLENext;
580
581 // Establish new head and tail pointers very quickly,
582 // since new items can be added already while the old ones
583 // are still being deleted without causing interference.
584 pFirst = NULL;
585 pLast = NULL;
586
587 while (pLLE)
588 {
589 pLLENext = pLLE->pNext;
590 free (pLLE);
591 pLLE = pLLENext;
592 }
593}
594
595
596CHashtableLookup::CHashtableLookup(int iInitialSize)
597 : CCollection(iInitialSize)
598{
599 // the parent constructor is expected to set iSize
600 // to iInitialSize
601
602 parrLists = new CLinearList* [iSize];
603 memset(parrLists,
604 0,
605 iSize * sizeof(CLinearList*) );
606
607 iElements = 0;
608}
609
610CHashtableLookup::~CHashtableLookup()
611{
612 // kill all the slot lists
613 for (int i = 0;
614 i < iSize;
615 i++)
616 {
617 // check if slot was occupied
618 if (parrLists[i] != NULL)
619 {
620 delete parrLists[i];
621 parrLists[i] = NULL;
622 }
623 }
624
625 iSize = 0;
626 iElements = 0;
627
628 delete [] parrLists;
629}
630
631
632PHASHTABLEENTRY CHashtableLookup::addElement(char *pszName, void *pObject)
633{
634 // get slot number
635 unsigned long ulHash = hashcode(iSize, pszName);
636
637 // create entry
638 PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)malloc(sizeof(HASHTABLEENTRY));
639 pHTE->pszName = pszName;
640 pHTE->pObject = pObject;
641
642 // check if slot has a list object already
643 if (parrLists[ulHash] == NULL)
644 parrLists[ulHash] = new CLinearList();
645
646 parrLists[ulHash]->addLast(pHTE);
647
648 // count allocated elements
649 iElements++;
650
651 return pHTE;
652}
653
654
655void* CHashtableLookup::removeElement(char *pszName)
656{
657 // get slot number
658 unsigned long ulHash = hashcode(iSize, pszName);
659
660 // check if slot is occupied
661 if (parrLists[ulHash] == NULL)
662 // signal not found
663 return NULL;
664
665 // search the list
666 PLINEARLISTENTRY pLLE = parrLists[ulHash]->getFirst();
667 if (pLLE == NULL)
668 // signal not found
669 return NULL;
670
671 // iterate over the list
672 while(pLLE)
673 {
674 PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
675
676 // quickly compare 1st character for equality
677 // before doing the strcmp call
678 if (*pHTE->pszName == *pszName)
679 if (strcmp(pHTE->pszName, pszName) == 0)
680 {
681 // save old object pointer
682 void* pTemp = pHTE->pObject;
683 free(pHTE);
684
685 // found the correct entry
686 parrLists[ulHash]->removeElement(pLLE);
687
688 // count allocated elements
689 iElements--;
690
691 // return old object pointer to signal success
692 return pTemp;
693 }
694
695 pLLE = pLLE->pNext;
696 }
697
698 // failed
699 return NULL;
700}
701
702
703void* CHashtableLookup::getElement(char *pszName)
704{
705 // get slot number
706 unsigned long ulHash = hashcode(iSize, pszName);
707
708 CLinearList *pLL = parrLists[ulHash];
709
710 // check if slot is occupied
711 if (pLL == NULL)
712 // signal not found
713 return NULL;
714
715 // search the list
716 PLINEARLISTENTRY pLLE = pLL->getFirst();
717 if (pLLE == NULL)
718 // signal not found
719 return NULL;
720
721 // even if this is the only element on the list,
722 // we cannot save the strcmp, since the pszName-key
723 // might still be different! (mismatch)
724
725 // iterate over the list
726 while(pLLE)
727 {
728 PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
729
730 // quickly compare 1st character for equality
731 // before doing the strcmp call
732 if (*pHTE->pszName == *pszName)
733 if (strcmp(pHTE->pszName, pszName) == 0)
734 {
735 // return result
736 return pHTE->pObject;
737 }
738
739 pLLE = pLLE->pNext;
740 }
741
742 // failed
743 return NULL;
744}
745
746
747void CHashtableLookup::clear()
748{
749 // clear all the slot lists
750 for (int i = 0;
751 i < iSize;
752 i++)
753 {
754 // check if slot was occupied
755 if (parrLists[i] != NULL)
756 parrLists[i]->clear();
757 }
758
759 iSize = 0;
760 iElements = 0;
761}
762
763
764void CHashtableLookup::rehash()
765{
766 // if there are less slots than elements,
767 // the blocking factor is expected to be high
768 setSize(iElements);
769}
770
771
772void CHashtableLookup::setSize(int iNewSize)
773{
774 // determine number of allocated elements
775 // actually, we need the prime next to
776 // the given number.
777 if (iSize < iNewSize)
778 setSize0(nextPrime(iNewSize));
779}
780
781
782int CHashtableLookup::getElementMap(PHASHTABLEENTRY pBuffer)
783{
784 int iIndex = 0;
785
786 // iterate over all registered entries and dump them to the buffer
787 // giving the caller direct access to the hashtable internals.
788 for (int i = 0;
789 i < iSize;
790 i++)
791 {
792 // check if slot was occupied
793 if (parrLists[i] != NULL)
794 {
795 // walk along any entry in that linear list
796 PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
797
798 while (pLLE)
799 {
800 PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
801 memcpy(&pBuffer[iIndex], pHTE, sizeof( HASHTABLEENTRY ) );
802 iIndex++;
803
804 pLLE = parrLists[i]->getNext(pLLE);
805 }
806 }
807 }
808
809 // return number of elements copied
810 return iIndex;
811}
812
813
814void CHashtableLookup::setSize0(int iNewSize)
815{
816 // check if rehashing is necessary at all
817 if (iSize == iNewSize)
818 return;
819
820 // save old array, allocate new array
821 CLinearList** parrNew = new CLinearList* [iNewSize];
822 memset(parrNew,
823 0,
824 sizeof(CLinearList*) * iNewSize);
825
826 // convert all the slot lists
827 for (int i = 0;
828 i < iSize;
829 i++)
830 {
831 // check if slot was occupied
832 if (parrLists[i] != NULL)
833 {
834 // iterate over the slot
835 PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
836 PHASHTABLEENTRY pHTE;
837 unsigned long ulHash;
838
839 while(pLLE)
840 {
841 // calculate new hashcode for the entry
842 pHTE = (PHASHTABLEENTRY)pLLE->pObject;
843 ulHash = hashcode(iNewSize, pHTE->pszName);
844
845 // reinsert the pHTE into new slot
846 if (parrNew[ulHash] == NULL)
847 parrNew[ulHash] = new CLinearList();
848
849 parrNew[ulHash]->addLast( (void*)pHTE );
850
851 pLLE=pLLE->pNext;
852 }
853
854 // kill the slot
855 delete parrLists[i];
856 parrLists[i] = NULL;
857 }
858 }
859
860 // swap the tables
861 iSize = iNewSize;
862 delete [] parrLists;
863
864 parrLists = parrNew;
865}
866
867
868unsigned long CHashtableLookup::nextPrime(unsigned long x)
869{
870 // Return next prime number after x, unless x is a prime in which case
871 // x is returned.
872
873 if (x < 2)
874 return 2;
875
876 if (x == 3)
877 return 3;
878
879 if (x % 2 == 0)
880 x++;
881
882 unsigned long sqr = (unsigned long) sqrt((double)x) + 1;
883
884 for (;;)
885 {
886 unsigned long n;
887
888 for (n = 3;
889 (n <= sqr) && ((x % n) != 0);
890 n += 2)
891 ;
892
893 if (n > sqr)
894 return x;
895
896 x += 2;
897 }
898}
899
900
901/*
902 * The following code provides classes for special purposes, whereas
903 * the above code is meant for general purposes. The above classes provide
904 * excellent performance when looking up entries, whereas they're rather
905 * slow when adding new elements to the collections.
906 *
907 * For the PELDR, it's more critical to have exceptionally fast adding
908 * of the exports to the lists as this outweighs resolving imports.
909 */
Note: See TracBrowser for help on using the repository browser.