Busybox: Upgrade to 1.21.1 (stable). lsof active.
[tomato.git] / release / src / router / php / Zend / zend_hash.c
blob0609d707f533f80541ef5fa240e6e1884718a7a7
1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2013 Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.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 +----------------------------------------------------------------------+
20 /* $Id$ */
22 #include "zend.h"
23 #include "zend_globals.h"
25 #define CONNECT_TO_BUCKET_DLLIST(element, list_head) \
26 (element)->pNext = (list_head); \
27 (element)->pLast = NULL; \
28 if ((element)->pNext) { \
29 (element)->pNext->pLast = (element); \
32 #define CONNECT_TO_GLOBAL_DLLIST(element, ht) \
33 (element)->pListLast = (ht)->pListTail; \
34 (ht)->pListTail = (element); \
35 (element)->pListNext = NULL; \
36 if ((element)->pListLast != NULL) { \
37 (element)->pListLast->pListNext = (element); \
38 } \
39 if (!(ht)->pListHead) { \
40 (ht)->pListHead = (element); \
41 } \
42 if ((ht)->pInternalPointer == NULL) { \
43 (ht)->pInternalPointer = (element); \
46 #if ZEND_DEBUG
47 #define HT_OK 0
48 #define HT_IS_DESTROYING 1
49 #define HT_DESTROYED 2
50 #define HT_CLEANING 3
52 static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
54 if (ht->inconsistent==HT_OK) {
55 return;
57 switch (ht->inconsistent) {
58 case HT_IS_DESTROYING:
59 zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
60 break;
61 case HT_DESTROYED:
62 zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file, line, ht);
63 break;
64 case HT_CLEANING:
65 zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file, line, ht);
66 break;
67 default:
68 zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file, line, ht);
69 break;
71 zend_bailout();
73 #define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
74 #define SET_INCONSISTENT(n) ht->inconsistent = n;
75 #else
76 #define IS_CONSISTENT(a)
77 #define SET_INCONSISTENT(n)
78 #endif
80 #define HASH_PROTECT_RECURSION(ht) \
81 if ((ht)->bApplyProtection) { \
82 if ((ht)->nApplyCount++ >= 3) { \
83 zend_error(E_ERROR, "Nesting level too deep - recursive dependency?"); \
84 } \
88 #define HASH_UNPROTECT_RECURSION(ht) \
89 if ((ht)->bApplyProtection) { \
90 (ht)->nApplyCount--; \
94 #define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
95 if ((ht)->nNumOfElements > (ht)->nTableSize) { \
96 zend_hash_do_resize(ht); \
99 static int zend_hash_do_resize(HashTable *ht);
101 ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
103 return zend_inline_hash_func(arKey, nKeyLength);
107 #define UPDATE_DATA(ht, p, pData, nDataSize) \
108 if (nDataSize == sizeof(void*)) { \
109 if ((p)->pData != &(p)->pDataPtr) { \
110 pefree_rel((p)->pData, (ht)->persistent); \
112 memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
113 (p)->pData = &(p)->pDataPtr; \
114 } else { \
115 if ((p)->pData == &(p)->pDataPtr) { \
116 (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent); \
117 (p)->pDataPtr=NULL; \
118 } else { \
119 (p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent); \
120 /* (p)->pDataPtr is already NULL so no need to initialize it */ \
122 memcpy((p)->pData, pData, nDataSize); \
125 #define INIT_DATA(ht, p, pData, nDataSize); \
126 if (nDataSize == sizeof(void*)) { \
127 memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
128 (p)->pData = &(p)->pDataPtr; \
129 } else { \
130 (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
131 if (!(p)->pData) { \
132 pefree_rel(p, (ht)->persistent); \
133 return FAILURE; \
135 memcpy((p)->pData, pData, nDataSize); \
136 (p)->pDataPtr=NULL; \
139 #define CHECK_INIT(ht) do { \
140 if (UNEXPECTED((ht)->nTableMask == 0)) { \
141 (ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent); \
142 (ht)->nTableMask = (ht)->nTableSize - 1; \
144 } while (0)
146 static const Bucket *uninitialized_bucket = NULL;
148 ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
150 uint i = 3;
152 SET_INCONSISTENT(HT_OK);
154 if (nSize >= 0x80000000) {
155 /* prevent overflow */
156 ht->nTableSize = 0x80000000;
157 } else {
158 while ((1U << i) < nSize) {
159 i++;
161 ht->nTableSize = 1 << i;
164 ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */
165 ht->pDestructor = pDestructor;
166 ht->arBuckets = (Bucket**)&uninitialized_bucket;
167 ht->pListHead = NULL;
168 ht->pListTail = NULL;
169 ht->nNumOfElements = 0;
170 ht->nNextFreeElement = 0;
171 ht->pInternalPointer = NULL;
172 ht->persistent = persistent;
173 ht->nApplyCount = 0;
174 ht->bApplyProtection = 1;
175 return SUCCESS;
179 ZEND_API int _zend_hash_init_ex(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC)
181 int retval = _zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent ZEND_FILE_LINE_CC);
183 ht->bApplyProtection = bApplyProtection;
184 return retval;
188 ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
190 ht->bApplyProtection = bApplyProtection;
195 ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
197 ulong h;
198 uint nIndex;
199 Bucket *p;
200 #ifdef ZEND_SIGNALS
201 TSRMLS_FETCH();
202 #endif
204 IS_CONSISTENT(ht);
206 if (nKeyLength <= 0) {
207 #if ZEND_DEBUG
208 ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
209 #endif
210 return FAILURE;
213 CHECK_INIT(ht);
215 h = zend_inline_hash_func(arKey, nKeyLength);
216 nIndex = h & ht->nTableMask;
218 p = ht->arBuckets[nIndex];
219 while (p != NULL) {
220 if (p->arKey == arKey ||
221 ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
222 if (flag & HASH_ADD) {
223 return FAILURE;
225 HANDLE_BLOCK_INTERRUPTIONS();
226 #if ZEND_DEBUG
227 if (p->pData == pData) {
228 ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
229 HANDLE_UNBLOCK_INTERRUPTIONS();
230 return FAILURE;
232 #endif
233 if (ht->pDestructor) {
234 ht->pDestructor(p->pData);
236 UPDATE_DATA(ht, p, pData, nDataSize);
237 if (pDest) {
238 *pDest = p->pData;
240 HANDLE_UNBLOCK_INTERRUPTIONS();
241 return SUCCESS;
243 p = p->pNext;
246 if (IS_INTERNED(arKey)) {
247 p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
248 if (!p) {
249 return FAILURE;
251 p->arKey = arKey;
252 } else {
253 p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
254 if (!p) {
255 return FAILURE;
257 p->arKey = (const char*)(p + 1);
258 memcpy((char*)p->arKey, arKey, nKeyLength);
260 p->nKeyLength = nKeyLength;
261 INIT_DATA(ht, p, pData, nDataSize);
262 p->h = h;
263 CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
264 if (pDest) {
265 *pDest = p->pData;
268 HANDLE_BLOCK_INTERRUPTIONS();
269 CONNECT_TO_GLOBAL_DLLIST(p, ht);
270 ht->arBuckets[nIndex] = p;
271 HANDLE_UNBLOCK_INTERRUPTIONS();
273 ht->nNumOfElements++;
274 ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
275 return SUCCESS;
278 ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
280 uint nIndex;
281 Bucket *p;
282 #ifdef ZEND_SIGNALS
283 TSRMLS_FETCH();
284 #endif
286 IS_CONSISTENT(ht);
288 if (nKeyLength == 0) {
289 return zend_hash_index_update(ht, h, pData, nDataSize, pDest);
292 CHECK_INIT(ht);
293 nIndex = h & ht->nTableMask;
295 p = ht->arBuckets[nIndex];
296 while (p != NULL) {
297 if (p->arKey == arKey ||
298 ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
299 if (flag & HASH_ADD) {
300 return FAILURE;
302 HANDLE_BLOCK_INTERRUPTIONS();
303 #if ZEND_DEBUG
304 if (p->pData == pData) {
305 ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
306 HANDLE_UNBLOCK_INTERRUPTIONS();
307 return FAILURE;
309 #endif
310 if (ht->pDestructor) {
311 ht->pDestructor(p->pData);
313 UPDATE_DATA(ht, p, pData, nDataSize);
314 if (pDest) {
315 *pDest = p->pData;
317 HANDLE_UNBLOCK_INTERRUPTIONS();
318 return SUCCESS;
320 p = p->pNext;
323 if (IS_INTERNED(arKey)) {
324 p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
325 if (!p) {
326 return FAILURE;
328 p->arKey = arKey;
329 } else {
330 p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
331 if (!p) {
332 return FAILURE;
334 p->arKey = (const char*)(p + 1);
335 memcpy((char*)p->arKey, arKey, nKeyLength);
338 p->nKeyLength = nKeyLength;
339 INIT_DATA(ht, p, pData, nDataSize);
340 p->h = h;
342 CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
344 if (pDest) {
345 *pDest = p->pData;
348 HANDLE_BLOCK_INTERRUPTIONS();
349 ht->arBuckets[nIndex] = p;
350 CONNECT_TO_GLOBAL_DLLIST(p, ht);
351 HANDLE_UNBLOCK_INTERRUPTIONS();
353 ht->nNumOfElements++;
354 ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
355 return SUCCESS;
359 ZEND_API int zend_hash_add_empty_element(HashTable *ht, const char *arKey, uint nKeyLength)
361 void *dummy = (void *) 1;
363 return zend_hash_add(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
367 ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
369 uint nIndex;
370 Bucket *p;
371 #ifdef ZEND_SIGNALS
372 TSRMLS_FETCH();
373 #endif
375 IS_CONSISTENT(ht);
376 CHECK_INIT(ht);
378 if (flag & HASH_NEXT_INSERT) {
379 h = ht->nNextFreeElement;
381 nIndex = h & ht->nTableMask;
383 p = ht->arBuckets[nIndex];
384 while (p != NULL) {
385 if ((p->nKeyLength == 0) && (p->h == h)) {
386 if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) {
387 return FAILURE;
389 HANDLE_BLOCK_INTERRUPTIONS();
390 #if ZEND_DEBUG
391 if (p->pData == pData) {
392 ZEND_PUTS("Fatal error in zend_hash_index_update: p->pData == pData\n");
393 HANDLE_UNBLOCK_INTERRUPTIONS();
394 return FAILURE;
396 #endif
397 if (ht->pDestructor) {
398 ht->pDestructor(p->pData);
400 UPDATE_DATA(ht, p, pData, nDataSize);
401 HANDLE_UNBLOCK_INTERRUPTIONS();
402 if ((long)h >= (long)ht->nNextFreeElement) {
403 ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
405 if (pDest) {
406 *pDest = p->pData;
408 return SUCCESS;
410 p = p->pNext;
412 p = (Bucket *) pemalloc_rel(sizeof(Bucket), ht->persistent);
413 if (!p) {
414 return FAILURE;
416 p->arKey = NULL;
417 p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
418 p->h = h;
419 INIT_DATA(ht, p, pData, nDataSize);
420 if (pDest) {
421 *pDest = p->pData;
424 CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
426 HANDLE_BLOCK_INTERRUPTIONS();
427 ht->arBuckets[nIndex] = p;
428 CONNECT_TO_GLOBAL_DLLIST(p, ht);
429 HANDLE_UNBLOCK_INTERRUPTIONS();
431 if ((long)h >= (long)ht->nNextFreeElement) {
432 ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
434 ht->nNumOfElements++;
435 ZEND_HASH_IF_FULL_DO_RESIZE(ht);
436 return SUCCESS;
440 static int zend_hash_do_resize(HashTable *ht)
442 Bucket **t;
443 #ifdef ZEND_SIGNALS
444 TSRMLS_FETCH();
445 #endif
447 IS_CONSISTENT(ht);
449 if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */
450 t = (Bucket **) perealloc_recoverable(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent);
451 if (t) {
452 HANDLE_BLOCK_INTERRUPTIONS();
453 ht->arBuckets = t;
454 ht->nTableSize = (ht->nTableSize << 1);
455 ht->nTableMask = ht->nTableSize - 1;
456 zend_hash_rehash(ht);
457 HANDLE_UNBLOCK_INTERRUPTIONS();
458 return SUCCESS;
460 return FAILURE;
462 return SUCCESS;
465 ZEND_API int zend_hash_rehash(HashTable *ht)
467 Bucket *p;
468 uint nIndex;
470 IS_CONSISTENT(ht);
471 if (UNEXPECTED(ht->nNumOfElements == 0)) {
472 return SUCCESS;
475 memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
476 p = ht->pListHead;
477 while (p != NULL) {
478 nIndex = p->h & ht->nTableMask;
479 CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
480 ht->arBuckets[nIndex] = p;
481 p = p->pListNext;
483 return SUCCESS;
486 ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
488 uint nIndex;
489 Bucket *p;
490 #ifdef ZEND_SIGNALS
491 TSRMLS_FETCH();
492 #endif
494 IS_CONSISTENT(ht);
496 if (flag == HASH_DEL_KEY) {
497 h = zend_inline_hash_func(arKey, nKeyLength);
499 nIndex = h & ht->nTableMask;
501 p = ht->arBuckets[nIndex];
502 while (p != NULL) {
503 if ((p->h == h)
504 && (p->nKeyLength == nKeyLength)
505 && ((p->nKeyLength == 0) /* Numeric index (short circuits the memcmp() check) */
506 || !memcmp(p->arKey, arKey, nKeyLength))) { /* String index */
507 HANDLE_BLOCK_INTERRUPTIONS();
508 if (p == ht->arBuckets[nIndex]) {
509 ht->arBuckets[nIndex] = p->pNext;
510 } else {
511 p->pLast->pNext = p->pNext;
513 if (p->pNext) {
514 p->pNext->pLast = p->pLast;
516 if (p->pListLast != NULL) {
517 p->pListLast->pListNext = p->pListNext;
518 } else {
519 /* Deleting the head of the list */
520 ht->pListHead = p->pListNext;
522 if (p->pListNext != NULL) {
523 p->pListNext->pListLast = p->pListLast;
524 } else {
525 ht->pListTail = p->pListLast;
527 if (ht->pInternalPointer == p) {
528 ht->pInternalPointer = p->pListNext;
530 if (ht->pDestructor) {
531 ht->pDestructor(p->pData);
533 if (p->pData != &p->pDataPtr) {
534 pefree(p->pData, ht->persistent);
536 pefree(p, ht->persistent);
537 HANDLE_UNBLOCK_INTERRUPTIONS();
538 ht->nNumOfElements--;
539 return SUCCESS;
541 p = p->pNext;
543 return FAILURE;
547 ZEND_API void zend_hash_destroy(HashTable *ht)
549 Bucket *p, *q;
551 IS_CONSISTENT(ht);
553 SET_INCONSISTENT(HT_IS_DESTROYING);
555 p = ht->pListHead;
556 while (p != NULL) {
557 q = p;
558 p = p->pListNext;
559 if (ht->pDestructor) {
560 ht->pDestructor(q->pData);
562 if (q->pData != &q->pDataPtr) {
563 pefree(q->pData, ht->persistent);
565 pefree(q, ht->persistent);
567 if (ht->nTableMask) {
568 pefree(ht->arBuckets, ht->persistent);
571 SET_INCONSISTENT(HT_DESTROYED);
575 ZEND_API void zend_hash_clean(HashTable *ht)
577 Bucket *p, *q;
579 IS_CONSISTENT(ht);
581 p = ht->pListHead;
583 if (ht->nTableMask) {
584 memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
586 ht->pListHead = NULL;
587 ht->pListTail = NULL;
588 ht->nNumOfElements = 0;
589 ht->nNextFreeElement = 0;
590 ht->pInternalPointer = NULL;
592 while (p != NULL) {
593 q = p;
594 p = p->pListNext;
595 if (ht->pDestructor) {
596 ht->pDestructor(q->pData);
598 if (q->pData != &q->pDataPtr) {
599 pefree(q->pData, ht->persistent);
601 pefree(q, ht->persistent);
605 /* This function is used by the various apply() functions.
606 * It deletes the passed bucket, and returns the address of the
607 * next bucket. The hash *may* be altered during that time, the
608 * returned value will still be valid.
610 static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p)
612 Bucket *retval;
613 #ifdef ZEND_SIGNALS
614 TSRMLS_FETCH();
615 #endif
617 HANDLE_BLOCK_INTERRUPTIONS();
618 if (p->pLast) {
619 p->pLast->pNext = p->pNext;
620 } else {
621 uint nIndex;
623 nIndex = p->h & ht->nTableMask;
624 ht->arBuckets[nIndex] = p->pNext;
626 if (p->pNext) {
627 p->pNext->pLast = p->pLast;
628 } else {
629 /* Nothing to do as this list doesn't have a tail */
632 if (p->pListLast != NULL) {
633 p->pListLast->pListNext = p->pListNext;
634 } else {
635 /* Deleting the head of the list */
636 ht->pListHead = p->pListNext;
638 if (p->pListNext != NULL) {
639 p->pListNext->pListLast = p->pListLast;
640 } else {
641 ht->pListTail = p->pListLast;
643 if (ht->pInternalPointer == p) {
644 ht->pInternalPointer = p->pListNext;
646 ht->nNumOfElements--;
647 HANDLE_UNBLOCK_INTERRUPTIONS();
649 if (ht->pDestructor) {
650 ht->pDestructor(p->pData);
652 if (p->pData != &p->pDataPtr) {
653 pefree(p->pData, ht->persistent);
655 retval = p->pListNext;
656 pefree(p, ht->persistent);
658 return retval;
662 ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
664 Bucket *p;
666 IS_CONSISTENT(ht);
668 p = ht->pListHead;
669 while (p != NULL) {
670 p = zend_hash_apply_deleter(ht, p);
672 if (ht->nTableMask) {
673 pefree(ht->arBuckets, ht->persistent);
676 SET_INCONSISTENT(HT_DESTROYED);
679 ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
681 Bucket *p;
683 IS_CONSISTENT(ht);
685 p = ht->pListTail;
686 while (p != NULL) {
687 zend_hash_apply_deleter(ht, p);
688 p = ht->pListTail;
691 if (ht->nTableMask) {
692 pefree(ht->arBuckets, ht->persistent);
695 SET_INCONSISTENT(HT_DESTROYED);
698 /* This is used to recurse elements and selectively delete certain entries
699 * from a hashtable. apply_func() receives the data and decides if the entry
700 * should be deleted or recursion should be stopped. The following three
701 * return codes are possible:
702 * ZEND_HASH_APPLY_KEEP - continue
703 * ZEND_HASH_APPLY_STOP - stop iteration
704 * ZEND_HASH_APPLY_REMOVE - delete the element, combineable with the former
707 ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
709 Bucket *p;
711 IS_CONSISTENT(ht);
713 HASH_PROTECT_RECURSION(ht);
714 p = ht->pListHead;
715 while (p != NULL) {
716 int result = apply_func(p->pData TSRMLS_CC);
718 if (result & ZEND_HASH_APPLY_REMOVE) {
719 p = zend_hash_apply_deleter(ht, p);
720 } else {
721 p = p->pListNext;
723 if (result & ZEND_HASH_APPLY_STOP) {
724 break;
727 HASH_UNPROTECT_RECURSION(ht);
731 ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument TSRMLS_DC)
733 Bucket *p;
735 IS_CONSISTENT(ht);
737 HASH_PROTECT_RECURSION(ht);
738 p = ht->pListHead;
739 while (p != NULL) {
740 int result = apply_func(p->pData, argument TSRMLS_CC);
742 if (result & ZEND_HASH_APPLY_REMOVE) {
743 p = zend_hash_apply_deleter(ht, p);
744 } else {
745 p = p->pListNext;
747 if (result & ZEND_HASH_APPLY_STOP) {
748 break;
751 HASH_UNPROTECT_RECURSION(ht);
755 ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func_args_t apply_func, int num_args, ...)
757 Bucket *p;
758 va_list args;
759 zend_hash_key hash_key;
761 IS_CONSISTENT(ht);
763 HASH_PROTECT_RECURSION(ht);
765 p = ht->pListHead;
766 while (p != NULL) {
767 int result;
768 va_start(args, num_args);
769 hash_key.arKey = p->arKey;
770 hash_key.nKeyLength = p->nKeyLength;
771 hash_key.h = p->h;
772 result = apply_func(p->pData TSRMLS_CC, num_args, args, &hash_key);
774 if (result & ZEND_HASH_APPLY_REMOVE) {
775 p = zend_hash_apply_deleter(ht, p);
776 } else {
777 p = p->pListNext;
779 if (result & ZEND_HASH_APPLY_STOP) {
780 va_end(args);
781 break;
783 va_end(args);
786 HASH_UNPROTECT_RECURSION(ht);
790 ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
792 Bucket *p, *q;
794 IS_CONSISTENT(ht);
796 HASH_PROTECT_RECURSION(ht);
797 p = ht->pListTail;
798 while (p != NULL) {
799 int result = apply_func(p->pData TSRMLS_CC);
801 q = p;
802 p = p->pListLast;
803 if (result & ZEND_HASH_APPLY_REMOVE) {
804 zend_hash_apply_deleter(ht, q);
806 if (result & ZEND_HASH_APPLY_STOP) {
807 break;
810 HASH_UNPROTECT_RECURSION(ht);
814 ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
816 Bucket *p;
817 void *new_entry;
818 zend_bool setTargetPointer;
820 IS_CONSISTENT(source);
821 IS_CONSISTENT(target);
823 setTargetPointer = !target->pInternalPointer;
824 p = source->pListHead;
825 while (p) {
826 if (setTargetPointer && source->pInternalPointer == p) {
827 target->pInternalPointer = NULL;
829 if (p->nKeyLength) {
830 zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &new_entry);
831 } else {
832 zend_hash_index_update(target, p->h, p->pData, size, &new_entry);
834 if (pCopyConstructor) {
835 pCopyConstructor(new_entry);
837 p = p->pListNext;
839 if (!target->pInternalPointer) {
840 target->pInternalPointer = target->pListHead;
845 ZEND_API void _zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, int overwrite ZEND_FILE_LINE_DC)
847 Bucket *p;
848 void *t;
849 int mode = (overwrite?HASH_UPDATE:HASH_ADD);
851 IS_CONSISTENT(source);
852 IS_CONSISTENT(target);
854 p = source->pListHead;
855 while (p) {
856 if (p->nKeyLength>0) {
857 if (_zend_hash_quick_add_or_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t, mode ZEND_FILE_LINE_RELAY_CC)==SUCCESS && pCopyConstructor) {
858 pCopyConstructor(t);
860 } else {
861 if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
862 pCopyConstructor(t);
865 p = p->pListNext;
867 target->pInternalPointer = target->pListHead;
871 static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
873 zend_hash_key hash_key;
875 hash_key.arKey = p->arKey;
876 hash_key.nKeyLength = p->nKeyLength;
877 hash_key.h = p->h;
878 return merge_checker_func(target, source_data, &hash_key, pParam);
882 ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, merge_checker_func_t pMergeSource, void *pParam)
884 Bucket *p;
885 void *t;
887 IS_CONSISTENT(source);
888 IS_CONSISTENT(target);
890 p = source->pListHead;
891 while (p) {
892 if (zend_hash_replace_checker_wrapper(target, p->pData, p, pParam, pMergeSource)) {
893 if (zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
894 pCopyConstructor(t);
897 p = p->pListNext;
899 target->pInternalPointer = target->pListHead;
903 ZEND_API ulong zend_get_hash_value(const char *arKey, uint nKeyLength)
905 return zend_inline_hash_func(arKey, nKeyLength);
909 /* Returns SUCCESS if found and FAILURE if not. The pointer to the
910 * data is returned in pData. The reason is that there's no reason
911 * someone using the hash table might not want to have NULL data
913 ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
915 ulong h;
916 uint nIndex;
917 Bucket *p;
919 IS_CONSISTENT(ht);
921 h = zend_inline_hash_func(arKey, nKeyLength);
922 nIndex = h & ht->nTableMask;
924 p = ht->arBuckets[nIndex];
925 while (p != NULL) {
926 if (p->arKey == arKey ||
927 ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
928 *pData = p->pData;
929 return SUCCESS;
931 p = p->pNext;
933 return FAILURE;
937 ZEND_API int zend_hash_quick_find(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void **pData)
939 uint nIndex;
940 Bucket *p;
942 if (nKeyLength==0) {
943 return zend_hash_index_find(ht, h, pData);
946 IS_CONSISTENT(ht);
948 nIndex = h & ht->nTableMask;
950 p = ht->arBuckets[nIndex];
951 while (p != NULL) {
952 if (p->arKey == arKey ||
953 ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
954 *pData = p->pData;
955 return SUCCESS;
957 p = p->pNext;
959 return FAILURE;
963 ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
965 ulong h;
966 uint nIndex;
967 Bucket *p;
969 IS_CONSISTENT(ht);
971 h = zend_inline_hash_func(arKey, nKeyLength);
972 nIndex = h & ht->nTableMask;
974 p = ht->arBuckets[nIndex];
975 while (p != NULL) {
976 if (p->arKey == arKey ||
977 ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
978 return 1;
980 p = p->pNext;
982 return 0;
986 ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h)
988 uint nIndex;
989 Bucket *p;
991 if (nKeyLength==0) {
992 return zend_hash_index_exists(ht, h);
995 IS_CONSISTENT(ht);
997 nIndex = h & ht->nTableMask;
999 p = ht->arBuckets[nIndex];
1000 while (p != NULL) {
1001 if (p->arKey == arKey ||
1002 ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
1003 return 1;
1005 p = p->pNext;
1007 return 0;
1012 ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
1014 uint nIndex;
1015 Bucket *p;
1017 IS_CONSISTENT(ht);
1019 nIndex = h & ht->nTableMask;
1021 p = ht->arBuckets[nIndex];
1022 while (p != NULL) {
1023 if ((p->h == h) && (p->nKeyLength == 0)) {
1024 *pData = p->pData;
1025 return SUCCESS;
1027 p = p->pNext;
1029 return FAILURE;
1033 ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h)
1035 uint nIndex;
1036 Bucket *p;
1038 IS_CONSISTENT(ht);
1040 nIndex = h & ht->nTableMask;
1042 p = ht->arBuckets[nIndex];
1043 while (p != NULL) {
1044 if ((p->h == h) && (p->nKeyLength == 0)) {
1045 return 1;
1047 p = p->pNext;
1049 return 0;
1053 ZEND_API int zend_hash_num_elements(const HashTable *ht)
1055 IS_CONSISTENT(ht);
1057 return ht->nNumOfElements;
1061 ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
1063 ptr->pos = ht->pInternalPointer;
1064 if (ht->pInternalPointer) {
1065 ptr->h = ht->pInternalPointer->h;
1066 return 1;
1067 } else {
1068 ptr->h = 0;
1069 return 0;
1073 ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
1075 if (ptr->pos == NULL) {
1076 ht->pInternalPointer = NULL;
1077 } else if (ht->pInternalPointer != ptr->pos) {
1078 Bucket *p;
1080 IS_CONSISTENT(ht);
1081 p = ht->arBuckets[ptr->h & ht->nTableMask];
1082 while (p != NULL) {
1083 if (p == ptr->pos) {
1084 ht->pInternalPointer = p;
1085 return 1;
1087 p = p->pNext;
1089 return 0;
1091 return 1;
1094 ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
1096 IS_CONSISTENT(ht);
1098 if (pos)
1099 *pos = ht->pListHead;
1100 else
1101 ht->pInternalPointer = ht->pListHead;
1105 /* This function will be extremely optimized by remembering
1106 * the end of the list
1108 ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
1110 IS_CONSISTENT(ht);
1112 if (pos)
1113 *pos = ht->pListTail;
1114 else
1115 ht->pInternalPointer = ht->pListTail;
1119 ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
1121 HashPosition *current = pos ? pos : &ht->pInternalPointer;
1123 IS_CONSISTENT(ht);
1125 if (*current) {
1126 *current = (*current)->pListNext;
1127 return SUCCESS;
1128 } else
1129 return FAILURE;
1132 ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
1134 HashPosition *current = pos ? pos : &ht->pInternalPointer;
1136 IS_CONSISTENT(ht);
1138 if (*current) {
1139 *current = (*current)->pListLast;
1140 return SUCCESS;
1141 } else
1142 return FAILURE;
1146 /* This function should be made binary safe */
1147 ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, char **str_index, uint *str_length, ulong *num_index, zend_bool duplicate, HashPosition *pos)
1149 Bucket *p;
1151 p = pos ? (*pos) : ht->pInternalPointer;
1153 IS_CONSISTENT(ht);
1155 if (p) {
1156 if (p->nKeyLength) {
1157 if (duplicate) {
1158 *str_index = estrndup(p->arKey, p->nKeyLength - 1);
1159 } else {
1160 *str_index = (char*)p->arKey;
1162 if (str_length) {
1163 *str_length = p->nKeyLength;
1165 return HASH_KEY_IS_STRING;
1166 } else {
1167 *num_index = p->h;
1168 return HASH_KEY_IS_LONG;
1171 return HASH_KEY_NON_EXISTANT;
1175 ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
1177 Bucket *p;
1179 p = pos ? (*pos) : ht->pInternalPointer;
1181 IS_CONSISTENT(ht);
1183 if (p) {
1184 if (p->nKeyLength) {
1185 return HASH_KEY_IS_STRING;
1186 } else {
1187 return HASH_KEY_IS_LONG;
1190 return HASH_KEY_NON_EXISTANT;
1194 ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos)
1196 Bucket *p;
1198 p = pos ? (*pos) : ht->pInternalPointer;
1200 IS_CONSISTENT(ht);
1202 if (p) {
1203 *pData = p->pData;
1204 return SUCCESS;
1205 } else {
1206 return FAILURE;
1210 /* This function changes key of current element without changing elements'
1211 * order. If element with target key already exists, it will be deleted first.
1213 ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const char *str_index, uint str_length, ulong num_index, int mode, HashPosition *pos)
1215 Bucket *p, *q;
1216 ulong h;
1217 #ifdef ZEND_SIGNALS
1218 TSRMLS_FETCH();
1219 #endif
1221 p = pos ? (*pos) : ht->pInternalPointer;
1223 IS_CONSISTENT(ht);
1225 if (p) {
1226 if (key_type == HASH_KEY_IS_LONG) {
1227 str_length = 0;
1228 if (!p->nKeyLength && p->h == num_index) {
1229 return SUCCESS;
1232 q = ht->arBuckets[num_index & ht->nTableMask];
1233 while (q != NULL) {
1234 if (!q->nKeyLength && q->h == num_index) {
1235 break;
1237 q = q->pNext;
1239 } else if (key_type == HASH_KEY_IS_STRING) {
1240 if (IS_INTERNED(str_index)) {
1241 h = INTERNED_HASH(str_index);
1242 } else {
1243 h = zend_inline_hash_func(str_index, str_length);
1246 if (p->arKey == str_index ||
1247 (p->nKeyLength == str_length &&
1248 p->h == h &&
1249 memcmp(p->arKey, str_index, str_length) == 0)) {
1250 return SUCCESS;
1253 q = ht->arBuckets[h & ht->nTableMask];
1255 while (q != NULL) {
1256 if (q->arKey == str_index ||
1257 (q->h == h && q->nKeyLength == str_length &&
1258 memcmp(q->arKey, str_index, str_length) == 0)) {
1259 break;
1261 q = q->pNext;
1263 } else {
1264 return FAILURE;
1267 HANDLE_BLOCK_INTERRUPTIONS();
1269 if (q) {
1270 if (mode != HASH_UPDATE_KEY_ANYWAY) {
1271 Bucket *r = p->pListLast;
1272 int found = HASH_UPDATE_KEY_IF_BEFORE;
1274 while (r) {
1275 if (r == q) {
1276 found = HASH_UPDATE_KEY_IF_AFTER;
1277 break;
1279 r = r->pListLast;
1281 if (mode & found) {
1282 /* delete current bucket */
1283 if (p == ht->arBuckets[p->h & ht->nTableMask]) {
1284 ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
1285 } else {
1286 p->pLast->pNext = p->pNext;
1288 if (p->pNext) {
1289 p->pNext->pLast = p->pLast;
1291 if (p->pListLast != NULL) {
1292 p->pListLast->pListNext = p->pListNext;
1293 } else {
1294 /* Deleting the head of the list */
1295 ht->pListHead = p->pListNext;
1297 if (p->pListNext != NULL) {
1298 p->pListNext->pListLast = p->pListLast;
1299 } else {
1300 ht->pListTail = p->pListLast;
1302 if (ht->pInternalPointer == p) {
1303 ht->pInternalPointer = p->pListNext;
1305 if (ht->pDestructor) {
1306 ht->pDestructor(p->pData);
1308 if (p->pData != &p->pDataPtr) {
1309 pefree(p->pData, ht->persistent);
1311 pefree(p, ht->persistent);
1312 ht->nNumOfElements--;
1313 HANDLE_UNBLOCK_INTERRUPTIONS();
1314 return FAILURE;
1317 /* delete another bucket with the same key */
1318 if (q == ht->arBuckets[q->h & ht->nTableMask]) {
1319 ht->arBuckets[q->h & ht->nTableMask] = q->pNext;
1320 } else {
1321 q->pLast->pNext = q->pNext;
1323 if (q->pNext) {
1324 q->pNext->pLast = q->pLast;
1326 if (q->pListLast != NULL) {
1327 q->pListLast->pListNext = q->pListNext;
1328 } else {
1329 /* Deleting the head of the list */
1330 ht->pListHead = q->pListNext;
1332 if (q->pListNext != NULL) {
1333 q->pListNext->pListLast = q->pListLast;
1334 } else {
1335 ht->pListTail = q->pListLast;
1337 if (ht->pInternalPointer == q) {
1338 ht->pInternalPointer = q->pListNext;
1340 if (ht->pDestructor) {
1341 ht->pDestructor(q->pData);
1343 if (q->pData != &q->pDataPtr) {
1344 pefree(q->pData, ht->persistent);
1346 pefree(q, ht->persistent);
1347 ht->nNumOfElements--;
1350 if (p->pNext) {
1351 p->pNext->pLast = p->pLast;
1353 if (p->pLast) {
1354 p->pLast->pNext = p->pNext;
1355 } else {
1356 ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
1359 if ((IS_INTERNED(p->arKey) != IS_INTERNED(str_index)) ||
1360 (!IS_INTERNED(p->arKey) && p->nKeyLength != str_length)) {
1361 Bucket *q;
1363 if (IS_INTERNED(str_index)) {
1364 q = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
1365 } else {
1366 q = (Bucket *) pemalloc(sizeof(Bucket) + str_length, ht->persistent);
1369 q->nKeyLength = str_length;
1370 if (p->pData == &p->pDataPtr) {
1371 q->pData = &q->pDataPtr;
1372 } else {
1373 q->pData = p->pData;
1375 q->pDataPtr = p->pDataPtr;
1376 q->pListNext = p->pListNext;
1377 q->pListLast = p->pListLast;
1378 if (q->pListNext) {
1379 p->pListNext->pListLast = q;
1380 } else {
1381 ht->pListTail = q;
1383 if (q->pListLast) {
1384 p->pListLast->pListNext = q;
1385 } else {
1386 ht->pListHead = q;
1388 if (ht->pInternalPointer == p) {
1389 ht->pInternalPointer = q;
1391 if (pos) {
1392 *pos = q;
1394 pefree(p, ht->persistent);
1395 p = q;
1398 if (key_type == HASH_KEY_IS_LONG) {
1399 p->h = num_index;
1400 } else {
1401 p->h = h;
1402 p->nKeyLength = str_length;
1403 if (IS_INTERNED(str_index)) {
1404 p->arKey = str_index;
1405 } else {
1406 p->arKey = (const char*)(p+1);
1407 memcpy((char*)p->arKey, str_index, str_length);
1411 CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[p->h & ht->nTableMask]);
1412 ht->arBuckets[p->h & ht->nTableMask] = p;
1413 HANDLE_UNBLOCK_INTERRUPTIONS();
1415 return SUCCESS;
1416 } else {
1417 return FAILURE;
1421 ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
1422 compare_func_t compar, int renumber TSRMLS_DC)
1424 Bucket **arTmp;
1425 Bucket *p;
1426 int i, j;
1428 IS_CONSISTENT(ht);
1430 if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
1431 return SUCCESS;
1433 arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
1434 if (!arTmp) {
1435 return FAILURE;
1437 p = ht->pListHead;
1438 i = 0;
1439 while (p) {
1440 arTmp[i] = p;
1441 p = p->pListNext;
1442 i++;
1445 (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
1447 HANDLE_BLOCK_INTERRUPTIONS();
1448 ht->pListHead = arTmp[0];
1449 ht->pListTail = NULL;
1450 ht->pInternalPointer = ht->pListHead;
1452 arTmp[0]->pListLast = NULL;
1453 if (i > 1) {
1454 arTmp[0]->pListNext = arTmp[1];
1455 for (j = 1; j < i-1; j++) {
1456 arTmp[j]->pListLast = arTmp[j-1];
1457 arTmp[j]->pListNext = arTmp[j+1];
1459 arTmp[j]->pListLast = arTmp[j-1];
1460 arTmp[j]->pListNext = NULL;
1461 } else {
1462 arTmp[0]->pListNext = NULL;
1464 ht->pListTail = arTmp[i-1];
1466 pefree(arTmp, ht->persistent);
1467 HANDLE_UNBLOCK_INTERRUPTIONS();
1469 if (renumber) {
1470 p = ht->pListHead;
1471 i=0;
1472 while (p != NULL) {
1473 p->nKeyLength = 0;
1474 p->h = i++;
1475 p = p->pListNext;
1477 ht->nNextFreeElement = i;
1478 zend_hash_rehash(ht);
1480 return SUCCESS;
1484 ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
1486 Bucket *p1, *p2 = NULL;
1487 int result;
1488 void *pData2;
1490 IS_CONSISTENT(ht1);
1491 IS_CONSISTENT(ht2);
1493 HASH_PROTECT_RECURSION(ht1);
1494 HASH_PROTECT_RECURSION(ht2);
1496 result = ht1->nNumOfElements - ht2->nNumOfElements;
1497 if (result!=0) {
1498 HASH_UNPROTECT_RECURSION(ht1);
1499 HASH_UNPROTECT_RECURSION(ht2);
1500 return result;
1503 p1 = ht1->pListHead;
1504 if (ordered) {
1505 p2 = ht2->pListHead;
1508 while (p1) {
1509 if (ordered && !p2) {
1510 HASH_UNPROTECT_RECURSION(ht1);
1511 HASH_UNPROTECT_RECURSION(ht2);
1512 return 1; /* That's not supposed to happen */
1514 if (ordered) {
1515 if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */
1516 result = p1->h - p2->h;
1517 if (result!=0) {
1518 HASH_UNPROTECT_RECURSION(ht1);
1519 HASH_UNPROTECT_RECURSION(ht2);
1520 return result;
1522 } else { /* string indices */
1523 result = p1->nKeyLength - p2->nKeyLength;
1524 if (result!=0) {
1525 HASH_UNPROTECT_RECURSION(ht1);
1526 HASH_UNPROTECT_RECURSION(ht2);
1527 return result;
1529 result = memcmp(p1->arKey, p2->arKey, p1->nKeyLength);
1530 if (result!=0) {
1531 HASH_UNPROTECT_RECURSION(ht1);
1532 HASH_UNPROTECT_RECURSION(ht2);
1533 return result;
1536 pData2 = p2->pData;
1537 } else {
1538 if (p1->nKeyLength==0) { /* numeric index */
1539 if (zend_hash_index_find(ht2, p1->h, &pData2)==FAILURE) {
1540 HASH_UNPROTECT_RECURSION(ht1);
1541 HASH_UNPROTECT_RECURSION(ht2);
1542 return 1;
1544 } else { /* string index */
1545 if (zend_hash_quick_find(ht2, p1->arKey, p1->nKeyLength, p1->h, &pData2)==FAILURE) {
1546 HASH_UNPROTECT_RECURSION(ht1);
1547 HASH_UNPROTECT_RECURSION(ht2);
1548 return 1;
1552 result = compar(p1->pData, pData2 TSRMLS_CC);
1553 if (result!=0) {
1554 HASH_UNPROTECT_RECURSION(ht1);
1555 HASH_UNPROTECT_RECURSION(ht2);
1556 return result;
1558 p1 = p1->pListNext;
1559 if (ordered) {
1560 p2 = p2->pListNext;
1564 HASH_UNPROTECT_RECURSION(ht1);
1565 HASH_UNPROTECT_RECURSION(ht2);
1566 return 0;
1570 ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC)
1572 Bucket *p, *res;
1574 IS_CONSISTENT(ht);
1576 if (ht->nNumOfElements == 0 ) {
1577 *pData=NULL;
1578 return FAILURE;
1581 res = p = ht->pListHead;
1582 while ((p = p->pListNext)) {
1583 if (flag) {
1584 if (compar(&res, &p TSRMLS_CC) < 0) { /* max */
1585 res = p;
1587 } else {
1588 if (compar(&res, &p TSRMLS_CC) > 0) { /* min */
1589 res = p;
1593 *pData = res->pData;
1594 return SUCCESS;
1597 ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
1599 IS_CONSISTENT(ht);
1601 return ht->nNextFreeElement;
1606 #if ZEND_DEBUG
1607 void zend_hash_display_pListTail(const HashTable *ht)
1609 Bucket *p;
1611 p = ht->pListTail;
1612 while (p != NULL) {
1613 zend_output_debug_string(0, "pListTail has key %s\n", p->arKey);
1614 p = p->pListLast;
1618 void zend_hash_display(const HashTable *ht)
1620 Bucket *p;
1621 uint i;
1623 if (UNEXPECTED(ht->nNumOfElements == 0)) {
1624 zend_output_debug_string(0, "The hash is empty");
1625 return;
1627 for (i = 0; i < ht->nTableSize; i++) {
1628 p = ht->arBuckets[i];
1629 while (p != NULL) {
1630 zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
1631 p = p->pNext;
1635 p = ht->pListTail;
1636 while (p != NULL) {
1637 zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
1638 p = p->pListLast;
1641 #endif
1644 * Local variables:
1645 * tab-width: 4
1646 * c-basic-offset: 4
1647 * indent-tabs-mode: t
1648 * End: