]> git.saurik.com Git - apple/objc4.git/blame - runtime/hashtable2.mm
objc4-646.tar.gz
[apple/objc4.git] / runtime / hashtable2.mm
CommitLineData
13d88034 1/*
7af964d1 2 * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
13d88034 3 *
b3962a83 4 * @APPLE_LICENSE_HEADER_START@
390d5862
A
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
13d88034
A
12 *
13 * The Original Code and all software distributed under the License are
390d5862 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
13d88034
A
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
390d5862
A
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
13d88034
A
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 hashtable2.m
25 Copyright 1989-1996 NeXT Software, Inc.
26 Created by Bertrand Serlet, Feb 89
27 */
28
7af964d1
A
29#include "objc-private.h"
30#include "hashtable2.h"
13d88034
A
31
32/* In order to improve efficiency, buckets contain a pointer to an array or directly the data when the array size is 1 */
33typedef union {
34 const void *one;
35 const void **many;
36 } oneOrMany;
37 /* an optimization consists of storing directly data when count = 1 */
38
39typedef struct {
40 unsigned count;
41 oneOrMany elements;
42 } HashBucket;
43 /* private data structure; may change */
44
45/*************************************************************************
46 *
47 * Macros and utilities
48 *
49 *************************************************************************/
50
b3962a83 51static unsigned log2u (unsigned x) { return (x<2) ? 0 : log2u (x>>1)+1; };
13d88034 52
13d88034
A
53#define PTRSIZE sizeof(void *)
54
8972963c 55#if !SUPPORT_ZONES
7af964d1
A
56# define DEFAULT_ZONE NULL
57# define ZONE_FROM_PTR(p) NULL
58# define ALLOCTABLE(z) ((NXHashTable *) malloc (sizeof (NXHashTable)))
59# define ALLOCBUCKETS(z,nb)((HashBucket *) calloc (nb, sizeof (HashBucket)))
cd5f04f5
A
60/* Return interior pointer so a table of classes doesn't look like objects */
61# define ALLOCPAIRS(z,nb) (1+(const void **) calloc (nb+1, sizeof (void *)))
62# define FREEPAIRS(p) (free((void*)(-1+p)))
7af964d1
A
63#else
64# define DEFAULT_ZONE malloc_default_zone()
65# define ZONE_FROM_PTR(p) malloc_zone_from_ptr(p)
cd5f04f5
A
66# define ALLOCTABLE(z) ((NXHashTable *) malloc_zone_malloc ((malloc_zone_t *)z,sizeof (NXHashTable)))
67# define ALLOCBUCKETS(z,nb)((HashBucket *) malloc_zone_calloc ((malloc_zone_t *)z, nb, sizeof (HashBucket)))
68/* Return interior pointer so a table of classes doesn't look like objects */
69# define ALLOCPAIRS(z,nb) (1+(const void **) malloc_zone_calloc ((malloc_zone_t *)z, nb+1, sizeof (void *)))
70# define FREEPAIRS(p) (free((void*)(-1+p)))
7af964d1
A
71#endif
72
8972963c 73#if !SUPPORT_MOD
7af964d1
A
74 /* nbBuckets must be a power of 2 */
75# define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) & (table->nbBuckets-1)))
76# define GOOD_CAPACITY(c) (c <= 1 ? 1 : 1 << (log2u (c-1)+1))
77# define MORE_CAPACITY(b) (b*2)
78#else
79 /* iff necessary this modulo can be optimized since the nbBuckets is of the form 2**n-1 */
80# define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) % table->nbBuckets))
81 static unsigned exp2m1 (unsigned x) { return (1 << x) - 1; };
82# define GOOD_CAPACITY(c) (exp2m1 (log2u (c)+1))
83# define MORE_CAPACITY(b) (b*2+1)
84#endif
13d88034
A
85
86#define ISEQUAL(table, data1, data2) ((data1 == data2) || (*table->prototype->isEqual)(table->info, data1, data2))
87 /* beware of double evaluation */
88
89/*************************************************************************
90 *
91 * Global data and bootstrap
92 *
93 *************************************************************************/
94
95static int isEqualPrototype (const void *info, const void *data1, const void *data2) {
96 NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
97 NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
98
99 return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
100 };
101
b3962a83 102static uintptr_t hashPrototype (const void *info, const void *data) {
13d88034
A
103 NXHashTablePrototype *proto = (NXHashTablePrototype *) data;
104
cd5f04f5 105 return NXPtrHash(info, (void*)proto->hash) ^ NXPtrHash(info, (void*)proto->isEqual) ^ NXPtrHash(info, (void*)proto->free) ^ (uintptr_t) proto->style;
13d88034
A
106 };
107
108void NXNoEffectFree (const void *info, void *data) {};
109
110static NXHashTablePrototype protoPrototype = {
111 hashPrototype, isEqualPrototype, NXNoEffectFree, 0
112 };
113
8972963c 114static NXHashTable *prototypes = NULL;
13d88034
A
115 /* table of all prototypes */
116
117static void bootstrap (void) {
118 free(malloc(8));
7af964d1 119 prototypes = ALLOCTABLE (DEFAULT_ZONE);
13d88034
A
120 prototypes->prototype = &protoPrototype;
121 prototypes->count = 1;
122 prototypes->nbBuckets = 1; /* has to be 1 so that the right bucket is 0 */
7af964d1 123 prototypes->buckets = ALLOCBUCKETS(DEFAULT_ZONE, 1);
13d88034
A
124 prototypes->info = NULL;
125 ((HashBucket *) prototypes->buckets)[0].count = 1;
126 ((HashBucket *) prototypes->buckets)[0].elements.one = &protoPrototype;
127 };
128
129int NXPtrIsEqual (const void *info, const void *data1, const void *data2) {
130 return data1 == data2;
131 };
132
133/*************************************************************************
134 *
135 * On z'y va
136 *
137 *************************************************************************/
138
139NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) {
7af964d1 140 return NXCreateHashTableFromZone(prototype, capacity, info, DEFAULT_ZONE);
13d88034
A
141}
142
143NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z) {
144 NXHashTable *table;
145 NXHashTablePrototype *proto;
146
147 table = ALLOCTABLE(z);
148 if (! prototypes) bootstrap ();
149 if (! prototype.hash) prototype.hash = NXPtrHash;
150 if (! prototype.isEqual) prototype.isEqual = NXPtrIsEqual;
151 if (! prototype.free) prototype.free = NXNoEffectFree;
152 if (prototype.style) {
b3962a83 153 _objc_inform ("*** NXCreateHashTable: invalid style\n");
13d88034
A
154 return NULL;
155 };
cd5f04f5 156 proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype);
13d88034
A
157 if (! proto) {
158 proto
7af964d1 159 = (NXHashTablePrototype *) malloc(sizeof (NXHashTablePrototype));
13d88034
A
160 bcopy ((const char*)&prototype, (char*)proto, sizeof (NXHashTablePrototype));
161 (void) NXHashInsert (prototypes, proto);
cd5f04f5 162 proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype);
13d88034 163 if (! proto) {
b3962a83 164 _objc_inform ("*** NXCreateHashTable: bug\n");
13d88034
A
165 return NULL;
166 };
167 };
168 table->prototype = proto; table->count = 0; table->info = info;
7af964d1 169 table->nbBuckets = GOOD_CAPACITY(capacity);
13d88034
A
170 table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
171 return table;
172 }
173
174static void freeBucketPairs (void (*freeProc)(const void *info, void *data), HashBucket bucket, const void *info) {
175 unsigned j = bucket.count;
176 const void **pairs;
177
178 if (j == 1) {
179 (*freeProc) (info, (void *) bucket.elements.one);
180 return;
181 };
182 pairs = bucket.elements.many;
183 while (j--) {
184 (*freeProc) (info, (void *) *pairs);
185 pairs ++;
186 };
cd5f04f5 187 FREEPAIRS (bucket.elements.many);
13d88034
A
188 };
189
190static void freeBuckets (NXHashTable *table, int freeObjects) {
191 unsigned i = table->nbBuckets;
192 HashBucket *buckets = (HashBucket *) table->buckets;
193
194 while (i--) {
195 if (buckets->count) {
196 freeBucketPairs ((freeObjects) ? table->prototype->free : NXNoEffectFree, *buckets, table->info);
197 buckets->count = 0;
198 buckets->elements.one = NULL;
199 };
200 buckets++;
201 };
202 };
203
204void NXFreeHashTable (NXHashTable *table) {
205 freeBuckets (table, YES);
206 free (table->buckets);
207 free (table);
208 };
209
210void NXEmptyHashTable (NXHashTable *table) {
211 freeBuckets (table, NO);
212 table->count = 0;
213 }
214
215void NXResetHashTable (NXHashTable *table) {
216 freeBuckets (table, YES);
217 table->count = 0;
218}
219
13d88034
A
220BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2) {
221 if (table1 == table2) return YES;
222 if (NXCountHashTable (table1) != NXCountHashTable (table2)) return NO;
223 else {
224 void *data;
225 NXHashState state = NXInitHashState (table1);
226 while (NXNextHashState (table1, &state, &data)) {
227 if (! NXHashMember (table2, data)) return NO;
228 }
229 return YES;
230 }
231}
232
233NXHashTable *NXCopyHashTable (NXHashTable *table) {
7af964d1 234 NXHashTable *newt;
13d88034
A
235 NXHashState state = NXInitHashState (table);
236 void *data;
7257e56c 237 __unused void *z = ZONE_FROM_PTR(table);
13d88034 238
7af964d1
A
239 newt = ALLOCTABLE(z);
240 newt->prototype = table->prototype; newt->count = 0;
241 newt->info = table->info;
242 newt->nbBuckets = table->nbBuckets;
243 newt->buckets = ALLOCBUCKETS(z, newt->nbBuckets);
13d88034 244 while (NXNextHashState (table, &state, &data))
7af964d1
A
245 NXHashInsert (newt, data);
246 return newt;
13d88034
A
247 }
248
249unsigned NXCountHashTable (NXHashTable *table) {
250 return table->count;
251 }
252
253int NXHashMember (NXHashTable *table, const void *data) {
254 HashBucket *bucket = BUCKETOF(table, data);
255 unsigned j = bucket->count;
256 const void **pairs;
257
258 if (! j) return 0;
259 if (j == 1) {
260 return ISEQUAL(table, data, bucket->elements.one);
261 };
262 pairs = bucket->elements.many;
263 while (j--) {
264 /* we don't cache isEqual because lists are short */
265 if (ISEQUAL(table, data, *pairs)) return 1;
266 pairs ++;
267 };
268 return 0;
269 }
270
271void *NXHashGet (NXHashTable *table, const void *data) {
272 HashBucket *bucket = BUCKETOF(table, data);
273 unsigned j = bucket->count;
274 const void **pairs;
275
276 if (! j) return NULL;
277 if (j == 1) {
278 return ISEQUAL(table, data, bucket->elements.one)
279 ? (void *) bucket->elements.one : NULL;
280 };
281 pairs = bucket->elements.many;
282 while (j--) {
283 /* we don't cache isEqual because lists are short */
284 if (ISEQUAL(table, data, *pairs)) return (void *) *pairs;
285 pairs ++;
286 };
287 return NULL;
288 }
289
cd5f04f5 290unsigned _NXHashCapacity (NXHashTable *table) {
2bfd4448
A
291 return table->nbBuckets;
292 }
293
cd5f04f5 294void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) {
13d88034
A
295 /* Rehash: we create a pseudo table pointing really to the old guys,
296 extend self, copy the old pairs, and free the pseudo table */
297 NXHashTable *old;
298 NXHashState state;
299 void *aux;
7257e56c 300 __unused void *z = ZONE_FROM_PTR(table);
13d88034
A
301
302 old = ALLOCTABLE(z);
303 old->prototype = table->prototype; old->count = table->count;
304 old->nbBuckets = table->nbBuckets; old->buckets = table->buckets;
2bfd4448 305 table->nbBuckets = newCapacity;
13d88034
A
306 table->count = 0; table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
307 state = NXInitHashState (old);
308 while (NXNextHashState (old, &state, &aux))
309 (void) NXHashInsert (table, aux);
310 freeBuckets (old, NO);
311 if (old->count != table->count)
b3962a83 312 _objc_inform("*** hashtable: count differs after rehashing; probably indicates a broken invariant: there are x and y such as isEqual(x, y) is TRUE but hash(x) != hash (y)\n");
13d88034
A
313 free (old->buckets);
314 free (old);
2bfd4448
A
315 }
316
317static void _NXHashRehash (NXHashTable *table) {
7af964d1 318 _NXHashRehashToCapacity (table, MORE_CAPACITY(table->nbBuckets));
2bfd4448 319 }
13d88034
A
320
321void *NXHashInsert (NXHashTable *table, const void *data) {
322 HashBucket *bucket = BUCKETOF(table, data);
323 unsigned j = bucket->count;
324 const void **pairs;
7af964d1 325 const void **newt;
7257e56c 326 __unused void *z = ZONE_FROM_PTR(table);
13d88034
A
327
328 if (! j) {
329 bucket->count++; bucket->elements.one = data;
330 table->count++;
331 return NULL;
332 };
333 if (j == 1) {
334 if (ISEQUAL(table, data, bucket->elements.one)) {
335 const void *old = bucket->elements.one;
336 bucket->elements.one = data;
337 return (void *) old;
338 };
7af964d1
A
339 newt = ALLOCPAIRS(z, 2);
340 newt[1] = bucket->elements.one;
341 *newt = data;
342 bucket->count++; bucket->elements.many = newt;
13d88034
A
343 table->count++;
344 if (table->count > table->nbBuckets) _NXHashRehash (table);
345 return NULL;
346 };
347 pairs = bucket->elements.many;
348 while (j--) {
349 /* we don't cache isEqual because lists are short */
350 if (ISEQUAL(table, data, *pairs)) {
351 const void *old = *pairs;
352 *pairs = data;
353 return (void *) old;
354 };
355 pairs ++;
356 };
357 /* we enlarge this bucket; and put new data in front */
7af964d1
A
358 newt = ALLOCPAIRS(z, bucket->count+1);
359 if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE);
360 *newt = data;
cd5f04f5 361 FREEPAIRS (bucket->elements.many);
7af964d1 362 bucket->count++; bucket->elements.many = newt;
13d88034
A
363 table->count++;
364 if (table->count > table->nbBuckets) _NXHashRehash (table);
365 return NULL;
366 }
367
368void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) {
369 HashBucket *bucket = BUCKETOF(table, data);
370 unsigned j = bucket->count;
371 const void **pairs;
7af964d1 372 const void **newt;
7257e56c 373 __unused void *z = ZONE_FROM_PTR(table);
13d88034
A
374
375 if (! j) {
376 bucket->count++; bucket->elements.one = data;
377 table->count++;
378 return (void *) data;
379 };
380 if (j == 1) {
381 if (ISEQUAL(table, data, bucket->elements.one))
382 return (void *) bucket->elements.one;
7af964d1
A
383 newt = ALLOCPAIRS(z, 2);
384 newt[1] = bucket->elements.one;
385 *newt = data;
386 bucket->count++; bucket->elements.many = newt;
13d88034
A
387 table->count++;
388 if (table->count > table->nbBuckets) _NXHashRehash (table);
389 return (void *) data;
390 };
391 pairs = bucket->elements.many;
392 while (j--) {
393 /* we don't cache isEqual because lists are short */
394 if (ISEQUAL(table, data, *pairs))
395 return (void *) *pairs;
396 pairs ++;
397 };
398 /* we enlarge this bucket; and put new data in front */
7af964d1
A
399 newt = ALLOCPAIRS(z, bucket->count+1);
400 if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE);
401 *newt = data;
cd5f04f5 402 FREEPAIRS (bucket->elements.many);
7af964d1 403 bucket->count++; bucket->elements.many = newt;
13d88034
A
404 table->count++;
405 if (table->count > table->nbBuckets) _NXHashRehash (table);
406 return (void *) data;
407 }
408
409void *NXHashRemove (NXHashTable *table, const void *data) {
410 HashBucket *bucket = BUCKETOF(table, data);
411 unsigned j = bucket->count;
412 const void **pairs;
7af964d1 413 const void **newt;
7257e56c 414 __unused void *z = ZONE_FROM_PTR(table);
13d88034
A
415
416 if (! j) return NULL;
417 if (j == 1) {
418 if (! ISEQUAL(table, data, bucket->elements.one)) return NULL;
419 data = bucket->elements.one;
420 table->count--; bucket->count--; bucket->elements.one = NULL;
421 return (void *) data;
422 };
423 pairs = bucket->elements.many;
424 if (j == 2) {
425 if (ISEQUAL(table, data, pairs[0])) {
426 bucket->elements.one = pairs[1]; data = pairs[0];
427 }
428 else if (ISEQUAL(table, data, pairs[1])) {
429 bucket->elements.one = pairs[0]; data = pairs[1];
430 }
431 else return NULL;
cd5f04f5 432 FREEPAIRS (pairs);
13d88034
A
433 table->count--; bucket->count--;
434 return (void *) data;
435 };
436 while (j--) {
437 if (ISEQUAL(table, data, *pairs)) {
438 data = *pairs;
439 /* we shrink this bucket */
7af964d1 440 newt = (bucket->count-1)
13d88034
A
441 ? ALLOCPAIRS(z, bucket->count-1) : NULL;
442 if (bucket->count-1 != j)
7af964d1 443 bcopy ((const char*)bucket->elements.many, (char*)newt, PTRSIZE*(bucket->count-j-1));
13d88034 444 if (j)
7af964d1 445 bcopy ((const char*)(bucket->elements.many + bucket->count-j), (char*)(newt+bucket->count-j-1), PTRSIZE*j);
cd5f04f5 446 FREEPAIRS (bucket->elements.many);
7af964d1 447 table->count--; bucket->count--; bucket->elements.many = newt;
13d88034
A
448 return (void *) data;
449 };
450 pairs ++;
451 };
452 return NULL;
453 }
454
455NXHashState NXInitHashState (NXHashTable *table) {
456 NXHashState state;
457
458 state.i = table->nbBuckets;
459 state.j = 0;
460 return state;
461 };
462
463int NXNextHashState (NXHashTable *table, NXHashState *state, void **data) {
464 HashBucket *buckets = (HashBucket *) table->buckets;
465
466 while (state->j == 0) {
467 if (state->i == 0) return NO;
468 state->i--; state->j = buckets[state->i].count;
469 }
470 state->j--;
471 buckets += state->i;
472 *data = (void *) ((buckets->count == 1)
473 ? buckets->elements.one : buckets->elements.many[state->j]);
474 return YES;
475 };
476
477/*************************************************************************
478 *
479 * Conveniences
480 *
481 *************************************************************************/
482
b3962a83
A
483uintptr_t NXPtrHash (const void *info, const void *data) {
484 return (((uintptr_t) data) >> 16) ^ ((uintptr_t) data);
13d88034
A
485 };
486
b3962a83 487uintptr_t NXStrHash (const void *info, const void *data) {
8070259c
A
488 uintptr_t hash = 0;
489 unsigned char *s = (unsigned char *) data;
13d88034
A
490 /* unsigned to avoid a sign-extend */
491 /* unroll the loop */
492 if (s) for (; ; ) {
493 if (*s == '\0') break;
b3962a83 494 hash ^= (uintptr_t) *s++;
13d88034 495 if (*s == '\0') break;
b3962a83 496 hash ^= (uintptr_t) *s++ << 8;
13d88034 497 if (*s == '\0') break;
b3962a83 498 hash ^= (uintptr_t) *s++ << 16;
13d88034 499 if (*s == '\0') break;
b3962a83 500 hash ^= (uintptr_t) *s++ << 24;
13d88034
A
501 }
502 return hash;
503 };
504
505int NXStrIsEqual (const void *info, const void *data1, const void *data2) {
506 if (data1 == data2) return YES;
507 if (! data1) return ! strlen ((char *) data2);
508 if (! data2) return ! strlen ((char *) data1);
509 if (((char *) data1)[0] != ((char *) data2)[0]) return NO;
510 return (strcmp ((char *) data1, (char *) data2)) ? NO : YES;
511 };
512
513void NXReallyFree (const void *info, void *data) {
514 free (data);
515 };
516
517/* All the following functions are really private, made non-static only for the benefit of shlibs */
b3962a83 518static uintptr_t hashPtrStructKey (const void *info, const void *data) {
13d88034
A
519 return NXPtrHash(info, *((void **) data));
520 };
521
522static int isEqualPtrStructKey (const void *info, const void *data1, const void *data2) {
523 return NXPtrIsEqual (info, *((void **) data1), *((void **) data2));
524 };
525
b3962a83 526static uintptr_t hashStrStructKey (const void *info, const void *data) {
13d88034
A
527 return NXStrHash(info, *((char **) data));
528 };
529
530static int isEqualStrStructKey (const void *info, const void *data1, const void *data2) {
531 return NXStrIsEqual (info, *((char **) data1), *((char **) data2));
532 };
533
534const NXHashTablePrototype NXPtrPrototype = {
535 NXPtrHash, NXPtrIsEqual, NXNoEffectFree, 0
536 };
537
538const NXHashTablePrototype NXStrPrototype = {
539 NXStrHash, NXStrIsEqual, NXNoEffectFree, 0
540 };
541
542const NXHashTablePrototype NXPtrStructKeyPrototype = {
543 hashPtrStructKey, isEqualPtrStructKey, NXReallyFree, 0
544 };
545
546const NXHashTablePrototype NXStrStructKeyPrototype = {
547 hashStrStructKey, isEqualStrStructKey, NXReallyFree, 0
548 };
549
550/*************************************************************************
551 *
552 * Unique strings
553 *
554 *************************************************************************/
555
7af964d1
A
556#if !__OBJC2__ && !TARGET_OS_WIN32
557
13d88034
A
558/* the implementation could be made faster at the expense of memory if the size of the strings were kept around */
559static NXHashTable *uniqueStrings = NULL;
560
561/* this is based on most apps using a few K of strings, and an average string size of 15 using sqrt(2*dataAlloced*perChunkOverhead) */
562#define CHUNK_SIZE 360
563
564static int accessUniqueString = 0;
565
566static char *z = NULL;
b3962a83 567static size_t zSize = 0;
7af964d1 568static mutex_t lock = MUTEX_INITIALIZER;
13d88034
A
569
570static const char *CopyIntoReadOnly (const char *str) {
b3962a83 571 size_t len = strlen (str) + 1;
cd5f04f5 572 char *result;
13d88034
A
573
574 if (len > CHUNK_SIZE/2) { /* dont let big strings waste space */
cd5f04f5
A
575 result = (char *)malloc (len);
576 bcopy (str, result, len);
577 return result;
13d88034
A
578 }
579
7af964d1 580 mutex_lock (&lock);
13d88034
A
581 if (zSize < len) {
582 zSize = CHUNK_SIZE *((len + CHUNK_SIZE - 1) / CHUNK_SIZE);
583 /* not enough room, we try to allocate. If no room left, too bad */
cd5f04f5 584 z = (char *)malloc (zSize);
13d88034
A
585 };
586
cd5f04f5
A
587 result = z;
588 bcopy (str, result, len);
13d88034
A
589 z += len;
590 zSize -= len;
7af964d1 591 mutex_unlock (&lock);
cd5f04f5 592 return result;
13d88034
A
593 };
594
595NXAtom NXUniqueString (const char *buffer) {
596 const char *previous;
597
598 if (! buffer) return buffer;
599 accessUniqueString++;
600 if (! uniqueStrings)
601 uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
602 previous = (const char *) NXHashGet (uniqueStrings, buffer);
603 if (previous) return previous;
604 previous = CopyIntoReadOnly (buffer);
605 if (NXHashInsert (uniqueStrings, previous)) {
b3962a83 606 _objc_inform ("*** NXUniqueString: invariant broken\n");
13d88034
A
607 return NULL;
608 };
609 return previous;
610 };
611
612NXAtom NXUniqueStringNoCopy (const char *string) {
613 accessUniqueString++;
614 if (! uniqueStrings)
615 uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
616 return (const char *) NXHashInsertIfAbsent (uniqueStrings, string);
617 };
618
619#define BUF_SIZE 256
620
621NXAtom NXUniqueStringWithLength (const char *buffer, int length) {
622 NXAtom atom;
623 char *nullTermStr;
624 char stackBuf[BUF_SIZE];
625
626 if (length+1 > BUF_SIZE)
cd5f04f5 627 nullTermStr = (char *)malloc (length+1);
13d88034
A
628 else
629 nullTermStr = stackBuf;
630 bcopy (buffer, nullTermStr, length);
631 nullTermStr[length] = '\0';
632 atom = NXUniqueString (nullTermStr);
633 if (length+1 > BUF_SIZE)
634 free (nullTermStr);
635 return atom;
636 };
637
cd5f04f5 638char *NXCopyStringBufferFromZone (const char *str, void *zone) {
8972963c 639#if !SUPPORT_ZONES
7af964d1
A
640 return strdup(str);
641#else
cd5f04f5 642 return strcpy ((char *) malloc_zone_malloc((malloc_zone_t *)zone, strlen (str) + 1), str);
7af964d1 643#endif
13d88034
A
644 };
645
646char *NXCopyStringBuffer (const char *str) {
7af964d1 647 return strdup(str);
13d88034
A
648 };
649
7af964d1 650#endif