From 7f7c84d64f2cb6a1d8ac84d15788310eac580289 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 20 Mar 2014 15:18:41 +0100 Subject: [PATCH 1/6] add info: empty strings are interned --- UPGRADING.INTERNALS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index e8e09820429..474aed55f7d 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -8,6 +8,7 @@ UPGRADE NOTES - PHP X.Y c. POST data handling d. Arginfo changes e. tsrm_virtual_cwd.h moved to zend_virtual_cwd.h + f. empty strings are interned 2. Build system changes a. Unix build system changes @@ -115,6 +116,13 @@ UPGRADE NOTES - PHP X.Y Memory allocation is now managed by emalloc/efree instead of malloc/free. + f. empty strings are interned + + String created using STR_EMPTY_ALLOC() are now interned. + convert_to_string use STR_EMPTY_ALLOC() for zval when IS_NULL. + STR_FREE() shoud be prefered as efree on such strings can raise memory corruption. + + ======================== 2. Build system changes ======================== From 994fcfcff90e3ce3c078e58d44beb636c5111eec Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 20 Mar 2014 22:58:10 +0100 Subject: [PATCH 2/6] Remove unnecessary check In case of an update the key already existed, so the next free element can not change. --- Zend/zend_hash.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 829cd336416..b5c310623e6 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -358,9 +358,6 @@ ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void } UPDATE_DATA(ht, p, pData, nDataSize); HANDLE_UNBLOCK_INTERRUPTIONS(); - if ((long)h >= (long)ht->nNextFreeElement) { - ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX; - } if (pDest) { *pDest = p->pData; } From c3a317117ad89694b60b9aa387b6737d6f2774cb Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 20 Mar 2014 23:08:31 +0100 Subject: [PATCH 3/6] Add helper function for updating bucket contents --- Zend/zend_hash.c | 72 +++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index b5c310623e6..ac713c6addb 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -141,6 +141,21 @@ ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength) static const Bucket *uninitialized_bucket = NULL; +static inline void zend_hash_bucket_update( + const HashTable *ht, Bucket *p, void *pData, uint nDataSize, void **pDest ZEND_FILE_LINE_DC +) { + ZEND_ASSERT(p->pData != pData); + HANDLE_BLOCK_INTERRUPTIONS(); + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + UPDATE_DATA(ht, p, pData, nDataSize); + HANDLE_UNBLOCK_INTERRUPTIONS(); + if (pDest) { + *pDest = p->pData; + } +} + ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) { uint i = 3; @@ -209,21 +224,14 @@ ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKe p = ht->arBuckets[nIndex]; while (p != NULL) { if (p->arKey == arKey || - ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) { - if (flag & HASH_ADD) { - return FAILURE; - } - ZEND_ASSERT(p->pData != pData); - HANDLE_BLOCK_INTERRUPTIONS(); - if (ht->pDestructor) { - ht->pDestructor(p->pData); - } - UPDATE_DATA(ht, p, pData, nDataSize); - if (pDest) { - *pDest = p->pData; - } - HANDLE_UNBLOCK_INTERRUPTIONS(); - return SUCCESS; + ((p->h == h) && (p->nKeyLength == nKeyLength) + && !memcmp(p->arKey, arKey, nKeyLength)) + ) { + if (flag & HASH_ADD) { + return FAILURE; + } + zend_hash_bucket_update(ht, p, pData, nDataSize, pDest ZEND_FILE_LINE_RELAY_CC); + return SUCCESS; } p = p->pNext; } @@ -272,21 +280,14 @@ ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, ui p = ht->arBuckets[nIndex]; while (p != NULL) { if (p->arKey == arKey || - ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) { - if (flag & HASH_ADD) { - return FAILURE; - } - ZEND_ASSERT(p->pData != pData); - HANDLE_BLOCK_INTERRUPTIONS(); - if (ht->pDestructor) { - ht->pDestructor(p->pData); - } - UPDATE_DATA(ht, p, pData, nDataSize); - if (pDest) { - *pDest = p->pData; - } - HANDLE_UNBLOCK_INTERRUPTIONS(); - return SUCCESS; + ((p->h == h) && (p->nKeyLength == nKeyLength) + && !memcmp(p->arKey, arKey, nKeyLength)) + ) { + if (flag & HASH_ADD) { + return FAILURE; + } + zend_hash_bucket_update(ht, p, pData, nDataSize, pDest ZEND_FILE_LINE_RELAY_CC); + return SUCCESS; } p = p->pNext; } @@ -351,16 +352,7 @@ ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) { return FAILURE; } - ZEND_ASSERT(p->pData != pData); - HANDLE_BLOCK_INTERRUPTIONS(); - if (ht->pDestructor) { - ht->pDestructor(p->pData); - } - UPDATE_DATA(ht, p, pData, nDataSize); - HANDLE_UNBLOCK_INTERRUPTIONS(); - if (pDest) { - *pDest = p->pData; - } + zend_hash_bucket_update(ht, p, pData, nDataSize, pDest ZEND_FILE_LINE_RELAY_CC); return SUCCESS; } p = p->pNext; From 4efa58dd593409eb5a4359780bf6b3f83995499b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 20 Mar 2014 23:25:53 +0100 Subject: [PATCH 4/6] Make zend_hash_apply_deleter() void, to allow reuse Also remove lies in the comment above the function. apply_deleter is not safe against modification, if the modification involves removing the next bucket. Also modified the implementation of zend_hash_graceful_destroy() to be fully graceful (the reverse variant already was). --- Zend/zend_hash.c | 58 +++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index ac713c6addb..ab8601c2ebe 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -543,13 +543,10 @@ ZEND_API void zend_hash_clean(HashTable *ht) } /* This function is used by the various apply() functions. - * It deletes the passed bucket, and returns the address of the - * next bucket. The hash *may* be altered during that time, the - * returned value will still be valid. + * It deletes the passed bucket. */ -static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p) +static void zend_hash_apply_deleter(HashTable *ht, Bucket *p) { - Bucket *retval; #ifdef ZEND_SIGNALS TSRMLS_FETCH(); #endif @@ -592,23 +589,18 @@ static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p) if (p->pData != &p->pDataPtr) { pefree(p->pData, ht->persistent); } - retval = p->pListNext; pefree(p, ht->persistent); - - return retval; } ZEND_API void zend_hash_graceful_destroy(HashTable *ht) { - Bucket *p; - IS_CONSISTENT(ht); - p = ht->pListHead; - while (p != NULL) { - p = zend_hash_apply_deleter(ht, p); + while (ht->pListHead != NULL) { + zend_hash_apply_deleter(ht, ht->pListHead); } + if (ht->nTableMask) { pefree(ht->arBuckets, ht->persistent); } @@ -618,14 +610,10 @@ ZEND_API void zend_hash_graceful_destroy(HashTable *ht) ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht) { - Bucket *p; - IS_CONSISTENT(ht); - p = ht->pListTail; - while (p != NULL) { - zend_hash_apply_deleter(ht, p); - p = ht->pListTail; + while (ht->pListTail != NULL) { + zend_hash_apply_deleter(ht, ht->pListTail); } if (ht->nTableMask) { @@ -654,12 +642,13 @@ ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC) p = ht->pListHead; while (p != NULL) { int result = apply_func(p->pData TSRMLS_CC); - + + Bucket *p_next = p->pListNext; if (result & ZEND_HASH_APPLY_REMOVE) { - p = zend_hash_apply_deleter(ht, p); - } else { - p = p->pListNext; + zend_hash_apply_deleter(ht, p); } + p = p_next; + if (result & ZEND_HASH_APPLY_STOP) { break; } @@ -679,11 +668,12 @@ ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t appl while (p != NULL) { int result = apply_func(p->pData, argument TSRMLS_CC); + Bucket *p_next = p->pListNext; if (result & ZEND_HASH_APPLY_REMOVE) { - p = zend_hash_apply_deleter(ht, p); - } else { - p = p->pListNext; + zend_hash_apply_deleter(ht, p); } + p = p_next; + if (result & ZEND_HASH_APPLY_STOP) { break; } @@ -711,11 +701,12 @@ ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func hash_key.h = p->h; result = apply_func(p->pData TSRMLS_CC, num_args, args, &hash_key); + Bucket *p_next = p->pListNext; if (result & ZEND_HASH_APPLY_REMOVE) { - p = zend_hash_apply_deleter(ht, p); - } else { - p = p->pListNext; + zend_hash_apply_deleter(ht, p); } + p = p_next; + if (result & ZEND_HASH_APPLY_STOP) { va_end(args); break; @@ -729,7 +720,7 @@ ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC) { - Bucket *p, *q; + Bucket *p; IS_CONSISTENT(ht); @@ -738,11 +729,12 @@ ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSR while (p != NULL) { int result = apply_func(p->pData TSRMLS_CC); - q = p; - p = p->pListLast; + Bucket *p_last = p->pListLast; if (result & ZEND_HASH_APPLY_REMOVE) { - zend_hash_apply_deleter(ht, q); + zend_hash_apply_deleter(ht, p); } + p = p_last; + if (result & ZEND_HASH_APPLY_STOP) { break; } From 4e7e301dda2fa081c1310279b027a2350c05801e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 20 Mar 2014 23:50:04 +0100 Subject: [PATCH 5/6] Rename+move zend_hash_apply_deleter and reuse it The interruption handling was normalized to wrap the entire deletion operation (instead of leaving out the destructor call and/or the deallocation) per the recommendation of somebody (don't remember who) familiar with zend signals. There is an always-inlined variant of the function for use in the main deletion function, to ensure there is no performance impact. --- Zend/zend_hash.c | 212 +++++++++++++---------------------------------- 1 file changed, 57 insertions(+), 155 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index ab8601c2ebe..aa3587c6759 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -156,6 +156,51 @@ static inline void zend_hash_bucket_update( } } +static zend_always_inline void i_zend_hash_bucket_delete(HashTable *ht, Bucket *p) +{ +#ifdef ZEND_SIGNALS + TSRMLS_FETCH(); +#endif + + HANDLE_BLOCK_INTERRUPTIONS(); + if (p->pLast) { + p->pLast->pNext = p->pNext; + } else { + ht->arBuckets[p->h & ht->nTableMask] = p->pNext; + } + if (p->pNext) { + p->pNext->pLast = p->pLast; + } + if (p->pListLast != NULL) { + p->pListLast->pListNext = p->pListNext; + } else { + /* Deleting the head of the list */ + ht->pListHead = p->pListNext; + } + if (p->pListNext != NULL) { + p->pListNext->pListLast = p->pListLast; + } else { + /* Deleting the tail of the list */ + ht->pListTail = p->pListLast; + } + if (ht->pInternalPointer == p) { + ht->pInternalPointer = p->pListNext; + } + ht->nNumOfElements--; + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + if (p->pData != &p->pDataPtr) { + pefree(p->pData, ht->persistent); + } + pefree(p, ht->persistent); + HANDLE_UNBLOCK_INTERRUPTIONS(); +} + +static void zend_hash_bucket_delete(HashTable *ht, Bucket *p) { + i_zend_hash_bucket_delete(ht, p); +} + ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) { uint i = 3; @@ -427,9 +472,6 @@ ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint n { uint nIndex; Bucket *p; -#ifdef ZEND_SIGNALS - TSRMLS_FETCH(); -#endif IS_CONSISTENT(ht); @@ -444,38 +486,7 @@ ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint n && (p->nKeyLength == nKeyLength) && ((p->nKeyLength == 0) /* Numeric index (short circuits the memcmp() check) */ || !memcmp(p->arKey, arKey, nKeyLength))) { /* String index */ - HANDLE_BLOCK_INTERRUPTIONS(); - if (p == ht->arBuckets[nIndex]) { - ht->arBuckets[nIndex] = p->pNext; - } else { - p->pLast->pNext = p->pNext; - } - if (p->pNext) { - p->pNext->pLast = p->pLast; - } - if (p->pListLast != NULL) { - p->pListLast->pListNext = p->pListNext; - } else { - /* Deleting the head of the list */ - ht->pListHead = p->pListNext; - } - if (p->pListNext != NULL) { - p->pListNext->pListLast = p->pListLast; - } else { - ht->pListTail = p->pListLast; - } - if (ht->pInternalPointer == p) { - ht->pInternalPointer = p->pListNext; - } - ht->nNumOfElements--; - if (ht->pDestructor) { - ht->pDestructor(p->pData); - } - if (p->pData != &p->pDataPtr) { - pefree(p->pData, ht->persistent); - } - pefree(p, ht->persistent); - HANDLE_UNBLOCK_INTERRUPTIONS(); + i_zend_hash_bucket_delete(ht, p); return SUCCESS; } p = p->pNext; @@ -542,63 +553,12 @@ ZEND_API void zend_hash_clean(HashTable *ht) } } -/* This function is used by the various apply() functions. - * It deletes the passed bucket. - */ -static void zend_hash_apply_deleter(HashTable *ht, Bucket *p) -{ -#ifdef ZEND_SIGNALS - TSRMLS_FETCH(); -#endif - - HANDLE_BLOCK_INTERRUPTIONS(); - if (p->pLast) { - p->pLast->pNext = p->pNext; - } else { - uint nIndex; - - nIndex = p->h & ht->nTableMask; - ht->arBuckets[nIndex] = p->pNext; - } - if (p->pNext) { - p->pNext->pLast = p->pLast; - } else { - /* Nothing to do as this list doesn't have a tail */ - } - - if (p->pListLast != NULL) { - p->pListLast->pListNext = p->pListNext; - } else { - /* Deleting the head of the list */ - ht->pListHead = p->pListNext; - } - if (p->pListNext != NULL) { - p->pListNext->pListLast = p->pListLast; - } else { - ht->pListTail = p->pListLast; - } - if (ht->pInternalPointer == p) { - ht->pInternalPointer = p->pListNext; - } - ht->nNumOfElements--; - HANDLE_UNBLOCK_INTERRUPTIONS(); - - if (ht->pDestructor) { - ht->pDestructor(p->pData); - } - if (p->pData != &p->pDataPtr) { - pefree(p->pData, ht->persistent); - } - pefree(p, ht->persistent); -} - - ZEND_API void zend_hash_graceful_destroy(HashTable *ht) { IS_CONSISTENT(ht); while (ht->pListHead != NULL) { - zend_hash_apply_deleter(ht, ht->pListHead); + zend_hash_bucket_delete(ht, ht->pListHead); } if (ht->nTableMask) { @@ -613,7 +573,7 @@ ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht) IS_CONSISTENT(ht); while (ht->pListTail != NULL) { - zend_hash_apply_deleter(ht, ht->pListTail); + zend_hash_bucket_delete(ht, ht->pListTail); } if (ht->nTableMask) { @@ -645,7 +605,7 @@ ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC) Bucket *p_next = p->pListNext; if (result & ZEND_HASH_APPLY_REMOVE) { - zend_hash_apply_deleter(ht, p); + zend_hash_bucket_delete(ht, p); } p = p_next; @@ -670,7 +630,7 @@ ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t appl Bucket *p_next = p->pListNext; if (result & ZEND_HASH_APPLY_REMOVE) { - zend_hash_apply_deleter(ht, p); + zend_hash_bucket_delete(ht, p); } p = p_next; @@ -703,7 +663,7 @@ ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func Bucket *p_next = p->pListNext; if (result & ZEND_HASH_APPLY_REMOVE) { - zend_hash_apply_deleter(ht, p); + zend_hash_bucket_delete(ht, p); } p = p_next; @@ -731,7 +691,7 @@ ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSR Bucket *p_last = p->pListLast; if (result & ZEND_HASH_APPLY_REMOVE) { - zend_hash_apply_deleter(ht, p); + zend_hash_bucket_delete(ht, p); } p = p_last; @@ -1204,8 +1164,6 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const return FAILURE; } - HANDLE_BLOCK_INTERRUPTIONS(); - if (q) { if (mode != HASH_UPDATE_KEY_ANYWAY) { Bucket *r = p->pListLast; @@ -1220,73 +1178,17 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const } if (mode & found) { /* delete current bucket */ - if (p == ht->arBuckets[p->h & ht->nTableMask]) { - ht->arBuckets[p->h & ht->nTableMask] = p->pNext; - } else { - p->pLast->pNext = p->pNext; - } - if (p->pNext) { - p->pNext->pLast = p->pLast; - } - if (p->pListLast != NULL) { - p->pListLast->pListNext = p->pListNext; - } else { - /* Deleting the head of the list */ - ht->pListHead = p->pListNext; - } - if (p->pListNext != NULL) { - p->pListNext->pListLast = p->pListLast; - } else { - ht->pListTail = p->pListLast; - } - if (ht->pInternalPointer == p) { - ht->pInternalPointer = p->pListNext; - } - ht->nNumOfElements--; - if (ht->pDestructor) { - ht->pDestructor(p->pData); - } - if (p->pData != &p->pDataPtr) { - pefree(p->pData, ht->persistent); - } - pefree(p, ht->persistent); - HANDLE_UNBLOCK_INTERRUPTIONS(); + zend_hash_bucket_delete(ht, p); return FAILURE; } } + /* delete another bucket with the same key */ - if (q == ht->arBuckets[q->h & ht->nTableMask]) { - ht->arBuckets[q->h & ht->nTableMask] = q->pNext; - } else { - q->pLast->pNext = q->pNext; - } - if (q->pNext) { - q->pNext->pLast = q->pLast; - } - if (q->pListLast != NULL) { - q->pListLast->pListNext = q->pListNext; - } else { - /* Deleting the head of the list */ - ht->pListHead = q->pListNext; - } - if (q->pListNext != NULL) { - q->pListNext->pListLast = q->pListLast; - } else { - ht->pListTail = q->pListLast; - } - if (ht->pInternalPointer == q) { - ht->pInternalPointer = q->pListNext; - } - ht->nNumOfElements--; - if (ht->pDestructor) { - ht->pDestructor(q->pData); - } - if (q->pData != &q->pDataPtr) { - pefree(q->pData, ht->persistent); - } - pefree(q, ht->persistent); + zend_hash_bucket_delete(ht, q); } + HANDLE_BLOCK_INTERRUPTIONS(); + if (p->pNext) { p->pNext->pLast = p->pLast; } From f331ed14143e78afe409018b00b19b05ed35c122 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 21 Mar 2014 01:32:49 +0100 Subject: [PATCH 6/6] Revert "Add helper function for updating bucket contents" Reverting this for now, because it would require further changes thanks to the zend signals tsrms-but-only-sometimes awesomeness. --- Zend/zend_hash.c | 72 +++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index aa3587c6759..135d3c3fa35 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -141,21 +141,6 @@ ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength) static const Bucket *uninitialized_bucket = NULL; -static inline void zend_hash_bucket_update( - const HashTable *ht, Bucket *p, void *pData, uint nDataSize, void **pDest ZEND_FILE_LINE_DC -) { - ZEND_ASSERT(p->pData != pData); - HANDLE_BLOCK_INTERRUPTIONS(); - if (ht->pDestructor) { - ht->pDestructor(p->pData); - } - UPDATE_DATA(ht, p, pData, nDataSize); - HANDLE_UNBLOCK_INTERRUPTIONS(); - if (pDest) { - *pDest = p->pData; - } -} - static zend_always_inline void i_zend_hash_bucket_delete(HashTable *ht, Bucket *p) { #ifdef ZEND_SIGNALS @@ -269,14 +254,21 @@ ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKe p = ht->arBuckets[nIndex]; while (p != NULL) { if (p->arKey == arKey || - ((p->h == h) && (p->nKeyLength == nKeyLength) - && !memcmp(p->arKey, arKey, nKeyLength)) - ) { - if (flag & HASH_ADD) { - return FAILURE; - } - zend_hash_bucket_update(ht, p, pData, nDataSize, pDest ZEND_FILE_LINE_RELAY_CC); - return SUCCESS; + ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) { + if (flag & HASH_ADD) { + return FAILURE; + } + ZEND_ASSERT(p->pData != pData); + HANDLE_BLOCK_INTERRUPTIONS(); + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + UPDATE_DATA(ht, p, pData, nDataSize); + if (pDest) { + *pDest = p->pData; + } + HANDLE_UNBLOCK_INTERRUPTIONS(); + return SUCCESS; } p = p->pNext; } @@ -325,14 +317,21 @@ ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, ui p = ht->arBuckets[nIndex]; while (p != NULL) { if (p->arKey == arKey || - ((p->h == h) && (p->nKeyLength == nKeyLength) - && !memcmp(p->arKey, arKey, nKeyLength)) - ) { - if (flag & HASH_ADD) { - return FAILURE; - } - zend_hash_bucket_update(ht, p, pData, nDataSize, pDest ZEND_FILE_LINE_RELAY_CC); - return SUCCESS; + ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) { + if (flag & HASH_ADD) { + return FAILURE; + } + ZEND_ASSERT(p->pData != pData); + HANDLE_BLOCK_INTERRUPTIONS(); + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + UPDATE_DATA(ht, p, pData, nDataSize); + if (pDest) { + *pDest = p->pData; + } + HANDLE_UNBLOCK_INTERRUPTIONS(); + return SUCCESS; } p = p->pNext; } @@ -397,7 +396,16 @@ ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) { return FAILURE; } - zend_hash_bucket_update(ht, p, pData, nDataSize, pDest ZEND_FILE_LINE_RELAY_CC); + ZEND_ASSERT(p->pData != pData); + HANDLE_BLOCK_INTERRUPTIONS(); + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + UPDATE_DATA(ht, p, pData, nDataSize); + HANDLE_UNBLOCK_INTERRUPTIONS(); + if (pDest) { + *pDest = p->pData; + } return SUCCESS; } p = p->pNext;