VirtualBox

source: vbox/trunk/src/VBox/GuestHost/OpenGL/util/hash.c@ 44198

Last change on this file since 44198 was 44193, checked in by vboxsync, 12 years ago

crOpenGL: hash table index pool rewrite

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.2 KB
Line 
1/* Copyright (c) 2001, Stanford University
2 * All rights reserved
3 *
4 * See the file LICENSE.txt for information on redistributing this software.
5 */
6
7#include "cr_threads.h"
8#include "cr_hash.h"
9#include "cr_mem.h"
10#include "cr_error.h"
11
12#include <iprt/list.h>
13
14#define CR_MAXUINT ((GLuint) 0xFFFFFFFF)
15#define CR_HASH_ID_MIN ((GLuint)1)
16#define CR_HASH_ID_MAX CR_MAXUINT
17
18#define CR_NUM_BUCKETS 1047
19
20typedef struct FreeElemRec {
21 RTLISTNODE Node;
22 GLuint min;
23 GLuint max;
24} FreeElem;
25
26typedef struct CRHashIdPoolRec {
27 RTLISTNODE freeList;
28} CRHashIdPool;
29
30typedef struct CRHashNode {
31 unsigned long key;
32 void *data;
33 struct CRHashNode *next;
34} CRHashNode;
35
36struct CRHashTable {
37 unsigned int num_elements;
38 CRHashNode *buckets[CR_NUM_BUCKETS];
39 CRHashIdPool *idPool;
40#ifdef CHROMIUM_THREADSAFE
41 CRmutex mutex;
42#endif
43};
44
45
46static CRHashIdPool *crAllocHashIdPool( void )
47{
48 CRHashIdPool *pool = (CRHashIdPool *) crCalloc(sizeof(CRHashIdPool));
49 FreeElem *elem = (FreeElem *) crCalloc(sizeof(FreeElem));
50 RTListInit(&pool->freeList);
51 elem->min = CR_HASH_ID_MIN;
52 elem->max = CR_HASH_ID_MAX;
53 RTListAppend(&pool->freeList, &elem->Node);
54 return pool;
55}
56
57static void crFreeHashIdPool( CRHashIdPool *pool )
58{
59 FreeElem *i, *next;
60 RTListForEachSafe(&pool->freeList, i, next, FreeElem, Node)
61 {
62 crFree(i);
63 }
64
65 crFree(pool);
66}
67
68static GLboolean crHashIdPoolIsIdFree( const CRHashIdPool *pool, GLuint id );
69
70#ifdef DEBUG_misha
71static void crHashIdPoolDbgCheckConsistency(CRHashIdPool *pool)
72{
73 FreeElem *i;
74 GLuint min = 0;
75
76 /* null is a special case, it is always treated as allocated */
77 Assert(!crHashIdPoolIsIdFree(pool, 0));
78
79 /* first ensure entries have correct values */
80 RTListForEach(&pool->freeList, i, FreeElem, Node)
81 {
82 Assert(i->min >= CR_HASH_ID_MIN);
83 Assert(i->max <= CR_HASH_ID_MAX);
84 Assert(i->min < i->max);
85 }
86
87 /* now ensure entries do not intersect */
88 /* and that they are sorted */
89 RTListForEach(&pool->freeList, i, FreeElem, Node)
90 {
91 Assert(min < i->min);
92 min = i->max;
93 }
94}
95
96static void crHashIdPoolDbgCheckUsed( const CRHashIdPool *pool, GLuint start, GLuint count, GLboolean fUsed )
97{
98 GLuint i;
99 CRASSERT(count);
100 CRASSERT(start >= CR_HASH_ID_MIN);
101 CRASSERT(start + count <= CR_HASH_ID_MAX);
102 CRASSERT(start + count > start);
103 for (i = 0; i < count; ++i)
104 {
105 Assert(!fUsed == !!crHashIdPoolIsIdFree( pool, start + i ));
106 }
107}
108
109# define CR_HASH_IDPOOL_DBG_CHECK_USED(_p, _start, _count, _used) do { \
110 crHashIdPoolDbgCheckConsistency((_p)); \
111 crHashIdPoolDbgCheckUsed( (_p), (_start), (_count), (_used) ); \
112 } while (0)
113
114# define CR_HASH_IDPOOL_DBG_CHECK_CONSISTENCY(_p) do { crHashIdPoolDbgCheckConsistency((_p)); } while (0)
115#else
116# define CR_HASH_IDPOOL_DBG_CHECK_USED(_p, _start, _count, _used) do { } while (0)
117# define CR_HASH_IDPOOL_DBG_CHECK_CONSISTENCY(_p) do { } while (0)
118#endif
119
120/*
121 * Allocate a block of <count> IDs. Return index of first one.
122 * Return 0 if we fail.
123 */
124static GLuint crHashIdPoolAllocBlock( CRHashIdPool *pool, GLuint count )
125{
126 FreeElem *f, *next;
127 GLuint ret;
128
129 CRASSERT(count > 0);
130 RTListForEachSafe(&pool->freeList, f, next, FreeElem, Node)
131 {
132 Assert(f->max > f->min);
133 if (f->max - f->min >= (GLuint) count)
134 {
135 /* found a sufficiently large enough block */
136 ret = f->min;
137 f->min += count;
138 if (f->min == f->max)
139 {
140 RTListNodeRemove(&f->Node);
141 crFree(f);
142 }
143
144 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, ret, count, GL_TRUE);
145 return ret;
146 }
147 }
148
149 /* failed to find free block */
150 crWarning("crHashIdPoolAllocBlock failed");
151 CR_HASH_IDPOOL_DBG_CHECK_CONSISTENCY(pool);
152 return 0;
153}
154
155
156/*
157 * Free a block of <count> IDs starting at <first>.
158 */
159static void crHashIdPoolFreeBlock( CRHashIdPool *pool, GLuint first, GLuint count )
160{
161 FreeElem *f;
162 GLuint last;
163 GLuint newMax;
164 FreeElem *cur, *curNext;
165
166 /* null is a special case, it is always treated as allocated */
167 if (!first)
168 {
169 Assert(!crHashIdPoolIsIdFree(pool, 0));
170 ++first;
171 --count;
172 if (!count)
173 return;
174 }
175
176 last = first + count;
177 CRASSERT(count > 0);
178 CRASSERT(last > first);
179 CRASSERT(first >= CR_HASH_ID_MIN);
180 CRASSERT(last <= CR_HASH_ID_MAX);
181
182 /* the id list is sorted, first find a place to insert */
183 RTListForEach(&pool->freeList, f, FreeElem, Node)
184 {
185 Assert(f->max > f->min);
186
187 if (f->max < first)
188 continue;
189
190 if (f->min > last)
191 {
192 /* we are here because first is > than prevEntry->max
193 * otherwise the previous loop iterations should handle that */
194 FreeElem *elem = (FreeElem *) crCalloc(sizeof(FreeElem));
195 elem->min = first;
196 elem->max = last;
197 RTListNodeInsertBefore(&f->Node, &elem->Node);
198 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE);
199 return;
200 }
201
202 /* now we have f->min <= last and f->max >= first,
203 * so we have either intersection */
204
205 if (f->min > first)
206 f->min = first; /* first is guarantied not to touch any prev regions */
207
208 newMax = last;
209
210 if (f->max >= last)
211 {
212 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE);
213 return;
214 }
215
216 for (cur = RTListNodeGetNext(&f->Node, FreeElem, Node),
217 curNext = RT_FROM_MEMBER(cur->Node.pNext, FreeElem, Node);
218 !RTListNodeIsDummy(&pool->freeList, cur, FreeElem, Node);
219 cur = curNext,
220 curNext = RT_FROM_MEMBER((cur)->Node.pNext, FreeElem, Node) )
221 {
222 if (cur->min > last)
223 break;
224
225 newMax = cur->max;
226 RTListNodeRemove(&cur->Node);
227 crFree(cur);
228
229 if (newMax >= last)
230 break;
231 }
232
233 f->max = newMax;
234 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE);
235 return;
236 }
237
238 /* we are here because either the list is empty or because all list rande elements have smaller values */
239 f = (FreeElem *) crCalloc(sizeof(FreeElem));
240 f->min = first;
241 f->max = last;
242 RTListAppend(&pool->freeList, &f->Node);
243 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE);
244}
245
246
247
248/*
249 * Mark the given Id as being allocated.
250 */
251static GLboolean crHashIdPoolAllocId( CRHashIdPool *pool, GLuint id )
252{
253 FreeElem *f, *next;
254
255 if (!id)
256 {
257 /* null is a special case, it is always treated as allocated */
258 Assert(!crHashIdPoolIsIdFree(pool, 0));
259 return GL_FALSE;
260 }
261
262// Assert(id != 2);
263
264 RTListForEachSafe(&pool->freeList, f, next, FreeElem, Node)
265 {
266 if (f->max <= id)
267 continue;
268 if (f->min > id)
269 {
270 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE);
271 return GL_FALSE;
272 }
273
274 /* f->min <= id && f->max > id */
275 if (id > f->min)
276 {
277 if (id + 1 < f->max)
278 {
279 FreeElem *elem = (FreeElem *) crCalloc(sizeof(FreeElem));
280 elem->min = id + 1;
281 elem->max = f->max;
282 RTListNodeInsertAfter(&f->Node, &elem->Node);
283 }
284 f->max = id;
285
286 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE);
287 }
288 else
289 {
290 Assert(id == f->min);
291 if (id + 1 < f->max)
292 {
293 f->min = id + 1;
294 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE);
295 }
296 else
297 {
298 RTListNodeRemove(&f->Node);
299 crFree(f);
300 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE);
301 }
302 }
303
304 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE);
305 return GL_TRUE;
306 }
307
308 /* if we get here, the ID was already allocated - that's OK */
309 CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE);
310 return GL_FALSE;
311}
312
313
314/*
315 * Determine if the given id is free. Return GL_TRUE if so.
316 */
317static GLboolean crHashIdPoolIsIdFree( const CRHashIdPool *pool, GLuint id )
318{
319 FreeElem *f;
320 CRASSERT(id >= 0);
321 CRASSERT(id <= CR_HASH_ID_MAX);
322
323 RTListForEach(&pool->freeList, f, FreeElem, Node)
324 {
325 if (f->max <= id)
326 continue;
327 if (f->min > id)
328 return GL_FALSE;
329 return GL_TRUE;
330 }
331 return GL_FALSE;
332}
333
334
335
336CRHashTable *crAllocHashtable( void )
337{
338 int i;
339 CRHashTable *hash = (CRHashTable *) crCalloc( sizeof( CRHashTable )) ;
340 hash->num_elements = 0;
341 for (i = 0 ; i < CR_NUM_BUCKETS ; i++)
342 {
343 hash->buckets[i] = NULL;
344 }
345 hash->idPool = crAllocHashIdPool();
346#ifdef CHROMIUM_THREADSAFE
347 crInitMutex(&hash->mutex);
348#endif
349 return hash;
350}
351
352void crFreeHashtable( CRHashTable *hash, CRHashtableCallback deleteFunc )
353{
354 int i;
355 CRHashNode *entry, *next;
356
357 if ( !hash) return;
358
359#ifdef CHROMIUM_THREADSAFE
360 crLockMutex(&hash->mutex);
361#endif
362
363 for ( i = 0; i < CR_NUM_BUCKETS; i++ )
364 {
365 entry = hash->buckets[i];
366 while (entry)
367 {
368 next = entry->next;
369 /* Clear the key in case crHashtableDelete() is called
370 * from this callback.
371 */
372 entry->key = 0;
373 if (deleteFunc && entry->data)
374 {
375 (*deleteFunc)(entry->data);
376 }
377 crFree(entry);
378 entry = next;
379
380 }
381 }
382 crFreeHashIdPool( hash->idPool );
383
384#ifdef CHROMIUM_THREADSAFE
385 crUnlockMutex(&hash->mutex);
386 crFreeMutex(&hash->mutex);
387#endif
388
389 crFree( hash );
390}
391
392void crHashtableLock(CRHashTable *h)
393{
394#ifdef CHROMIUM_THREADSAFE
395 crLockMutex(&h->mutex);
396#endif
397}
398
399void crHashtableUnlock(CRHashTable *h)
400{
401#ifdef CHROMIUM_THREADSAFE
402 crUnlockMutex(&h->mutex);
403#endif
404}
405
406void crHashtableWalk( CRHashTable *hash, CRHashtableWalkCallback walkFunc , void *dataPtr2)
407{
408 int i;
409 CRHashNode *entry, *next;
410
411 if (!hash)
412 return;
413
414#ifdef CHROMIUM_THREADSAFE
415 crLockMutex(&hash->mutex);
416#endif
417 for (i = 0; i < CR_NUM_BUCKETS; i++)
418 {
419 entry = hash->buckets[i];
420 while (entry)
421 {
422 /* save next ptr here, in case walkFunc deletes this entry */
423 next = entry->next;
424 if (entry->data && walkFunc) {
425 (*walkFunc)( entry->key, entry->data, dataPtr2 );
426 }
427 entry = next;
428 }
429 }
430#ifdef CHROMIUM_THREADSAFE
431 crUnlockMutex(&hash->mutex);
432#endif
433}
434
435static unsigned int crHash( unsigned long key )
436{
437 return key % CR_NUM_BUCKETS;
438}
439
440void crHashtableAdd( CRHashTable *h, unsigned long key, void *data )
441{
442 CRHashNode *node = (CRHashNode *) crCalloc( sizeof( CRHashNode ) );
443#ifdef CHROMIUM_THREADSAFE
444 crLockMutex(&h->mutex);
445#endif
446 node->key = key;
447 node->data = data;
448 node->next = h->buckets[crHash( key )];
449 h->buckets[ crHash( key ) ] = node;
450 h->num_elements++;
451 crHashIdPoolAllocId (h->idPool, key);
452#ifdef CHROMIUM_THREADSAFE
453 crUnlockMutex(&h->mutex);
454#endif
455}
456
457GLboolean crHashtableAllocRegisterKey( CRHashTable *h, GLuint key)
458{
459 GLboolean fAllocated;
460#ifdef CHROMIUM_THREADSAFE
461 crLockMutex(&h->mutex);
462#endif
463 fAllocated = crHashIdPoolAllocId (h->idPool, key);
464#ifdef CHROMIUM_THREADSAFE
465 crUnlockMutex(&h->mutex);
466#endif
467 return fAllocated;
468}
469
470GLuint crHashtableAllocKeys( CRHashTable *h, GLsizei range)
471{
472 GLuint res;
473 int i;
474
475#ifdef CHROMIUM_THREADSAFE
476 crLockMutex(&h->mutex);
477#endif
478 res = crHashIdPoolAllocBlock (h->idPool, range);
479#ifdef DEBUG_misha
480 Assert(res);
481 for (i = 0; i < range; ++i)
482 {
483 void *search = crHashtableSearch( h, res+i );
484 Assert(!search);
485 }
486#endif
487#ifdef CHROMIUM_THREADSAFE
488 crUnlockMutex(&h->mutex);
489#endif
490 return res;
491}
492
493void crHashtableDelete( CRHashTable *h, unsigned long key, CRHashtableCallback deleteFunc )
494{
495 unsigned int index = crHash( key );
496 CRHashNode *temp, *beftemp = NULL;
497
498#ifdef CHROMIUM_THREADSAFE
499 crLockMutex(&h->mutex);
500#endif
501 for ( temp = h->buckets[index]; temp; temp = temp->next )
502 {
503 if ( temp->key == key )
504 break;
505 beftemp = temp;
506 }
507 if ( !temp ) {
508#ifdef CHROMIUM_THREADSAFE
509 crUnlockMutex(&h->mutex);
510#endif
511 return; /* not an error */
512 }
513 if ( beftemp )
514 beftemp->next = temp->next;
515 else
516 h->buckets[index] = temp->next;
517 h->num_elements--;
518 if (temp->data && deleteFunc) {
519 (*deleteFunc)( temp->data );
520 }
521
522 crFree( temp );
523
524 crHashIdPoolFreeBlock( h->idPool, key, 1 );
525#ifdef CHROMIUM_THREADSAFE
526 crUnlockMutex(&h->mutex);
527#endif
528}
529
530void crHashtableDeleteBlock( CRHashTable *h, unsigned long key, GLsizei range, CRHashtableCallback deleteFunc )
531{
532 /* XXX optimize someday */
533 GLuint i;
534 for (i = 0; i < (GLuint)range; i++) {
535 crHashtableDelete( h, key, deleteFunc );
536 }
537}
538
539void *crHashtableSearch( const CRHashTable *h, unsigned long key )
540{
541 unsigned int index = crHash( key );
542 CRHashNode *temp;
543#ifdef CHROMIUM_THREADSAFE
544 crLockMutex((CRmutex *)&h->mutex);
545#endif
546 for ( temp = h->buckets[index]; temp; temp = temp->next )
547 {
548 if ( temp->key == key )
549 break;
550 }
551#ifdef CHROMIUM_THREADSAFE
552 crUnlockMutex((CRmutex *)&h->mutex);
553#endif
554 if ( !temp )
555 {
556 return NULL;
557 }
558 return temp->data;
559}
560
561void crHashtableReplace( CRHashTable *h, unsigned long key, void *data,
562 CRHashtableCallback deleteFunc)
563{
564 unsigned int index = crHash( key );
565 CRHashNode *temp;
566#ifdef CHROMIUM_THREADSAFE
567 crLockMutex(&h->mutex);
568#endif
569 for ( temp = h->buckets[index]; temp; temp = temp->next )
570 {
571 if ( temp->key == key )
572 break;
573 }
574#ifdef CHROMIUM_THREADSAFE
575 crUnlockMutex(&h->mutex);
576#endif
577 if ( !temp )
578 {
579 crHashtableAdd( h, key, data );
580 return;
581 }
582#ifdef CHROMIUM_THREADSAFE
583 crLockMutex(&h->mutex);
584#endif
585 if ( temp->data && deleteFunc )
586 {
587 (*deleteFunc)( temp->data );
588 }
589 temp->data = data;
590#ifdef CHROMIUM_THREADSAFE
591 crUnlockMutex(&h->mutex);
592#endif
593}
594
595unsigned int crHashtableNumElements( const CRHashTable *h)
596{
597 if (h)
598 return h->num_elements;
599 else
600 return 0;
601}
602
603/*
604 * Determine if the given key is used. Return GL_TRUE if so.
605 */
606GLboolean crHashtableIsKeyUsed( const CRHashTable *h, GLuint id )
607{
608 return (GLboolean) !crHashIdPoolIsIdFree( h->idPool, id);
609}
610
611GLboolean crHashtableGetDataKey(CRHashTable *pHash, void *pData, unsigned long *pKey)
612{
613 int i;
614 CRHashNode *entry;
615 GLboolean rc = GL_FALSE;
616
617 if (!pHash)
618 return rc;
619
620#ifdef CHROMIUM_THREADSAFE
621 crLockMutex(&pHash->mutex);
622#endif
623 for (i = 0; i<CR_NUM_BUCKETS && !rc; i++)
624 {
625 entry = pHash->buckets[i];
626 while (entry)
627 {
628 if (entry->data == pData) {
629 if (pKey)
630 *pKey = entry->key;
631 rc = GL_TRUE;
632 break;
633 }
634 entry = entry->next;
635 }
636 }
637#ifdef CHROMIUM_THREADSAFE
638 crUnlockMutex(&pHash->mutex);
639#endif
640
641 return rc;
642}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette