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 ======================== diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 829cd336416..135d3c3fa35 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -141,6 +141,51 @@ ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength) static const Bucket *uninitialized_bucket = NULL; +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; @@ -358,9 +403,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; } @@ -438,9 +480,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); @@ -455,38 +494,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; @@ -553,73 +561,14 @@ 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. - */ -static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p) -{ - Bucket *retval; -#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); - } - 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_bucket_delete(ht, ht->pListHead); } + if (ht->nTableMask) { pefree(ht->arBuckets, ht->persistent); } @@ -629,14 +578,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_bucket_delete(ht, ht->pListTail); } if (ht->nTableMask) { @@ -665,12 +610,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_bucket_delete(ht, p); } + p = p_next; + if (result & ZEND_HASH_APPLY_STOP) { break; } @@ -690,11 +636,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_bucket_delete(ht, p); } + p = p_next; + if (result & ZEND_HASH_APPLY_STOP) { break; } @@ -722,11 +669,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_bucket_delete(ht, p); } + p = p_next; + if (result & ZEND_HASH_APPLY_STOP) { va_end(args); break; @@ -740,7 +688,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); @@ -749,11 +697,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_bucket_delete(ht, p); } + p = p_last; + if (result & ZEND_HASH_APPLY_STOP) { break; } @@ -1223,8 +1172,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; @@ -1239,73 +1186,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; }