hashtable2.mm 21 KB

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