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

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

Revert to old but optimized loader

File size: 18.9 KB
Line 
1/* $Id: ccollection.cpp,v 1.6 2001-06-01 01:21:13 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
782void CHashtableLookup::setSize0(int iNewSize)
783{
784 // check if rehashing is necessary at all
785 if (iSize == iNewSize)
786 return;
787
788 // save old array, allocate new array
789 CLinearList** parrNew = new CLinearList* [iNewSize];
790 memset(parrNew,
791 0,
792 sizeof(CLinearList*) * iNewSize);
793
794 // convert all the slot lists
795 for (int i = 0;
796 i < iSize;
797 i++)
798 {
799 // check if slot was occupied
800 if (parrLists[i] != NULL)
801 {
802 // iterate over the slot
803 PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
804 PHASHTABLEENTRY pHTE;
805 unsigned long ulHash;
806
807 while(pLLE)
808 {
809 // calculate new hashcode for the entry
810 pHTE = (PHASHTABLEENTRY)pLLE->pObject;
811 ulHash = hashcode(iNewSize, pHTE->pszName);
812
813 // reinsert the pHTE into new slot
814 if (parrNew[ulHash] == NULL)
815 parrNew[ulHash] = new CLinearList();
816
817 parrNew[ulHash]->addLast( (void*)pHTE );
818
819 pLLE=pLLE->pNext;
820 }
821
822 // kill the slot
823 delete parrLists[i];
824 parrLists[i] = NULL;
825 }
826 }
827
828 // swap the tables
829 iSize = iNewSize;
830 delete [] parrLists;
831
832 parrLists = parrNew;
833}
834
835
836unsigned long CHashtableLookup::nextPrime(unsigned long x)
837{
838 // Return next prime number after x, unless x is a prime in which case
839 // x is returned.
840
841 if (x < 2)
842 return 2;
843
844 if (x == 3)
845 return 3;
846
847 if (x % 2 == 0)
848 x++;
849
850 unsigned long sqr = (unsigned long) sqrt((double)x) + 1;
851
852 for (;;)
853 {
854 unsigned long n;
855
856 for (n = 3;
857 (n <= sqr) && ((x % n) != 0);
858 n += 2)
859 ;
860
861 if (n > sqr)
862 return x;
863
864 x += 2;
865 }
866}
867
868
869/*
870 * The following code provides classes for special purposes, whereas
871 * the above code is meant for general purposes. The above classes provide
872 * excellent performance when looking up entries, whereas they're rather
873 * slow when adding new elements to the collections.
874 *
875 * For the PELDR, it's more critical to have exceptionally fast adding
876 * of the exports to the lists as this outweighs resolving imports.
877 */
Note: See TracBrowser for help on using the repository browser.