summaryrefslogtreecommitdiff
path: root/Zend/zend_hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_hash.c')
-rw-r--r--Zend/zend_hash.c2087
1 files changed, 1200 insertions, 887 deletions
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index a9fd3e9a43..78b38f7ea2 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -22,45 +22,21 @@
#include "zend.h"
#include "zend_globals.h"
-#define CONNECT_TO_BUCKET_DLLIST(element, list_head) \
- (element)->pNext = (list_head); \
- (element)->pLast = NULL; \
- if ((element)->pNext) { \
- (element)->pNext->pLast = (element); \
- }
-
-#define CONNECT_TO_GLOBAL_DLLIST_EX(element, ht, last, next)\
- (element)->pListLast = (last); \
- (element)->pListNext = (next); \
- if ((last) != NULL) { \
- (last)->pListNext = (element); \
- } else { \
- (ht)->pListHead = (element); \
- } \
- if ((next) != NULL) { \
- (next)->pListLast = (element); \
- } else { \
- (ht)->pListTail = (element); \
- } \
-
-#define CONNECT_TO_GLOBAL_DLLIST(element, ht) \
- CONNECT_TO_GLOBAL_DLLIST_EX(element, ht, (ht)->pListTail, (Bucket *) NULL); \
- if ((ht)->pInternalPointer == NULL) { \
- (ht)->pInternalPointer = (element); \
- }
-
#if ZEND_DEBUG
-#define HT_OK 0
-#define HT_IS_DESTROYING 1
-#define HT_DESTROYED 2
-#define HT_CLEANING 3
+/*
+#define HASH_MASK_CONSISTENCY 0x60
+*/
+#define HT_OK 0x00
+#define HT_IS_DESTROYING 0x20
+#define HT_DESTROYED 0x40
+#define HT_CLEANING 0x60
static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
{
- if (ht->inconsistent==HT_OK) {
+ if ((ht->u.flags & HASH_MASK_CONSISTENCY) == HT_OK) {
return;
}
- switch (ht->inconsistent) {
+ switch ((ht->u.flags & HASH_MASK_CONSISTENCY)) {
case HT_IS_DESTROYING:
zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
break;
@@ -77,122 +53,51 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
zend_bailout();
}
#define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
-#define SET_INCONSISTENT(n) ht->inconsistent = n;
+#define SET_INCONSISTENT(n) do { \
+ (ht)->u.flags |= n; \
+ } while (0)
#else
#define IS_CONSISTENT(a)
#define SET_INCONSISTENT(n)
#endif
#define HASH_PROTECT_RECURSION(ht) \
- if ((ht)->bApplyProtection) { \
- if ((ht)->nApplyCount++ >= 3) { \
- zend_error(E_ERROR, "Nesting level too deep - recursive dependency?"); \
+ if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) { \
+ if ((ht)->u.flags >= (3 << 8)) { \
+ zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");\
} \
+ ZEND_HASH_INC_APPLY_COUNT(ht); \
}
-
#define HASH_UNPROTECT_RECURSION(ht) \
- if ((ht)->bApplyProtection) { \
- (ht)->nApplyCount--; \
+ if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) { \
+ ZEND_HASH_DEC_APPLY_COUNT(ht); \
}
-
#define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
- if ((ht)->nNumOfElements > (ht)->nTableSize) { \
+ if ((ht)->nNumUsed >= (ht)->nTableSize) { \
zend_hash_do_resize(ht); \
}
static void zend_hash_do_resize(HashTable *ht);
-ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
-{
- return zend_inline_hash_func(arKey, nKeyLength);
-}
-
-
-#define UPDATE_DATA(ht, p, pData, nDataSize) \
- if (nDataSize == sizeof(void*)) { \
- if ((p)->pData != &(p)->pDataPtr) { \
- pefree_rel((p)->pData, (ht)->persistent); \
- } \
- memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
- (p)->pData = &(p)->pDataPtr; \
- } else { \
- if ((p)->pData == &(p)->pDataPtr) { \
- (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent); \
- (p)->pDataPtr=NULL; \
- } else { \
- (p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent); \
- /* (p)->pDataPtr is already NULL so no need to initialize it */ \
- } \
- memcpy((p)->pData, pData, nDataSize); \
- }
-
-#define INIT_DATA(ht, p, _pData, nDataSize); \
- if (nDataSize == sizeof(void*)) { \
- memcpy(&(p)->pDataPtr, (_pData), sizeof(void *)); \
- (p)->pData = &(p)->pDataPtr; \
- } else { \
- (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
- memcpy((p)->pData, (_pData), nDataSize); \
- (p)->pDataPtr=NULL; \
- }
-
-#define CHECK_INIT(ht) do { \
- if (UNEXPECTED((ht)->nTableMask == 0)) { \
- (ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent); \
+#define CHECK_INIT(ht, packed) do { \
+ if (UNEXPECTED((ht)->nTableMask == 0)) { \
+ if (packed) { \
+ (ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
+ (ht)->u.flags |= HASH_FLAG_PACKED; \
+ } else { \
+ (ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket) + sizeof(zend_uint), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
+ (ht)->arHash = (zend_uint*)((ht)->arData + (ht)->nTableSize); \
+ memset((ht)->arHash, INVALID_IDX, (ht)->nTableSize * sizeof(zend_uint)); \
+ } \
(ht)->nTableMask = (ht)->nTableSize - 1; \
} \
} while (0)
-static const Bucket *uninitialized_bucket = NULL;
+static const zend_uint uninitialized_bucket = {INVALID_IDX};
-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)
+ZEND_API void _zend_hash_init(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
{
uint i = 3;
@@ -209,105 +114,143 @@ ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, dtor_func_t pDestructor,
}
ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */
- ht->pDestructor = pDestructor;
- ht->arBuckets = (Bucket**)&uninitialized_bucket;
- ht->pListHead = NULL;
- ht->pListTail = NULL;
+ ht->nNumUsed = 0;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
- ht->pInternalPointer = NULL;
- ht->persistent = persistent;
- ht->nApplyCount = 0;
- ht->bApplyProtection = 1;
- return SUCCESS;
+ ht->arData = NULL;
+ ht->arHash = (zend_uint*)&uninitialized_bucket;
+ ht->pDestructor = pDestructor;
+ ht->nInternalPointer = INVALID_IDX;
+ if (persistent) {
+ ht->u.flags = HASH_FLAG_PERSISTENT | HASH_FLAG_APPLY_PROTECTION;
+ } else {
+ ht->u.flags = HASH_FLAG_APPLY_PROTECTION;
+ }
}
+static void zend_hash_packed_grow(HashTable *ht)
+{
+ HANDLE_BLOCK_INTERRUPTIONS();
+ ht->arData = (Bucket *) safe_perealloc(ht->arData, (ht->nTableSize << 1), sizeof(Bucket), 0, ht->u.flags & HASH_FLAG_PERSISTENT);
+ ht->nTableSize = (ht->nTableSize << 1);
+ ht->nTableMask = ht->nTableSize - 1;
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+}
-ZEND_API int _zend_hash_init_ex(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC)
+ZEND_API void zend_hash_real_init(HashTable *ht, int packed)
{
- int retval = _zend_hash_init(ht, nSize, pDestructor, persistent ZEND_FILE_LINE_CC);
+ IS_CONSISTENT(ht);
- ht->bApplyProtection = bApplyProtection;
- return retval;
+ CHECK_INIT(ht, packed);
}
+ZEND_API void zend_hash_packed_to_hash(HashTable *ht)
+{
+ HANDLE_BLOCK_INTERRUPTIONS();
+ ht->u.flags &= ~HASH_FLAG_PACKED;
+ ht->arData = (Bucket *) safe_perealloc(ht->arData, ht->nTableSize, sizeof(Bucket) + sizeof(zend_uint), 0, ht->u.flags & HASH_FLAG_PERSISTENT);
+ ht->arHash = (zend_uint*)(ht->arData + ht->nTableSize);
+ zend_hash_rehash(ht);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+}
-ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
+ZEND_API void zend_hash_to_packed(HashTable *ht)
+{
+ HANDLE_BLOCK_INTERRUPTIONS();
+ ht->u.flags |= HASH_FLAG_PACKED;
+ ht->arData = erealloc(ht->arData, ht->nTableSize * sizeof(Bucket));
+ ht->arHash = (zend_uint*)&uninitialized_bucket;
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+}
+
+ZEND_API void _zend_hash_init_ex(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC)
{
- ht->bApplyProtection = bApplyProtection;
+ _zend_hash_init(ht, nSize, pDestructor, persistent ZEND_FILE_LINE_CC);
+ if (!bApplyProtection) {
+ ht->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
+ }
}
+ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
+{
+ if (bApplyProtection) {
+ ht->u.flags |= HASH_FLAG_APPLY_PROTECTION;
+ } else {
+ ht->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
+ }
+}
-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)
+static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key)
{
ulong h;
uint nIndex;
+ uint idx;
Bucket *p;
-#ifdef ZEND_SIGNALS
- TSRMLS_FETCH();
-#endif
- IS_CONSISTENT(ht);
-
- ZEND_ASSERT(nKeyLength != 0);
+ h = STR_HASH_VAL(key);
+ nIndex = h & ht->nTableMask;
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ if ((p->key == key) || /* check for the the same interned string */
+ (p->h == h &&
+ p->key &&
+ p->key->len == key->len &&
+ memcmp(p->key->val, key->val, key->len) == 0)) {
+ return p;
+ }
+ idx = Z_NEXT(p->val);
+ }
+ return NULL;
+}
- CHECK_INIT(ht);
+static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht, const char *str, int len, ulong h)
+{
+ uint nIndex;
+ uint idx;
+ Bucket *p;
- h = zend_inline_hash_func(arKey, nKeyLength);
nIndex = h & ht->nTableMask;
-
- 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;
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ ZEND_ASSERT(idx < ht->nTableSize);
+ p = ht->arData + idx;
+ if ((p->h == h)
+ && p->key
+ && (p->key->len == len)
+ && !memcmp(p->key->val, str, len)) {
+ return p;
}
- p = p->pNext;
- }
-
- if (IS_INTERNED(arKey)) {
- p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
- p->arKey = arKey;
- } else {
- p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
- p->arKey = (const char*)(p + 1);
- memcpy((char*)p->arKey, arKey, nKeyLength);
- }
- p->nKeyLength = nKeyLength;
- INIT_DATA(ht, p, pData, nDataSize);
- p->h = h;
- CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
- if (pDest) {
- *pDest = p->pData;
+ idx = Z_NEXT(p->val);
}
+ return NULL;
+}
- HANDLE_BLOCK_INTERRUPTIONS();
- CONNECT_TO_GLOBAL_DLLIST(p, ht);
- ht->arBuckets[nIndex] = p;
- HANDLE_UNBLOCK_INTERRUPTIONS();
+static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, ulong h)
+{
+ uint nIndex;
+ uint idx;
+ Bucket *p;
- ht->nNumOfElements++;
- ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
- return SUCCESS;
+ nIndex = h & ht->nTableMask;
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ ZEND_ASSERT(idx < ht->nTableSize);
+ p = ht->arData + idx;
+ if (p->h == h && !p->key) {
+ return p;
+ }
+ idx = Z_NEXT(p->val);
+ }
+ return NULL;
}
-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)
+static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_string *key, zval *pData, int flag ZEND_FILE_LINE_DC)
{
+ ulong h;
uint nIndex;
+ uint idx;
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
@@ -315,145 +258,314 @@ ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, ui
IS_CONSISTENT(ht);
- ZEND_ASSERT(nKeyLength != 0);
+ CHECK_INIT(ht, 0);
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ zend_hash_packed_to_hash(ht);
+ }
- CHECK_INIT(ht);
- nIndex = h & ht->nTableMask;
-
- 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;
+ h = STR_HASH_VAL(key);
+
+ if ((flag & HASH_ADD_NEW) == 0) {
+ p = zend_hash_find_bucket(ht, key);
+
+ if (p) {
+ zval *data;
+
+ if (flag & HASH_ADD) {
+ return NULL;
+ }
+ ZEND_ASSERT(&p->val != pData);
+ data = &p->val;
+ if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) {
+ data = Z_INDIRECT_P(data);
+ }
+ HANDLE_BLOCK_INTERRUPTIONS();
+ if (ht->pDestructor) {
+ ht->pDestructor(data);
+ }
+ ZVAL_COPY_VALUE(data, pData);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+ return data;
}
- p = p->pNext;
}
- if (IS_INTERNED(arKey)) {
- p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
- p->arKey = arKey;
- } else {
- p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
- p->arKey = (const char*)(p + 1);
- memcpy((char*)p->arKey, arKey, nKeyLength);
- }
+ ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
- p->nKeyLength = nKeyLength;
- INIT_DATA(ht, p, pData, nDataSize);
+ HANDLE_BLOCK_INTERRUPTIONS();
+ idx = ht->nNumUsed++;
+ ht->nNumOfElements++;
+ if (ht->nInternalPointer == INVALID_IDX) {
+ ht->nInternalPointer = idx;
+ }
+ p = ht->arData + idx;
p->h = h;
-
- CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
+ p->key = key;
+ STR_ADDREF(key);
+ ZVAL_COPY_VALUE(&p->val, pData);
+ nIndex = h & ht->nTableMask;
+ Z_NEXT(p->val) = ht->arHash[nIndex];
+ ht->arHash[nIndex] = idx;
+ HANDLE_UNBLOCK_INTERRUPTIONS();
- if (pDest) {
- *pDest = p->pData;
- }
+ return &p->val;
+}
- HANDLE_BLOCK_INTERRUPTIONS();
- ht->arBuckets[nIndex] = p;
- CONNECT_TO_GLOBAL_DLLIST(p, ht);
- HANDLE_UNBLOCK_INTERRUPTIONS();
+ZEND_API zval *_zend_hash_add_or_update(HashTable *ht, zend_string *key, zval *pData, int flag ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_add_or_update_i(ht, key, pData, flag ZEND_FILE_LINE_RELAY_CC);
+}
- ht->nNumOfElements++;
- ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
- return SUCCESS;
+ZEND_API zval *_zend_hash_add(HashTable *ht, zend_string *key, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_update(HashTable *ht, zend_string *key, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_update_ind(HashTable *ht, zend_string *key, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_str_add_or_update(HashTable *ht, const char *str, int len, zval *pData, int flag ZEND_FILE_LINE_DC)
+{
+ zend_string *key = STR_INIT(str, len, ht->u.flags & HASH_FLAG_PERSISTENT);
+ zval *ret = _zend_hash_add_or_update_i(ht, key, pData, flag ZEND_FILE_LINE_CC);
+ STR_RELEASE(key);
+ return ret;
+}
+
+ZEND_API zval *_zend_hash_str_update(HashTable *ht, const char *str, int len, zval *pData ZEND_FILE_LINE_DC)
+{
+ zend_string *key = STR_INIT(str, len, ht->u.flags & HASH_FLAG_PERSISTENT);
+ zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE ZEND_FILE_LINE_CC);
+ STR_RELEASE(key);
+ return ret;
+}
+
+ZEND_API zval *_zend_hash_str_update_ind(HashTable *ht, const char *str, int len, zval *pData ZEND_FILE_LINE_DC)
+{
+ zend_string *key = STR_INIT(str, len, ht->u.flags & HASH_FLAG_PERSISTENT);
+ zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_CC);
+ STR_RELEASE(key);
+ return ret;
}
+ZEND_API zval *_zend_hash_str_add(HashTable *ht, const char *str, int len, zval *pData ZEND_FILE_LINE_DC)
+{
+ zend_string *key = STR_INIT(str, len, ht->u.flags & HASH_FLAG_PERSISTENT);
+ zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD ZEND_FILE_LINE_CC);
+ STR_RELEASE(key);
+ return ret;
+}
+
+ZEND_API zval *_zend_hash_str_add_new(HashTable *ht, const char *str, int len, zval *pData ZEND_FILE_LINE_DC)
+{
+ zend_string *key = STR_INIT(str, len, ht->u.flags & HASH_FLAG_PERSISTENT);
+ zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW ZEND_FILE_LINE_CC);
+ STR_RELEASE(key);
+ return ret;
+}
+
+ZEND_API zval *zend_hash_index_add_empty_element(HashTable *ht, ulong h)
+{
+
+ zval dummy;
+
+ ZVAL_NULL(&dummy);
+ return zend_hash_index_add(ht, h, &dummy);
+}
-ZEND_API int zend_hash_add_empty_element(HashTable *ht, const char *arKey, uint nKeyLength)
+ZEND_API zval *zend_hash_add_empty_element(HashTable *ht, zend_string *key)
{
- void *dummy = (void *) 1;
+
+ zval dummy;
- return zend_hash_add(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
+ ZVAL_NULL(&dummy);
+ return zend_hash_add(ht, key, &dummy);
}
+ZEND_API zval *zend_hash_str_add_empty_element(HashTable *ht, const char *str, int len)
+{
+
+ zval dummy;
-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)
+ ZVAL_NULL(&dummy);
+ return zend_hash_str_add(ht, str, len, &dummy);
+}
+
+static zend_always_inline zval *_zend_hash_index_update_or_next_insert_i(HashTable *ht, ulong h, zval *pData, int flag ZEND_FILE_LINE_DC)
{
uint nIndex;
+ uint idx;
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
- CHECK_INIT(ht);
if (flag & HASH_NEXT_INSERT) {
h = ht->nNextFreeElement;
}
- nIndex = h & ht->nTableMask;
+ CHECK_INIT(ht, h >= 0 && h < ht->nTableSize);
+
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ if (EXPECTED(h >= 0)) {
+ if (h < ht->nNumUsed) {
+ p = ht->arData + h;
+ if (Z_TYPE(p->val) != IS_UNDEF) {
+ if (flag & (HASH_NEXT_INSERT | HASH_ADD)) {
+ return NULL;
+ }
+ if (ht->pDestructor) {
+ ht->pDestructor(&p->val);
+ }
+ ZVAL_COPY_VALUE(&p->val, pData);
+ if ((long)h >= (long)ht->nNextFreeElement) {
+ ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
+ }
+ return &p->val;
+ } else { /* we have to keep the order :( */
+ goto convert_to_hash;
+ }
+ } else if (EXPECTED(h < ht->nTableSize)) {
+ p = ht->arData + h;
+ } else if (h < ht->nTableSize * 2 &&
+ ht->nTableSize - ht->nNumOfElements < ht->nTableSize / 2) {
+ zend_hash_packed_grow(ht);
+ p = ht->arData + h;
+ } else {
+ goto convert_to_hash;
+ }
+ HANDLE_BLOCK_INTERRUPTIONS();
+ /* incremental initialization of empty Buckets */
+ if (h >= ht->nNumUsed) {
+ Bucket *q = ht->arData + ht->nNumUsed;
+ while (q != p) {
+ ZVAL_UNDEF(&q->val);
+ q++;
+ }
+ ht->nNumUsed = h + 1;
+ }
+ ht->nNumOfElements++;
+ if (ht->nInternalPointer == INVALID_IDX) {
+ ht->nInternalPointer = h;
+ }
+ if ((long)h >= (long)ht->nNextFreeElement) {
+ ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
+ }
+ p->h = h;
+ p->key = NULL;
+ ZVAL_COPY_VALUE(&p->val, pData);
+ Z_NEXT(p->val) = INVALID_IDX;
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
- if ((p->nKeyLength == 0) && (p->h == h)) {
- if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) {
- return FAILURE;
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+
+ return &p->val;
+ } else {
+convert_to_hash:
+ zend_hash_packed_to_hash(ht);
+ }
+ }
+
+ if ((flag & HASH_ADD_NEW) == 0) {
+ p = zend_hash_index_find_bucket(ht, h);
+ if (p) {
+ if (flag & (HASH_NEXT_INSERT | HASH_ADD)) {
+ return NULL;
}
- ZEND_ASSERT(p->pData != pData);
+ ZEND_ASSERT(&p->val != pData);
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pDestructor) {
- ht->pDestructor(p->pData);
+ ht->pDestructor(&p->val);
}
- UPDATE_DATA(ht, p, pData, nDataSize);
+ ZVAL_COPY_VALUE(&p->val, pData);
HANDLE_UNBLOCK_INTERRUPTIONS();
- if (pDest) {
- *pDest = p->pData;
+ if ((long)h >= (long)ht->nNextFreeElement) {
+ ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
}
- return SUCCESS;
+ return &p->val;
}
- p = p->pNext;
- }
- p = (Bucket *) pemalloc_rel(sizeof(Bucket), ht->persistent);
- p->arKey = NULL;
- p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
- p->h = h;
- INIT_DATA(ht, p, pData, nDataSize);
- if (pDest) {
- *pDest = p->pData;
}
- CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
+ ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
HANDLE_BLOCK_INTERRUPTIONS();
- ht->arBuckets[nIndex] = p;
- CONNECT_TO_GLOBAL_DLLIST(p, ht);
- HANDLE_UNBLOCK_INTERRUPTIONS();
-
+ idx = ht->nNumUsed++;
+ ht->nNumOfElements++;
+ if (ht->nInternalPointer == INVALID_IDX) {
+ ht->nInternalPointer = idx;
+ }
if ((long)h >= (long)ht->nNextFreeElement) {
ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
}
- ht->nNumOfElements++;
- ZEND_HASH_IF_FULL_DO_RESIZE(ht);
- return SUCCESS;
+ p = ht->arData + idx;
+ p->h = h;
+ p->key = NULL;
+ nIndex = h & ht->nTableMask;
+ ZVAL_COPY_VALUE(&p->val, pData);
+ Z_NEXT(p->val) = ht->arHash[nIndex];
+ ht->arHash[nIndex] = idx;
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+
+ return &p->val;
+}
+
+ZEND_API zval *_zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, zval *pData, int flag ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_index_update_or_next_insert_i(ht, h, pData, flag ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_index_add(HashTable *ht, ulong h, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_index_update_or_next_insert_i(ht, h, pData, HASH_ADD ZEND_FILE_LINE_RELAY_CC);
}
+ZEND_API zval *_zend_hash_index_add_new(HashTable *ht, ulong h, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_index_update_or_next_insert_i(ht, h, pData, HASH_ADD | HASH_ADD_NEW ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_index_update(HashTable *ht, ulong h, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_index_update_or_next_insert_i(ht, h, pData, HASH_UPDATE ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_next_index_insert(HashTable *ht, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_index_update_or_next_insert_i(ht, ht->nNextFreeElement, pData, HASH_NEXT_INSERT ZEND_FILE_LINE_RELAY_CC);
+}
+
+ZEND_API zval *_zend_hash_next_index_insert_new(HashTable *ht, zval *pData ZEND_FILE_LINE_DC)
+{
+ return _zend_hash_index_update_or_next_insert_i(ht, ht->nNextFreeElement, pData, HASH_NEXT_INSERT | HASH_ADD_NEW ZEND_FILE_LINE_RELAY_CC);
+}
static void zend_hash_do_resize(HashTable *ht)
{
- Bucket **t;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
- if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */
- t = (Bucket **) perealloc(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent);
+ if (ht->nNumUsed < ht->nNumOfElements) {
HANDLE_BLOCK_INTERRUPTIONS();
- ht->arBuckets = t;
+ zend_hash_rehash(ht);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+ } else if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */
+ HANDLE_BLOCK_INTERRUPTIONS();
+ ht->arData = (Bucket *) safe_perealloc(ht->arData, (ht->nTableSize << 1), sizeof(Bucket) + sizeof(zend_uint), 0, ht->u.flags & HASH_FLAG_PERSISTENT);
+ ht->arHash = (zend_uint*)(ht->arData + (ht->nTableSize << 1));
ht->nTableSize = (ht->nTableSize << 1);
ht->nTableMask = ht->nTableSize - 1;
zend_hash_rehash(ht);
@@ -464,96 +576,335 @@ static void zend_hash_do_resize(HashTable *ht)
ZEND_API int zend_hash_rehash(HashTable *ht)
{
Bucket *p;
- uint nIndex;
+ uint nIndex, i, j;
IS_CONSISTENT(ht);
+
if (UNEXPECTED(ht->nNumOfElements == 0)) {
+ if (ht->nTableMask) {
+ memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(zend_uint));
+ }
return SUCCESS;
}
- memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
- for (p = ht->pListHead; p != NULL; p = p->pListNext) {
- nIndex = p->h & ht->nTableMask;
- CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
- ht->arBuckets[nIndex] = p;
+ memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(zend_uint));
+ for (i = 0, j = 0; i < ht->nNumUsed; i++) {
+ p = ht->arData + i;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (i != j) {
+ ht->arData[j] = ht->arData[i];
+ if (ht->nInternalPointer == i) {
+ ht->nInternalPointer = j;
+ }
+ }
+ nIndex = ht->arData[j].h & ht->nTableMask;
+ Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex];
+ ht->arHash[nIndex] = j;
+ j++;
}
+ ht->nNumUsed = j;
return SUCCESS;
}
-ZEND_API void zend_hash_reindex(HashTable *ht, zend_bool only_integer_keys) {
- Bucket *p;
+static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint idx, Bucket *p, Bucket *prev)
+{
+ if (!(ht->u.flags & HASH_FLAG_PACKED)) {
+ if (prev) {
+ Z_NEXT(prev->val) = Z_NEXT(p->val);
+ } else {
+ ht->arHash[p->h & ht->nTableMask] = Z_NEXT(p->val);
+ }
+ }
+ if (ht->nNumUsed - 1 == idx) {
+ do {
+ ht->nNumUsed--;
+ } while (ht->nNumUsed > 0 && (Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF));
+ }
+ ht->nNumOfElements--;
+ if (ht->nInternalPointer == idx) {
+ while (1) {
+ idx++;
+ if (idx >= ht->nNumUsed) {
+ ht->nInternalPointer = INVALID_IDX;
+ break;
+ } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
+ ht->nInternalPointer = idx;
+ break;
+ }
+ }
+ }
+ if (p->key) {
+ STR_RELEASE(p->key);
+ }
+ if (ht->pDestructor) {
+ zval tmp;
+ ZVAL_COPY_VALUE(&tmp, &p->val);
+ ZVAL_UNDEF(&p->val);
+ ht->pDestructor(&tmp);
+ } else {
+ ZVAL_UNDEF(&p->val);
+ }
+}
+
+static zend_always_inline void _zend_hash_del_el(HashTable *ht, uint idx, Bucket *p)
+{
+ uint nIndex;
+ Bucket *prev = NULL;
+
+ if (!(ht->u.flags & HASH_FLAG_PACKED)) {
+ nIndex = p->h & ht->nTableMask;
+ idx = ht->arHash[nIndex];
+ if (p != ht->arData + idx) {
+ prev = ht->arData + idx;
+ while (ht->arData + Z_NEXT(prev->val) != p) {
+ idx = Z_NEXT(prev->val);
+ prev = ht->arData + idx;
+ }
+ idx = Z_NEXT(prev->val);
+ }
+ }
+
+ _zend_hash_del_el_ex(ht, idx, p, prev);
+}
+
+ZEND_API int zend_hash_del(HashTable *ht, zend_string *key)
+{
+ ulong h;
uint nIndex;
- ulong offset = 0;
+ uint idx;
+ Bucket *p;
+ Bucket *prev = NULL;
+#ifdef ZEND_SIGNALS
+ TSRMLS_FETCH();
+#endif
IS_CONSISTENT(ht);
- if (UNEXPECTED(ht->nNumOfElements == 0)) {
- ht->nNextFreeElement = 0;
- return;
+
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ return FAILURE;
}
- memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
- for (p = ht->pListHead; p != NULL; p = p->pListNext) {
- if (!only_integer_keys || p->nKeyLength == 0) {
- p->h = offset++;
- p->nKeyLength = 0;
+ h = STR_HASH_VAL(key);
+ nIndex = h & ht->nTableMask;
+
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ if ((p->key == key) ||
+ (p->h == h &&
+ p->key &&
+ p->key->len == key->len &&
+ memcmp(p->key->val, key->val, key->len) == 0)) {
+ HANDLE_BLOCK_INTERRUPTIONS();
+ _zend_hash_del_el_ex(ht, idx, p, prev);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+ return SUCCESS;
}
+ prev = p;
+ idx = Z_NEXT(p->val);
+ }
+ return FAILURE;
+}
- nIndex = p->h & ht->nTableMask;
- CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
- ht->arBuckets[nIndex] = p;
+ZEND_API int zend_hash_del_ind(HashTable *ht, zend_string *key)
+{
+ ulong h;
+ uint nIndex;
+ uint idx;
+ Bucket *p;
+ Bucket *prev = NULL;
+#ifdef ZEND_SIGNALS
+ TSRMLS_FETCH();
+#endif
+
+ IS_CONSISTENT(ht);
+
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ return FAILURE;
+ }
+
+ h = STR_HASH_VAL(key);
+ nIndex = h & ht->nTableMask;
+
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ if ((p->key == key) ||
+ (p->h == h &&
+ p->key &&
+ p->key->len == key->len &&
+ memcmp(p->key->val, key->val, key->len) == 0)) {
+ if (Z_TYPE(p->val) == IS_INDIRECT) {
+ zval *data = Z_INDIRECT(p->val);
+
+ if (Z_TYPE_P(data) == IS_UNDEF) {
+ return FAILURE;
+ } else {
+ if (ht->pDestructor) {
+ ht->pDestructor(data);
+ }
+ ZVAL_UNDEF(data);
+ }
+ } else {
+ HANDLE_BLOCK_INTERRUPTIONS();
+ _zend_hash_del_el_ex(ht, idx, p, prev);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+ }
+ return SUCCESS;
+ }
+ prev = p;
+ idx = Z_NEXT(p->val);
}
- ht->nNextFreeElement = offset;
+ return FAILURE;
}
-ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
+ZEND_API int zend_hash_str_del(HashTable *ht, const char *str, int len)
{
+ ulong h;
uint nIndex;
+ uint idx;
Bucket *p;
+ Bucket *prev = NULL;
+#ifdef ZEND_SIGNALS
+ TSRMLS_FETCH();
+#endif
IS_CONSISTENT(ht);
- if (flag == HASH_DEL_KEY) {
- h = zend_inline_hash_func(arKey, nKeyLength);
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ return FAILURE;
+ }
+
+ h = zend_inline_hash_func(str, len);
+ nIndex = h & ht->nTableMask;
+
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ if ((p->h == h)
+ && p->key
+ && (p->key->len == len)
+ && !memcmp(p->key->val, str, len)) {
+ if (Z_TYPE(p->val) == IS_INDIRECT) {
+ zval *data = Z_INDIRECT(p->val);
+
+ if (Z_TYPE_P(data) == IS_UNDEF) {
+ return FAILURE;
+ } else {
+ if (ht->pDestructor) {
+ ht->pDestructor(data);
+ }
+ ZVAL_UNDEF(data);
+ }
+ } else {
+ HANDLE_BLOCK_INTERRUPTIONS();
+ _zend_hash_del_el_ex(ht, idx, p, prev);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+ }
+ return SUCCESS;
+ }
+ prev = p;
+ idx = Z_NEXT(p->val);
}
+ return FAILURE;
+}
+
+ZEND_API int zend_hash_str_del_ind(HashTable *ht, const char *str, int len)
+{
+ ulong h;
+ uint nIndex;
+ uint idx;
+ Bucket *p;
+ Bucket *prev = NULL;
+#ifdef ZEND_SIGNALS
+ TSRMLS_FETCH();
+#endif
+
+ IS_CONSISTENT(ht);
+
+ h = zend_inline_hash_func(str, len);
nIndex = h & ht->nTableMask;
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ p = ht->arData + idx;
if ((p->h == h)
- && (p->nKeyLength == nKeyLength)
- && ((p->nKeyLength == 0) /* Numeric index (short circuits the memcmp() check) */
- || !memcmp(p->arKey, arKey, nKeyLength))) { /* String index */
- i_zend_hash_bucket_delete(ht, p);
+ && p->key
+ && (p->key->len == len)
+ && !memcmp(p->key->val, str, len)) {
+ HANDLE_BLOCK_INTERRUPTIONS();
+ _zend_hash_del_el_ex(ht, idx, p, prev);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
- p = p->pNext;
+ prev = p;
+ idx = Z_NEXT(p->val);
}
return FAILURE;
}
+ZEND_API int zend_hash_index_del(HashTable *ht, ulong h)
+{
+ uint nIndex;
+ uint idx;
+ Bucket *p;
+ Bucket *prev = NULL;
+#ifdef ZEND_SIGNALS
+ TSRMLS_FETCH();
+#endif
+
+ IS_CONSISTENT(ht);
+
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ if (h >=0 && h < ht->nNumUsed) {
+ p = ht->arData + h;
+ if (Z_TYPE(p->val) != IS_UNDEF) {
+ HANDLE_BLOCK_INTERRUPTIONS();
+ _zend_hash_del_el_ex(ht, h, p, NULL);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+ return SUCCESS;
+ }
+ }
+ return FAILURE;
+ }
+ nIndex = h & ht->nTableMask;
+
+ idx = ht->arHash[nIndex];
+ while (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ if ((p->h == h) && (p->key == NULL)) {
+ HANDLE_BLOCK_INTERRUPTIONS();
+ _zend_hash_del_el_ex(ht, idx, p, prev);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+ return SUCCESS;
+ }
+ prev = p;
+ idx = Z_NEXT(p->val);
+ }
+ return FAILURE;
+}
ZEND_API void zend_hash_destroy(HashTable *ht)
{
- Bucket *p, *q;
+ uint idx;
+ Bucket *p;
IS_CONSISTENT(ht);
SET_INCONSISTENT(HT_IS_DESTROYING);
- p = ht->pListHead;
- while (p != NULL) {
- q = p;
- p = p->pListNext;
+ for (idx = 0 ; idx < ht->nNumUsed; idx++) {
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
if (ht->pDestructor) {
- ht->pDestructor(q->pData);
+ ht->pDestructor(&p->val);
}
- if (q->pData != &q->pDataPtr) {
- pefree(q->pData, ht->persistent);
+ if (p->key) {
+ STR_RELEASE(p->key);
}
- pefree(q, ht->persistent);
}
if (ht->nTableMask) {
- pefree(ht->arBuckets, ht->persistent);
+ pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
}
SET_INCONSISTENT(HT_DESTROYED);
@@ -562,44 +913,63 @@ ZEND_API void zend_hash_destroy(HashTable *ht)
ZEND_API void zend_hash_clean(HashTable *ht)
{
- Bucket *p, *q;
+ uint idx;
+ Bucket *p;
IS_CONSISTENT(ht);
- p = ht->pListHead;
-
- if (ht->nTableMask) {
- memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
+ for (idx = 0; idx < ht->nNumUsed; idx++) {
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (ht->pDestructor) {
+ ht->pDestructor(&p->val);
+ }
+ if (p->key) {
+ STR_RELEASE(p->key);
+ }
}
- ht->pListHead = NULL;
- ht->pListTail = NULL;
+ ht->nNumUsed = 0;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
- ht->pInternalPointer = NULL;
-
- while (p != NULL) {
- q = p;
- p = p->pListNext;
- if (ht->pDestructor) {
- ht->pDestructor(q->pData);
- }
- if (q->pData != &q->pDataPtr) {
- pefree(q->pData, ht->persistent);
+ ht->nInternalPointer = INVALID_IDX;
+ if (ht->nTableMask) {
+ if (!(ht->u.flags & HASH_FLAG_PACKED)) {
+ memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(zend_uint));
}
- pefree(q, ht->persistent);
}
}
+/* 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 void zend_hash_apply_deleter(HashTable *ht, uint idx, Bucket *p)
+{
+#ifdef ZEND_SIGNALS
+ TSRMLS_FETCH();
+#endif
+
+ HANDLE_BLOCK_INTERRUPTIONS();
+ _zend_hash_del_el(ht, idx, p);
+ HANDLE_UNBLOCK_INTERRUPTIONS();
+}
+
+
ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
{
+ uint idx;
+ Bucket *p;
+
IS_CONSISTENT(ht);
- while (ht->pListHead != NULL) {
- zend_hash_bucket_delete(ht, ht->pListHead);
+ for (idx = 0; idx < ht->nNumUsed; idx++) {
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ zend_hash_apply_deleter(ht, idx, p);
}
-
if (ht->nTableMask) {
- pefree(ht->arBuckets, ht->persistent);
+ pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
}
SET_INCONSISTENT(HT_DESTROYED);
@@ -607,14 +977,21 @@ ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
{
+ uint idx;
+ Bucket *p;
+
IS_CONSISTENT(ht);
- while (ht->pListTail != NULL) {
- zend_hash_bucket_delete(ht, ht->pListTail);
+ idx = ht->nNumUsed;
+ while (idx > 0) {
+ idx--;
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ zend_hash_apply_deleter(ht, idx, p);
}
if (ht->nTableMask) {
- pefree(ht->arBuckets, ht->persistent);
+ pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
}
SET_INCONSISTENT(HT_DESTROYED);
@@ -631,21 +1008,22 @@ ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
{
+ uint idx;
Bucket *p;
+ int result;
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
- p = ht->pListHead;
- while (p != NULL) {
- int result = apply_func(p->pData TSRMLS_CC);
-
- Bucket *p_next = p->pListNext;
+ for (idx = 0; idx < ht->nNumUsed; idx++) {
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+
+ result = apply_func(&p->val TSRMLS_CC);
+
if (result & ZEND_HASH_APPLY_REMOVE) {
- zend_hash_bucket_delete(ht, p);
+ zend_hash_apply_deleter(ht, idx, p);
}
- p = p_next;
-
if (result & ZEND_HASH_APPLY_STOP) {
break;
}
@@ -656,21 +1034,22 @@ ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument TSRMLS_DC)
{
+ uint idx;
Bucket *p;
+ int result;
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
- p = ht->pListHead;
- while (p != NULL) {
- int result = apply_func(p->pData, argument TSRMLS_CC);
+ for (idx = 0; idx < ht->nNumUsed; idx++) {
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+
+ result = apply_func(&p->val, argument TSRMLS_CC);
- Bucket *p_next = p->pListNext;
if (result & ZEND_HASH_APPLY_REMOVE) {
- zend_hash_bucket_delete(ht, p);
+ zend_hash_apply_deleter(ht, idx, p);
}
- p = p_next;
-
if (result & ZEND_HASH_APPLY_STOP) {
break;
}
@@ -681,31 +1060,28 @@ ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t appl
ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func_args_t apply_func, int num_args, ...)
{
+ uint idx;
Bucket *p;
va_list args;
zend_hash_key hash_key;
+ int result;
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
- p = ht->pListHead;
- while (p != NULL) {
- int result;
- Bucket *p_next;
-
+ for (idx = 0; idx < ht->nNumUsed; idx++) {
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
va_start(args, num_args);
- hash_key.arKey = p->arKey;
- hash_key.nKeyLength = p->nKeyLength;
hash_key.h = p->h;
- result = apply_func(p->pData TSRMLS_CC, num_args, args, &hash_key);
+ hash_key.key = p->key;
+
+ result = apply_func(&p->val TSRMLS_CC, num_args, args, &hash_key);
- p_next = p->pListNext;
if (result & ZEND_HASH_APPLY_REMOVE) {
- zend_hash_bucket_delete(ht, p);
+ zend_hash_apply_deleter(ht, idx, p);
}
- p = p_next;
-
if (result & ZEND_HASH_APPLY_STOP) {
va_end(args);
break;
@@ -719,21 +1095,24 @@ 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)
{
+ uint idx;
Bucket *p;
+ int result;
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
- p = ht->pListTail;
- while (p != NULL) {
- int result = apply_func(p->pData TSRMLS_CC);
+ idx = ht->nNumUsed;
+ while (idx > 0) {
+ idx--;
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+
+ result = apply_func(&p->val TSRMLS_CC);
- Bucket *p_last = p->pListLast;
if (result & ZEND_HASH_APPLY_REMOVE) {
- zend_hash_bucket_delete(ht, p);
+ zend_hash_apply_deleter(ht, idx, p);
}
- p = p_last;
-
if (result & ZEND_HASH_APPLY_STOP) {
break;
}
@@ -742,92 +1121,245 @@ ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSR
}
-ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
+ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor)
{
+ uint idx;
Bucket *p;
- void *new_entry;
+ zval *new_entry, *data;
zend_bool setTargetPointer;
IS_CONSISTENT(source);
IS_CONSISTENT(target);
- setTargetPointer = !target->pInternalPointer;
- p = source->pListHead;
- while (p) {
- if (setTargetPointer && source->pInternalPointer == p) {
- target->pInternalPointer = NULL;
+ setTargetPointer = (target->nInternalPointer == INVALID_IDX);
+ for (idx = 0; idx < source->nNumUsed; idx++) {
+ p = source->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+
+ if (setTargetPointer && source->nInternalPointer == idx) {
+ target->nInternalPointer = INVALID_IDX;
+ }
+ /* INDIRECT element may point to UNDEF-ined slots */
+ data = &p->val;
+ if (Z_TYPE_P(data) == IS_INDIRECT) {
+ data = Z_INDIRECT_P(data);
+ if (Z_TYPE_P(data) == IS_UNDEF) {
+ continue;
+ }
}
- if (p->nKeyLength) {
- zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &new_entry);
+ if (p->key) {
+ new_entry = zend_hash_update(target, p->key, data);
} else {
- zend_hash_index_update(target, p->h, p->pData, size, &new_entry);
+ new_entry = zend_hash_index_update(target, p->h, data);
}
if (pCopyConstructor) {
pCopyConstructor(new_entry);
}
- p = p->pListNext;
}
- if (!target->pInternalPointer) {
- target->pInternalPointer = target->pListHead;
+ if (target->nInternalPointer == INVALID_IDX && target->nNumOfElements > 0) {
+ idx = 0;
+ while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) {
+ idx++;
+ }
+ target->nInternalPointer = idx;
}
}
-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)
+ZEND_API void zend_array_dup(HashTable *target, HashTable *source)
{
+ uint idx, target_idx;
+ uint nIndex;
+ Bucket *p, *q;
+ zval *data;
+
+ IS_CONSISTENT(source);
+
+ target->nTableMask = source->nTableMask;
+ target->nTableSize = source->nTableSize;
+ target->pDestructor = source->pDestructor;
+ target->nInternalPointer = INVALID_IDX;
+ target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT) | HASH_FLAG_APPLY_PROTECTION;
+
+ target_idx = 0;
+ if (target->nTableMask) {
+ if (target->u.flags & HASH_FLAG_PACKED) {
+ target->nNumUsed = source->nNumUsed;
+ target->nNumOfElements = source->nNumOfElements;
+ target->nNextFreeElement = source->nNextFreeElement;
+ target->arData = (Bucket *) safe_pemalloc(target->nTableSize, sizeof(Bucket), 0, 0);
+ target->arHash = (zend_uint*)&uninitialized_bucket;
+ target->nInternalPointer = source->nInternalPointer;
+
+ for (idx = 0; idx < source->nNumUsed; idx++) {
+ p = source->arData + idx;
+ q = target->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) {
+ ZVAL_UNDEF(&q->val);
+ continue;
+ }
+ /* INDIRECT element may point to UNDEF-ined slots */
+ data = &p->val;
+ if (Z_TYPE_P(data) == IS_INDIRECT) {
+ data = Z_INDIRECT_P(data);
+ if (Z_TYPE_P(data) == IS_UNDEF) {
+ ZVAL_UNDEF(&q->val);
+ continue;
+ }
+ }
+
+ q->h = p->h;
+ q->key = NULL;
+ if (Z_OPT_REFCOUNTED_P(data)) {
+ if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
+ ZVAL_COPY(&q->val, Z_REFVAL_P(data));
+ } else {
+ ZVAL_COPY(&q->val, data);
+ }
+ } else {
+ ZVAL_COPY_VALUE(&q->val, data);
+ }
+ }
+ if (target->nNumOfElements > 0 &&
+ target->nInternalPointer == INVALID_IDX) {
+ idx = 0;
+ while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) {
+ idx++;
+ }
+ target->nInternalPointer = idx;
+ }
+ } else {
+ target->nNextFreeElement = source->nNextFreeElement;
+ target->arData = (Bucket *) safe_pemalloc(target->nTableSize, sizeof(Bucket) + sizeof(zend_uint), 0, 0);
+ target->arHash = (zend_uint*)(target->arData + target->nTableSize);
+ memset(target->arHash, INVALID_IDX, target->nTableSize * sizeof(zend_uint));
+
+ for (idx = 0; idx < source->nNumUsed; idx++) {
+ p = source->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ /* INDIRECT element may point to UNDEF-ined slots */
+ data = &p->val;
+ if (Z_TYPE_P(data) == IS_INDIRECT) {
+ data = Z_INDIRECT_P(data);
+ if (Z_TYPE_P(data) == IS_UNDEF) {
+ continue;
+ }
+ }
+
+ if (source->nInternalPointer == idx) {
+ target->nInternalPointer = target_idx;
+ }
+
+ q = target->arData + target_idx;
+ q->h = p->h;
+ q->key = p->key;
+ if (q->key) {
+ STR_ADDREF(q->key);
+ }
+ nIndex = q->h & target->nTableMask;
+ Z_NEXT(q->val) = target->arHash[nIndex];
+ target->arHash[nIndex] = target_idx;
+ if (Z_OPT_REFCOUNTED_P(data)) {
+ if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
+ ZVAL_COPY(&q->val, Z_REFVAL_P(data));
+ } else {
+ ZVAL_COPY(&q->val, data);
+ }
+ } else {
+ ZVAL_COPY_VALUE(&q->val, data);
+ }
+ target_idx++;
+ }
+ target->nNumUsed = target_idx;
+ target->nNumOfElements = target_idx;
+ if (target->nNumOfElements > 0 &&
+ target->nInternalPointer == INVALID_IDX) {
+ target->nInternalPointer = 0;
+ }
+ }
+ } else {
+ target->nNumUsed = 0;
+ target->nNumOfElements = 0;
+ target->nNextFreeElement = 0;
+ target->arData = NULL;
+ target->arHash = (zend_uint*)&uninitialized_bucket;
+ }
+}
+
+
+ZEND_API void _zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, int overwrite ZEND_FILE_LINE_DC)
+{
+ uint idx;
Bucket *p;
- void *t;
+ zval *t;
int mode = (overwrite?HASH_UPDATE:HASH_ADD);
IS_CONSISTENT(source);
IS_CONSISTENT(target);
- p = source->pListHead;
- while (p) {
- if (p->nKeyLength>0) {
- 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) {
+ for (idx = 0; idx < source->nNumUsed; idx++) {
+ p = source->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (p->key) {
+ t = _zend_hash_add_or_update(target, p->key, &p->val, mode ZEND_FILE_LINE_RELAY_CC);
+ if (t && pCopyConstructor) {
pCopyConstructor(t);
}
} else {
- if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
- pCopyConstructor(t);
+ if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h))) {
+ t = zend_hash_index_update(target, p->h, &p->val);
+ if (t && pCopyConstructor) {
+ pCopyConstructor(t);
+ }
}
}
- p = p->pListNext;
}
- target->pInternalPointer = target->pListHead;
+ if (target->nNumOfElements > 0) {
+ idx = 0;
+ while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) {
+ idx++;
+ }
+ target->nInternalPointer = idx;
+ }
}
-static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
+static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
{
zend_hash_key hash_key;
- hash_key.arKey = p->arKey;
- hash_key.nKeyLength = p->nKeyLength;
hash_key.h = p->h;
+ hash_key.key = p->key;
return merge_checker_func(target, source_data, &hash_key, pParam);
}
-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)
+ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam)
{
+ uint idx;
Bucket *p;
- void *t;
+ zval *t;
IS_CONSISTENT(source);
IS_CONSISTENT(target);
- p = source->pListHead;
- while (p) {
- if (zend_hash_replace_checker_wrapper(target, p->pData, p, pParam, pMergeSource)) {
- if (zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
+ for (idx = 0; idx < source->nNumUsed; idx++) {
+ p = source->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (zend_hash_replace_checker_wrapper(target, &p->val, p, pParam, pMergeSource)) {
+ t = zend_hash_update(target, p->key, &p->val);
+ if (t && pCopyConstructor) {
pCopyConstructor(t);
}
}
- p = p->pListNext;
}
- target->pInternalPointer = target->pListHead;
+ if (target->nNumOfElements > 0) {
+ idx = 0;
+ while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) {
+ idx++;
+ }
+ target->nInternalPointer = idx;
+ }
}
@@ -835,155 +1367,113 @@ ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor
* data is returned in pData. The reason is that there's no reason
* someone using the hash table might not want to have NULL data
*/
-ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
+ZEND_API zval *zend_hash_find(const HashTable *ht, zend_string *key)
{
- ulong h;
- uint nIndex;
Bucket *p;
IS_CONSISTENT(ht);
- h = zend_inline_hash_func(arKey, nKeyLength);
- nIndex = h & ht->nTableMask;
-
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
- if (p->arKey == arKey ||
- ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
- *pData = p->pData;
- return SUCCESS;
- }
- p = p->pNext;
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ return NULL;
}
- return FAILURE;
-}
+ p = zend_hash_find_bucket(ht, key);
+ return p ? &p->val : NULL;
+}
-ZEND_API int zend_hash_quick_find(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void **pData)
+ZEND_API zval *zend_hash_str_find(const HashTable *ht, const char *str, int len)
{
- uint nIndex;
+ ulong h;
Bucket *p;
- ZEND_ASSERT(nKeyLength != 0);
-
IS_CONSISTENT(ht);
- nIndex = h & ht->nTableMask;
-
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
- if (p->arKey == arKey ||
- ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
- *pData = p->pData;
- return SUCCESS;
- }
- p = p->pNext;
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ return NULL;
}
- return FAILURE;
-}
+ h = zend_inline_hash_func(str, len);
+ p = zend_hash_str_find_bucket(ht, str, len, h);
+ return p ? &p->val : NULL;
+}
-ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
+ZEND_API int zend_hash_exists(const HashTable *ht, zend_string *key)
{
- ulong h;
- uint nIndex;
Bucket *p;
IS_CONSISTENT(ht);
- h = zend_inline_hash_func(arKey, nKeyLength);
- nIndex = h & ht->nTableMask;
-
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
- if (p->arKey == arKey ||
- ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
- return 1;
- }
- p = p->pNext;
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ return 0;
}
- return 0;
-}
+ p = zend_hash_find_bucket(ht, key);
+ return p ? 1 : 0;
+}
-ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h)
+ZEND_API int zend_hash_str_exists(const HashTable *ht, const char *str, int len)
{
- uint nIndex;
+ ulong h;
Bucket *p;
- ZEND_ASSERT(nKeyLength != 0);
-
IS_CONSISTENT(ht);
- nIndex = h & ht->nTableMask;
-
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
- if (p->arKey == arKey ||
- ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
- return 1;
- }
- p = p->pNext;
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ return 0;
}
- return 0;
+ h = zend_inline_hash_func(str, len);
+ p = zend_hash_str_find_bucket(ht, str, len, h);
+ return p ? 1 : 0;
}
-
-ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
+ZEND_API zval *zend_hash_index_find(const HashTable *ht, ulong h)
{
- uint nIndex;
Bucket *p;
IS_CONSISTENT(ht);
- nIndex = h & ht->nTableMask;
-
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
- if ((p->h == h) && (p->nKeyLength == 0)) {
- *pData = p->pData;
- return SUCCESS;
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ if (h >= 0 && h < ht->nNumUsed) {
+ p = ht->arData + h;
+ if (Z_TYPE(p->val) != IS_UNDEF) {
+ return &p->val;
+ }
}
- p = p->pNext;
+ return NULL;
}
- return FAILURE;
+
+ p = zend_hash_index_find_bucket(ht, h);
+ return p ? &p->val : NULL;
}
ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h)
{
- uint nIndex;
Bucket *p;
IS_CONSISTENT(ht);
- nIndex = h & ht->nTableMask;
-
- p = ht->arBuckets[nIndex];
- while (p != NULL) {
- if ((p->h == h) && (p->nKeyLength == 0)) {
- return 1;
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ if (h >= 0 && h < ht->nNumUsed) {
+ if (Z_TYPE(ht->arData[h].val) != IS_UNDEF) {
+ return 1;
+ }
}
- p = p->pNext;
+ return 0;
}
- return 0;
-}
-
-
-ZEND_API int zend_hash_num_elements(const HashTable *ht)
-{
- IS_CONSISTENT(ht);
- return ht->nNumOfElements;
+ p = zend_hash_index_find_bucket(ht, h);
+ return p ? 1 : 0;
}
ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
{
- ptr->pos = ht->pInternalPointer;
- if (ht->pInternalPointer) {
- ptr->h = ht->pInternalPointer->h;
+ ptr->pos = ht->nInternalPointer;
+ ptr->ht = (HashTable*)ht;
+ if (ht->nInternalPointer != INVALID_IDX) {
+ ptr->h = ht->arData[ht->nInternalPointer].h;
return 1;
} else {
ptr->h = 0;
@@ -993,19 +1483,37 @@ ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
{
- if (ptr->pos == NULL) {
- ht->pInternalPointer = NULL;
- } else if (ht->pInternalPointer != ptr->pos) {
- Bucket *p;
+ uint idx;
+ if (ptr->pos == INVALID_IDX) {
+ ht->nInternalPointer = INVALID_IDX;
+ } else if (ptr->ht != ht) {
IS_CONSISTENT(ht);
- p = ht->arBuckets[ptr->h & ht->nTableMask];
- while (p != NULL) {
- if (p == ptr->pos) {
- ht->pInternalPointer = p;
+ for (idx = 0; idx < ht->nNumUsed; idx++) {
+ if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
+ ht->nInternalPointer = idx;
+ return 0;
+ }
+ }
+ idx = INVALID_IDX;
+ return 0;
+ } else if (ht->nInternalPointer != ptr->pos) {
+ IS_CONSISTENT(ht);
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ if (ptr->h < ht->nNumUsed &&
+ Z_TYPE(ht->arData[ptr->h].val) != IS_UNDEF) {
+ ht->nInternalPointer = ptr->h;
return 1;
}
- p = p->pNext;
+ } else {
+ idx = ht->arHash[ptr->h & ht->nTableMask];
+ while (idx != INVALID_IDX) {
+ if (ht->arData[idx].h == ptr->h && idx == ptr->pos) {
+ ht->nInternalPointer = idx;
+ return 1;
+ }
+ idx = Z_NEXT(ht->arData[idx].val);
+ }
}
return 0;
}
@@ -1014,12 +1522,16 @@ ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
{
+ uint idx;
+
IS_CONSISTENT(ht);
-
- if (pos)
- *pos = ht->pListHead;
- else
- ht->pInternalPointer = ht->pListHead;
+ for (idx = 0; idx < ht->nNumUsed; idx++) {
+ if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
+ *pos = idx;
+ return;
+ }
+ }
+ *pos = INVALID_IDX;
}
@@ -1028,60 +1540,81 @@ ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *p
*/
ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
{
+ uint idx;
+
IS_CONSISTENT(ht);
- if (pos)
- *pos = ht->pListTail;
- else
- ht->pInternalPointer = ht->pListTail;
+ idx = ht->nNumUsed;
+ while (idx > 0) {
+ idx--;
+ if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
+ *pos = idx;
+ return;
+ }
+ }
+ *pos = INVALID_IDX;
}
ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
{
- HashPosition *current = pos ? pos : &ht->pInternalPointer;
+ uint idx = *pos;
IS_CONSISTENT(ht);
- if (*current) {
- *current = (*current)->pListNext;
- return SUCCESS;
- } else
- return FAILURE;
+ if (idx != INVALID_IDX) {
+ while (1) {
+ idx++;
+ if (idx >= ht->nNumUsed) {
+ *pos = INVALID_IDX;
+ return SUCCESS;
+ }
+ if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
+ *pos = idx;
+ return SUCCESS;
+ }
+ }
+ } else {
+ return FAILURE;
+ }
}
ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
{
- HashPosition *current = pos ? pos : &ht->pInternalPointer;
+ uint idx = *pos;
IS_CONSISTENT(ht);
- if (*current) {
- *current = (*current)->pListLast;
- return SUCCESS;
- } else
- return FAILURE;
+ if (idx != INVALID_IDX) {
+ while (idx > 0) {
+ idx--;
+ if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
+ *pos = idx;
+ return SUCCESS;
+ }
+ }
+ *pos = INVALID_IDX;
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
}
/* This function should be made binary safe */
-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)
+ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, ulong *num_index, zend_bool duplicate, HashPosition *pos)
{
+ uint idx = *pos;
Bucket *p;
- p = pos ? (*pos) : ht->pInternalPointer;
-
IS_CONSISTENT(ht);
-
- if (p) {
- if (p->nKeyLength) {
+ if (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ if (p->key) {
if (duplicate) {
- *str_index = estrndup(p->arKey, p->nKeyLength - 1);
+ *str_index = STR_COPY(p->key);
} else {
- *str_index = (char*)p->arKey;
- }
- if (str_length) {
- *str_length = p->nKeyLength;
+ *str_index = p->key;
}
return HASH_KEY_IS_STRING;
} else {
@@ -1092,35 +1625,34 @@ ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, char **str_index,
return HASH_KEY_NON_EXISTENT;
}
-ZEND_API void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) {
+ZEND_API void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos)
+{
+ uint idx = *pos;
Bucket *p;
IS_CONSISTENT(ht);
-
- p = pos ? (*pos) : ht->pInternalPointer;
-
- if (!p) {
- Z_TYPE_P(key) = IS_NULL;
- } else if (p->nKeyLength) {
- Z_TYPE_P(key) = IS_STRING;
- Z_STRVAL_P(key) = IS_INTERNED(p->arKey) ? (char*)p->arKey : estrndup(p->arKey, p->nKeyLength - 1);
- Z_STRLEN_P(key) = p->nKeyLength - 1;
+ if (idx == INVALID_IDX) {
+ ZVAL_NULL(key);
} else {
- Z_TYPE_P(key) = IS_LONG;
- Z_LVAL_P(key) = p->h;
+ p = ht->arData + idx;
+ if (p->key) {
+ ZVAL_STR(key, p->key);
+ STR_ADDREF(p->key);
+ } else {
+ ZVAL_LONG(key, p->h);
+ }
}
}
ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
{
+ uint idx = *pos;
Bucket *p;
- p = pos ? (*pos) : ht->pInternalPointer;
-
IS_CONSISTENT(ht);
-
- if (p) {
- if (p->nKeyLength) {
+ if (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ if (p->key) {
return HASH_KEY_IS_STRING;
} else {
return HASH_KEY_IS_LONG;
@@ -1130,292 +1662,90 @@ ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
}
-ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos)
+ZEND_API zval *zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos)
{
+ uint idx = *pos;
Bucket *p;
- p = pos ? (*pos) : ht->pInternalPointer;
-
IS_CONSISTENT(ht);
-
- if (p) {
- *pData = p->pData;
- return SUCCESS;
+ if (idx != INVALID_IDX) {
+ p = ht->arData + idx;
+ return &p->val;
} else {
- return FAILURE;
+ return NULL;
}
}
-/* This function changes key of current element without changing elements'
- * order. If element with target key already exists, it will be deleted first.
- */
-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)
+ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
+ compare_func_t compar, int renumber TSRMLS_DC)
{
- Bucket *p, *q;
- ulong h;
-#ifdef ZEND_SIGNALS
- TSRMLS_FETCH();
-#endif
-
- p = pos ? (*pos) : ht->pInternalPointer;
+ Bucket *p;
+ int i, j;
IS_CONSISTENT(ht);
- if (p) {
- if (key_type == HASH_KEY_IS_LONG) {
- str_length = 0;
- if (!p->nKeyLength && p->h == num_index) {
- return SUCCESS;
- }
-
- q = ht->arBuckets[num_index & ht->nTableMask];
- while (q != NULL) {
- if (!q->nKeyLength && q->h == num_index) {
- break;
- }
- q = q->pNext;
- }
- } else if (key_type == HASH_KEY_IS_STRING) {
- if (IS_INTERNED(str_index)) {
- h = INTERNED_HASH(str_index);
- } else {
- h = zend_inline_hash_func(str_index, str_length);
- }
-
- if (p->arKey == str_index ||
- (p->nKeyLength == str_length &&
- p->h == h &&
- memcmp(p->arKey, str_index, str_length) == 0)) {
- return SUCCESS;
- }
-
- q = ht->arBuckets[h & ht->nTableMask];
-
- while (q != NULL) {
- if (q->arKey == str_index ||
- (q->h == h && q->nKeyLength == str_length &&
- memcmp(q->arKey, str_index, str_length) == 0)) {
- break;
- }
- q = q->pNext;
- }
- } else {
- return FAILURE;
- }
-
- if (q) {
- if (mode != HASH_UPDATE_KEY_ANYWAY) {
- Bucket *r = p->pListLast;
- int found = HASH_UPDATE_KEY_IF_BEFORE;
-
- while (r) {
- if (r == q) {
- found = HASH_UPDATE_KEY_IF_AFTER;
- break;
- }
- r = r->pListLast;
- }
- if (mode & found) {
- /* delete current bucket */
- zend_hash_bucket_delete(ht, p);
- return FAILURE;
- }
- }
-
- /* delete another bucket with the same key */
- zend_hash_bucket_delete(ht, q);
- }
-
- HANDLE_BLOCK_INTERRUPTIONS();
-
- if (p->pNext) {
- p->pNext->pLast = p->pLast;
- }
- if (p->pLast) {
- p->pLast->pNext = p->pNext;
- } else {
- ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
- }
-
- if ((IS_INTERNED(p->arKey) != IS_INTERNED(str_index)) ||
- (!IS_INTERNED(p->arKey) && p->nKeyLength != str_length)) {
- Bucket *q;
-
- if (IS_INTERNED(str_index)) {
- q = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
- } else {
- q = (Bucket *) pemalloc(sizeof(Bucket) + str_length, ht->persistent);
- }
-
- q->nKeyLength = str_length;
- if (p->pData == &p->pDataPtr) {
- q->pData = &q->pDataPtr;
- } else {
- q->pData = p->pData;
- }
- q->pDataPtr = p->pDataPtr;
-
- CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p->pListLast, p->pListNext);
- if (ht->pInternalPointer == p) {
- ht->pInternalPointer = q;
- }
-
- if (pos) {
- *pos = q;
- }
- pefree(p, ht->persistent);
- p = q;
- }
-
- if (key_type == HASH_KEY_IS_LONG) {
- p->h = num_index;
- } else {
- p->h = h;
- p->nKeyLength = str_length;
- if (IS_INTERNED(str_index)) {
- p->arKey = str_index;
- } else {
- p->arKey = (const char*)(p+1);
- memcpy((char*)p->arKey, str_index, str_length);
- }
- }
-
- CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[p->h & ht->nTableMask]);
- ht->arBuckets[p->h & ht->nTableMask] = p;
- HANDLE_UNBLOCK_INTERRUPTIONS();
-
+ if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
return SUCCESS;
- } else {
- return FAILURE;
}
-}
-
-/* Performs an in-place splice operation on a hashtable:
- * The elements between offset and offset+length are removed and the elements in list[list_count]
- * are inserted in their place. The removed elements can be optionally collected into a hashtable.
- * This operation reindexes the hashtable, i.e. integer keys will be zero-based and sequential,
- * while string keys stay intact. The same applies to the elements inserted into the removed HT. */
-ZEND_API void _zend_hash_splice(HashTable *ht, uint nDataSize, copy_ctor_func_t pCopyConstructor, uint offset, uint length, void **list, uint list_count, HashTable *removed ZEND_FILE_LINE_DC) /* {{{ */
-{
- int pos;
- Bucket *p;
-
- IS_CONSISTENT(ht);
- CHECK_INIT(ht);
-
- /* Skip all elements until offset */
- for (pos = 0, p = ht->pListHead; pos < offset && p; pos++, p = p->pListNext);
-
- while (pos < offset + length && p) {
- /* Copy removed element into HT, if it was specified */
- if (removed != NULL) {
- void *new_entry;
-
- if (p->nKeyLength == 0) {
- zend_hash_next_index_insert(removed, p->pData, sizeof(zval *), &new_entry);
- } else {
- zend_hash_quick_update(removed, p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval *), &new_entry);
- }
- if (pCopyConstructor) {
- pCopyConstructor(new_entry);
+ if (ht->nNumUsed == ht->nNumOfElements) {
+ i = ht->nNumUsed;
+ } else {
+ for (j = 0, i = 0; j < ht->nNumUsed; j++) {
+ p = ht->arData + j;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (i != j) {
+ ht->arData[i] = *p;
}
+ i++;
}
-
- /* Remove element */
- {
- Bucket *p_next = p->pListNext;
- zend_hash_bucket_delete(ht, p);
- p = p_next;
- }
-
- pos++;
}
- if (list != NULL) {
- int i;
- for (i = 0; i < list_count; i++) {
- /* Add new element only to the global linked list, not the bucket list.
- * Also use key 0 for everything, as we'll reindex the hashtable anyways. */
- Bucket *q = pemalloc_rel(sizeof(Bucket), ht->persistent);
- q->arKey = NULL;
- q->nKeyLength = 0;
- q->h = 0;
- INIT_DATA(ht, q, list[i], nDataSize);
-
- CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p ? p->pListLast : ht->pListTail, p);
+ (*sort_func)((void *) ht->arData, i, sizeof(Bucket), compar TSRMLS_CC);
- ht->nNumOfElements++;
+ HANDLE_BLOCK_INTERRUPTIONS();
+ ht->nNumUsed = i;
+ ht->nInternalPointer = 0;
- if (pCopyConstructor) {
- pCopyConstructor(q->pData);
+ if (renumber) {
+ for (j = 0; j < i; j++) {
+ p = ht->arData + j;
+ p->h = j;
+ if (p->key) {
+ STR_RELEASE(p->key);
+ p->key = NULL;
}
}
-
- ZEND_HASH_IF_FULL_DO_RESIZE(ht);
- }
-
- zend_hash_reindex(ht, 1);
-}
-/* }}} */
-
-ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
- compare_func_t compar, int renumber TSRMLS_DC)
-{
- Bucket **arTmp;
- Bucket *p;
- int i, j;
-
- IS_CONSISTENT(ht);
-
- if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
- return SUCCESS;
}
- arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
- p = ht->pListHead;
- i = 0;
- while (p) {
- arTmp[i] = p;
- p = p->pListNext;
- i++;
+ if (renumber) {
+ ht->nNextFreeElement = i;
}
-
- (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
-
- HANDLE_BLOCK_INTERRUPTIONS();
- ht->pListHead = arTmp[0];
- ht->pListTail = NULL;
- ht->pInternalPointer = ht->pListHead;
-
- arTmp[0]->pListLast = NULL;
- if (i > 1) {
- arTmp[0]->pListNext = arTmp[1];
- for (j = 1; j < i-1; j++) {
- arTmp[j]->pListLast = arTmp[j-1];
- arTmp[j]->pListNext = arTmp[j+1];
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ if (!renumber) {
+ zend_hash_packed_to_hash(ht);
}
- arTmp[j]->pListLast = arTmp[j-1];
- arTmp[j]->pListNext = NULL;
} else {
- arTmp[0]->pListNext = NULL;
+ if (renumber) {
+ ht->u.flags |= HASH_FLAG_PACKED;
+ ht->arData = erealloc(ht->arData, ht->nTableSize * sizeof(Bucket));
+ ht->arHash = (zend_uint*)&uninitialized_bucket;
+ } else {
+ zend_hash_rehash(ht);
+ }
}
- ht->pListTail = arTmp[i-1];
- pefree(arTmp, ht->persistent);
HANDLE_UNBLOCK_INTERRUPTIONS();
- if (renumber) {
- zend_hash_reindex(ht, 0);
- }
return SUCCESS;
}
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
{
+ uint idx1, idx2;
Bucket *p1, *p2 = NULL;
int result;
- void *pData2;
+ zval *pData1, *pData2;
IS_CONSISTENT(ht1);
IS_CONSISTENT(ht2);
@@ -1430,64 +1760,83 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co
return result;
}
- p1 = ht1->pListHead;
- if (ordered) {
- p2 = ht2->pListHead;
- }
+ for (idx1 = 0, idx2 = 0; idx1 < ht1->nNumUsed; idx1++) {
+ p1 = ht1->arData + idx1;
+ if (Z_TYPE(p1->val) == IS_UNDEF) continue;
- while (p1) {
- if (ordered && !p2) {
- HASH_UNPROTECT_RECURSION(ht1);
- HASH_UNPROTECT_RECURSION(ht2);
- return 1; /* That's not supposed to happen */
- }
if (ordered) {
- if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */
+ while (1) {
+ p2 = ht2->arData + idx2;
+ if (idx2 == ht2->nNumUsed) {
+ HASH_UNPROTECT_RECURSION(ht1);
+ HASH_UNPROTECT_RECURSION(ht2);
+ return 1; /* That's not supposed to happen */
+ }
+ if (Z_TYPE(p2->val) != IS_UNDEF) break;
+ idx2++;
+ }
+ if (p1->key == NULL && p2->key == NULL) { /* numeric indices */
result = p1->h - p2->h;
- if (result!=0) {
+ if (result != 0) {
HASH_UNPROTECT_RECURSION(ht1);
HASH_UNPROTECT_RECURSION(ht2);
return result;
}
} else { /* string indices */
- result = p1->nKeyLength - p2->nKeyLength;
- if (result!=0) {
+ result = (p1->key ? p1->key->len : 0) - (p2->key ? p2->key->len : 0);
+ if (result != 0) {
HASH_UNPROTECT_RECURSION(ht1);
HASH_UNPROTECT_RECURSION(ht2);
return result;
}
- result = memcmp(p1->arKey, p2->arKey, p1->nKeyLength);
- if (result!=0) {
+ result = memcmp(p1->key->val, p2->key->val, p1->key->len);
+ if (result != 0) {
HASH_UNPROTECT_RECURSION(ht1);
HASH_UNPROTECT_RECURSION(ht2);
return result;
}
}
- pData2 = p2->pData;
+ pData2 = &p2->val;
} else {
- if (p1->nKeyLength==0) { /* numeric index */
- if (zend_hash_index_find(ht2, p1->h, &pData2)==FAILURE) {
+ if (p1->key == NULL) { /* numeric index */
+ pData2 = zend_hash_index_find(ht2, p1->h);
+ if (pData2 == NULL) {
HASH_UNPROTECT_RECURSION(ht1);
HASH_UNPROTECT_RECURSION(ht2);
return 1;
}
} else { /* string index */
- if (zend_hash_quick_find(ht2, p1->arKey, p1->nKeyLength, p1->h, &pData2)==FAILURE) {
+ pData2 = zend_hash_find(ht2, p1->key);
+ if (pData2 == NULL) {
HASH_UNPROTECT_RECURSION(ht1);
HASH_UNPROTECT_RECURSION(ht2);
return 1;
}
}
}
- result = compar(p1->pData, pData2 TSRMLS_CC);
- if (result!=0) {
+ pData1 = &p1->val;
+ if (Z_TYPE_P(pData1) == IS_INDIRECT) {
+ pData1 = Z_INDIRECT_P(pData1);
+ }
+ if (Z_TYPE_P(pData2) == IS_INDIRECT) {
+ pData2 = Z_INDIRECT_P(pData2);
+ }
+ if (Z_TYPE_P(pData1) == IS_UNDEF) {
+ if (Z_TYPE_P(pData2) != IS_UNDEF) {
+ return -1;
+ }
+ } else if (Z_TYPE_P(pData2) == IS_UNDEF) {
+ return 1;
+ } else {
+ result = compar(pData1, pData2 TSRMLS_CC);
+ }
+ if (result != 0) {
HASH_UNPROTECT_RECURSION(ht1);
HASH_UNPROTECT_RECURSION(ht2);
return result;
}
- p1 = p1->pListNext;
if (ordered) {
- p2 = p2->pListNext;
+ idx2++;
}
}
@@ -1497,78 +1846,42 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co
}
-ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC)
+ZEND_API zval *zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag TSRMLS_DC)
{
+ uint idx;
Bucket *p, *res;
IS_CONSISTENT(ht);
if (ht->nNumOfElements == 0 ) {
- *pData=NULL;
- return FAILURE;
+ return NULL;
}
- res = p = ht->pListHead;
- while ((p = p->pListNext)) {
+ idx = 0;
+ while (1) {
+ if (idx == ht->nNumUsed) {
+ return NULL;
+ }
+ if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) break;
+ idx++;
+ }
+ res = ht->arData + idx;
+ for (; idx < ht->nNumUsed; idx++) {
+ p = ht->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+
if (flag) {
- if (compar(&res, &p TSRMLS_CC) < 0) { /* max */
+ if (compar(res, p TSRMLS_CC) < 0) { /* max */
res = p;
}
} else {
- if (compar(&res, &p TSRMLS_CC) > 0) { /* min */
+ if (compar(res, p TSRMLS_CC) > 0) { /* min */
res = p;
}
}
}
- *pData = res->pData;
- return SUCCESS;
-}
-
-ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
-{
- IS_CONSISTENT(ht);
-
- return ht->nNextFreeElement;
-
-}
-
-
-#if ZEND_DEBUG
-void zend_hash_display_pListTail(const HashTable *ht)
-{
- Bucket *p;
-
- p = ht->pListTail;
- while (p != NULL) {
- zend_output_debug_string(0, "pListTail has key %s\n", p->arKey);
- p = p->pListLast;
- }
-}
-
-void zend_hash_display(const HashTable *ht)
-{
- Bucket *p;
- uint i;
-
- if (UNEXPECTED(ht->nNumOfElements == 0)) {
- zend_output_debug_string(0, "The hash is empty");
- return;
- }
- for (i = 0; i < ht->nTableSize; i++) {
- p = ht->arBuckets[i];
- while (p != NULL) {
- zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
- p = p->pNext;
- }
- }
-
- p = ht->pListTail;
- while (p != NULL) {
- zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
- p = p->pListLast;
- }
+ return &res->val;
}
-#endif
/*
* Local variables: