~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Headers/Additions/GNUstepBase/GSIMap.h

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* A fast (Inline) map/hash table implementation for NSObjects
 
2
 * Copyright (C) 1998,1999  Free Software Foundation, Inc.
 
3
 * 
 
4
 * Author:      Richard Frith-Macdonald <richard@brainstorm.co.uk>
 
5
 * Created:     Thu Oct  1 09:30:00 GMT 1998
 
6
 * 
 
7
 * Based on original o_map code by Albin L. Jones <Albin.L.Jones@Dartmouth.EDU>
 
8
 *
 
9
 * This file is part of the GNUstep Base Library.
 
10
 * 
 
11
 * This library is free software; you can redistribute it and/or
 
12
 * modify it under the terms of the GNU Library General Public
 
13
 * License as published by the Free Software Foundation; either
 
14
 * version 2 of the License, or (at your option) any later version.
 
15
 * 
 
16
 * This library is distributed in the hope that it will be useful,
 
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
19
 * Library General Public License for more details.
 
20
 * 
 
21
 * You should have received a copy of the GNU Library General Public
 
22
 * License along with this library; if not, write to the Free
 
23
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 
24
 
 
25
#include <Foundation/NSObject.h>
 
26
#include <Foundation/NSZone.h>
 
27
 
 
28
/* To easily un-inline functions for debugging */
 
29
#ifndef INLINE
 
30
#define INLINE inline
 
31
#endif
 
32
 
 
33
/*
 
34
 *      This file should be INCLUDED in files wanting to use the GSIMap
 
35
 *      functions - these are all declared inline for maximum performance.
 
36
 *
 
37
 *      The file including this one may predefine some macros to alter
 
38
 *      the behaviour
 
39
 *
 
40
 *      GSI_MAP_HAS_VALUE
 
41
 *              If defined as 0, then this becomes a hash table rather than
 
42
 *              a map table.
 
43
 *
 
44
 *      GSI_MAP_RETAIN_KEY()
 
45
 *              Macro to retain the key item in a map or hash table.
 
46
 *
 
47
 *      GSI_MAP_RETAIN_VAL()
 
48
 *              Macro to retain the value item in a map table.
 
49
 *
 
50
 *      GSI_MAP_RELEASE_KEY()
 
51
 *              Macro to release the key item in a map or hash table.
 
52
 *
 
53
 *      GSI_MAP_RELEASE_VAL()
 
54
 *              Macro to release the value item in a map table.
 
55
 *
 
56
 *      GSI_MAP_HASH()
 
57
 *              Macro to get the hash of a key item.
 
58
 *
 
59
 *      GSI_MAP_EQUAL()
 
60
 *              Macro to compare two key items for equality - produces zero
 
61
 *              if the items are not equal.
 
62
 *
 
63
 *      GSI_MAP_EXTRA
 
64
 *              If this value is defined, there is an 'extra' field in each
 
65
 *              map table whose type is that specified by the value of the
 
66
 *              preprocessor constant. This field can be used
 
67
 *              to store additional information for the map.
 
68
 *
 
69
 *      GSI_MAP_NOCLEAN
 
70
 *              Define this to a non-zero integer value if the map keys and
 
71
 *              values do not need to be released when the map is emptied.
 
72
 *              This permits some optimisation.
 
73
 *
 
74
 */
 
75
 
 
76
#ifndef GSI_MAP_HAS_VALUE
 
77
#define GSI_MAP_HAS_VALUE       1
 
78
#endif
 
79
 
 
80
#ifndef GSI_MAP_RETAIN_KEY
 
81
#define GSI_MAP_RETAIN_KEY(M, X)        [(X).obj retain]
 
82
#endif
 
83
#ifndef GSI_MAP_RELEASE_KEY
 
84
#define GSI_MAP_RELEASE_KEY(M, X)       [(X).obj release]
 
85
#endif
 
86
#ifndef GSI_MAP_RETAIN_VAL
 
87
#define GSI_MAP_RETAIN_VAL(M, X)        [(X).obj retain]
 
88
#endif
 
89
#ifndef GSI_MAP_RELEASE_VAL
 
90
#define GSI_MAP_RELEASE_VAL(M, X)       [(X).obj release]
 
91
#endif
 
92
#ifndef GSI_MAP_HASH
 
93
#define GSI_MAP_HASH(M, X)              [(X).obj hash]
 
94
#endif
 
95
#ifndef GSI_MAP_EQUAL
 
96
#define GSI_MAP_EQUAL(M, X, Y)          [(X).obj isEqual: (Y).obj]
 
97
#endif
 
98
 
 
99
/*
 
100
 *      If there is no bitmask defined to supply the types that
 
101
 *      may be used as keys in the  map, default to permitting all types.
 
102
 */
 
103
#ifndef GSI_MAP_KTYPES
 
104
#define GSI_MAP_KTYPES        GSUNION_ALL
 
105
#endif
 
106
 
 
107
/*
 
108
 *      Set up the name of the union to store keys.
 
109
 */
 
110
#ifdef  GSUNION
 
111
#undef  GSUNION
 
112
#endif
 
113
#define GSUNION GSIMapKey
 
114
 
 
115
/*
 
116
 *      Set up the types that will be storable in the union.
 
117
 *      See 'GSUnion.h' for further information.
 
118
 */
 
119
#ifdef  GSUNION_TYPES
 
120
#undef  GSUNION_TYPES
 
121
#endif
 
122
#define GSUNION_TYPES   GSI_MAP_KTYPES
 
123
#ifdef  GSUNION_EXTRA
 
124
#undef  GSUNION_EXTRA
 
125
#endif
 
126
#ifdef  GSI_MAP_KEXTRA
 
127
#define GSUNION_EXTRA   GSI_MAP_KEXTRA
 
128
#endif
 
129
 
 
130
/*
 
131
 *      Generate the union typedef
 
132
 */
 
133
#include <GNUstepBase/GSUnion.h>
 
134
 
 
135
 
 
136
#if (GSI_MAP_KTYPES) & GSUNION_OBJ
 
137
#define GSI_MAP_CLEAR_KEY(node)  node->key.obj = nil
 
138
#elif  (GSI_MAP_KTYPES) & GSUNION_PTR
 
139
#define GSI_MAP_CLEAR_KEY(node)  node->key.ptr = 0
 
140
#else
 
141
#define GSI_MAP_CLEAR_KEY(node)  
 
142
#endif
 
143
 
 
144
/*
 
145
 *      If there is no bitmask defined to supply the types that
 
146
 *      may be used as values in the  map, default to permitting all types.
 
147
 */
 
148
#ifndef GSI_MAP_VTYPES
 
149
#define GSI_MAP_VTYPES        GSUNION_ALL
 
150
#endif
 
151
 
 
152
/*
 
153
 *      Set up the name of the union to store map values.
 
154
 */
 
155
#ifdef  GSUNION
 
156
#undef  GSUNION
 
157
#endif
 
158
#define GSUNION GSIMapVal
 
159
 
 
160
/*
 
161
 *      Set up the types that will be storable in the union.
 
162
 *      See 'GSUnion.h' for further information.
 
163
 */
 
164
#ifdef  GSUNION_TYPES
 
165
#undef  GSUNION_TYPES
 
166
#endif
 
167
#define GSUNION_TYPES   GSI_MAP_VTYPES
 
168
#ifdef  GSUNION_EXTRA
 
169
#undef  GSUNION_EXTRA
 
170
#endif
 
171
#ifdef  GSI_MAP_VEXTRA
 
172
#define GSUNION_EXTRA   GSI_MAP_VEXTRA
 
173
#endif
 
174
 
 
175
#ifndef GSI_MAP_SIMPLE
 
176
#define GSI_MAP_SIMPLE  0
 
177
#endif
 
178
 
 
179
/*
 
180
 *      Generate the union typedef
 
181
 */
 
182
#include <GNUstepBase/GSUnion.h>
 
183
 
 
184
#if (GSI_MAP_VTYPES) & GSUNION_OBJ
 
185
#define GSI_MAP_CLEAR_VAL(node)  node->value.obj = nil
 
186
#elif  (GSI_MAP_VTYPES) & GSUNION_PTR
 
187
#define GSI_MAP_CLEAR_VAL(node)  node->value.ptr = 0
 
188
#else
 
189
#define GSI_MAP_CLEAR_VAL(node)  
 
190
#endif
 
191
 
 
192
/*
 
193
 *  Description of the datastructure
 
194
 *  --------------------------------
 
195
 *  The complete GSIMap implementation can be viewed in two different ways,
 
196
 *
 
197
 *  (1) viewed as a structure to add and retrieve elements
 
198
 *  (2) viewed as a memory management structure to facilitate (1)
 
199
 *
 
200
 *  The first view is best described as follows:
 
201
 *
 
202
 *   _GSIMapTable   ----->  C-array of buckets 
 
203
 *
 
204
 *  Where each bucket contains a count (nodeCount), describing the number
 
205
 *  of nodes in the bucket and a pointer (firstNode) to a single linked
 
206
 *  list of nodes.
 
207
 *  
 
208
 *  The second view is slightly more complicated.
 
209
 *  The individual nodes are allocated and deallocated in chunks.
 
210
 *  In order to keep track of this we have:
 
211
 *
 
212
 *   _GSIMapTable   ----->  C-array of chunks
 
213
 *
 
214
 *  Where each chunk points to a C-array of nodes.
 
215
 *  Also the _GSIMapTable contains a pointer to the free nodes
 
216
 *
 
217
 *   _GSIMapTable   ----->  single linked list of free nodes
 
218
 *
 
219
 *  Consequence of this is that EVERY node is part of a single linked list.
 
220
 *  Either it is in use and reachable from a bucket, or it is part of the
 
221
 *  freeNodes linked list.
 
222
 *  Also EVERY node is part of chunk, due to the way the nodes are allocated.
 
223
 *
 
224
 *  A rough picture is include below:
 
225
 *   
 
226
 *  
 
227
 *   This is the map                C - array of the buckets
 
228
 *   +---------------+             +---------------+
 
229
 *   | _GSIMapTable  |      /----->| nodeCount     |  
 
230
 *   |---------------|     /       | firstNode ----+--\  
 
231
 *   | buckets    ---+----/        | ..........    |  |
 
232
 *   | bucketCount  =| size of --> | nodeCount     |  |
 
233
 *   | nodeChunks ---+--\          | firstNode     |  |
 
234
 *   | chunkCount  =-+\ |          |     .         |  | 
 
235
 *   |   ....        || |          |     .         |  |
 
236
 *   +---------------+| |          | nodeCount     |  |
 
237
 *                    | |          | fistNode      |  | 
 
238
 *                    / |          +---------------+  | 
 
239
 *         ----------   v                             v
 
240
 *       /       +----------+      +---------------------------+ 
 
241
 *       |       |  * ------+----->| Node1 | Node2 | Node3 ... | a chunk
 
242
 *   chunkCount  |  * ------+--\   +---------------------------+
 
243
 *  is size of = |  .       |   \  +-------------------------------+
 
244
 *               |  .       |    ->| Node n | Node n + 1 | ...     | another
 
245
 *               +----------+      +-------------------------------+
 
246
 *               array pointing
 
247
 *               to the chunks
 
248
 *
 
249
 *
 
250
 *  NOTES on the way chunks are allocated
 
251
 *  -------------------------------------
 
252
 *  Chunks are allocated when needed, that is a new chunk is allocated
 
253
 *  whenever the freeNodes list is empty and a new node is required.
 
254
 *  In gnustep-base-1.9.0 the size of the new chunk is calculated as
 
255
 *  roughly 3/4 of the number of nodes in use.
 
256
 *  The problem with this approach is that it can lead to unnecessary
 
257
 *  address space fragmentation.  So in this version the algorithm we
 
258
 *  will use the 3/4 rule until the nodeCount reaches the "increment"
 
259
 *  member variable.
 
260
 *  If nodeCount is bigger than the "increment" it will allocate chunks
 
261
 *  of size "increment".
 
262
 */
 
263
 
 
264
typedef struct _GSIMapTable GSIMapTable_t;
 
265
typedef struct _GSIMapBucket GSIMapBucket_t;
 
266
typedef struct _GSIMapNode GSIMapNode_t;
 
267
 
 
268
typedef GSIMapTable_t *GSIMapTable;
 
269
typedef GSIMapBucket_t *GSIMapBucket;
 
270
typedef GSIMapNode_t *GSIMapNode;
 
271
 
 
272
struct  _GSIMapNode {
 
273
  GSIMapNode    nextInBucket;   /* Linked list of bucket.       */
 
274
  GSIMapKey     key;
 
275
#if     GSI_MAP_HAS_VALUE
 
276
  GSIMapVal     value;
 
277
#endif
 
278
};
 
279
 
 
280
struct  _GSIMapBucket {
 
281
  size_t        nodeCount;      /* Number of nodes in bucket.   */
 
282
  GSIMapNode    firstNode;      /* The linked list of nodes.    */
 
283
};
 
284
 
 
285
struct  _GSIMapTable {
 
286
  NSZone        *zone;
 
287
  size_t        nodeCount;      /* Number of used nodes in map. */
 
288
  size_t        bucketCount;    /* Number of buckets in map.    */
 
289
  GSIMapBucket  buckets;        /* Array of buckets.            */
 
290
  GSIMapNode    freeNodes;      /* List of unused nodes.        */
 
291
  size_t        chunkCount;     /* Number of chunks in array.   */
 
292
  GSIMapNode    *nodeChunks;    /* Chunks of allocated memory.  */
 
293
  size_t        increment;
 
294
#ifdef  GSI_MAP_EXTRA
 
295
  GSI_MAP_EXTRA extra;
 
296
#endif
 
297
};
 
298
 
 
299
typedef struct  _GSIMapEnumerator {
 
300
  GSIMapTable   map;            /* the map being enumerated.    */
 
301
  GSIMapNode    node;           /* The next node to use.        */
 
302
  size_t        bucket;         /* The next bucket to use.      */
 
303
} *_GSIE;
 
304
 
 
305
#ifdef  GSI_MAP_ENUMERATOR
 
306
typedef GSI_MAP_ENUMERATOR      GSIMapEnumerator_t;
 
307
#else
 
308
typedef struct _GSIMapEnumerator GSIMapEnumerator_t;
 
309
#endif
 
310
typedef GSIMapEnumerator_t      *GSIMapEnumerator;
 
311
 
 
312
static INLINE GSIMapBucket
 
313
GSIMapPickBucket(unsigned hash, GSIMapBucket buckets, size_t bucketCount)
 
314
{
 
315
  return buckets + hash % bucketCount;
 
316
}
 
317
 
 
318
static INLINE GSIMapBucket
 
319
GSIMapBucketForKey(GSIMapTable map, GSIMapKey key)
 
320
{
 
321
  return GSIMapPickBucket(GSI_MAP_HASH(map, key),
 
322
    map->buckets, map->bucketCount);
 
323
}
 
324
 
 
325
static INLINE void
 
326
GSIMapLinkNodeIntoBucket(GSIMapBucket bucket, GSIMapNode node)
 
327
{
 
328
  node->nextInBucket = bucket->firstNode;
 
329
  bucket->firstNode = node;
 
330
}
 
331
 
 
332
static INLINE void
 
333
GSIMapUnlinkNodeFromBucket(GSIMapBucket bucket, GSIMapNode node)
 
334
{
 
335
  if (node == bucket->firstNode)
 
336
    {
 
337
      bucket->firstNode = node->nextInBucket;
 
338
    }
 
339
  else
 
340
    {
 
341
      GSIMapNode        tmp = bucket->firstNode;
 
342
 
 
343
      while (tmp->nextInBucket != node)
 
344
        {
 
345
          tmp = tmp->nextInBucket;
 
346
        }
 
347
      tmp->nextInBucket = node->nextInBucket;
 
348
    }
 
349
  node->nextInBucket = 0;
 
350
}
 
351
 
 
352
static INLINE void
 
353
GSIMapAddNodeToBucket(GSIMapBucket bucket, GSIMapNode node)
 
354
{
 
355
  GSIMapLinkNodeIntoBucket(bucket, node);
 
356
  bucket->nodeCount += 1;
 
357
}
 
358
 
 
359
static INLINE void
 
360
GSIMapAddNodeToMap(GSIMapTable map, GSIMapNode node)
 
361
{
 
362
  GSIMapBucket  bucket;
 
363
 
 
364
  bucket = GSIMapBucketForKey(map, node->key);
 
365
  GSIMapAddNodeToBucket(bucket, node);
 
366
  map->nodeCount++;
 
367
}
 
368
 
 
369
static INLINE void
 
370
GSIMapRemoveNodeFromBucket(GSIMapBucket bucket, GSIMapNode node)
 
371
{
 
372
  bucket->nodeCount--;
 
373
  GSIMapUnlinkNodeFromBucket(bucket, node);
 
374
}
 
375
 
 
376
static INLINE void
 
377
GSIMapRemoveNodeFromMap(GSIMapTable map, GSIMapBucket bkt, GSIMapNode node)
 
378
{
 
379
  map->nodeCount--;
 
380
  GSIMapRemoveNodeFromBucket(bkt, node);
 
381
}
 
382
 
 
383
static INLINE void
 
384
GSIMapRemangleBuckets(GSIMapTable map,
 
385
  GSIMapBucket old_buckets, size_t old_bucketCount,
 
386
  GSIMapBucket new_buckets, size_t new_bucketCount)
 
387
{
 
388
  while (old_bucketCount-- > 0)
 
389
    {
 
390
      GSIMapNode        node;
 
391
 
 
392
      while ((node = old_buckets->firstNode) != 0)
 
393
        {
 
394
          GSIMapBucket  bkt;
 
395
 
 
396
          GSIMapRemoveNodeFromBucket(old_buckets, node);
 
397
          bkt = GSIMapPickBucket(GSI_MAP_HASH(map, node->key),
 
398
            new_buckets, new_bucketCount);
 
399
          GSIMapAddNodeToBucket(bkt, node);
 
400
        }
 
401
      old_buckets++;
 
402
    }
 
403
}
 
404
 
 
405
static INLINE void
 
406
GSIMapMoreNodes(GSIMapTable map, unsigned required)
 
407
{
 
408
  GSIMapNode    *newArray;
 
409
  size_t        arraySize = (map->chunkCount+1)*sizeof(GSIMapNode);
 
410
 
 
411
#if     GS_WITH_GC == 1
 
412
  /*
 
413
   * Our nodes may be allocated from the atomic zone - but we don't want
 
414
   * them freed - so we must keep the array of pointers to memory chunks in
 
415
   * the default zone
 
416
   */
 
417
  if (map->zone == GSAtomicMallocZone())
 
418
    {
 
419
      newArray = (GSIMapNode*)NSZoneMalloc(NSDefaultMallocZone(), arraySize);
 
420
    }
 
421
  else
 
422
#endif
 
423
  newArray = (GSIMapNode*)NSZoneMalloc(map->zone, arraySize);
 
424
  if (newArray)
 
425
    {
 
426
      GSIMapNode        newNodes;
 
427
      size_t            chunkCount;
 
428
      size_t            chunkSize;
 
429
 
 
430
      memcpy(newArray, map->nodeChunks, (map->chunkCount)*sizeof(GSIMapNode));
 
431
      if (map->nodeChunks != 0)
 
432
        {
 
433
          NSZoneFree(map->zone, map->nodeChunks);
 
434
        }
 
435
      map->nodeChunks = newArray;
 
436
 
 
437
      if (required == 0)
 
438
        {
 
439
          if (map->chunkCount == 0)
 
440
            {
 
441
              chunkCount = map->bucketCount > 1 ? map->bucketCount : 2;
 
442
            }
 
443
          else
 
444
            {
 
445
              chunkCount = ((map->nodeCount>>2)+1)<<1;
 
446
            }
 
447
        }
 
448
      else
 
449
        {
 
450
          chunkCount = required;
 
451
        }
 
452
      chunkSize = chunkCount * sizeof(GSIMapNode_t);
 
453
      newNodes = (GSIMapNode)NSZoneMalloc(map->zone, chunkSize);
 
454
      if (newNodes)
 
455
        {
 
456
          map->nodeChunks[map->chunkCount++] = newNodes;
 
457
          newNodes[--chunkCount].nextInBucket = map->freeNodes;
 
458
          while (chunkCount--)
 
459
            {
 
460
              newNodes[chunkCount].nextInBucket = &newNodes[chunkCount+1];
 
461
            }
 
462
          map->freeNodes = newNodes;
 
463
        }
 
464
    }
 
465
}
 
466
 
 
467
#if     GSI_MAP_HAS_VALUE
 
468
static INLINE GSIMapNode
 
469
GSIMapNewNode(GSIMapTable map, GSIMapKey key, GSIMapVal value)
 
470
{
 
471
  GSIMapNode    node = map->freeNodes;
 
472
 
 
473
  if (node == 0)
 
474
    {
 
475
      GSIMapMoreNodes(map, map->nodeCount < map->increment ? 0: map->increment);
 
476
      node = map->freeNodes;
 
477
      if (node == 0)
 
478
        {
 
479
          return 0;
 
480
        }
 
481
    }
 
482
 
 
483
  map->freeNodes = node->nextInBucket;
 
484
  node->key = key;
 
485
  node->value = value;
 
486
  node->nextInBucket = 0;
 
487
 
 
488
  return node;
 
489
}
 
490
#else
 
491
static INLINE GSIMapNode
 
492
GSIMapNewNode(GSIMapTable map, GSIMapKey key)
 
493
{
 
494
  GSIMapNode    node = map->freeNodes;
 
495
 
 
496
  if (node == 0)
 
497
    {
 
498
      GSIMapMoreNodes(map, map->nodeCount < map->increment ? 0: map->increment);
 
499
      node = map->freeNodes;
 
500
      if (node == 0)
 
501
        {
 
502
          return 0;
 
503
        }
 
504
    }
 
505
 
 
506
  map->freeNodes = node->nextInBucket;
 
507
  node->key = key;
 
508
  node->nextInBucket = 0;
 
509
  return node;
 
510
}
 
511
#endif
 
512
 
 
513
static INLINE void
 
514
GSIMapFreeNode(GSIMapTable map, GSIMapNode node)
 
515
{
 
516
  GSI_MAP_RELEASE_KEY(map, node->key);
 
517
  GSI_MAP_CLEAR_KEY(node);
 
518
#if     GSI_MAP_HAS_VALUE
 
519
  GSI_MAP_RELEASE_VAL(map, node->value);
 
520
  GSI_MAP_CLEAR_VAL(node);
 
521
#endif
 
522
  
 
523
  node->nextInBucket = map->freeNodes;
 
524
  map->freeNodes = node;
 
525
}
 
526
 
 
527
static INLINE GSIMapNode 
 
528
GSIMapNodeForKeyInBucket(GSIMapTable map, GSIMapBucket bucket, GSIMapKey key)
 
529
{
 
530
  GSIMapNode    node = bucket->firstNode;
 
531
 
 
532
  while ((node != 0) && GSI_MAP_EQUAL(map, node->key, key) == NO)
 
533
    {
 
534
      node = node->nextInBucket;
 
535
    }
 
536
  return node;
 
537
}
 
538
 
 
539
static INLINE GSIMapNode 
 
540
GSIMapNodeForKey(GSIMapTable map, GSIMapKey key)
 
541
{
 
542
  GSIMapBucket  bucket;
 
543
  GSIMapNode    node;
 
544
 
 
545
  if (map->nodeCount == 0)
 
546
    {
 
547
      return 0;
 
548
    }
 
549
  bucket = GSIMapBucketForKey(map, key);
 
550
  node = GSIMapNodeForKeyInBucket(map, bucket, key);
 
551
  return node;
 
552
}
 
553
 
 
554
#if     (GSI_MAP_KTYPES & GSUNION_INT)
 
555
/*
 
556
 * Specialized lookup for the case where keys are known to be simple integer
 
557
 * or pointer values that are their own hash values and con be compared with
 
558
 * a test for integer equality.
 
559
 */
 
560
static INLINE GSIMapNode 
 
561
GSIMapNodeForSimpleKey(GSIMapTable map, GSIMapKey key)
 
562
{
 
563
  GSIMapBucket  bucket;
 
564
  GSIMapNode    node;
 
565
 
 
566
  if (map->nodeCount == 0)
 
567
    {
 
568
      return 0;
 
569
    }
 
570
  bucket = map->buckets + key.uint % map->bucketCount;
 
571
  node = bucket->firstNode;
 
572
  while ((node != 0) && node->key.uint != key.uint)
 
573
    {
 
574
      node = node->nextInBucket;
 
575
    }
 
576
  return node;
 
577
}
 
578
#endif
 
579
 
 
580
static INLINE void
 
581
GSIMapResize(GSIMapTable map, size_t new_capacity)
 
582
{
 
583
  GSIMapBucket  new_buckets;
 
584
  size_t        size = 1;
 
585
  size_t        old = 1;
 
586
 
 
587
  /*
 
588
   *    Find next size up in the fibonacci series
 
589
   */
 
590
  while (size < new_capacity)
 
591
    {
 
592
      size_t    tmp = old;
 
593
 
 
594
      old = size;
 
595
      size += tmp;
 
596
    }
 
597
  /*
 
598
   *    Avoid even numbers - since hash functions frequently generate uneven
 
599
   *    distributions around powers of two -
 
600
   *    we don't want lots of keys falling into a single bucket.
 
601
   */
 
602
  if (size % 2 == 0)
 
603
    {
 
604
      size++;
 
605
    }
 
606
 
 
607
  /*
 
608
   *    Make a new set of buckets for this map
 
609
   */
 
610
  new_buckets = (GSIMapBucket)NSZoneCalloc(map->zone, size,
 
611
    sizeof(GSIMapBucket_t));
 
612
  if (new_buckets != 0)
 
613
    {
 
614
      GSIMapRemangleBuckets(map, map->buckets, map->bucketCount, new_buckets,
 
615
        size);
 
616
 
 
617
      if (map->buckets != 0)
 
618
        {
 
619
          NSZoneFree(map->zone, map->buckets);
 
620
        }
 
621
      map->buckets = new_buckets;
 
622
      map->bucketCount = size;
 
623
    }
 
624
}
 
625
 
 
626
static INLINE void
 
627
GSIMapRightSizeMap(GSIMapTable map, size_t capacity)
 
628
{
 
629
  /* FIXME: Now, this is a guess, based solely on my intuition.  If anyone
 
630
   * knows of a better ratio (or other test, for that matter) and can
 
631
   * provide evidence of its goodness, please get in touch with me, Albin
 
632
   * L. Jones <Albin.L.Jones@Dartmouth.EDU>. */
 
633
 
 
634
  if (3 * capacity >= 4 * map->bucketCount)
 
635
    {
 
636
      GSIMapResize(map, (3 * capacity)/4 + 1);
 
637
    }
 
638
}
 
639
 
 
640
/** Enumerating **/
 
641
 
 
642
/* IMPORTANT WARNING: Enumerators have a wonderous property.
 
643
 * Once a node has been returned by `GSIMapEnumeratorNextNode()', it may be
 
644
 * removed from the map without effecting the rest of the current
 
645
 * enumeration. */
 
646
 
 
647
/* EXTREMELY IMPORTANT WARNING: The purpose of this warning is point
 
648
 * out that, various (i.e., many) functions currently depend on
 
649
 * the behaviour outlined above.  So be prepared for some serious
 
650
 * breakage when you go fudging around with these things. */
 
651
 
 
652
/**
 
653
 * Create an return an enumerator for the specified map.<br />
 
654
 * You must call GSIMapEndEnumerator() when you have finished
 
655
 * with the enumerator.<br />
 
656
 * <strong>WARNING</strong> You should not alter a map while an enumeration
 
657
 * is in progress.  The results of doing so are reasonably unpredictable.
 
658
 * <br />Remember, DON'T MESS WITH A MAP WHILE YOU'RE ENUMERATING IT.
 
659
 */
 
660
static INLINE GSIMapEnumerator_t
 
661
GSIMapEnumeratorForMap(GSIMapTable map)
 
662
{
 
663
  GSIMapEnumerator_t    enumerator;
 
664
 
 
665
  enumerator.map = map;
 
666
  enumerator.node = 0;
 
667
  enumerator.bucket = 0;
 
668
  /*
 
669
   * Locate next bucket and node to be returned.
 
670
   */
 
671
  while (enumerator.bucket < map->bucketCount)
 
672
    {
 
673
      enumerator.node = map->buckets[enumerator.bucket].firstNode;
 
674
      if (enumerator.node != 0)
 
675
        {
 
676
          break;        // Got first node, and recorded its bucket.
 
677
        }
 
678
      enumerator.bucket++;
 
679
    }
 
680
 
 
681
  return enumerator;
 
682
}
 
683
 
 
684
/**
 
685
 * Tidies up after map enumeration ... effectively destroys the enumerator.
 
686
 */
 
687
static INLINE void
 
688
GSIMapEndEnumerator(GSIMapEnumerator enumerator)
 
689
{
 
690
  ((_GSIE)enumerator)->map = 0;
 
691
  ((_GSIE)enumerator)->node = 0;
 
692
  ((_GSIE)enumerator)->bucket = 0;
 
693
}
 
694
 
 
695
/**
 
696
 * Returns the bucket from which the next node in the enumeration will
 
697
 * come.  Once the next node has been enumerated, you can use the
 
698
 * bucket and node to remove the node from the map using the
 
699
 * GSIMapRemoveNodeFromMap() function.
 
700
 */
 
701
static INLINE GSIMapBucket 
 
702
GSIMapEnumeratorBucket(GSIMapEnumerator enumerator)
 
703
{
 
704
  if (((_GSIE)enumerator)->node != 0)
 
705
    {
 
706
      GSIMapTable       map = ((_GSIE)enumerator)->map;
 
707
 
 
708
      return &((map->buckets)[((_GSIE)enumerator)->bucket]);
 
709
    }
 
710
  return 0;
 
711
}
 
712
 
 
713
/**
 
714
 * Returns the next node in the map, or a nul pointer if at the end.
 
715
 */
 
716
static INLINE GSIMapNode 
 
717
GSIMapEnumeratorNextNode(GSIMapEnumerator enumerator)
 
718
{
 
719
  GSIMapNode    node = ((_GSIE)enumerator)->node;
 
720
 
 
721
  if (node != 0)
 
722
    {
 
723
      GSIMapNode        next = node->nextInBucket;
 
724
 
 
725
      if (next == 0)
 
726
        {
 
727
          GSIMapTable   map = ((_GSIE)enumerator)->map;
 
728
          size_t        bucketCount = map->bucketCount;
 
729
          size_t        bucket = ((_GSIE)enumerator)->bucket;
 
730
 
 
731
          while (next == 0 && ++bucket < bucketCount)
 
732
            {
 
733
              next = (map->buckets[bucket]).firstNode;
 
734
            }
 
735
          ((_GSIE)enumerator)->bucket = bucket;
 
736
        }
 
737
      ((_GSIE)enumerator)->node = next;
 
738
    }
 
739
  return node;
 
740
}
 
741
 
 
742
#if     GSI_MAP_HAS_VALUE
 
743
static INLINE GSIMapNode
 
744
GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value)
 
745
{
 
746
  GSIMapNode node;
 
747
 
 
748
  node = GSIMapNewNode(map, key, value);
 
749
 
 
750
  if (node != 0)
 
751
    {
 
752
      GSIMapRightSizeMap(map, map->nodeCount);
 
753
      GSIMapAddNodeToMap(map, node);
 
754
    }
 
755
  return node;
 
756
}
 
757
 
 
758
static INLINE GSIMapNode
 
759
GSIMapAddPair(GSIMapTable map, GSIMapKey key, GSIMapVal value)
 
760
{
 
761
  GSIMapNode node;
 
762
 
 
763
  GSI_MAP_RETAIN_KEY(map, key);
 
764
  GSI_MAP_RETAIN_VAL(map, value);
 
765
  node = GSIMapNewNode(map, key, value);
 
766
 
 
767
  if (node != 0)
 
768
    {
 
769
      GSIMapRightSizeMap(map, map->nodeCount);
 
770
      GSIMapAddNodeToMap(map, node);
 
771
    }
 
772
  return node;
 
773
}
 
774
#else
 
775
static INLINE GSIMapNode
 
776
GSIMapAddKeyNoRetain(GSIMapTable map, GSIMapKey key)
 
777
{
 
778
  GSIMapNode node;
 
779
 
 
780
  node = GSIMapNewNode(map, key);
 
781
 
 
782
  if (node != 0)
 
783
    {
 
784
      GSIMapRightSizeMap(map, map->nodeCount);
 
785
      GSIMapAddNodeToMap(map, node);
 
786
    }
 
787
  return node;
 
788
}
 
789
 
 
790
static INLINE GSIMapNode
 
791
GSIMapAddKey(GSIMapTable map, GSIMapKey key)
 
792
{
 
793
  GSIMapNode node;
 
794
 
 
795
  GSI_MAP_RETAIN_KEY(map, key);
 
796
  node = GSIMapNewNode(map, key);
 
797
 
 
798
  if (node != 0)
 
799
    {
 
800
      GSIMapRightSizeMap(map, map->nodeCount);
 
801
      GSIMapAddNodeToMap(map, node);
 
802
    }
 
803
  return node;
 
804
}
 
805
#endif
 
806
 
 
807
static INLINE void
 
808
GSIMapRemoveKey(GSIMapTable map, GSIMapKey key)
 
809
{
 
810
  GSIMapBucket  bucket = GSIMapBucketForKey(map, key);
 
811
  GSIMapNode    node;
 
812
  
 
813
  node = GSIMapNodeForKeyInBucket(map, bucket, key);
 
814
  if (node != 0)
 
815
    {
 
816
      GSIMapRemoveNodeFromMap(map, bucket, node);
 
817
      GSIMapFreeNode(map, node);
 
818
    }
 
819
}
 
820
 
 
821
static INLINE void
 
822
GSIMapCleanMap(GSIMapTable map)
 
823
{
 
824
  if (map->nodeCount > 0)
 
825
    {
 
826
      GSIMapBucket      bucket = map->buckets;
 
827
      unsigned int      i;
 
828
      GSIMapNode        startNode = 0;
 
829
      GSIMapNode        prevNode = 0;
 
830
      GSIMapNode        node;
 
831
      
 
832
      map->nodeCount = 0;
 
833
      for (i = 0; i < map->bucketCount; i++)
 
834
        {
 
835
          node = bucket->firstNode;
 
836
          if (prevNode != 0)
 
837
            {
 
838
              prevNode->nextInBucket = node;
 
839
            }
 
840
          else
 
841
            {
 
842
              startNode = node;
 
843
            }
 
844
          while(node != 0)
 
845
            {
 
846
              GSI_MAP_RELEASE_KEY(map, node->key);
 
847
          
 
848
#if     GSI_MAP_HAS_VALUE
 
849
              GSI_MAP_RELEASE_VAL(map, node->value);
 
850
#endif
 
851
              prevNode = node;
 
852
              node = node->nextInBucket;
 
853
            }
 
854
          bucket->nodeCount = 0;
 
855
          bucket->firstNode = 0;
 
856
          bucket++;
 
857
        }
 
858
      
 
859
      prevNode->nextInBucket = map->freeNodes;
 
860
      map->freeNodes = startNode;
 
861
    }
 
862
}
 
863
 
 
864
static INLINE void
 
865
GSIMapEmptyMap(GSIMapTable map)
 
866
{
 
867
  unsigned int  i;
 
868
 
 
869
#ifdef  GSI_MAP_NOCLEAN
 
870
  if (GSI_MAP_NOCLEAN)
 
871
    {
 
872
      map->nodeCount = 0;
 
873
    }
 
874
  else
 
875
    {
 
876
      GSIMapCleanMap(map);
 
877
    }
 
878
#else
 
879
  GSIMapCleanMap(map);
 
880
#endif
 
881
  if (map->buckets != 0)
 
882
    {
 
883
      NSZoneFree(map->zone, map->buckets);
 
884
      map->buckets = 0;
 
885
      map->bucketCount = 0;
 
886
    }
 
887
  if (map->nodeChunks != 0)
 
888
    {
 
889
      for (i = 0; i < map->chunkCount; i++)
 
890
        {
 
891
          NSZoneFree(map->zone, map->nodeChunks[i]);
 
892
        }
 
893
      map->chunkCount = 0;
 
894
      NSZoneFree(map->zone, map->nodeChunks);
 
895
      map->nodeChunks = 0;
 
896
    }
 
897
  map->freeNodes = 0;
 
898
  map->zone = 0;
 
899
}
 
900
 
 
901
static INLINE void 
 
902
GSIMapInitWithZoneAndCapacity(GSIMapTable map, NSZone *zone, size_t capacity)
 
903
{
 
904
  map->zone = zone;
 
905
  map->nodeCount = 0;
 
906
  map->bucketCount = 0;
 
907
  map->buckets = 0;
 
908
  map->nodeChunks = 0;
 
909
  map->freeNodes = 0;
 
910
  map->chunkCount = 0;
 
911
  map->increment = 300000;   // choosen so the chunksize will be less than 4Mb
 
912
  GSIMapRightSizeMap(map, capacity);
 
913
  GSIMapMoreNodes(map, capacity);
 
914
}
 
915