2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
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); \
39 if (!(ht)->pListHead) { \
40 (ht)->pListHead = (element); \
42 if ((ht)->pInternalPointer == NULL) { \
43 (ht)->pInternalPointer = (element); \
48 #define HT_IS_DESTROYING 1
49 #define HT_DESTROYED 2
52 static void _zend_is_inconsistent(const HashTable
*ht
, const char *file
, int line
)
54 if (ht
->inconsistent
==HT_OK
) {
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
);
62 zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file
, line
, ht
);
65 zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file
, line
, ht
);
68 zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file
, line
, ht
);
73 #define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
74 #define SET_INCONSISTENT(n) ht->inconsistent = n;
76 #define IS_CONSISTENT(a)
77 #define SET_INCONSISTENT(n)
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?"); \
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; \
115 if ((p)->pData == &(p)->pDataPtr) { \
116 (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent); \
117 (p)->pDataPtr=NULL; \
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; \
130 (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
132 pefree_rel(p, (ht)->persistent); \
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; \
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
)
152 SET_INCONSISTENT(HT_OK
);
154 if (nSize
>= 0x80000000) {
155 /* prevent overflow */
156 ht
->nTableSize
= 0x80000000;
158 while ((1U << i
) < nSize
) {
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
;
174 ht
->bApplyProtection
= 1;
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
;
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
)
206 if (nKeyLength
<= 0) {
208 ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
215 h
= zend_inline_hash_func(arKey
, nKeyLength
);
216 nIndex
= h
& ht
->nTableMask
;
218 p
= ht
->arBuckets
[nIndex
];
220 if (p
->arKey
== arKey
||
221 ((p
->h
== h
) && (p
->nKeyLength
== nKeyLength
) && !memcmp(p
->arKey
, arKey
, nKeyLength
))) {
222 if (flag
& HASH_ADD
) {
225 HANDLE_BLOCK_INTERRUPTIONS();
227 if (p
->pData
== pData
) {
228 ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
229 HANDLE_UNBLOCK_INTERRUPTIONS();
233 if (ht
->pDestructor
) {
234 ht
->pDestructor(p
->pData
);
236 UPDATE_DATA(ht
, p
, pData
, nDataSize
);
240 HANDLE_UNBLOCK_INTERRUPTIONS();
246 if (IS_INTERNED(arKey
)) {
247 p
= (Bucket
*) pemalloc(sizeof(Bucket
), ht
->persistent
);
253 p
= (Bucket
*) pemalloc(sizeof(Bucket
) + nKeyLength
, ht
->persistent
);
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
);
263 CONNECT_TO_BUCKET_DLLIST(p
, ht
->arBuckets
[nIndex
]);
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 */
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
)
288 if (nKeyLength
== 0) {
289 return zend_hash_index_update(ht
, h
, pData
, nDataSize
, pDest
);
293 nIndex
= h
& ht
->nTableMask
;
295 p
= ht
->arBuckets
[nIndex
];
297 if (p
->arKey
== arKey
||
298 ((p
->h
== h
) && (p
->nKeyLength
== nKeyLength
) && !memcmp(p
->arKey
, arKey
, nKeyLength
))) {
299 if (flag
& HASH_ADD
) {
302 HANDLE_BLOCK_INTERRUPTIONS();
304 if (p
->pData
== pData
) {
305 ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
306 HANDLE_UNBLOCK_INTERRUPTIONS();
310 if (ht
->pDestructor
) {
311 ht
->pDestructor(p
->pData
);
313 UPDATE_DATA(ht
, p
, pData
, nDataSize
);
317 HANDLE_UNBLOCK_INTERRUPTIONS();
323 if (IS_INTERNED(arKey
)) {
324 p
= (Bucket
*) pemalloc(sizeof(Bucket
), ht
->persistent
);
330 p
= (Bucket
*) pemalloc(sizeof(Bucket
) + nKeyLength
, ht
->persistent
);
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
);
342 CONNECT_TO_BUCKET_DLLIST(p
, ht
->arBuckets
[nIndex
]);
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 */
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
)
378 if (flag
& HASH_NEXT_INSERT
) {
379 h
= ht
->nNextFreeElement
;
381 nIndex
= h
& ht
->nTableMask
;
383 p
= ht
->arBuckets
[nIndex
];
385 if ((p
->nKeyLength
== 0) && (p
->h
== h
)) {
386 if (flag
& HASH_NEXT_INSERT
|| flag
& HASH_ADD
) {
389 HANDLE_BLOCK_INTERRUPTIONS();
391 if (p
->pData
== pData
) {
392 ZEND_PUTS("Fatal error in zend_hash_index_update: p->pData == pData\n");
393 HANDLE_UNBLOCK_INTERRUPTIONS();
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
;
412 p
= (Bucket
*) pemalloc_rel(sizeof(Bucket
), ht
->persistent
);
417 p
->nKeyLength
= 0; /* Numeric indices are marked by making the nKeyLength == 0 */
419 INIT_DATA(ht
, p
, pData
, nDataSize
);
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
);
440 static int zend_hash_do_resize(HashTable
*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
);
452 HANDLE_BLOCK_INTERRUPTIONS();
454 ht
->nTableSize
= (ht
->nTableSize
<< 1);
455 ht
->nTableMask
= ht
->nTableSize
- 1;
456 zend_hash_rehash(ht
);
457 HANDLE_UNBLOCK_INTERRUPTIONS();
465 ZEND_API
int zend_hash_rehash(HashTable
*ht
)
471 if (UNEXPECTED(ht
->nNumOfElements
== 0)) {
475 memset(ht
->arBuckets
, 0, ht
->nTableSize
* sizeof(Bucket
*));
478 nIndex
= p
->h
& ht
->nTableMask
;
479 CONNECT_TO_BUCKET_DLLIST(p
, ht
->arBuckets
[nIndex
]);
480 ht
->arBuckets
[nIndex
] = p
;
486 ZEND_API
int zend_hash_del_key_or_index(HashTable
*ht
, const char *arKey
, uint nKeyLength
, ulong h
, int flag
)
496 if (flag
== HASH_DEL_KEY
) {
497 h
= zend_inline_hash_func(arKey
, nKeyLength
);
499 nIndex
= h
& ht
->nTableMask
;
501 p
= ht
->arBuckets
[nIndex
];
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
;
511 p
->pLast
->pNext
= p
->pNext
;
514 p
->pNext
->pLast
= p
->pLast
;
516 if (p
->pListLast
!= NULL
) {
517 p
->pListLast
->pListNext
= p
->pListNext
;
519 /* Deleting the head of the list */
520 ht
->pListHead
= p
->pListNext
;
522 if (p
->pListNext
!= NULL
) {
523 p
->pListNext
->pListLast
= p
->pListLast
;
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
--;
547 ZEND_API
void zend_hash_destroy(HashTable
*ht
)
553 SET_INCONSISTENT(HT_IS_DESTROYING
);
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
)
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
;
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
)
617 HANDLE_BLOCK_INTERRUPTIONS();
619 p
->pLast
->pNext
= p
->pNext
;
623 nIndex
= p
->h
& ht
->nTableMask
;
624 ht
->arBuckets
[nIndex
] = p
->pNext
;
627 p
->pNext
->pLast
= p
->pLast
;
629 /* Nothing to do as this list doesn't have a tail */
632 if (p
->pListLast
!= NULL
) {
633 p
->pListLast
->pListNext
= p
->pListNext
;
635 /* Deleting the head of the list */
636 ht
->pListHead
= p
->pListNext
;
638 if (p
->pListNext
!= NULL
) {
639 p
->pListNext
->pListLast
= p
->pListLast
;
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
);
662 ZEND_API
void zend_hash_graceful_destroy(HashTable
*ht
)
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
)
687 zend_hash_apply_deleter(ht
, p
);
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
)
713 HASH_PROTECT_RECURSION(ht
);
716 int result
= apply_func(p
->pData TSRMLS_CC
);
718 if (result
& ZEND_HASH_APPLY_REMOVE
) {
719 p
= zend_hash_apply_deleter(ht
, p
);
723 if (result
& ZEND_HASH_APPLY_STOP
) {
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
)
737 HASH_PROTECT_RECURSION(ht
);
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
);
747 if (result
& ZEND_HASH_APPLY_STOP
) {
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
, ...)
759 zend_hash_key hash_key
;
763 HASH_PROTECT_RECURSION(ht
);
768 va_start(args
, num_args
);
769 hash_key
.arKey
= p
->arKey
;
770 hash_key
.nKeyLength
= p
->nKeyLength
;
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
);
779 if (result
& ZEND_HASH_APPLY_STOP
) {
786 HASH_UNPROTECT_RECURSION(ht
);
790 ZEND_API
void zend_hash_reverse_apply(HashTable
*ht
, apply_func_t apply_func TSRMLS_DC
)
796 HASH_PROTECT_RECURSION(ht
);
799 int result
= apply_func(p
->pData TSRMLS_CC
);
803 if (result
& ZEND_HASH_APPLY_REMOVE
) {
804 zend_hash_apply_deleter(ht
, q
);
806 if (result
& ZEND_HASH_APPLY_STOP
) {
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
)
818 zend_bool setTargetPointer
;
820 IS_CONSISTENT(source
);
821 IS_CONSISTENT(target
);
823 setTargetPointer
= !target
->pInternalPointer
;
824 p
= source
->pListHead
;
826 if (setTargetPointer
&& source
->pInternalPointer
== p
) {
827 target
->pInternalPointer
= NULL
;
830 zend_hash_quick_update(target
, p
->arKey
, p
->nKeyLength
, p
->h
, p
->pData
, size
, &new_entry
);
832 zend_hash_index_update(target
, p
->h
, p
->pData
, size
, &new_entry
);
834 if (pCopyConstructor
) {
835 pCopyConstructor(new_entry
);
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
)
849 int mode
= (overwrite
?HASH_UPDATE
:HASH_ADD
);
851 IS_CONSISTENT(source
);
852 IS_CONSISTENT(target
);
854 p
= source
->pListHead
;
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
) {
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
) {
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
;
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
)
887 IS_CONSISTENT(source
);
888 IS_CONSISTENT(target
);
890 p
= source
->pListHead
;
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
) {
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
)
921 h
= zend_inline_hash_func(arKey
, nKeyLength
);
922 nIndex
= h
& ht
->nTableMask
;
924 p
= ht
->arBuckets
[nIndex
];
926 if (p
->arKey
== arKey
||
927 ((p
->h
== h
) && (p
->nKeyLength
== nKeyLength
) && !memcmp(p
->arKey
, arKey
, nKeyLength
))) {
937 ZEND_API
int zend_hash_quick_find(const HashTable
*ht
, const char *arKey
, uint nKeyLength
, ulong h
, void **pData
)
943 return zend_hash_index_find(ht
, h
, pData
);
948 nIndex
= h
& ht
->nTableMask
;
950 p
= ht
->arBuckets
[nIndex
];
952 if (p
->arKey
== arKey
||
953 ((p
->h
== h
) && (p
->nKeyLength
== nKeyLength
) && !memcmp(p
->arKey
, arKey
, nKeyLength
))) {
963 ZEND_API
int zend_hash_exists(const HashTable
*ht
, const char *arKey
, uint nKeyLength
)
971 h
= zend_inline_hash_func(arKey
, nKeyLength
);
972 nIndex
= h
& ht
->nTableMask
;
974 p
= ht
->arBuckets
[nIndex
];
976 if (p
->arKey
== arKey
||
977 ((p
->h
== h
) && (p
->nKeyLength
== nKeyLength
) && !memcmp(p
->arKey
, arKey
, nKeyLength
))) {
986 ZEND_API
int zend_hash_quick_exists(const HashTable
*ht
, const char *arKey
, uint nKeyLength
, ulong h
)
992 return zend_hash_index_exists(ht
, h
);
997 nIndex
= h
& ht
->nTableMask
;
999 p
= ht
->arBuckets
[nIndex
];
1001 if (p
->arKey
== arKey
||
1002 ((p
->h
== h
) && (p
->nKeyLength
== nKeyLength
) && !memcmp(p
->arKey
, arKey
, nKeyLength
))) {
1012 ZEND_API
int zend_hash_index_find(const HashTable
*ht
, ulong h
, void **pData
)
1019 nIndex
= h
& ht
->nTableMask
;
1021 p
= ht
->arBuckets
[nIndex
];
1023 if ((p
->h
== h
) && (p
->nKeyLength
== 0)) {
1033 ZEND_API
int zend_hash_index_exists(const HashTable
*ht
, ulong h
)
1040 nIndex
= h
& ht
->nTableMask
;
1042 p
= ht
->arBuckets
[nIndex
];
1044 if ((p
->h
== h
) && (p
->nKeyLength
== 0)) {
1053 ZEND_API
int zend_hash_num_elements(const HashTable
*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
;
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
) {
1081 p
= ht
->arBuckets
[ptr
->h
& ht
->nTableMask
];
1083 if (p
== ptr
->pos
) {
1084 ht
->pInternalPointer
= p
;
1094 ZEND_API
void zend_hash_internal_pointer_reset_ex(HashTable
*ht
, HashPosition
*pos
)
1099 *pos
= ht
->pListHead
;
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
)
1113 *pos
= ht
->pListTail
;
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
;
1126 *current
= (*current
)->pListNext
;
1132 ZEND_API
int zend_hash_move_backwards_ex(HashTable
*ht
, HashPosition
*pos
)
1134 HashPosition
*current
= pos
? pos
: &ht
->pInternalPointer
;
1139 *current
= (*current
)->pListLast
;
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
)
1151 p
= pos
? (*pos
) : ht
->pInternalPointer
;
1156 if (p
->nKeyLength
) {
1158 *str_index
= estrndup(p
->arKey
, p
->nKeyLength
- 1);
1160 *str_index
= (char*)p
->arKey
;
1163 *str_length
= p
->nKeyLength
;
1165 return HASH_KEY_IS_STRING
;
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
)
1179 p
= pos
? (*pos
) : ht
->pInternalPointer
;
1184 if (p
->nKeyLength
) {
1185 return HASH_KEY_IS_STRING
;
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
)
1198 p
= pos
? (*pos
) : ht
->pInternalPointer
;
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
)
1221 p
= pos
? (*pos
) : ht
->pInternalPointer
;
1226 if (key_type
== HASH_KEY_IS_LONG
) {
1228 if (!p
->nKeyLength
&& p
->h
== num_index
) {
1232 q
= ht
->arBuckets
[num_index
& ht
->nTableMask
];
1234 if (!q
->nKeyLength
&& q
->h
== num_index
) {
1239 } else if (key_type
== HASH_KEY_IS_STRING
) {
1240 if (IS_INTERNED(str_index
)) {
1241 h
= INTERNED_HASH(str_index
);
1243 h
= zend_inline_hash_func(str_index
, str_length
);
1246 if (p
->arKey
== str_index
||
1247 (p
->nKeyLength
== str_length
&&
1249 memcmp(p
->arKey
, str_index
, str_length
) == 0)) {
1253 q
= ht
->arBuckets
[h
& ht
->nTableMask
];
1256 if (q
->arKey
== str_index
||
1257 (q
->h
== h
&& q
->nKeyLength
== str_length
&&
1258 memcmp(q
->arKey
, str_index
, str_length
) == 0)) {
1267 HANDLE_BLOCK_INTERRUPTIONS();
1270 if (mode
!= HASH_UPDATE_KEY_ANYWAY
) {
1271 Bucket
*r
= p
->pListLast
;
1272 int found
= HASH_UPDATE_KEY_IF_BEFORE
;
1276 found
= HASH_UPDATE_KEY_IF_AFTER
;
1282 /* delete current bucket */
1283 if (p
== ht
->arBuckets
[p
->h
& ht
->nTableMask
]) {
1284 ht
->arBuckets
[p
->h
& ht
->nTableMask
] = p
->pNext
;
1286 p
->pLast
->pNext
= p
->pNext
;
1289 p
->pNext
->pLast
= p
->pLast
;
1291 if (p
->pListLast
!= NULL
) {
1292 p
->pListLast
->pListNext
= p
->pListNext
;
1294 /* Deleting the head of the list */
1295 ht
->pListHead
= p
->pListNext
;
1297 if (p
->pListNext
!= NULL
) {
1298 p
->pListNext
->pListLast
= p
->pListLast
;
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();
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
;
1321 q
->pLast
->pNext
= q
->pNext
;
1324 q
->pNext
->pLast
= q
->pLast
;
1326 if (q
->pListLast
!= NULL
) {
1327 q
->pListLast
->pListNext
= q
->pListNext
;
1329 /* Deleting the head of the list */
1330 ht
->pListHead
= q
->pListNext
;
1332 if (q
->pListNext
!= NULL
) {
1333 q
->pListNext
->pListLast
= q
->pListLast
;
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
--;
1351 p
->pNext
->pLast
= p
->pLast
;
1354 p
->pLast
->pNext
= p
->pNext
;
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
)) {
1363 if (IS_INTERNED(str_index
)) {
1364 q
= (Bucket
*) pemalloc(sizeof(Bucket
), ht
->persistent
);
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
;
1373 q
->pData
= p
->pData
;
1375 q
->pDataPtr
= p
->pDataPtr
;
1376 q
->pListNext
= p
->pListNext
;
1377 q
->pListLast
= p
->pListLast
;
1379 p
->pListNext
->pListLast
= q
;
1384 p
->pListLast
->pListNext
= q
;
1388 if (ht
->pInternalPointer
== p
) {
1389 ht
->pInternalPointer
= q
;
1394 pefree(p
, ht
->persistent
);
1398 if (key_type
== HASH_KEY_IS_LONG
) {
1402 p
->nKeyLength
= str_length
;
1403 if (IS_INTERNED(str_index
)) {
1404 p
->arKey
= str_index
;
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();
1421 ZEND_API
int zend_hash_sort(HashTable
*ht
, sort_func_t sort_func
,
1422 compare_func_t compar
, int renumber TSRMLS_DC
)
1430 if (!(ht
->nNumOfElements
>1) && !(renumber
&& ht
->nNumOfElements
>0)) { /* Doesn't require sorting */
1433 arTmp
= (Bucket
**) pemalloc(ht
->nNumOfElements
* sizeof(Bucket
*), ht
->persistent
);
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
;
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
;
1462 arTmp
[0]->pListNext
= NULL
;
1464 ht
->pListTail
= arTmp
[i
-1];
1466 pefree(arTmp
, ht
->persistent
);
1467 HANDLE_UNBLOCK_INTERRUPTIONS();
1477 ht
->nNextFreeElement
= i
;
1478 zend_hash_rehash(ht
);
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
;
1493 HASH_PROTECT_RECURSION(ht1
);
1494 HASH_PROTECT_RECURSION(ht2
);
1496 result
= ht1
->nNumOfElements
- ht2
->nNumOfElements
;
1498 HASH_UNPROTECT_RECURSION(ht1
);
1499 HASH_UNPROTECT_RECURSION(ht2
);
1503 p1
= ht1
->pListHead
;
1505 p2
= ht2
->pListHead
;
1509 if (ordered
&& !p2
) {
1510 HASH_UNPROTECT_RECURSION(ht1
);
1511 HASH_UNPROTECT_RECURSION(ht2
);
1512 return 1; /* That's not supposed to happen */
1515 if (p1
->nKeyLength
==0 && p2
->nKeyLength
==0) { /* numeric indices */
1516 result
= p1
->h
- p2
->h
;
1518 HASH_UNPROTECT_RECURSION(ht1
);
1519 HASH_UNPROTECT_RECURSION(ht2
);
1522 } else { /* string indices */
1523 result
= p1
->nKeyLength
- p2
->nKeyLength
;
1525 HASH_UNPROTECT_RECURSION(ht1
);
1526 HASH_UNPROTECT_RECURSION(ht2
);
1529 result
= memcmp(p1
->arKey
, p2
->arKey
, p1
->nKeyLength
);
1531 HASH_UNPROTECT_RECURSION(ht1
);
1532 HASH_UNPROTECT_RECURSION(ht2
);
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
);
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
);
1552 result
= compar(p1
->pData
, pData2 TSRMLS_CC
);
1554 HASH_UNPROTECT_RECURSION(ht1
);
1555 HASH_UNPROTECT_RECURSION(ht2
);
1564 HASH_UNPROTECT_RECURSION(ht1
);
1565 HASH_UNPROTECT_RECURSION(ht2
);
1570 ZEND_API
int zend_hash_minmax(const HashTable
*ht
, compare_func_t compar
, int flag
, void **pData TSRMLS_DC
)
1576 if (ht
->nNumOfElements
== 0 ) {
1581 res
= p
= ht
->pListHead
;
1582 while ((p
= p
->pListNext
)) {
1584 if (compar(&res
, &p TSRMLS_CC
) < 0) { /* max */
1588 if (compar(&res
, &p TSRMLS_CC
) > 0) { /* min */
1593 *pData
= res
->pData
;
1597 ZEND_API ulong
zend_hash_next_free_element(const HashTable
*ht
)
1601 return ht
->nNextFreeElement
;
1607 void zend_hash_display_pListTail(const HashTable
*ht
)
1613 zend_output_debug_string(0, "pListTail has key %s\n", p
->arKey
);
1618 void zend_hash_display(const HashTable
*ht
)
1623 if (UNEXPECTED(ht
->nNumOfElements
== 0)) {
1624 zend_output_debug_string(0, "The hash is empty");
1627 for (i
= 0; i
< ht
->nTableSize
; i
++) {
1628 p
= ht
->arBuckets
[i
];
1630 zend_output_debug_string(0, "%s <==> 0x%lX\n", p
->arKey
, p
->h
);
1637 zend_output_debug_string(0, "%s <==> 0x%lX\n", p
->arKey
, p
->h
);
1647 * indent-tabs-mode: t