maptable.mm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. * Copyright (c) 1999-2003, 2005-2007 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. /* maptable.m
  24. Copyright 1990-1996 NeXT Software, Inc.
  25. Created by Bertrand Serlet, August 1990
  26. */
  27. #include <string.h>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include "objc-private.h"
  31. #include "maptable.h"
  32. #include "hashtable2.h"
  33. /****** Macros and utilities ****************************/
  34. #if defined(DEBUG)
  35. #define INLINE
  36. #else
  37. #define INLINE inline
  38. #endif
  39. typedef struct _MapPair {
  40. const void *key;
  41. const void *value;
  42. } MapPair;
  43. static INLINE unsigned xorHash(unsigned hash) {
  44. unsigned xored = (hash & 0xffff) ^ (hash >> 16);
  45. return ((xored * 65521) + hash);
  46. }
  47. static INLINE unsigned bucketOf(NXMapTable *table, const void *key) {
  48. unsigned hash = (table->prototype->hash)(table, key);
  49. return hash & table->nbBucketsMinusOne;
  50. }
  51. static INLINE int isEqual(NXMapTable *table, const void *key1, const void *key2) {
  52. return (key1 == key2) ? 1 : (table->prototype->isEqual)(table, key1, key2);
  53. }
  54. static INLINE unsigned nextIndex(NXMapTable *table, unsigned index) {
  55. return (index + 1) & table->nbBucketsMinusOne;
  56. }
  57. static INLINE void *allocBuckets(void *z, unsigned nb) {
  58. MapPair *pairs = 1+(MapPair *)malloc_zone_malloc((malloc_zone_t *)z, ((nb+1) * sizeof(MapPair)));
  59. MapPair *pair = pairs;
  60. while (nb--) { pair->key = NX_MAPNOTAKEY; pair->value = NULL; pair++; }
  61. return pairs;
  62. }
  63. static INLINE void freeBuckets(void *p) {
  64. free(-1+(MapPair *)p);
  65. }
  66. /***** Global data and bootstrap **********************/
  67. static int isEqualPrototype (const void *info, const void *data1, const void *data2) {
  68. NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
  69. NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
  70. return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
  71. };
  72. static uintptr_t hashPrototype (const void *info, const void *data) {
  73. NXHashTablePrototype *proto = (NXHashTablePrototype *) data;
  74. return NXPtrHash(info, (void*)proto->hash) ^ NXPtrHash(info, (void*)proto->isEqual) ^ NXPtrHash(info, (void*)proto->free) ^ (uintptr_t) proto->style;
  75. };
  76. static NXHashTablePrototype protoPrototype = {
  77. hashPrototype, isEqualPrototype, NXNoEffectFree, 0
  78. };
  79. static NXHashTable *prototypes = NULL;
  80. /* table of all prototypes */
  81. /**** Fundamentals Operations **************/
  82. NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) {
  83. NXMapTable *table = (NXMapTable *)malloc_zone_malloc((malloc_zone_t *)z, sizeof(NXMapTable));
  84. NXMapTablePrototype *proto;
  85. if (! prototypes) prototypes = NXCreateHashTable(protoPrototype, 0, NULL);
  86. if (! prototype.hash || ! prototype.isEqual || ! prototype.free || prototype.style) {
  87. _objc_inform("*** NXCreateMapTable: invalid creation parameters\n");
  88. return NULL;
  89. }
  90. proto = (NXMapTablePrototype *)NXHashGet(prototypes, &prototype);
  91. if (! proto) {
  92. proto = (NXMapTablePrototype *)malloc(sizeof(NXMapTablePrototype));
  93. *proto = prototype;
  94. (void)NXHashInsert(prototypes, proto);
  95. }
  96. table->prototype = proto; table->count = 0;
  97. table->nbBucketsMinusOne = exp2u(log2u(capacity)+1) - 1;
  98. table->buckets = allocBuckets(z, table->nbBucketsMinusOne + 1);
  99. return table;
  100. }
  101. NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity) {
  102. return NXCreateMapTableFromZone(prototype, capacity, malloc_default_zone());
  103. }
  104. void NXFreeMapTable(NXMapTable *table) {
  105. NXResetMapTable(table);
  106. freeBuckets(table->buckets);
  107. free(table);
  108. }
  109. void NXResetMapTable(NXMapTable *table) {
  110. MapPair *pairs = (MapPair *)table->buckets;
  111. void (*freeProc)(struct _NXMapTable *, void *, void *) = table->prototype->free;
  112. unsigned index = table->nbBucketsMinusOne + 1;
  113. while (index--) {
  114. if (pairs->key != NX_MAPNOTAKEY) {
  115. freeProc(table, (void *)pairs->key, (void *)pairs->value);
  116. pairs->key = NX_MAPNOTAKEY; pairs->value = NULL;
  117. }
  118. pairs++;
  119. }
  120. table->count = 0;
  121. }
  122. BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) {
  123. if (table1 == table2) return YES;
  124. if (table1->count != table2->count) return NO;
  125. else {
  126. const void *key;
  127. const void *value;
  128. NXMapState state = NXInitMapState(table1);
  129. while (NXNextMapState(table1, &state, &key, &value)) {
  130. if (NXMapMember(table2, key, (void**)&value) == NX_MAPNOTAKEY) return NO;
  131. }
  132. return YES;
  133. }
  134. }
  135. unsigned NXCountMapTable(NXMapTable *table) { return table->count; }
  136. #if __x86_64__
  137. extern "C" void __NXMAPTABLE_CORRUPTED__
  138. (const void *table, const void *buckets, uint64_t count,
  139. uint64_t nbBucketsMinusOne, uint64_t badkeys, uint64_t index,
  140. uint64_t index2, uint64_t pairIndexes, const void *key1,
  141. const void *value1, const void *key2, const void *value2,
  142. const void *key3, const void *value3);
  143. static int _mapStrIsEqual(NXMapTable *table, const void *key1, const void *key2);
  144. asm("\n .text"
  145. "\n .private_extern ___NXMAPTABLE_CORRUPTED__"
  146. "\n ___NXMAPTABLE_CORRUPTED__:"
  147. // push a frame for the unwinder to see
  148. "\n pushq %rbp"
  149. "\n mov %rsp, %rbp"
  150. // push register parameters to the stack in reverse order
  151. "\n pushq %r9"
  152. "\n pushq %r8"
  153. "\n pushq %rcx"
  154. "\n pushq %rdx"
  155. "\n pushq %rsi"
  156. "\n pushq %rdi"
  157. // pop the pushed register parameters into their destinations
  158. "\n popq %rax" // table
  159. "\n popq %rbx" // buckets
  160. "\n popq %rcx" // count
  161. "\n popq %rdx" // nbBucketsMinusOne
  162. "\n popq %rdi" // badkeys
  163. "\n popq %rsi" // index
  164. // read stack parameters into their destinations
  165. "\n mov 0*8+16(%rbp), %r8" // index2
  166. "\n mov 1*8+16(%rbp), %r9" // pairIndexes
  167. "\n mov 2*8+16(%rbp), %r10" // key1
  168. "\n mov 3*8+16(%rbp), %r11" // value1
  169. "\n mov 4*8+16(%rbp), %r12" // key2
  170. "\n mov 5*8+16(%rbp), %r13" // value2
  171. "\n mov 6*8+16(%rbp), %r14" // key3
  172. "\n mov 7*8+16(%rbp), %r15" // value3
  173. "\n ud2");
  174. #endif
  175. // Look for a particular case of data corruption (rdar://36373000)
  176. // and investigate it further before crashing.
  177. static void validateKey(NXMapTable *table, MapPair *pair,
  178. unsigned index, unsigned index2)
  179. {
  180. #if __x86_64__
  181. # define BADKEY ((void * _Nonnull)(0xfffffffffffffffeULL))
  182. if (pair->key != BADKEY ||
  183. table->prototype->isEqual != _mapStrIsEqual)
  184. {
  185. return;
  186. }
  187. _objc_inform_now_and_on_crash
  188. ("NXMapTable %p (%p) has invalid key/value pair %p->%p (%p)",
  189. table, table->buckets, pair->key, pair->value, pair);
  190. _objc_inform_now_and_on_crash
  191. ("table %p, buckets %p, count %u, nbNucketsMinusOne %u, "
  192. "prototype %p (hash %p, isEqual %p, free %p)",
  193. table, table->buckets, table->count, table->nbBucketsMinusOne,
  194. table->prototype, table->prototype->hash, table->prototype->isEqual,
  195. table->prototype->free);
  196. // Count the number of bad keys in the table.
  197. MapPair *pairs = (MapPair *)table->buckets;
  198. unsigned badKeys = 0;
  199. for (unsigned i = 0; i < table->nbBucketsMinusOne+1; i++) {
  200. if (pairs[i].key == BADKEY) badKeys++;
  201. }
  202. _objc_inform_now_and_on_crash("%u invalid keys in table", badKeys);
  203. // Record some additional key pairs for posterity.
  204. unsigned pair2Index = nextIndex(table, index);
  205. unsigned pair3Index = nextIndex(table, pair2Index);
  206. MapPair *pair2 = pairs + pair2Index;
  207. MapPair *pair3 = pairs + pair3Index;
  208. uint64_t pairIndexes = ((uint64_t)pair2Index << 32) | pair3Index;
  209. // Save a bunch of values to registers so we can see them in the crash log.
  210. __NXMAPTABLE_CORRUPTED__
  211. (// rax, rbx, rcx, rdx
  212. table, table->buckets, table->count, table->nbBucketsMinusOne,
  213. // rdi, rsi, skip rbp, skip rsp
  214. badKeys, index,
  215. // r8, r9, r10, r11
  216. index2, pairIndexes, pair->key, pair->value,
  217. // r12, r13, r14, r15
  218. pair2->key, pair2->value, pair3->key, pair3->value);
  219. #endif
  220. }
  221. static INLINE void *_NXMapMember(NXMapTable *table, const void *key, void **value) {
  222. MapPair *pairs = (MapPair *)table->buckets;
  223. unsigned index = bucketOf(table, key);
  224. MapPair *pair = pairs + index;
  225. if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
  226. validateKey(table, pair, index, index);
  227. if (isEqual(table, pair->key, key)) {
  228. *value = (void *)pair->value;
  229. return (void *)pair->key;
  230. } else {
  231. unsigned index2 = index;
  232. while ((index2 = nextIndex(table, index2)) != index) {
  233. pair = pairs + index2;
  234. if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
  235. validateKey(table, pair, index, index2);
  236. if (isEqual(table, pair->key, key)) {
  237. *value = (void *)pair->value;
  238. return (void *)pair->key;
  239. }
  240. }
  241. return NX_MAPNOTAKEY;
  242. }
  243. }
  244. void *NXMapMember(NXMapTable *table, const void *key, void **value) {
  245. return _NXMapMember(table, key, value);
  246. }
  247. void *NXMapGet(NXMapTable *table, const void *key) {
  248. void *value;
  249. return (_NXMapMember(table, key, &value) != NX_MAPNOTAKEY) ? value : NULL;
  250. }
  251. static void _NXMapRehash(NXMapTable *table) {
  252. MapPair *pairs = (MapPair *)table->buckets;
  253. MapPair *pair = pairs;
  254. unsigned numBuckets = table->nbBucketsMinusOne + 1;
  255. unsigned index = numBuckets;
  256. unsigned oldCount = table->count;
  257. table->nbBucketsMinusOne = 2 * numBuckets - 1;
  258. table->count = 0;
  259. table->buckets = allocBuckets(malloc_zone_from_ptr(table), table->nbBucketsMinusOne + 1);
  260. while (index--) {
  261. if (pair->key != NX_MAPNOTAKEY) {
  262. (void)NXMapInsert(table, pair->key, pair->value);
  263. }
  264. pair++;
  265. }
  266. if (oldCount != table->count)
  267. _objc_inform("*** maptable: 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");
  268. freeBuckets(pairs);
  269. }
  270. void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
  271. MapPair *pairs = (MapPair *)table->buckets;
  272. unsigned index = bucketOf(table, key);
  273. MapPair *pair = pairs + index;
  274. if (key == NX_MAPNOTAKEY) {
  275. _objc_inform("*** NXMapInsert: invalid key: -1\n");
  276. return NULL;
  277. }
  278. unsigned numBuckets = table->nbBucketsMinusOne + 1;
  279. if (pair->key == NX_MAPNOTAKEY) {
  280. pair->key = key; pair->value = value;
  281. table->count++;
  282. if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
  283. return NULL;
  284. }
  285. if (isEqual(table, pair->key, key)) {
  286. const void *old = pair->value;
  287. if (old != value) pair->value = value;/* avoid writing unless needed! */
  288. return (void *)old;
  289. } else if (table->count == numBuckets) {
  290. /* no room: rehash and retry */
  291. _NXMapRehash(table);
  292. return NXMapInsert(table, key, value);
  293. } else {
  294. unsigned index2 = index;
  295. while ((index2 = nextIndex(table, index2)) != index) {
  296. pair = pairs + index2;
  297. if (pair->key == NX_MAPNOTAKEY) {
  298. pair->key = key; pair->value = value;
  299. table->count++;
  300. if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
  301. return NULL;
  302. }
  303. if (isEqual(table, pair->key, key)) {
  304. const void *old = pair->value;
  305. if (old != value) pair->value = value;/* avoid writing unless needed! */
  306. return (void *)old;
  307. }
  308. }
  309. /* no room: can't happen! */
  310. _objc_inform("**** NXMapInsert: bug\n");
  311. return NULL;
  312. }
  313. }
  314. static int mapRemove = 0;
  315. void *NXMapRemove(NXMapTable *table, const void *key) {
  316. MapPair *pairs = (MapPair *)table->buckets;
  317. unsigned index = bucketOf(table, key);
  318. MapPair *pair = pairs + index;
  319. unsigned chain = 1; /* number of non-nil pairs in a row */
  320. int found = 0;
  321. const void *old = NULL;
  322. if (pair->key == NX_MAPNOTAKEY) return NULL;
  323. mapRemove ++;
  324. /* compute chain */
  325. {
  326. unsigned index2 = index;
  327. if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
  328. while ((index2 = nextIndex(table, index2)) != index) {
  329. pair = pairs + index2;
  330. if (pair->key == NX_MAPNOTAKEY) break;
  331. if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
  332. chain++;
  333. }
  334. }
  335. if (! found) return NULL;
  336. if (found != 1) _objc_inform("**** NXMapRemove: incorrect table\n");
  337. /* remove then reinsert */
  338. {
  339. MapPair buffer[16];
  340. MapPair *aux = (chain > 16) ? (MapPair *)malloc(sizeof(MapPair)*(chain-1)) : buffer;
  341. unsigned auxnb = 0;
  342. int nb = chain;
  343. unsigned index2 = index;
  344. while (nb--) {
  345. pair = pairs + index2;
  346. if (! isEqual(table, pair->key, key)) aux[auxnb++] = *pair;
  347. pair->key = NX_MAPNOTAKEY; pair->value = NULL;
  348. index2 = nextIndex(table, index2);
  349. }
  350. table->count -= chain;
  351. if (auxnb != chain-1) _objc_inform("**** NXMapRemove: bug\n");
  352. while (auxnb--) NXMapInsert(table, aux[auxnb].key, aux[auxnb].value);
  353. if (chain > 16) free(aux);
  354. }
  355. return (void *)old;
  356. }
  357. NXMapState NXInitMapState(NXMapTable *table) {
  358. NXMapState state;
  359. state.index = table->nbBucketsMinusOne + 1;
  360. return state;
  361. }
  362. int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value) {
  363. MapPair *pairs = (MapPair *)table->buckets;
  364. while (state->index--) {
  365. MapPair *pair = pairs + state->index;
  366. if (pair->key != NX_MAPNOTAKEY) {
  367. *key = pair->key; *value = pair->value;
  368. return YES;
  369. }
  370. }
  371. return NO;
  372. }
  373. /***********************************************************************
  374. * NXMapKeyCopyingInsert
  375. * Like NXMapInsert, but strdups the key if necessary.
  376. * Used to prevent stale pointers when bundles are unloaded.
  377. **********************************************************************/
  378. void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value)
  379. {
  380. void *realKey;
  381. void *realValue = NULL;
  382. if ((realKey = NXMapMember(table, key, &realValue)) != NX_MAPNOTAKEY) {
  383. // key DOES exist in table - use table's key for insertion
  384. } else {
  385. // key DOES NOT exist in table - copy the new key before insertion
  386. realKey = (void *)strdupIfMutable((char *)key);
  387. }
  388. return NXMapInsert(table, realKey, value);
  389. }
  390. /***********************************************************************
  391. * NXMapKeyFreeingRemove
  392. * Like NXMapRemove, but frees the existing key if necessary.
  393. * Used to prevent stale pointers when bundles are unloaded.
  394. **********************************************************************/
  395. void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key)
  396. {
  397. void *realKey;
  398. void *realValue = NULL;
  399. if ((realKey = NXMapMember(table, key, &realValue)) != NX_MAPNOTAKEY) {
  400. // key DOES exist in table - remove pair and free key
  401. realValue = NXMapRemove(table, realKey);
  402. // free the key from the table, not necessarily the one given
  403. freeIfMutable((char *)realKey);
  404. return realValue;
  405. } else {
  406. // key DOES NOT exist in table - nothing to do
  407. return NULL;
  408. }
  409. }
  410. /**** Conveniences *************************************/
  411. static unsigned _mapPtrHash(NXMapTable *table, const void *key) {
  412. #ifdef __LP64__
  413. return (unsigned)(((uintptr_t)key) >> 3);
  414. #else
  415. return ((uintptr_t)key) >> 2;
  416. #endif
  417. }
  418. static unsigned _mapStrHash(NXMapTable *table, const void *key) {
  419. unsigned hash = 0;
  420. unsigned char *s = (unsigned char *)key;
  421. /* unsigned to avoid a sign-extend */
  422. /* unroll the loop */
  423. if (s) for (; ; ) {
  424. if (*s == '\0') break;
  425. hash ^= *s++;
  426. if (*s == '\0') break;
  427. hash ^= *s++ << 8;
  428. if (*s == '\0') break;
  429. hash ^= *s++ << 16;
  430. if (*s == '\0') break;
  431. hash ^= *s++ << 24;
  432. }
  433. return xorHash(hash);
  434. }
  435. static int _mapPtrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
  436. return key1 == key2;
  437. }
  438. static int _mapStrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
  439. if (key1 == key2) return YES;
  440. if (! key1) return ! strlen ((char *) key2);
  441. if (! key2) return ! strlen ((char *) key1);
  442. if (((char *) key1)[0] != ((char *) key2)[0]) return NO;
  443. return (strcmp((char *) key1, (char *) key2)) ? NO : YES;
  444. }
  445. static void _mapNoFree(NXMapTable *table, void *key, void *value) {}
  446. const NXMapTablePrototype NXPtrValueMapPrototype = {
  447. _mapPtrHash, _mapPtrIsEqual, _mapNoFree, 0
  448. };
  449. const NXMapTablePrototype NXStrValueMapPrototype = {
  450. _mapStrHash, _mapStrIsEqual, _mapNoFree, 0
  451. };
  452. #if !__OBJC2__ && !TARGET_OS_WIN32
  453. /* This only works with class Object, which is unavailable. */
  454. /* Method prototypes */
  455. @interface DoesNotExist
  456. + (id)class;
  457. + (id)initialize;
  458. - (id)description;
  459. - (const char *)UTF8String;
  460. - (unsigned long)hash;
  461. - (BOOL)isEqual:(id)object;
  462. - (void)free;
  463. @end
  464. static unsigned _mapObjectHash(NXMapTable *table, const void *key) {
  465. return [(id)key hash];
  466. }
  467. static int _mapObjectIsEqual(NXMapTable *table, const void *key1, const void *key2) {
  468. return [(id)key1 isEqual:(id)key2];
  469. }
  470. static void _mapObjectFree(NXMapTable *table, void *key, void *value) {
  471. [(id)key free];
  472. }
  473. const NXMapTablePrototype NXObjectMapPrototype = {
  474. _mapObjectHash, _mapObjectIsEqual, _mapObjectFree, 0
  475. };
  476. #endif