You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1156 lines
25 KiB

27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
26 years ago
27 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
27 years ago
26 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
26 years ago
27 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
27 years ago
27 years ago
26 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
26 years ago
26 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
26 years ago
26 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
27 years ago
26 years ago
26 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
26 years ago
27 years ago
27 years ago
27 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1998, 1999 Andi Gutmans, Zeev Suraski |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 0.91 of the Zend license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available at through the world-wide-web at |
  10. | http://www.zend.com/license/0_91.txt. |
  11. | If you did not receive a copy of the Zend license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@zend.com so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Andi Gutmans <andi@zend.com> |
  16. | Zeev Suraski <zeev@zend.com> |
  17. +----------------------------------------------------------------------+
  18. */
  19. #include "zend.h"
  20. #include <stdio.h>
  21. #ifdef HAVE_STDLIB_H
  22. # include <stdlib.h>
  23. #endif
  24. #ifdef HAVE_LIMITS_H
  25. # include <limits.h>
  26. #endif
  27. #define HANDLE_NUMERIC(key, length, func) { \
  28. register char *tmp=key; \
  29. \
  30. if ((*tmp>='0' && *tmp<='9')) do { /* possibly a numeric index */ \
  31. char *end=tmp+length-1; \
  32. ulong idx; \
  33. \
  34. if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */ \
  35. break; \
  36. } \
  37. while (tmp<end) { \
  38. if (!(*tmp>='0' && *tmp<='9')) { \
  39. break; \
  40. } \
  41. tmp++; \
  42. } \
  43. if (tmp==end && *tmp=='\0') { /* a numeric index */ \
  44. idx = strtol(key, NULL, 10); \
  45. if (idx!=LONG_MAX) { \
  46. return func; \
  47. } \
  48. } \
  49. } while(0); \
  50. }
  51. #define CONNECT_TO_BUCKET_DLLIST(element, list_head) \
  52. (element)->pNext = (list_head); \
  53. (element)->pLast = NULL; \
  54. if ((element)->pNext) { \
  55. (element)->pNext->pLast = (element); \
  56. }
  57. #define CONNECT_TO_GLOBAL_DLLIST(element, ht) \
  58. (element)->pListLast = (ht)->pListTail; \
  59. (ht)->pListTail = (element); \
  60. (element)->pListNext = NULL; \
  61. if ((element)->pListLast != NULL) { \
  62. (element)->pListLast->pListNext = (element); \
  63. } \
  64. if (!(ht)->pListHead) { \
  65. (ht)->pListHead = (element); \
  66. } \
  67. if ((ht)->pInternalPointer == NULL) { \
  68. (ht)->pInternalPointer = (element); \
  69. }
  70. #if ZEND_DEBUG
  71. #define HT_IS_DESTROYING 1
  72. #define HT_DESTROYED 2
  73. #define HT_CLEANING 3
  74. #define HT_OK 0
  75. static void _zend_is_inconsistent(HashTable *ht, char *file, int line)
  76. {
  77. switch (ht->inconsistent) {
  78. case HT_IS_DESTROYING:
  79. zend_error(E_CORE_ERROR, "ht=%08x is destroying in %s:%d", ht, file, line);
  80. break;
  81. case HT_DESTROYED:
  82. zend_error(E_CORE_ERROR, "ht=%08x is already destroyed in %s:%d", ht, file, line);
  83. break;
  84. case HT_CLEANING:
  85. zend_error(E_CORE_ERROR, "ht=%08x is cleaning %s:%d", ht, file, line);
  86. break;
  87. }
  88. }
  89. #define IS_CONSISTENT(a) _zend_is_inconsistent(a,__FILE__,__LINE__);
  90. #define SET_INCONSISTENT(n) ht->inconsistent = n;
  91. #else
  92. #define IS_CONSISTENT(a)
  93. #define SET_INCONSISTENT(n)
  94. #endif
  95. /* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */
  96. static uint PrimeNumbers[] =
  97. {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793, 2097397, 4194103, 8388857, 16777447, 33554201, 67108961, 134217487, 268435697, 536870683, 1073741621, 2147483399};
  98. static int zend_hash_if_full_do_resize(HashTable *ht);
  99. static uint nNumPrimeNumbers = sizeof(PrimeNumbers) / sizeof(ulong);
  100. ZEND_API ulong hashpjw(char *arKey, uint nKeyLength)
  101. {
  102. ulong h = 0, g;
  103. char *arEnd=arKey+nKeyLength;
  104. while (arKey < arEnd) {
  105. h = (h << 4) + *arKey++;
  106. if ((g = (h & 0xF0000000))) {
  107. h = h ^ (g >> 24);
  108. h = h ^ g;
  109. }
  110. }
  111. return h;
  112. }
  113. ZEND_API int zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, int persistent)
  114. {
  115. uint i;
  116. SET_INCONSISTENT(HT_OK);
  117. for (i = 0; i < nNumPrimeNumbers; i++) {
  118. if (nSize <= PrimeNumbers[i]) {
  119. nSize = PrimeNumbers[i];
  120. ht->nHashSizeIndex = i;
  121. break;
  122. }
  123. }
  124. if (i == nNumPrimeNumbers) { /* This shouldn't really happen unless the ask for a ridiculous size */
  125. nSize = PrimeNumbers[i - 1];
  126. ht->nHashSizeIndex = i - 1;
  127. }
  128. /* Uses ecalloc() so that Bucket* == NULL */
  129. ht->arBuckets = (Bucket **) pecalloc(nSize, sizeof(Bucket *), persistent);
  130. if (!ht->arBuckets) {
  131. return FAILURE;
  132. }
  133. if (pHashFunction == NULL) {
  134. ht->pHashFunction = hashpjw;
  135. } else {
  136. ht->pHashFunction = pHashFunction;
  137. }
  138. ht->pDestructor = pDestructor;
  139. ht->nTableSize = nSize;
  140. ht->pListHead = NULL;
  141. ht->pListTail = NULL;
  142. ht->nNumOfElements = 0;
  143. ht->nNextFreeElement = 0;
  144. ht->pInternalPointer = NULL;
  145. ht->persistent = persistent;
  146. return SUCCESS;
  147. }
  148. ZEND_API int zend_hash_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag)
  149. {
  150. ulong h;
  151. uint nIndex;
  152. Bucket *p;
  153. IS_CONSISTENT(ht);
  154. if (nKeyLength <= 0) {
  155. #if ZEND_DEBUG
  156. ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
  157. #endif
  158. return FAILURE;
  159. }
  160. HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_update_or_next_insert(ht, idx, pData, nDataSize, pDest, flag));
  161. h = ht->pHashFunction(arKey, nKeyLength);
  162. nIndex = h % ht->nTableSize;
  163. p = ht->arBuckets[nIndex];
  164. while (p != NULL) {
  165. if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
  166. if (!memcmp(p->arKey, arKey, nKeyLength)) {
  167. if (flag & HASH_ADD) {
  168. return FAILURE;
  169. }
  170. HANDLE_BLOCK_INTERRUPTIONS();
  171. #if ZEND_DEBUG
  172. if (p->pData == pData) {
  173. ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
  174. HANDLE_UNBLOCK_INTERRUPTIONS();
  175. return FAILURE;
  176. }
  177. #endif
  178. if (ht->pDestructor) {
  179. ht->pDestructor(p->pData);
  180. }
  181. if (flag & HASH_ADD_PTR) {
  182. if (!p->pDataPtr) {
  183. efree(p->pData);
  184. }
  185. p->pDataPtr = pData;
  186. p->pData = &p->pDataPtr;
  187. } else {
  188. if (p->pDataPtr) {
  189. p->pData = (void *) emalloc(nDataSize);
  190. p->pDataPtr=NULL;
  191. }
  192. memcpy(p->pData, pData, nDataSize);
  193. }
  194. if (pDest) {
  195. *pDest = p->pData;
  196. }
  197. HANDLE_UNBLOCK_INTERRUPTIONS();
  198. return SUCCESS;
  199. }
  200. }
  201. p = p->pNext;
  202. }
  203. p = (Bucket *) pemalloc(sizeof(Bucket)-1+nKeyLength, ht->persistent);
  204. if (!p) {
  205. return FAILURE;
  206. }
  207. memcpy(p->arKey, arKey, nKeyLength);
  208. p->nKeyLength = nKeyLength;
  209. if (flag & HASH_ADD_PTR) {
  210. p->pDataPtr = pData;
  211. p->pData = &p->pDataPtr;
  212. } else {
  213. p->pData = (void *) pemalloc(nDataSize, ht->persistent);
  214. if (!p->pData) {
  215. pefree(p, ht->persistent);
  216. pefree(p->arKey, ht->persistent);
  217. return FAILURE;
  218. }
  219. memcpy(p->pData, pData, nDataSize);
  220. p->pDataPtr=NULL;
  221. }
  222. p->h = h;
  223. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  224. if (pDest) {
  225. *pDest = p->pData;
  226. }
  227. HANDLE_BLOCK_INTERRUPTIONS();
  228. CONNECT_TO_GLOBAL_DLLIST(p, ht);
  229. ht->arBuckets[nIndex] = p;
  230. HANDLE_UNBLOCK_INTERRUPTIONS();
  231. ht->nNumOfElements++;
  232. zend_hash_if_full_do_resize(ht); /* If the Hash table is full, resize it */
  233. return SUCCESS;
  234. }
  235. ZEND_API int zend_hash_quick_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag)
  236. {
  237. uint nIndex;
  238. Bucket *p;
  239. IS_CONSISTENT(ht);
  240. if (nKeyLength <= 0) {
  241. #if ZEND_DEBUG
  242. ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
  243. #endif
  244. return FAILURE;
  245. }
  246. nIndex = h % ht->nTableSize;
  247. p = ht->arBuckets[nIndex];
  248. while (p != NULL) {
  249. if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
  250. if (!memcmp(p->arKey, arKey, nKeyLength)) {
  251. if (flag & HASH_ADD) {
  252. return FAILURE;
  253. }
  254. HANDLE_BLOCK_INTERRUPTIONS();
  255. #if ZEND_DEBUG
  256. if (p->pData == pData) {
  257. ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
  258. HANDLE_UNBLOCK_INTERRUPTIONS();
  259. return FAILURE;
  260. }
  261. #endif
  262. if (ht->pDestructor) {
  263. ht->pDestructor(p->pData);
  264. }
  265. if (flag & HASH_ADD_PTR) {
  266. if (!p->pDataPtr) {
  267. efree(p->pData);
  268. }
  269. p->pDataPtr = pData;
  270. p->pData = &p->pDataPtr;
  271. } else {
  272. if (p->pDataPtr) {
  273. p->pData = (void *) emalloc(nDataSize);
  274. p->pDataPtr=NULL;
  275. }
  276. memcpy(p->pData, pData, nDataSize);
  277. }
  278. if (pDest) {
  279. *pDest = p->pData;
  280. }
  281. HANDLE_UNBLOCK_INTERRUPTIONS();
  282. return SUCCESS;
  283. }
  284. }
  285. p = p->pNext;
  286. }
  287. p = (Bucket *) pemalloc(sizeof(Bucket)-1+nKeyLength, ht->persistent);
  288. if (!p) {
  289. return FAILURE;
  290. }
  291. memcpy(p->arKey, arKey, nKeyLength);
  292. p->nKeyLength = nKeyLength;
  293. if (flag & HASH_ADD_PTR) {
  294. p->pDataPtr = pData;
  295. p->pData = &p->pDataPtr;
  296. } else {
  297. p->pData = (void *) pemalloc(nDataSize, ht->persistent);
  298. if (!p->pData) {
  299. pefree(p, ht->persistent);
  300. pefree(p->arKey, ht->persistent);
  301. return FAILURE;
  302. }
  303. memcpy(p->pData, pData, nDataSize);
  304. p->pDataPtr=NULL;
  305. }
  306. p->h = h;
  307. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  308. if (pDest) {
  309. *pDest = p->pData;
  310. }
  311. HANDLE_BLOCK_INTERRUPTIONS();
  312. ht->arBuckets[nIndex] = p;
  313. CONNECT_TO_GLOBAL_DLLIST(p, ht);
  314. HANDLE_UNBLOCK_INTERRUPTIONS();
  315. ht->nNumOfElements++;
  316. zend_hash_if_full_do_resize(ht); /* If the Hash table is full, resize it */
  317. return SUCCESS;
  318. }
  319. ZEND_API int zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag)
  320. {
  321. uint nIndex;
  322. Bucket *p;
  323. IS_CONSISTENT(ht);
  324. if (flag & HASH_NEXT_INSERT) {
  325. h = ht->nNextFreeElement;
  326. }
  327. nIndex = h % ht->nTableSize;
  328. p = ht->arBuckets[nIndex];
  329. while (p != NULL) {
  330. if ((p->nKeyLength == 0) && (p->h == h)) {
  331. if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) {
  332. return FAILURE;
  333. }
  334. HANDLE_BLOCK_INTERRUPTIONS();
  335. #if ZEND_DEBUG
  336. if (p->pData == pData) {
  337. ZEND_PUTS("Fatal error in zend_hash_index_update: p->pData == pData\n");
  338. HANDLE_UNBLOCK_INTERRUPTIONS();
  339. return FAILURE;
  340. }
  341. #endif
  342. if (ht->pDestructor) {
  343. ht->pDestructor(p->pData);
  344. }
  345. if (flag & HASH_ADD_PTR) {
  346. if (!p->pDataPtr) {
  347. efree(p->pData);
  348. }
  349. p->pDataPtr = pData;
  350. p->pData = &p->pDataPtr;
  351. } else {
  352. if (p->pDataPtr) {
  353. p->pData = (void *) emalloc(nDataSize);
  354. p->pDataPtr=NULL;
  355. }
  356. memcpy(p->pData, pData, nDataSize);
  357. }
  358. HANDLE_UNBLOCK_INTERRUPTIONS();
  359. if (h >= ht->nNextFreeElement) {
  360. ht->nNextFreeElement = h + 1;
  361. }
  362. if (pDest) {
  363. *pDest = p->pData;
  364. }
  365. return SUCCESS;
  366. }
  367. p = p->pNext;
  368. }
  369. p = (Bucket *) pemalloc(sizeof(Bucket)-1, ht->persistent);
  370. if (!p) {
  371. return FAILURE;
  372. }
  373. p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
  374. p->h = h;
  375. if (flag & HASH_ADD_PTR) {
  376. p->pDataPtr = pData;
  377. p->pData = &p->pDataPtr;
  378. } else {
  379. p->pData = (void *) pemalloc(nDataSize, ht->persistent);
  380. if (!p->pData) {
  381. pefree(p, ht->persistent);
  382. return FAILURE;
  383. }
  384. memcpy(p->pData, pData, nDataSize);
  385. p->pDataPtr=NULL;
  386. }
  387. if (pDest) {
  388. *pDest = p->pData;
  389. }
  390. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  391. HANDLE_BLOCK_INTERRUPTIONS();
  392. ht->arBuckets[nIndex] = p;
  393. CONNECT_TO_GLOBAL_DLLIST(p, ht);
  394. HANDLE_UNBLOCK_INTERRUPTIONS();
  395. if (h >= ht->nNextFreeElement) {
  396. ht->nNextFreeElement = h + 1;
  397. }
  398. ht->nNumOfElements++;
  399. zend_hash_if_full_do_resize(ht);
  400. return SUCCESS;
  401. }
  402. static int zend_hash_if_full_do_resize(HashTable *ht)
  403. {
  404. Bucket **t;
  405. IS_CONSISTENT(ht);
  406. if ((ht->nNumOfElements > ht->nTableSize)
  407. && (ht->nHashSizeIndex < nNumPrimeNumbers - 1)) { /* Let's double the table size */
  408. t = (Bucket **) perealloc_recoverable(ht->arBuckets, PrimeNumbers[ht->nHashSizeIndex + 1] * sizeof(Bucket *), ht->persistent);
  409. if (t) {
  410. HANDLE_BLOCK_INTERRUPTIONS();
  411. ht->arBuckets = t;
  412. ht->nTableSize = PrimeNumbers[ht->nHashSizeIndex + 1];
  413. ht->nHashSizeIndex++;
  414. zend_hash_rehash(ht);
  415. HANDLE_UNBLOCK_INTERRUPTIONS();
  416. return SUCCESS;
  417. }
  418. return FAILURE;
  419. }
  420. return SUCCESS;
  421. }
  422. ZEND_API int zend_hash_rehash(HashTable *ht)
  423. {
  424. Bucket *p;
  425. uint nIndex;
  426. IS_CONSISTENT(ht);
  427. memset(ht->arBuckets, 0, PrimeNumbers[ht->nHashSizeIndex] * sizeof(Bucket *));
  428. p = ht->pListHead;
  429. while (p != NULL) {
  430. nIndex = p->h % ht->nTableSize;
  431. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  432. ht->arBuckets[nIndex] = p;
  433. p = p->pListNext;
  434. }
  435. return SUCCESS;
  436. }
  437. ZEND_API int zend_hash_del_key_or_index(HashTable *ht, char *arKey, uint nKeyLength, ulong h, int flag)
  438. {
  439. uint nIndex;
  440. Bucket *p;
  441. IS_CONSISTENT(ht);
  442. if (flag == HASH_DEL_KEY) {
  443. HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_del_key_or_index(ht, arKey, nKeyLength, idx, HASH_DEL_INDEX));
  444. h = ht->pHashFunction(arKey, nKeyLength);
  445. }
  446. nIndex = h % ht->nTableSize;
  447. p = ht->arBuckets[nIndex];
  448. while (p != NULL) {
  449. if ((p->h == h) && ((p->nKeyLength == 0) || /* Numeric index */
  450. ((p->nKeyLength == nKeyLength) && (!memcmp(p->arKey, arKey, nKeyLength))))) {
  451. HANDLE_BLOCK_INTERRUPTIONS();
  452. if (p == ht->arBuckets[nIndex]) {
  453. ht->arBuckets[nIndex] = p->pNext;
  454. } else {
  455. p->pLast->pNext = p->pNext;
  456. }
  457. if (p->pNext) {
  458. p->pNext->pLast = p->pLast;
  459. }
  460. if (p->pListLast != NULL) {
  461. p->pListLast->pListNext = p->pListNext;
  462. } else {
  463. /* Deleting the head of the list */
  464. ht->pListHead = p->pListNext;
  465. }
  466. if (p->pListNext != NULL) {
  467. p->pListNext->pListLast = p->pListLast;
  468. } else {
  469. ht->pListTail = p->pListLast;
  470. }
  471. if (ht->pInternalPointer == p) {
  472. ht->pInternalPointer = p->pListNext;
  473. }
  474. if (ht->pDestructor) {
  475. ht->pDestructor(p->pData);
  476. }
  477. if (!p->pDataPtr) {
  478. pefree(p->pData, ht->persistent);
  479. }
  480. pefree(p, ht->persistent);
  481. HANDLE_UNBLOCK_INTERRUPTIONS();
  482. ht->nNumOfElements--;
  483. return SUCCESS;
  484. }
  485. p = p->pNext;
  486. }
  487. return FAILURE;
  488. }
  489. ZEND_API void zend_hash_destroy(HashTable *ht)
  490. {
  491. Bucket *p, *q;
  492. IS_CONSISTENT(ht);
  493. SET_INCONSISTENT(HT_IS_DESTROYING);
  494. p = ht->pListHead;
  495. while (p != NULL) {
  496. q = p;
  497. p = p->pListNext;
  498. if (ht->pDestructor) {
  499. ht->pDestructor(q->pData);
  500. }
  501. if (!q->pDataPtr && q->pData) {
  502. pefree(q->pData, ht->persistent);
  503. }
  504. pefree(q, ht->persistent);
  505. }
  506. pefree(ht->arBuckets, ht->persistent);
  507. SET_INCONSISTENT(HT_DESTROYED);
  508. }
  509. ZEND_API void zend_hash_clean(HashTable *ht)
  510. {
  511. Bucket *p, *q;
  512. IS_CONSISTENT(ht);
  513. SET_INCONSISTENT(HT_CLEANING);
  514. p = ht->pListHead;
  515. while (p != NULL) {
  516. q = p;
  517. p = p->pListNext;
  518. if (ht->pDestructor) {
  519. ht->pDestructor(q->pData);
  520. }
  521. if (!q->pDataPtr && q->pData) {
  522. pefree(q->pData, ht->persistent);
  523. }
  524. pefree(q, ht->persistent);
  525. }
  526. memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
  527. ht->pListHead = NULL;
  528. ht->pListTail = NULL;
  529. ht->nNumOfElements = 0;
  530. ht->nNextFreeElement = 0;
  531. ht->pInternalPointer = NULL;
  532. SET_INCONSISTENT(HT_OK);
  533. }
  534. /* This function is used by the various apply() functions.
  535. * It deletes the passed bucket, and returns the address of the
  536. * next bucket. The hash *may* be altered during that time, the
  537. * returned value will still be valid.
  538. */
  539. static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p)
  540. {
  541. Bucket *retval;
  542. HANDLE_BLOCK_INTERRUPTIONS();
  543. if (ht->pDestructor) {
  544. ht->pDestructor(p->pData);
  545. }
  546. if (!p->pDataPtr) {
  547. pefree(p->pData, ht->persistent);
  548. }
  549. retval = p->pListNext;
  550. if (p->pLast) {
  551. p->pLast->pNext = p->pNext;
  552. } else {
  553. uint nIndex;
  554. nIndex = p->h % ht->nTableSize;
  555. ht->arBuckets[nIndex] = p->pNext;
  556. }
  557. if (p->pNext) {
  558. p->pNext->pLast = p->pLast;
  559. } else {
  560. /* Nothing to do as this list doesn't have a tail */
  561. }
  562. if (p->pListLast != NULL) {
  563. p->pListLast->pListNext = p->pListNext;
  564. } else {
  565. /* Deleting the head of the list */
  566. ht->pListHead = p->pListNext;
  567. }
  568. if (p->pListNext != NULL) {
  569. p->pListNext->pListLast = p->pListLast;
  570. } else {
  571. ht->pListTail = p->pListLast;
  572. }
  573. if (ht->pInternalPointer == p) {
  574. ht->pInternalPointer = p->pListNext;
  575. }
  576. pefree(p, ht->persistent);
  577. HANDLE_UNBLOCK_INTERRUPTIONS();
  578. ht->nNumOfElements--;
  579. return retval;
  580. }
  581. ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
  582. {
  583. Bucket *p;
  584. IS_CONSISTENT(ht);
  585. p = ht->pListHead;
  586. while (p != NULL) {
  587. p = zend_hash_apply_deleter(ht, p);
  588. }
  589. pefree(ht->arBuckets, ht->persistent);
  590. SET_INCONSISTENT(HT_DESTROYED);
  591. }
  592. /* This is used to selectively delete certain entries from a hashtable.
  593. * destruct() receives the data and decides if the entry should be deleted
  594. * or not
  595. */
  596. ZEND_API void zend_hash_apply(HashTable *ht, int (*destruct)(void *))
  597. {
  598. Bucket *p;
  599. IS_CONSISTENT(ht);
  600. p = ht->pListHead;
  601. while (p != NULL) {
  602. if (destruct(p->pData)) {
  603. p = zend_hash_apply_deleter(ht, p);
  604. } else {
  605. p = p->pListNext;
  606. }
  607. }
  608. }
  609. ZEND_API void zend_hash_apply_with_argument(HashTable *ht, int (*destruct)(void *, void *), void *argument)
  610. {
  611. Bucket *p;
  612. IS_CONSISTENT(ht);
  613. p = ht->pListHead;
  614. while (p != NULL) {
  615. if (destruct(p->pData, argument)) {
  616. p = zend_hash_apply_deleter(ht, p);
  617. } else {
  618. p = p->pListNext;
  619. }
  620. }
  621. }
  622. ZEND_API void zend_hash_apply_with_arguments(HashTable *ht, int (*destruct)(void *, int, va_list, zend_hash_key *), int num_args, ...)
  623. {
  624. Bucket *p;
  625. va_list args;
  626. zend_hash_key hash_key;
  627. IS_CONSISTENT(ht);
  628. va_start(args, num_args);
  629. p = ht->pListHead;
  630. while (p != NULL) {
  631. hash_key.arKey = p->arKey;
  632. hash_key.nKeyLength = p->nKeyLength;
  633. hash_key.h = p->h;
  634. if (destruct(p->pData, num_args, args, &hash_key)) {
  635. p = zend_hash_apply_deleter(ht, p);
  636. } else {
  637. p = p->pListNext;
  638. }
  639. }
  640. va_end(args);
  641. }
  642. ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
  643. {
  644. Bucket *p;
  645. IS_CONSISTENT(source);
  646. IS_CONSISTENT(target);
  647. p = source->pListHead;
  648. while (p) {
  649. memcpy(tmp, p->pData, size);
  650. if (pCopyConstructor) {
  651. pCopyConstructor(tmp);
  652. }
  653. if (p->nKeyLength) {
  654. zend_hash_update(target, p->arKey, p->nKeyLength, tmp, size, NULL);
  655. } else {
  656. zend_hash_index_update(target, p->h, tmp, size, NULL);
  657. }
  658. p = p->pListNext;
  659. }
  660. target->pInternalPointer = target->pListHead;
  661. }
  662. ZEND_API void zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, int overwrite)
  663. {
  664. Bucket *p;
  665. void *t;
  666. int mode = (overwrite?HASH_UPDATE:HASH_ADD);
  667. IS_CONSISTENT(source);
  668. IS_CONSISTENT(target);
  669. p = source->pListHead;
  670. while (p) {
  671. memcpy(tmp, p->pData, size);
  672. if (p->nKeyLength>0) {
  673. if (zend_hash_add_or_update(target, p->arKey, p->nKeyLength, tmp, size, &t, mode)==SUCCESS && pCopyConstructor) {
  674. pCopyConstructor(t);
  675. }
  676. } else {
  677. if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, tmp, size, &t)==SUCCESS && pCopyConstructor) {
  678. pCopyConstructor(t);
  679. }
  680. }
  681. p = p->pListNext;
  682. }
  683. target->pInternalPointer = target->pListHead;
  684. }
  685. ZEND_API ulong zend_get_hash_value(HashTable *ht, char *arKey, uint nKeyLength)
  686. {
  687. IS_CONSISTENT(ht);
  688. return ht->pHashFunction(arKey, nKeyLength);
  689. }
  690. /* Returns SUCCESS if found and FAILURE if not. The pointer to the
  691. * data is returned in pData. The reason is that there's no reason
  692. * someone using the hash table might not want to have NULL data
  693. */
  694. ZEND_API int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength, void **pData)
  695. {
  696. ulong h;
  697. uint nIndex;
  698. Bucket *p;
  699. IS_CONSISTENT(ht);
  700. HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_find(ht, idx, pData));
  701. h = ht->pHashFunction(arKey, nKeyLength);
  702. nIndex = h % ht->nTableSize;
  703. p = ht->arBuckets[nIndex];
  704. while (p != NULL) {
  705. if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
  706. if (!memcmp(p->arKey, arKey, nKeyLength)) {
  707. *pData = p->pData;
  708. return SUCCESS;
  709. }
  710. }
  711. p = p->pNext;
  712. }
  713. return FAILURE;
  714. }
  715. ZEND_API int zend_hash_quick_find(HashTable *ht, char *arKey, uint nKeyLength, ulong h, void **pData)
  716. {
  717. uint nIndex;
  718. Bucket *p;
  719. IS_CONSISTENT(ht);
  720. nIndex = h % ht->nTableSize;
  721. p = ht->arBuckets[nIndex];
  722. while (p != NULL) {
  723. if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
  724. if (!memcmp(p->arKey, arKey, nKeyLength)) {
  725. *pData = p->pData;
  726. return SUCCESS;
  727. }
  728. }
  729. p = p->pNext;
  730. }
  731. return FAILURE;
  732. }
  733. ZEND_API int zend_hash_exists(HashTable *ht, char *arKey, uint nKeyLength)
  734. {
  735. ulong h;
  736. uint nIndex;
  737. Bucket *p;
  738. IS_CONSISTENT(ht);
  739. HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_exists(ht, idx));
  740. h = ht->pHashFunction(arKey, nKeyLength);
  741. nIndex = h % ht->nTableSize;
  742. p = ht->arBuckets[nIndex];
  743. while (p != NULL) {
  744. if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
  745. if (!memcmp(p->arKey, arKey, nKeyLength)) {
  746. return 1;
  747. }
  748. }
  749. p = p->pNext;
  750. }
  751. return 0;
  752. }
  753. ZEND_API int zend_hash_index_find(HashTable *ht, ulong h, void **pData)
  754. {
  755. uint nIndex;
  756. Bucket *p;
  757. IS_CONSISTENT(ht);
  758. nIndex = h % ht->nTableSize;
  759. p = ht->arBuckets[nIndex];
  760. while (p != NULL) {
  761. if ((p->h == h) && (p->nKeyLength == 0)) {
  762. *pData = p->pData;
  763. return SUCCESS;
  764. }
  765. p = p->pNext;
  766. }
  767. return FAILURE;
  768. }
  769. ZEND_API int zend_hash_index_exists(HashTable *ht, ulong h)
  770. {
  771. uint nIndex;
  772. Bucket *p;
  773. IS_CONSISTENT(ht);
  774. nIndex = h % ht->nTableSize;
  775. p = ht->arBuckets[nIndex];
  776. while (p != NULL) {
  777. if ((p->h == h) && (p->nKeyLength == 0)) {
  778. return 1;
  779. }
  780. p = p->pNext;
  781. }
  782. return 0;
  783. }
  784. ZEND_API int zend_hash_num_elements(HashTable *ht)
  785. {
  786. IS_CONSISTENT(ht);
  787. return ht->nNumOfElements;
  788. }
  789. ZEND_API void zend_hash_internal_pointer_reset(HashTable *ht)
  790. {
  791. IS_CONSISTENT(ht);
  792. ht->pInternalPointer = ht->pListHead;
  793. }
  794. /* This function will be extremely optimized by remembering
  795. * the end of the list
  796. */
  797. ZEND_API void zend_hash_internal_pointer_end(HashTable *ht)
  798. {
  799. IS_CONSISTENT(ht);
  800. ht->pInternalPointer = ht->pListTail;
  801. }
  802. ZEND_API void zend_hash_move_forward(HashTable *ht)
  803. {
  804. IS_CONSISTENT(ht);
  805. if (ht->pInternalPointer) {
  806. ht->pInternalPointer = ht->pInternalPointer->pListNext;
  807. }
  808. }
  809. ZEND_API void zend_hash_move_backwards(HashTable *ht)
  810. {
  811. IS_CONSISTENT(ht);
  812. if (ht->pInternalPointer) {
  813. ht->pInternalPointer = ht->pInternalPointer->pListLast;
  814. }
  815. }
  816. /* This function should be made binary safe */
  817. ZEND_API int zend_hash_get_current_key(HashTable *ht, char **str_index, ulong *num_index)
  818. {
  819. Bucket *p = ht->pInternalPointer;
  820. IS_CONSISTENT(ht);
  821. if (p) {
  822. if (p->nKeyLength) {
  823. *str_index = (char *) pemalloc(p->nKeyLength, ht->persistent);
  824. memcpy(*str_index, p->arKey, p->nKeyLength);
  825. return HASH_KEY_IS_STRING;
  826. } else {
  827. *num_index = p->h;
  828. return HASH_KEY_IS_LONG;
  829. }
  830. }
  831. return HASH_KEY_NON_EXISTANT;
  832. }
  833. ZEND_API int zend_hash_get_current_key_type(HashTable *ht)
  834. {
  835. Bucket *p = ht->pInternalPointer;
  836. IS_CONSISTENT(ht);
  837. if (p) {
  838. if (p->nKeyLength) {
  839. return HASH_KEY_IS_STRING;
  840. } else {
  841. return HASH_KEY_IS_LONG;
  842. }
  843. }
  844. return HASH_KEY_NON_EXISTANT;
  845. }
  846. ZEND_API int zend_hash_get_current_data(HashTable *ht, void **pData)
  847. {
  848. Bucket *p = ht->pInternalPointer;
  849. IS_CONSISTENT(ht);
  850. if (p) {
  851. *pData = p->pData;
  852. return SUCCESS;
  853. } else {
  854. return FAILURE;
  855. }
  856. }
  857. ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
  858. compare_func_t compar, int renumber)
  859. {
  860. Bucket **arTmp;
  861. Bucket *p;
  862. int i, j;
  863. IS_CONSISTENT(ht);
  864. if (ht->nNumOfElements <= 1) { /* Doesn't require sorting */
  865. return SUCCESS;
  866. }
  867. arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
  868. if (!arTmp) {
  869. return FAILURE;
  870. }
  871. p = ht->pListHead;
  872. i = 0;
  873. while (p) {
  874. arTmp[i] = p;
  875. p = p->pListNext;
  876. i++;
  877. }
  878. (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar);
  879. HANDLE_BLOCK_INTERRUPTIONS();
  880. ht->pListHead = arTmp[0];
  881. ht->pListTail = NULL;
  882. ht->pInternalPointer = ht->pListHead;
  883. for (j = 0; j < i; j++) {
  884. if (ht->pListTail) {
  885. ht->pListTail->pListNext = arTmp[j];
  886. }
  887. arTmp[j]->pListLast = ht->pListTail;
  888. arTmp[j]->pListNext = NULL;
  889. ht->pListTail = arTmp[j];
  890. }
  891. pefree(arTmp, ht->persistent);
  892. HANDLE_UNBLOCK_INTERRUPTIONS();
  893. if (renumber) {
  894. p = ht->pListHead;
  895. i=0;
  896. while (p != NULL) {
  897. p->nKeyLength = 0;
  898. p->h = i++;
  899. p = p->pListNext;
  900. }
  901. ht->nNextFreeElement = i;
  902. zend_hash_rehash(ht);
  903. }
  904. return SUCCESS;
  905. }
  906. ZEND_API int zend_hash_minmax(HashTable *ht, int (*compar) (const void *, const void *), int flag, void **pData)
  907. {
  908. Bucket *p,*res;
  909. IS_CONSISTENT(ht);
  910. if (ht->nNumOfElements == 0 ) {
  911. *pData=NULL;
  912. return FAILURE;
  913. }
  914. res = p = ht->pListHead;
  915. while ((p = p->pListNext)) {
  916. if (flag) {
  917. if (compar(&res,&p) < 0) { /* max */
  918. res = p;
  919. }
  920. } else {
  921. if (compar(&res,&p) > 0) { /* min */
  922. res = p;
  923. }
  924. }
  925. }
  926. *pData = res->pData;
  927. return SUCCESS;
  928. }
  929. ZEND_API ulong zend_hash_next_free_element(HashTable *ht)
  930. {
  931. IS_CONSISTENT(ht);
  932. return ht->nNextFreeElement;
  933. }
  934. #if ZEND_DEBUG
  935. void zend_hash_display_pListTail(HashTable *ht)
  936. {
  937. Bucket *p;
  938. p = ht->pListTail;
  939. while (p != NULL) {
  940. zend_printf("pListTail has key %s\n", p->arKey);
  941. p = p->pListLast;
  942. }
  943. }
  944. void zend_hash_display(HashTable *ht)
  945. {
  946. Bucket *p;
  947. uint i;
  948. for (i = 0; i < ht->nTableSize; i++) {
  949. p = ht->arBuckets[i];
  950. while (p != NULL) {
  951. zend_printf("%s <==> 0x%X\n", p->arKey, p->h);
  952. p = p->pNext;
  953. }
  954. }
  955. p = ht->pListTail;
  956. while (p != NULL) {
  957. zend_printf("%s <==> 0x%X\n", p->arKey, p->h);
  958. p = p->pListLast;
  959. }
  960. }
  961. #endif
  962. /*
  963. * Local variables:
  964. * tab-width: 4
  965. * c-basic-offset: 4
  966. * End:
  967. */