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

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

.

File size: 17.3 KB
Line 
1/* $Id: ccollection.cpp,v 1.3 2001-05-30 03:28:02 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 // verify if shrink can do anything good
90 if ( (iOffset < iUsedOffset) ||
91 (iHigh > iUsedHigh) )
92 {
93 // OK, reallocate the beast
94 int iRequiredSize = iUsedHigh - iUsedOffset + 1;
95
96 PINDEXLOOKUPENTRY pNewData = (PINDEXLOOKUPENTRY)malloc(iRequiredSize * sizeof(INDEXLOOKUPENTRY));
97 if (NULL == pNewData)
98 {
99 /* signal failure */
100 return 0;
101 }
102
103 // copy old data and write new boundary info
104 if (NULL != pEntries)
105 {
106 int iRelative = iUsedOffset - iOffset;
107
108 memcpy(pNewData,
109 pEntries + (iRelative * sizeof(INDEXLOOKUPENTRY)),
110 iRequiredSize * sizeof(INDEXLOOKUPENTRY));
111
112 // deferred delete ensures we've always got a valid array
113 PINDEXLOOKUPENTRY pTemp = pEntries;
114 pEntries = pNewData;
115 free(pTemp);
116 }
117 else
118 {
119 // set new pointer and we're done
120 memset(pNewData,
121 0,
122 iRequiredSize * sizeof(INDEXLOOKUPENTRY));
123
124 pEntries = pNewData;
125 }
126
127 iSize = iRequiredSize;
128 iOffset = iUsedOffset;
129 iHigh = iUsedHigh;
130
131 return 1; // shrunk
132 }
133 else
134 return 0; // didn't do anything
135}
136
137
138PINDEXLOOKUPENTRY CIndexLookup::reallocateData(int iShift, int iRequiredSize)
139{
140 // Note: assumption is iRequiredSize is > iSize!
141 // if (iRequiredSize < iSize)
142 // return NULL;
143
144 PINDEXLOOKUPENTRY pNewData = (PINDEXLOOKUPENTRY)malloc(iRequiredSize * sizeof(INDEXLOOKUPENTRY));
145 if (NULL == pNewData)
146 {
147 /* signal failure */
148 return NULL;
149 }
150
151 // copy current data
152 if (pEntries != NULL)
153 {
154 // copy the data, array cannot overlap
155 memmove(pNewData,
156 pEntries,
157 iSize);
158
159 // zero out at the beginning or at the end
160 if (iShift == 0)
161 {
162 // clear trailing
163 memset(pNewData + sizeof(INDEXLOOKUPENTRY) * iSize,
164 0,
165 sizeof(INDEXLOOKUPENTRY) * (iRequiredSize - iSize));
166 }
167 else
168 {
169 // clear leading
170 memset(pNewData,
171 0,
172 sizeof(INDEXLOOKUPENTRY) * (iShift));
173 }
174
175 }
176 else
177 // clear brand-new array
178 memset(pNewData,
179 0,
180 iRequiredSize * sizeof(INDEXLOOKUPENTRY));
181
182
183 return pNewData;
184}
185
186
187int CIndexLookup::ensureCapacity(int iIndex)
188{
189 // we need to resize if the index is not valid
190 if (!isValidIndex(iIndex))
191 {
192 // calculate new array bounds,
193 // copy original table,
194 // swap table pointers
195 // and free original pointer
196
197 // check if new base offset is lower
198 if (iIndex < iOffset)
199 {
200 // insert space at the front
201
202 // calculate new size
203 int iRequiredSize = iHigh - iIndex + 1;
204
205 // reallocate the array
206 int iShift = iOffset - iIndex; // for how many elements to shift left
207
208 PINDEXLOOKUPENTRY pNewData = reallocateData(iShift, iRequiredSize);
209 if (NULL == pNewData)
210 // signal failure
211 return 0;
212
213 // deferred delete ensures we've always got a valid array
214 PINDEXLOOKUPENTRY pTemp = pEntries;
215 pEntries = pNewData;
216 free(pTemp);
217
218 iSize = iRequiredSize;
219 iOffset = iIndex;
220 }
221 else
222 if (iIndex > iHigh)
223 {
224 // lengthen the array
225
226 // calculate new size
227 int iRequiredSize = iIndex - iOffset + 1;
228
229 // reallocate the array
230 PINDEXLOOKUPENTRY pNewData = reallocateData(0, iRequiredSize);
231 if (NULL == pNewData)
232 // signal failure
233 return 0;
234
235 // deferred delete ensures we've always got a valid array
236 PINDEXLOOKUPENTRY pTemp = pEntries;
237 pEntries = pNewData;
238 free(pTemp);
239
240 iSize = iRequiredSize;
241 iHigh = iIndex;
242 }
243 else
244 // internal logic flaw!
245 return 0;
246 }
247
248 return 1; /* OK */
249}
250
251
252void* CIndexLookup::addElement(int iIndex, void* pObject)
253{
254 // ensure the array can hold the entry
255 if (ensureCapacity(iIndex))
256 {
257 // update the used low and high marks
258 if (iIndex < iUsedOffset)
259 iUsedOffset = iIndex;
260
261 if (iIndex > iUsedHigh)
262 iUsedHigh = iIndex;
263
264 // insert entry and quit
265 pEntries[iIndex - iOffset].pObject = pObject;
266 return pObject;
267 }
268 else
269 {
270 // signal failure
271 return NULL;
272 }
273}
274
275
276void* CIndexLookup::removeElement(int iIndex)
277{
278 // check if index is within table
279 if (isValidIndex(iIndex))
280 {
281 // remove specified element from the array
282 void *pOld = pEntries[iIndex - iOffset].pObject;
283 pEntries[iIndex - iOffset].pObject = NULL;
284
285 return pOld;
286 }
287 else
288 {
289 // signal failure
290 return NULL;
291 }
292}
293
294
295void CIndexLookup::clear(void)
296{
297 free(pEntries);
298 pEntries = NULL;
299 iOffset = INT_MAX;
300 iHigh = INT_MIN;
301 iSize = 0;
302 iUsedOffset = INT_MAX;
303 iUsedHigh = INT_MIN;
304}
305
306
307void* CIndexLookup::getElement(int iIndex)
308{
309 if (isValidIndex(iIndex))
310 {
311 // OK, return entry
312 return pEntries[iIndex - iOffset].pObject;
313 }
314 else
315 {
316 // signal: not found
317 return NULL;
318 }
319}
320
321
322
323CIndexLookupLimit::CIndexLookupLimit(int iHardLimitLow,
324 int iHardLimitHigh)
325 : CIndexLookup(0)
326{
327 // iSize is expected to be set by parent constructor
328
329 // enable hard limits
330 iLimitLow = min(iHardLimitLow, iHardLimitHigh);
331 iLimitHigh = max (iHardLimitLow, iHardLimitHigh);
332
333 // size the array
334 iOffset = iLimitLow;
335 iHigh = iLimitHigh;
336 iSize = iHigh - iOffset + 1;
337 pEntries = reallocateData(0, iSize);
338}
339
340
341CIndexLookupLimit::~CIndexLookupLimit()
342{
343}
344
345
346int CIndexLookupLimit::ensureCapacity(int iIndex)
347{
348 // verify against hard limits if enabled
349 // prevent growing under the low limit
350 if (iIndex < iLimitLow)
351 return 0;
352
353 // prevent growing above the high limit
354 if (iIndex > iLimitHigh)
355 return 0;
356
357
358 return CIndexLookup::ensureCapacity(iIndex);
359}
360
361
362CLinearList::CLinearList()
363 : CCollection(0)
364{
365 pFirst = NULL;
366 pLast = NULL;
367}
368
369
370CLinearList::~CLinearList()
371{
372 clear();
373}
374
375
376PLINEARLISTENTRY CLinearList::addFirst(void* pObject)
377{
378 if (pFirst)
379 return addBefore(pFirst, pObject);
380 else
381 return add0(pObject);
382}
383
384
385PLINEARLISTENTRY CLinearList::addLast(void* pObject)
386{
387 if (pLast)
388 return addAfter(pLast, pObject);
389 else
390 return add0(pObject);
391}
392
393
394// special internal entry to add the 1st element
395PLINEARLISTENTRY CLinearList::add0 (void *pObject)
396{
397 // allocate new entry
398 PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
399 if (NULL == pLLENew)
400 // signal failure
401 return NULL;
402
403 // setup object
404 pLLENew->pObject = pObject;
405 pLLENew->pPrev = NULL;
406 pLLENew->pNext = NULL;
407
408 pFirst = pLLENew;
409 pLast = pLLENew;
410
411 // count items
412 iSize = 1;
413
414 return pLLENew;
415}
416
417
418
419PLINEARLISTENTRY CLinearList::addBefore (PLINEARLISTENTRY pLLE, void *pObject)
420{
421 // allocate new entry
422 PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
423 if (NULL == pLLENew)
424 // signal failure
425 return NULL;
426
427 // setup object
428 pLLENew->pObject = pObject;
429 pLLENew->pPrev = pLLE->pPrev;
430 pLLENew->pNext = pLLE;
431 pLLE->pPrev = pLLENew;
432
433 // establish linking, append to end of list
434 if (pFirst == pLLE)
435 pFirst = pLLENew;
436
437 // count items
438 iSize++;
439
440 return pLLENew;
441}
442
443
444PLINEARLISTENTRY CLinearList::addAfter(PLINEARLISTENTRY pLLE, void *pObject)
445{
446 // allocate new entry
447 PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
448 if (NULL == pLLENew)
449 // signal failure
450 return NULL;
451
452 // setup object
453 pLLENew->pObject = pObject;
454 pLLENew->pNext = pLLE->pNext;
455 pLLENew->pPrev = pLLE;
456 pLLE->pNext = pLLENew;
457
458 // establish linking, append to end of list
459 if (pLast == pLLE)
460 pLast = pLLENew;
461
462 // count items
463 iSize++;
464
465 return pLLENew;
466}
467
468
469
470PLINEARLISTENTRY CLinearList::findForward(void *pObject)
471{
472 return findForward(pFirst, pObject);
473}
474
475
476PLINEARLISTENTRY CLinearList::findBackward(void *pObject)
477{
478 return findBackward(pLast, pObject);
479}
480
481
482PLINEARLISTENTRY CLinearList::findForward(PLINEARLISTENTRY pLLECurrent,
483 void *pObject)
484{
485 while(pLLECurrent)
486 {
487 // check current item
488 if (pLLECurrent->pObject == pObject)
489 return pLLECurrent;
490
491 pLLECurrent = pLLECurrent->pNext;
492 }
493
494 // signal not found
495 return NULL;
496}
497
498
499PLINEARLISTENTRY CLinearList::findBackward(PLINEARLISTENTRY pLLECurrent,
500 void *pObject)
501{
502 while(pLLECurrent)
503 {
504 // check current item
505 if (pLLECurrent->pObject == pObject)
506 return pLLECurrent;
507
508 pLLECurrent = pLLECurrent->pPrev;
509 }
510
511 // signal not found
512 return NULL;
513}
514
515
516void CLinearList::removeElement(PLINEARLISTENTRY pLLE)
517{
518 // check if we're removing from the head
519 if (pLLE == pFirst)
520 pFirst = pLLE->pNext;
521
522 // check if we're removing from the tail
523 if (pLLE == pLast)
524 pLast = pLLE->pPrev;
525
526 // remove from chain
527 if (pLLE->pPrev != NULL)
528 pLLE->pPrev->pNext = pLLE->pNext;
529
530 if (pLLE->pNext != NULL)
531 pLLE->pNext->pPrev = pLLE->pPrev;
532
533 // free memory of the pLLE
534 free(pLLE);
535
536 // count elements
537 iSize--;
538}
539
540
541int CLinearList::removeElement(void* pObject)
542{
543 PLINEARLISTENTRY pLLE = findForward(pObject);
544 if (pLLE == NULL)
545 // signal not found
546 return 0;
547 else
548 {
549 removeElement(pLLE);
550 return 1;
551 }
552}
553
554
555void CLinearList::clear()
556{
557 PLINEARLISTENTRY pLLE = pFirst;
558 PLINEARLISTENTRY pLLENext;
559
560 // Establish new head and tail pointers very quickly,
561 // since new items can be added already while the old ones
562 // are still being deleted without causing interference.
563 pFirst = NULL;
564 pLast = NULL;
565
566 while (pLLE)
567 {
568 pLLENext = pLLE->pNext;
569 free (pLLE);
570 pLLE = pLLENext;
571 }
572}
573
574
575CHashtableLookup::CHashtableLookup(int iInitialSize)
576 : CCollection(iInitialSize)
577{
578 // the parent constructor is expected to set iSize
579 // to iInitialSize
580
581 parrLists = new CLinearList* [iSize];
582 memset(parrLists,
583 0,
584 iSize * sizeof(CLinearList*) );
585
586 iElements = 0;
587}
588
589CHashtableLookup::~CHashtableLookup()
590{
591 // kill all the slot lists
592 for (int i = 0;
593 i < iSize;
594 i++)
595 {
596 // check if slot was occupied
597 if (parrLists[i] != NULL)
598 {
599 delete parrLists[i];
600 parrLists[i] = NULL;
601 }
602 }
603
604 iSize = 0;
605 iElements = 0;
606
607 delete [] parrLists;
608}
609
610
611PHASHTABLEENTRY CHashtableLookup::addElement(char *pszName, void *pObject)
612{
613 // get slot number
614 unsigned long ulHash = hashcode(iSize, pszName);
615
616 // create entry
617 PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)malloc(sizeof(HASHTABLEENTRY));
618 pHTE->pszName = pszName;
619 pHTE->pObject = pObject;
620
621 // check if slot has a list object already
622 if (parrLists[ulHash] == NULL)
623 parrLists[ulHash] = new CLinearList();
624
625 parrLists[ulHash]->addLast(pHTE);
626
627 // count allocated elements
628 iElements++;
629
630 return pHTE;
631}
632
633
634void* CHashtableLookup::removeElement(char *pszName)
635{
636 // get slot number
637 unsigned long ulHash = hashcode(iSize, pszName);
638
639 // check if slot is occupied
640 if (parrLists[ulHash] == NULL)
641 // signal not found
642 return NULL;
643
644 // search the list
645 PLINEARLISTENTRY pLLE = parrLists[ulHash]->getFirst();
646 if (pLLE == NULL)
647 // signal not found
648 return NULL;
649
650 // iterate over the list
651 while(pLLE)
652 {
653 PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
654 if (strcmp(pHTE->pszName, pszName) == 0)
655 {
656 // save old object pointer
657 void* pTemp = pHTE->pObject;
658 free(pHTE);
659
660 // found the correct entry
661 parrLists[ulHash]->removeElement(pLLE);
662
663 // count allocated elements
664 iElements--;
665
666 // return old object pointer to signal success
667 return pTemp;
668 }
669
670 pLLE = pLLE->pNext;
671 }
672
673 // failed
674 return NULL;
675}
676
677
678void* CHashtableLookup::getElement(char *pszName)
679{
680 // get slot number
681 unsigned long ulHash = hashcode(iSize, pszName);
682
683 CLinearList *pLL = parrLists[ulHash];
684
685 // check if slot is occupied
686 if (pLL == NULL)
687 // signal not found
688 return NULL;
689
690 // search the list
691 PLINEARLISTENTRY pLLE = pLL->getFirst();
692 if (pLLE == NULL)
693 // signal not found
694 return NULL;
695
696 // even if this is the only element on the list,
697 // we cannot save the strcmp, since the pszName-key
698 // might still be different! (mismatch)
699
700 // iterate over the list
701 while(pLLE)
702 {
703 PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
704 if (strcmp(pHTE->pszName, pszName) == 0)
705 {
706 // return result
707 return pHTE->pObject;
708 }
709
710 pLLE = pLLE->pNext;
711 }
712
713 // failed
714 return NULL;
715}
716
717
718void CHashtableLookup::clear()
719{
720 // clear all the slot lists
721 for (int i = 0;
722 i < iSize;
723 i++)
724 {
725 // check if slot was occupied
726 if (parrLists[i] != NULL)
727 parrLists[i]->clear();
728 }
729
730 iSize = 0;
731 iElements = 0;
732}
733
734
735void CHashtableLookup::rehash()
736{
737 // determine number of allocated elements
738 // actually, we need the prime next to
739 // the given number.
740 int iNewSize = nextPrime(iElements);
741
742 setSize(iNewSize);
743}
744
745
746void CHashtableLookup::setSize(int iNewSize)
747{
748 // check if rehashing is necessary at all
749 if (iSize == iNewSize)
750 return;
751
752 // save old array, allocate new array
753 CLinearList** parrNew = new CLinearList* [iNewSize];
754 memset(parrNew,
755 0,
756 sizeof(CLinearList*) * iNewSize);
757
758 // convert all the slot lists
759 for (int i = 0;
760 i < iSize;
761 i++)
762 {
763 // check if slot was occupied
764 if (parrLists[i] != NULL)
765 {
766 // iterate over the slot
767 PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
768 PHASHTABLEENTRY pHTE;
769 unsigned long ulHash;
770
771 while(pLLE)
772 {
773 // calculate new hashcode for the entry
774 pHTE = (PHASHTABLEENTRY)pLLE->pObject;
775 ulHash = hashcode(iNewSize, pHTE->pszName);
776
777 // reinsert the pHTE into new slot
778 if (parrNew[ulHash] == NULL)
779 parrNew[ulHash] = new CLinearList();
780
781 parrNew[ulHash]->addLast( (void*)pHTE );
782
783 pLLE=pLLE->pNext;
784 }
785
786 // kill the slot
787 delete parrLists[i];
788 parrLists[i] = NULL;
789 }
790 }
791
792 // swap the tables
793 iSize = iNewSize;
794 delete [] parrLists;
795
796 parrLists = parrNew;
797}
798
799
800unsigned long CHashtableLookup::nextPrime(unsigned long x)
801{
802 // Return next prime number after x, unless x is a prime in which case
803 // x is returned.
804
805 if (x < 2)
806 return 2;
807
808 if (x == 3)
809 return 3;
810
811 if (x % 2 == 0)
812 x++;
813
814 unsigned long sqr = (unsigned long) sqrt((double)x) + 1;
815
816 for (;;)
817 {
818 unsigned long n;
819
820 for (n = 3;
821 (n <= sqr) && ((x % n) != 0);
822 n += 2)
823 ;
824
825 if (n > sqr)
826 return x;
827
828 x += 2;
829 }
830}
831
Note: See TracBrowser for help on using the repository browser.