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

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

.

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