1999-04-08 02:10:10 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Zend Engine |
|
|
|
|
+----------------------------------------------------------------------+
|
2008-12-31 19:15:49 +08:00
|
|
|
| Copyright (c) 1998-2009 Zend Technologies Ltd. (http://www.zend.com) |
|
1999-04-08 02:10:10 +08:00
|
|
|
+----------------------------------------------------------------------+
|
2001-12-11 23:16:21 +08:00
|
|
|
| This source file is subject to version 2.00 of the Zend license, |
|
1999-07-16 22:58:16 +08:00
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
2003-06-11 04:04:29 +08:00
|
|
|
| available through the world-wide-web at the following url: |
|
2001-12-11 23:16:21 +08:00
|
|
|
| http://www.zend.com/license/2_00.txt. |
|
1999-07-16 22:58:16 +08:00
|
|
|
| If you did not receive a copy of the Zend license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@zend.com so we can mail you a copy immediately. |
|
1999-04-08 02:10:10 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Authors: Andi Gutmans <andi@zend.com> |
|
|
|
|
| Zeev Suraski <zeev@zend.com> |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
2003-02-01 09:49:15 +08:00
|
|
|
/* $Id$ */
|
1999-07-16 22:58:16 +08:00
|
|
|
|
1999-09-07 00:14:08 +08:00
|
|
|
#include "zend.h"
|
|
|
|
|
2000-01-20 22:26:31 +08:00
|
|
|
#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(element, ht) \
|
|
|
|
(element)->pListLast = (ht)->pListTail; \
|
|
|
|
(ht)->pListTail = (element); \
|
|
|
|
(element)->pListNext = NULL; \
|
|
|
|
if ((element)->pListLast != NULL) { \
|
|
|
|
(element)->pListLast->pListNext = (element); \
|
|
|
|
} \
|
|
|
|
if (!(ht)->pListHead) { \
|
|
|
|
(ht)->pListHead = (element); \
|
|
|
|
} \
|
|
|
|
if ((ht)->pInternalPointer == NULL) { \
|
|
|
|
(ht)->pInternalPointer = (element); \
|
|
|
|
}
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
#if ZEND_DEBUG
|
2000-06-03 09:49:49 +08:00
|
|
|
#define HT_OK 0
|
2000-01-18 03:17:58 +08:00
|
|
|
#define HT_IS_DESTROYING 1
|
|
|
|
#define HT_DESTROYED 2
|
2000-06-03 09:49:49 +08:00
|
|
|
#define HT_CLEANING 3
|
2000-01-18 03:17:58 +08:00
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
|
2000-02-20 03:21:45 +08:00
|
|
|
{
|
2000-06-03 09:49:49 +08:00
|
|
|
if (ht->inconsistent==HT_OK) {
|
|
|
|
return;
|
|
|
|
}
|
2002-07-24 03:29:02 +08:00
|
|
|
switch (ht->inconsistent) {
|
2000-06-03 09:49:49 +08:00
|
|
|
case HT_IS_DESTROYING:
|
2003-08-29 15:34:37 +08:00
|
|
|
zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
|
2000-06-03 09:49:49 +08:00
|
|
|
break;
|
|
|
|
case HT_DESTROYED:
|
2003-08-29 15:34:37 +08:00
|
|
|
zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file, line, ht);
|
2000-06-03 09:49:49 +08:00
|
|
|
break;
|
|
|
|
case HT_CLEANING:
|
2003-08-29 15:34:37 +08:00
|
|
|
zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file, line, ht);
|
2000-06-03 09:49:49 +08:00
|
|
|
break;
|
2002-07-24 03:29:02 +08:00
|
|
|
}
|
2000-06-03 09:49:49 +08:00
|
|
|
zend_bailout();
|
2000-01-15 21:40:17 +08:00
|
|
|
}
|
2001-08-11 23:56:40 +08:00
|
|
|
#define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
|
2000-01-16 17:45:10 +08:00
|
|
|
#define SET_INCONSISTENT(n) ht->inconsistent = n;
|
2000-01-15 21:40:17 +08:00
|
|
|
#else
|
|
|
|
#define IS_CONSISTENT(a)
|
2000-01-16 17:45:10 +08:00
|
|
|
#define SET_INCONSISTENT(n)
|
2000-01-15 21:40:17 +08:00
|
|
|
#endif
|
|
|
|
|
2001-07-16 02:57:43 +08:00
|
|
|
#define HASH_PROTECT_RECURSION(ht) \
|
2000-07-11 22:27:31 +08:00
|
|
|
if ((ht)->bApplyProtection) { \
|
2001-07-16 02:57:43 +08:00
|
|
|
if ((ht)->nApplyCount++ >= 3) { \
|
|
|
|
zend_error(E_ERROR, "Nesting level too deep - recursive dependency?"); \
|
2000-07-11 22:27:31 +08:00
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
2001-07-16 02:57:43 +08:00
|
|
|
|
2002-04-20 00:43:27 +08:00
|
|
|
#define HASH_UNPROTECT_RECURSION(ht) \
|
|
|
|
if ((ht)->bApplyProtection) { \
|
|
|
|
(ht)->nApplyCount--; \
|
|
|
|
}
|
2000-06-17 22:11:57 +08:00
|
|
|
|
|
|
|
|
2000-02-13 21:31:29 +08:00
|
|
|
#define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
|
|
|
|
if ((ht)->nNumOfElements > (ht)->nTableSize) { \
|
|
|
|
zend_hash_do_resize(ht); \
|
|
|
|
}
|
|
|
|
|
|
|
|
static int zend_hash_do_resize(HashTable *ht);
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2007-09-28 02:00:48 +08:00
|
|
|
ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
|
2001-07-11 04:31:42 +08:00
|
|
|
{
|
|
|
|
return zend_inline_hash_func(arKey, nKeyLength);
|
|
|
|
}
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2003-07-31 03:47:39 +08:00
|
|
|
#define UPDATE_DATA(ht, p, pData, nDataSize) \
|
|
|
|
if (nDataSize == sizeof(void*)) { \
|
2006-04-07 18:06:21 +08:00
|
|
|
if ((p)->pData != &(p)->pDataPtr) { \
|
|
|
|
pefree_rel((p)->pData, (ht)->persistent); \
|
2003-07-31 03:47:39 +08:00
|
|
|
} \
|
|
|
|
memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
|
|
|
|
(p)->pData = &(p)->pDataPtr; \
|
|
|
|
} else { \
|
2006-04-07 18:06:21 +08:00
|
|
|
if ((p)->pData == &(p)->pDataPtr) { \
|
|
|
|
(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent); \
|
2003-07-31 03:47:39 +08:00
|
|
|
(p)->pDataPtr=NULL; \
|
|
|
|
} else { \
|
2003-08-19 05:17:26 +08:00
|
|
|
(p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent); \
|
2003-07-31 03:47:39 +08:00
|
|
|
/* (p)->pDataPtr is already NULL so no need to initialize it */ \
|
|
|
|
} \
|
|
|
|
memcpy((p)->pData, pData, nDataSize); \
|
2000-02-13 20:45:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define INIT_DATA(ht, p, pData, nDataSize); \
|
2000-03-24 19:12:30 +08:00
|
|
|
if (nDataSize == sizeof(void*)) { \
|
2000-12-07 05:24:10 +08:00
|
|
|
memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
|
2000-02-13 20:45:36 +08:00
|
|
|
(p)->pData = &(p)->pDataPtr; \
|
|
|
|
} else { \
|
2006-04-07 18:06:21 +08:00
|
|
|
(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
|
2000-02-13 20:45:36 +08:00
|
|
|
if (!(p)->pData) { \
|
2006-04-07 18:06:21 +08:00
|
|
|
pefree_rel(p, (ht)->persistent); \
|
2000-02-13 20:45:36 +08:00
|
|
|
return FAILURE; \
|
|
|
|
} \
|
|
|
|
memcpy((p)->pData, pData, nDataSize); \
|
|
|
|
(p)->pDataPtr=NULL; \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-23 00:06:07 +08:00
|
|
|
|
2002-09-17 22:04:37 +08:00
|
|
|
ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2001-07-11 04:31:42 +08:00
|
|
|
uint i = 3;
|
2004-07-10 15:46:17 +08:00
|
|
|
Bucket **tmp;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-18 03:17:58 +08:00
|
|
|
SET_INCONSISTENT(HT_OK);
|
2001-07-11 04:31:42 +08:00
|
|
|
|
2007-01-21 07:10:02 +08:00
|
|
|
if (nSize >= 0x80000000) {
|
|
|
|
/* prevent overflow */
|
|
|
|
ht->nTableSize = 0x80000000;
|
|
|
|
} else {
|
|
|
|
while ((1U << i) < nSize) {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
ht->nTableSize = 1 << i;
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
2001-07-11 04:31:42 +08:00
|
|
|
|
|
|
|
ht->nTableMask = ht->nTableSize - 1;
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->pDestructor = pDestructor;
|
2004-07-10 15:46:17 +08:00
|
|
|
ht->arBuckets = NULL;
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->pListHead = NULL;
|
|
|
|
ht->pListTail = NULL;
|
|
|
|
ht->nNumOfElements = 0;
|
|
|
|
ht->nNextFreeElement = 0;
|
|
|
|
ht->pInternalPointer = NULL;
|
|
|
|
ht->persistent = persistent;
|
2000-06-17 22:11:57 +08:00
|
|
|
ht->nApplyCount = 0;
|
2000-07-11 22:27:31 +08:00
|
|
|
ht->bApplyProtection = 1;
|
2004-07-10 15:46:17 +08:00
|
|
|
|
|
|
|
/* Uses ecalloc() so that Bucket* == NULL */
|
|
|
|
if (persistent) {
|
|
|
|
tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *));
|
|
|
|
if (!tmp) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
ht->arBuckets = tmp;
|
|
|
|
} else {
|
|
|
|
tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *));
|
|
|
|
if (tmp) {
|
|
|
|
ht->arBuckets = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2000-07-11 22:27:31 +08:00
|
|
|
|
2002-09-17 22:04:37 +08:00
|
|
|
ZEND_API int _zend_hash_init_ex(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC)
|
2000-07-11 22:27:31 +08:00
|
|
|
{
|
2002-09-17 22:04:37 +08:00
|
|
|
int retval = _zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent ZEND_FILE_LINE_CC);
|
2000-07-11 22:27:31 +08:00
|
|
|
|
|
|
|
ht->bApplyProtection = bApplyProtection;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
|
|
|
|
{
|
|
|
|
ht->bApplyProtection = bApplyProtection;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-09-28 02:00:48 +08:00
|
|
|
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)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
ulong h;
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
if (nKeyLength <= 0) {
|
|
|
|
#if ZEND_DEBUG
|
|
|
|
ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
|
|
|
|
#endif
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
h = zend_inline_hash_func(arKey, nKeyLength);
|
|
|
|
nIndex = h & ht->nTableMask;
|
2000-02-13 21:22:02 +08:00
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
|
|
|
|
if (!memcmp(p->arKey, arKey, nKeyLength)) {
|
|
|
|
if (flag & HASH_ADD) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
|
|
|
#if ZEND_DEBUG
|
|
|
|
if (p->pData == pData) {
|
|
|
|
ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
|
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (ht->pDestructor) {
|
|
|
|
ht->pDestructor(p->pData);
|
|
|
|
}
|
2000-02-13 20:45:36 +08:00
|
|
|
UPDATE_DATA(ht, p, pData, nDataSize);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (pDest) {
|
|
|
|
*pDest = p->pData;
|
|
|
|
}
|
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
|
2005-04-25 16:21:15 +08:00
|
|
|
p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (!p) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
memcpy(p->arKey, arKey, nKeyLength);
|
|
|
|
p->nKeyLength = nKeyLength;
|
2000-02-13 20:45:36 +08:00
|
|
|
INIT_DATA(ht, p, pData, nDataSize);
|
1999-04-08 02:10:10 +08:00
|
|
|
p->h = h;
|
2000-01-20 22:26:31 +08:00
|
|
|
CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (pDest) {
|
|
|
|
*pDest = p->pData;
|
|
|
|
}
|
|
|
|
|
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
2000-01-20 22:26:31 +08:00
|
|
|
CONNECT_TO_GLOBAL_DLLIST(p, ht);
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->arBuckets[nIndex] = p;
|
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
2000-01-20 22:26:31 +08:00
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->nNumOfElements++;
|
2000-02-13 21:31:29 +08:00
|
|
|
ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
|
1999-04-08 02:10:10 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2007-09-28 02:00:48 +08:00
|
|
|
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)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2003-02-04 20:12:34 +08:00
|
|
|
if (nKeyLength == 0) {
|
|
|
|
return zend_hash_index_update(ht, h, pData, nDataSize, pDest);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
|
|
|
|
if (!memcmp(p->arKey, arKey, nKeyLength)) {
|
|
|
|
if (flag & HASH_ADD) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
|
|
|
#if ZEND_DEBUG
|
|
|
|
if (p->pData == pData) {
|
|
|
|
ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
|
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (ht->pDestructor) {
|
|
|
|
ht->pDestructor(p->pData);
|
|
|
|
}
|
2000-02-13 20:45:36 +08:00
|
|
|
UPDATE_DATA(ht, p, pData, nDataSize);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (pDest) {
|
|
|
|
*pDest = p->pData;
|
|
|
|
}
|
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
|
2005-04-25 16:21:15 +08:00
|
|
|
p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (!p) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(p->arKey, arKey, nKeyLength);
|
|
|
|
p->nKeyLength = nKeyLength;
|
2000-02-13 20:45:36 +08:00
|
|
|
INIT_DATA(ht, p, pData, nDataSize);
|
1999-04-08 02:10:10 +08:00
|
|
|
p->h = h;
|
2000-01-20 22:26:31 +08:00
|
|
|
|
|
|
|
CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
if (pDest) {
|
|
|
|
*pDest = p->pData;
|
|
|
|
}
|
|
|
|
|
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
|
|
|
ht->arBuckets[nIndex] = p;
|
2000-01-20 22:26:31 +08:00
|
|
|
CONNECT_TO_GLOBAL_DLLIST(p, ht);
|
1999-04-08 02:10:10 +08:00
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
2000-01-20 22:26:31 +08:00
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->nNumOfElements++;
|
2000-02-13 21:31:29 +08:00
|
|
|
ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
|
1999-04-08 02:10:10 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-28 02:00:48 +08:00
|
|
|
ZEND_API int zend_hash_add_empty_element(HashTable *ht, const char *arKey, uint nKeyLength)
|
2001-05-17 01:22:01 +08:00
|
|
|
{
|
2001-05-19 22:53:55 +08:00
|
|
|
void *dummy = (void *) 1;
|
2001-05-17 01:22:01 +08:00
|
|
|
|
|
|
|
return zend_hash_add(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-19 05:17:26 +08:00
|
|
|
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)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
if (flag & HASH_NEXT_INSERT) {
|
|
|
|
h = ht->nNextFreeElement;
|
|
|
|
}
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
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_BLOCK_INTERRUPTIONS();
|
|
|
|
#if ZEND_DEBUG
|
|
|
|
if (p->pData == pData) {
|
|
|
|
ZEND_PUTS("Fatal error in zend_hash_index_update: p->pData == pData\n");
|
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (ht->pDestructor) {
|
|
|
|
ht->pDestructor(p->pData);
|
|
|
|
}
|
2000-02-13 20:45:36 +08:00
|
|
|
UPDATE_DATA(ht, p, pData, nDataSize);
|
1999-04-08 02:10:10 +08:00
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
2003-08-12 14:21:02 +08:00
|
|
|
if ((long)h >= (long)ht->nNextFreeElement) {
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->nNextFreeElement = h + 1;
|
|
|
|
}
|
|
|
|
if (pDest) {
|
|
|
|
*pDest = p->pData;
|
|
|
|
}
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
2005-04-25 16:21:15 +08:00
|
|
|
p = (Bucket *) pemalloc_rel(sizeof(Bucket) - 1, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (!p) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
2005-04-25 16:21:15 +08:00
|
|
|
p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
|
1999-04-08 02:10:10 +08:00
|
|
|
p->h = h;
|
2000-02-13 20:45:36 +08:00
|
|
|
INIT_DATA(ht, p, pData, nDataSize);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (pDest) {
|
|
|
|
*pDest = p->pData;
|
|
|
|
}
|
|
|
|
|
2000-01-20 22:26:31 +08:00
|
|
|
CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
|
|
|
ht->arBuckets[nIndex] = p;
|
2000-01-20 22:26:31 +08:00
|
|
|
CONNECT_TO_GLOBAL_DLLIST(p, ht);
|
1999-04-08 02:10:10 +08:00
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
2000-01-20 22:26:31 +08:00
|
|
|
|
2003-08-12 14:21:02 +08:00
|
|
|
if ((long)h >= (long)ht->nNextFreeElement) {
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->nNextFreeElement = h + 1;
|
|
|
|
}
|
|
|
|
ht->nNumOfElements++;
|
2000-02-13 21:31:29 +08:00
|
|
|
ZEND_HASH_IF_FULL_DO_RESIZE(ht);
|
1999-04-08 02:10:10 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2000-01-18 02:45:46 +08:00
|
|
|
|
2000-02-13 21:31:29 +08:00
|
|
|
static int zend_hash_do_resize(HashTable *ht)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
Bucket **t;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */
|
|
|
|
t = (Bucket **) perealloc_recoverable(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (t) {
|
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
|
|
|
ht->arBuckets = t;
|
2001-07-11 04:31:42 +08:00
|
|
|
ht->nTableSize = (ht->nTableSize << 1);
|
|
|
|
ht->nTableMask = ht->nTableSize - 1;
|
1999-04-08 02:10:10 +08:00
|
|
|
zend_hash_rehash(ht);
|
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
1999-11-05 05:02:35 +08:00
|
|
|
ZEND_API int zend_hash_rehash(HashTable *ht)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
Bucket *p;
|
|
|
|
uint nIndex;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
|
1999-04-08 02:10:10 +08:00
|
|
|
p = ht->pListHead;
|
|
|
|
while (p != NULL) {
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = p->h & ht->nTableMask;
|
2000-01-20 22:26:31 +08:00
|
|
|
CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->arBuckets[nIndex] = p;
|
|
|
|
p = p->pListNext;
|
|
|
|
}
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2007-09-28 02:00:48 +08:00
|
|
|
ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
uint nIndex;
|
2000-01-20 03:47:16 +08:00
|
|
|
Bucket *p;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
if (flag == HASH_DEL_KEY) {
|
2001-07-11 04:31:42 +08:00
|
|
|
h = zend_inline_hash_func(arKey, nKeyLength);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
2006-02-01 09:01:05 +08:00
|
|
|
if ((p->h == h)
|
|
|
|
&& (p->nKeyLength == nKeyLength)
|
|
|
|
&& ((p->nKeyLength == 0) /* Numeric index (short circuits the memcmp() check) */
|
|
|
|
|| !memcmp(p->arKey, arKey, nKeyLength))) { /* String index */
|
1999-04-08 02:10:10 +08:00
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
2000-01-20 01:31:33 +08:00
|
|
|
if (p == ht->arBuckets[nIndex]) {
|
1999-04-08 02:10:10 +08:00
|
|
|
ht->arBuckets[nIndex] = p->pNext;
|
2000-01-20 01:31:33 +08:00
|
|
|
} else {
|
2000-01-20 03:47:16 +08:00
|
|
|
p->pLast->pNext = p->pNext;
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
2000-01-20 03:44:32 +08:00
|
|
|
if (p->pNext) {
|
|
|
|
p->pNext->pLast = p->pLast;
|
|
|
|
}
|
1999-04-08 02:10:10 +08:00
|
|
|
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;
|
|
|
|
}
|
2000-01-20 02:12:05 +08:00
|
|
|
if (ht->pDestructor) {
|
|
|
|
ht->pDestructor(p->pData);
|
|
|
|
}
|
2006-04-07 18:06:21 +08:00
|
|
|
if (p->pData != &p->pDataPtr) {
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(p->pData, ht->persistent);
|
2000-01-20 02:14:20 +08:00
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(p, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
ht->nNumOfElements--;
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZEND_API void zend_hash_destroy(HashTable *ht)
|
|
|
|
{
|
|
|
|
Bucket *p, *q;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2000-01-18 03:17:58 +08:00
|
|
|
SET_INCONSISTENT(HT_IS_DESTROYING);
|
2000-01-15 21:40:17 +08:00
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
p = ht->pListHead;
|
|
|
|
while (p != NULL) {
|
|
|
|
q = p;
|
|
|
|
p = p->pListNext;
|
2000-01-18 02:45:46 +08:00
|
|
|
if (ht->pDestructor) {
|
|
|
|
ht->pDestructor(q->pData);
|
|
|
|
}
|
2006-04-07 18:06:21 +08:00
|
|
|
if (q->pData != &q->pDataPtr) {
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(q->pData, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(q, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(ht->arBuckets, ht->persistent);
|
2000-01-15 21:40:17 +08:00
|
|
|
|
2000-01-18 03:17:58 +08:00
|
|
|
SET_INCONSISTENT(HT_DESTROYED);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZEND_API void zend_hash_clean(HashTable *ht)
|
|
|
|
{
|
|
|
|
Bucket *p, *q;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2000-01-18 03:17:58 +08:00
|
|
|
SET_INCONSISTENT(HT_CLEANING);
|
2000-01-15 21:40:17 +08:00
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
p = ht->pListHead;
|
|
|
|
while (p != NULL) {
|
|
|
|
q = p;
|
|
|
|
p = p->pListNext;
|
2000-01-18 02:45:46 +08:00
|
|
|
if (ht->pDestructor) {
|
|
|
|
ht->pDestructor(q->pData);
|
|
|
|
}
|
2006-04-07 18:06:21 +08:00
|
|
|
if (q->pData != &q->pDataPtr) {
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(q->pData, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(q, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
|
|
|
|
ht->pListHead = NULL;
|
|
|
|
ht->pListTail = NULL;
|
|
|
|
ht->nNumOfElements = 0;
|
|
|
|
ht->nNextFreeElement = 0;
|
|
|
|
ht->pInternalPointer = NULL;
|
2000-01-15 21:40:17 +08:00
|
|
|
|
2000-01-18 03:17:58 +08:00
|
|
|
SET_INCONSISTENT(HT_OK);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
2000-01-17 04:59:03 +08:00
|
|
|
/* 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;
|
|
|
|
|
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
2000-01-20 03:44:32 +08:00
|
|
|
if (p->pLast) {
|
|
|
|
p->pLast->pNext = p->pNext;
|
|
|
|
} else {
|
|
|
|
uint nIndex;
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = p->h & ht->nTableMask;
|
2000-01-18 02:09:03 +08:00
|
|
|
ht->arBuckets[nIndex] = p->pNext;
|
|
|
|
}
|
2000-01-20 03:44:32 +08:00
|
|
|
if (p->pNext) {
|
|
|
|
p->pNext->pLast = p->pLast;
|
|
|
|
} else {
|
|
|
|
/* Nothing to do as this list doesn't have a tail */
|
|
|
|
}
|
2000-01-18 02:09:03 +08:00
|
|
|
|
2000-01-17 04:59:03 +08:00
|
|
|
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--;
|
2006-08-24 17:42:35 +08:00
|
|
|
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);
|
2000-01-17 04:59:03 +08:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
|
|
|
|
{
|
2000-01-20 03:44:32 +08:00
|
|
|
Bucket *p;
|
2000-01-17 04:59:03 +08:00
|
|
|
|
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
|
|
|
p = ht->pListHead;
|
|
|
|
while (p != NULL) {
|
2000-01-20 13:27:57 +08:00
|
|
|
p = zend_hash_apply_deleter(ht, p);
|
2000-01-17 04:59:03 +08:00
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(ht->arBuckets, ht->persistent);
|
2000-01-17 04:59:03 +08:00
|
|
|
|
2000-01-18 03:17:58 +08:00
|
|
|
SET_INCONSISTENT(HT_DESTROYED);
|
2000-01-17 04:59:03 +08:00
|
|
|
}
|
|
|
|
|
2002-04-20 00:53:36 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
pefree(ht->arBuckets, ht->persistent);
|
|
|
|
|
|
|
|
SET_INCONSISTENT(HT_DESTROYED);
|
|
|
|
}
|
|
|
|
|
2006-11-04 03:02:16 +08:00
|
|
|
/* This is used to recurse elements and selectively delete certain entries
|
|
|
|
* from a hashtable. apply_func() receives the data and decides if the entry
|
|
|
|
* should be deleted or recursion should be stopped. The following three
|
|
|
|
* return codes are possible:
|
|
|
|
* ZEND_HASH_APPLY_KEEP - continue
|
|
|
|
* ZEND_HASH_APPLY_STOP - stop iteration
|
|
|
|
* ZEND_HASH_APPLY_REMOVE - delete the element, combineable with the former
|
1999-04-08 02:10:10 +08:00
|
|
|
*/
|
2000-01-17 04:59:03 +08:00
|
|
|
|
2001-07-31 12:53:54 +08:00
|
|
|
ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-01-20 13:27:57 +08:00
|
|
|
Bucket *p;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-20 13:27:57 +08:00
|
|
|
IS_CONSISTENT(ht);
|
2000-01-15 21:40:17 +08:00
|
|
|
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_PROTECT_RECURSION(ht);
|
2000-01-20 13:27:57 +08:00
|
|
|
p = ht->pListHead;
|
|
|
|
while (p != NULL) {
|
2006-11-04 03:02:16 +08:00
|
|
|
int result = apply_func(p->pData TSRMLS_CC);
|
|
|
|
|
|
|
|
if (result & ZEND_HASH_APPLY_REMOVE) {
|
2000-01-20 13:27:57 +08:00
|
|
|
p = zend_hash_apply_deleter(ht, p);
|
|
|
|
} else {
|
2000-01-20 03:44:32 +08:00
|
|
|
p = p->pListNext;
|
|
|
|
}
|
2006-11-04 03:02:16 +08:00
|
|
|
if (result & ZEND_HASH_APPLY_STOP) {
|
|
|
|
break;
|
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
}
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-31 12:53:54 +08:00
|
|
|
ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument TSRMLS_DC)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-01-20 13:27:57 +08:00
|
|
|
Bucket *p;
|
|
|
|
|
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_PROTECT_RECURSION(ht);
|
2000-01-20 13:27:57 +08:00
|
|
|
p = ht->pListHead;
|
|
|
|
while (p != NULL) {
|
2006-11-04 03:02:16 +08:00
|
|
|
int result = apply_func(p->pData, argument TSRMLS_CC);
|
|
|
|
|
|
|
|
if (result & ZEND_HASH_APPLY_REMOVE) {
|
2000-01-20 13:27:57 +08:00
|
|
|
p = zend_hash_apply_deleter(ht, p);
|
|
|
|
} else {
|
|
|
|
p = p->pListNext;
|
|
|
|
}
|
2006-11-04 03:02:16 +08:00
|
|
|
if (result & ZEND_HASH_APPLY_STOP) {
|
|
|
|
break;
|
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
}
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-25 03:52:24 +08:00
|
|
|
ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func_args_t apply_func, int num_args, ...)
|
1999-08-26 03:02:13 +08:00
|
|
|
{
|
2000-01-20 03:44:32 +08:00
|
|
|
Bucket *p;
|
1999-08-26 03:02:13 +08:00
|
|
|
va_list args;
|
|
|
|
zend_hash_key hash_key;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_PROTECT_RECURSION(ht);
|
1999-08-26 03:02:13 +08:00
|
|
|
|
|
|
|
p = ht->pListHead;
|
|
|
|
while (p != NULL) {
|
2006-11-04 03:02:16 +08:00
|
|
|
int result;
|
2002-11-15 22:25:44 +08:00
|
|
|
va_start(args, num_args);
|
2000-01-20 03:44:32 +08:00
|
|
|
hash_key.arKey = p->arKey;
|
|
|
|
hash_key.nKeyLength = p->nKeyLength;
|
|
|
|
hash_key.h = p->h;
|
2008-07-25 03:52:24 +08:00
|
|
|
result = apply_func(p->pData TSRMLS_CC, num_args, args, &hash_key);
|
2006-11-04 03:02:16 +08:00
|
|
|
|
|
|
|
if (result & ZEND_HASH_APPLY_REMOVE) {
|
2000-01-20 13:27:57 +08:00
|
|
|
p = zend_hash_apply_deleter(ht, p);
|
|
|
|
} else {
|
|
|
|
p = p->pListNext;
|
1999-08-26 03:02:13 +08:00
|
|
|
}
|
2006-11-04 03:02:16 +08:00
|
|
|
if (result & ZEND_HASH_APPLY_STOP) {
|
|
|
|
break;
|
|
|
|
}
|
2002-11-15 22:25:44 +08:00
|
|
|
va_end(args);
|
1999-08-26 03:02:13 +08:00
|
|
|
}
|
2000-06-17 22:11:57 +08:00
|
|
|
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht);
|
1999-08-26 03:02:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-08-02 15:00:43 +08:00
|
|
|
ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
|
|
|
|
{
|
|
|
|
Bucket *p, *q;
|
|
|
|
|
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
|
|
|
HASH_PROTECT_RECURSION(ht);
|
|
|
|
p = ht->pListTail;
|
|
|
|
while (p != NULL) {
|
|
|
|
int result = apply_func(p->pData TSRMLS_CC);
|
|
|
|
|
|
|
|
q = p;
|
|
|
|
p = p->pListLast;
|
|
|
|
if (result & ZEND_HASH_APPLY_REMOVE) {
|
2006-04-07 18:06:21 +08:00
|
|
|
zend_hash_apply_deleter(ht, q);
|
2001-08-02 15:00:43 +08:00
|
|
|
}
|
|
|
|
if (result & ZEND_HASH_APPLY_STOP) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HASH_UNPROTECT_RECURSION(ht);
|
|
|
|
}
|
|
|
|
|
1999-08-26 03:02:13 +08:00
|
|
|
|
2000-02-05 23:11:24 +08:00
|
|
|
ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
Bucket *p;
|
2000-09-28 23:17:50 +08:00
|
|
|
void *new_entry;
|
2007-07-25 02:28:39 +08:00
|
|
|
zend_bool setTargetPointer;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(source);
|
|
|
|
IS_CONSISTENT(target);
|
|
|
|
|
2007-07-25 02:28:39 +08:00
|
|
|
setTargetPointer = !target->pInternalPointer;
|
2000-01-20 03:44:32 +08:00
|
|
|
p = source->pListHead;
|
1999-04-08 02:10:10 +08:00
|
|
|
while (p) {
|
2007-07-25 02:28:39 +08:00
|
|
|
if (setTargetPointer && source->pInternalPointer == p) {
|
|
|
|
target->pInternalPointer = NULL;
|
|
|
|
}
|
1999-04-08 02:10:10 +08:00
|
|
|
if (p->nKeyLength) {
|
2006-11-09 00:02:03 +08:00
|
|
|
zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &new_entry);
|
1999-04-08 02:10:10 +08:00
|
|
|
} else {
|
2000-09-28 23:17:50 +08:00
|
|
|
zend_hash_index_update(target, p->h, p->pData, size, &new_entry);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
2005-04-25 16:21:15 +08:00
|
|
|
if (pCopyConstructor) {
|
|
|
|
pCopyConstructor(new_entry);
|
|
|
|
}
|
1999-04-08 02:10:10 +08:00
|
|
|
p = p->pListNext;
|
|
|
|
}
|
2007-07-25 02:28:39 +08:00
|
|
|
if (!target->pInternalPointer) {
|
|
|
|
target->pInternalPointer = target->pListHead;
|
|
|
|
}
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-19 05:17:26 +08:00
|
|
|
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)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
Bucket *p;
|
|
|
|
void *t;
|
1999-05-30 02:59:58 +08:00
|
|
|
int mode = (overwrite?HASH_UPDATE:HASH_ADD);
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(source);
|
|
|
|
IS_CONSISTENT(target);
|
|
|
|
|
2005-04-25 16:21:15 +08:00
|
|
|
p = source->pListHead;
|
1999-04-08 02:10:10 +08:00
|
|
|
while (p) {
|
1999-05-15 23:47:24 +08:00
|
|
|
if (p->nKeyLength>0) {
|
2006-11-09 00:02:03 +08:00
|
|
|
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) {
|
1999-04-08 02:10:10 +08:00
|
|
|
pCopyConstructor(t);
|
|
|
|
}
|
|
|
|
} else {
|
2000-09-29 07:21:08 +08:00
|
|
|
if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
|
1999-04-08 02:10:10 +08:00
|
|
|
pCopyConstructor(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pListNext;
|
|
|
|
}
|
|
|
|
target->pInternalPointer = target->pListHead;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-06 08:14:49 +08:00
|
|
|
static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
|
2003-02-04 20:12:34 +08:00
|
|
|
{
|
|
|
|
zend_hash_key hash_key;
|
|
|
|
|
|
|
|
hash_key.arKey = p->arKey;
|
|
|
|
hash_key.nKeyLength = p->nKeyLength;
|
|
|
|
hash_key.h = p->h;
|
|
|
|
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)
|
2000-10-12 00:22:40 +08:00
|
|
|
{
|
|
|
|
Bucket *p;
|
|
|
|
void *t;
|
|
|
|
|
|
|
|
IS_CONSISTENT(source);
|
|
|
|
IS_CONSISTENT(target);
|
|
|
|
|
2005-04-25 16:21:15 +08:00
|
|
|
p = source->pListHead;
|
2000-10-12 00:22:40 +08:00
|
|
|
while (p) {
|
2003-02-04 20:12:34 +08:00
|
|
|
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) {
|
|
|
|
pCopyConstructor(t);
|
2000-10-12 00:22:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pListNext;
|
|
|
|
}
|
|
|
|
target->pInternalPointer = target->pListHead;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-28 02:00:48 +08:00
|
|
|
ZEND_API ulong zend_get_hash_value(const char *arKey, uint nKeyLength)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2001-07-11 04:31:42 +08:00
|
|
|
return zend_inline_hash_func(arKey, nKeyLength);
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns SUCCESS if found and FAILURE if not. The pointer to the
|
|
|
|
* 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
|
|
|
|
*/
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
ulong h;
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
h = zend_inline_hash_func(arKey, nKeyLength);
|
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
|
|
|
|
if (!memcmp(p->arKey, arKey, nKeyLength)) {
|
|
|
|
*pData = p->pData;
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_quick_find(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void **pData)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2003-02-04 20:12:34 +08:00
|
|
|
if (nKeyLength==0) {
|
|
|
|
return zend_hash_index_find(ht, h, pData);
|
|
|
|
}
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
|
|
|
|
if (!memcmp(p->arKey, arKey, nKeyLength)) {
|
|
|
|
*pData = p->pData;
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
ulong h;
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
h = zend_inline_hash_func(arKey, nKeyLength);
|
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
|
|
|
|
if (!memcmp(p->arKey, arKey, nKeyLength)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h)
|
2003-02-05 21:19:59 +08:00
|
|
|
{
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
|
|
|
if (nKeyLength==0) {
|
|
|
|
return zend_hash_index_exists(ht, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
|
|
|
nIndex = h & ht->nTableMask;
|
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
|
|
|
|
if (!memcmp(p->arKey, arKey, nKeyLength)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == 0)) {
|
|
|
|
*pData = p->pData;
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
uint nIndex;
|
|
|
|
Bucket *p;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2001-07-11 04:31:42 +08:00
|
|
|
nIndex = h & ht->nTableMask;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
p = ht->arBuckets[nIndex];
|
|
|
|
while (p != NULL) {
|
|
|
|
if ((p->h == h) && (p->nKeyLength == 0)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_num_elements(const HashTable *ht)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
return ht->nNumOfElements;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
|
2007-01-10 23:58:08 +08:00
|
|
|
{
|
|
|
|
ptr->pos = ht->pInternalPointer;
|
|
|
|
if (ht->pInternalPointer) {
|
|
|
|
ptr->h = ht->pInternalPointer->h;
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
ptr->h = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
|
|
|
|
{
|
2007-02-21 22:11:00 +08:00
|
|
|
if (ptr->pos == NULL) {
|
|
|
|
ht->pInternalPointer = NULL;
|
|
|
|
} else if (ht->pInternalPointer != ptr->pos) {
|
2007-01-10 23:58:08 +08:00
|
|
|
Bucket *p;
|
|
|
|
|
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
p = ht->arBuckets[ptr->h & ht->nTableMask];
|
|
|
|
while (p != NULL) {
|
|
|
|
if (p == ptr->pos) {
|
|
|
|
ht->pInternalPointer = p;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
if (pos)
|
|
|
|
*pos = ht->pListHead;
|
|
|
|
else
|
|
|
|
ht->pInternalPointer = ht->pListHead;
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function will be extremely optimized by remembering
|
|
|
|
* the end of the list
|
|
|
|
*/
|
2000-03-13 23:25:18 +08:00
|
|
|
ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
if (pos)
|
|
|
|
*pos = ht->pListTail;
|
|
|
|
else
|
|
|
|
ht->pInternalPointer = ht->pListTail;
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-03-16 00:25:59 +08:00
|
|
|
ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-03-16 00:25:59 +08:00
|
|
|
HashPosition *current = pos ? pos : &ht->pInternalPointer;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2000-03-16 00:25:59 +08:00
|
|
|
if (*current) {
|
|
|
|
*current = (*current)->pListNext;
|
|
|
|
return SUCCESS;
|
|
|
|
} else
|
|
|
|
return FAILURE;
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
2000-03-16 00:25:59 +08:00
|
|
|
ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-03-16 00:25:59 +08:00
|
|
|
HashPosition *current = pos ? pos : &ht->pInternalPointer;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2000-03-16 00:25:59 +08:00
|
|
|
if (*current) {
|
|
|
|
*current = (*current)->pListLast;
|
|
|
|
return SUCCESS;
|
|
|
|
} else
|
|
|
|
return FAILURE;
|
1999-04-08 02:10:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function should be made binary safe */
|
2008-08-13 01:20:25 +08:00
|
|
|
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)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-03-13 23:25:18 +08:00
|
|
|
Bucket *p;
|
2005-04-25 16:21:15 +08:00
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
p = pos ? (*pos) : ht->pInternalPointer;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
if (p) {
|
|
|
|
if (p->nKeyLength) {
|
2000-12-22 20:49:51 +08:00
|
|
|
if (duplicate) {
|
2005-04-25 16:21:15 +08:00
|
|
|
*str_index = estrndup(p->arKey, p->nKeyLength - 1);
|
2000-12-22 20:49:51 +08:00
|
|
|
} else {
|
|
|
|
*str_index = p->arKey;
|
2000-12-22 20:08:04 +08:00
|
|
|
}
|
2000-05-03 01:52:31 +08:00
|
|
|
if (str_length) {
|
|
|
|
*str_length = p->nKeyLength;
|
|
|
|
}
|
1999-04-08 02:10:10 +08:00
|
|
|
return HASH_KEY_IS_STRING;
|
|
|
|
} else {
|
|
|
|
*num_index = p->h;
|
|
|
|
return HASH_KEY_IS_LONG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return HASH_KEY_NON_EXISTANT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
|
1999-06-08 06:49:33 +08:00
|
|
|
{
|
2000-03-13 23:25:18 +08:00
|
|
|
Bucket *p;
|
2005-04-25 16:21:15 +08:00
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
p = pos ? (*pos) : ht->pInternalPointer;
|
1999-06-08 06:49:33 +08:00
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-06-08 06:49:33 +08:00
|
|
|
if (p) {
|
|
|
|
if (p->nKeyLength) {
|
|
|
|
return HASH_KEY_IS_STRING;
|
|
|
|
} else {
|
|
|
|
return HASH_KEY_IS_LONG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return HASH_KEY_NON_EXISTANT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-03-13 23:25:18 +08:00
|
|
|
Bucket *p;
|
2005-04-25 16:21:15 +08:00
|
|
|
|
2000-03-13 23:25:18 +08:00
|
|
|
p = pos ? (*pos) : ht->pInternalPointer;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
if (p) {
|
|
|
|
*pData = p->pData;
|
|
|
|
return SUCCESS;
|
|
|
|
} else {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-07 23:16:57 +08:00
|
|
|
/* This function changes key of currevt element without changing elements'
|
|
|
|
* order. If element with target key already exists, it will be deleted first.
|
|
|
|
*/
|
2008-08-01 22:22:03 +08:00
|
|
|
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)
|
2005-07-07 23:16:57 +08:00
|
|
|
{
|
|
|
|
Bucket *p;
|
|
|
|
|
|
|
|
p = pos ? (*pos) : ht->pInternalPointer;
|
|
|
|
|
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
|
|
|
if (p) {
|
|
|
|
if (key_type == HASH_KEY_IS_LONG) {
|
|
|
|
str_length = 0;
|
|
|
|
if (!p->nKeyLength && p->h == num_index) {
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
2008-08-01 22:22:03 +08:00
|
|
|
|
|
|
|
if (mode != HASH_UPDATE_KEY_ANYWAY) {
|
|
|
|
Bucket *q = ht->arBuckets[num_index & ht->nTableMask];
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
while (q != NULL) {
|
|
|
|
if (q == p) {
|
|
|
|
found = 1;
|
|
|
|
} else if (!q->nKeyLength && q->h == num_index) {
|
2008-08-07 19:51:54 +08:00
|
|
|
if (found) {
|
|
|
|
if (mode & HASH_UPDATE_KEY_IF_BEFORE) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (p->nKeyLength) {
|
2008-08-07 19:45:35 +08:00
|
|
|
zend_hash_del(ht, p->arKey, p->nKeyLength);
|
|
|
|
} else {
|
|
|
|
zend_hash_index_del(ht, p->h);
|
|
|
|
}
|
2008-08-07 19:51:54 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mode & HASH_UPDATE_KEY_IF_AFTER) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (p->nKeyLength) {
|
2008-08-07 19:45:35 +08:00
|
|
|
zend_hash_del(ht, p->arKey, p->nKeyLength);
|
|
|
|
} else {
|
|
|
|
zend_hash_index_del(ht, p->h);
|
|
|
|
}
|
2008-08-07 19:51:54 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
2008-08-01 22:22:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
q = q->pNext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-07 23:16:57 +08:00
|
|
|
zend_hash_index_del(ht, num_index);
|
|
|
|
} else if (key_type == HASH_KEY_IS_STRING) {
|
|
|
|
if (p->nKeyLength == str_length &&
|
|
|
|
memcmp(p->arKey, str_index, str_length) == 0) {
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
2008-08-01 22:22:03 +08:00
|
|
|
|
|
|
|
if (mode != HASH_UPDATE_KEY_ANYWAY) {
|
|
|
|
ulong h = zend_inline_hash_func(str_index, str_length);
|
|
|
|
Bucket *q = ht->arBuckets[h & ht->nTableMask];
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
while (q != NULL) {
|
|
|
|
if (q == p) {
|
|
|
|
found = 1;
|
|
|
|
} else if (q->h == h && q->nKeyLength == str_length &&
|
|
|
|
memcmp(q->arKey, str_index, str_length) == 0) {
|
|
|
|
if (found) {
|
2008-08-07 19:51:54 +08:00
|
|
|
if (mode & HASH_UPDATE_KEY_IF_BEFORE) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (p->nKeyLength) {
|
2008-08-07 19:45:35 +08:00
|
|
|
zend_hash_del(ht, p->arKey, p->nKeyLength);
|
|
|
|
} else {
|
|
|
|
zend_hash_index_del(ht, p->h);
|
|
|
|
}
|
2008-08-07 19:51:54 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mode & HASH_UPDATE_KEY_IF_AFTER) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (p->nKeyLength) {
|
2008-08-07 19:45:35 +08:00
|
|
|
zend_hash_del(ht, p->arKey, p->nKeyLength);
|
|
|
|
} else {
|
|
|
|
zend_hash_index_del(ht, p->h);
|
|
|
|
}
|
2008-08-07 19:51:54 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
2008-08-01 22:22:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
q = q->pNext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-07 23:16:57 +08:00
|
|
|
zend_hash_del(ht, str_index, str_length);
|
|
|
|
} else {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (p->nKeyLength != str_length) {
|
|
|
|
Bucket *q = (Bucket *) pemalloc(sizeof(Bucket) - 1 + 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;
|
|
|
|
q->pListNext = p->pListNext;
|
|
|
|
q->pListLast = p->pListLast;
|
|
|
|
if (q->pListNext) {
|
|
|
|
p->pListNext->pListLast = q;
|
|
|
|
} else {
|
|
|
|
ht->pListTail = q;
|
|
|
|
}
|
|
|
|
if (q->pListLast) {
|
|
|
|
p->pListLast->pListNext = q;
|
|
|
|
} else {
|
|
|
|
ht->pListHead = q;
|
|
|
|
}
|
|
|
|
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 {
|
|
|
|
memcpy(p->arKey, str_index, str_length);
|
|
|
|
p->h = zend_inline_hash_func(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();
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
} else {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
}
|
1999-04-08 02:10:10 +08:00
|
|
|
|
1999-10-16 04:37:53 +08:00
|
|
|
ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
|
2001-09-19 18:25:04 +08:00
|
|
|
compare_func_t compar, int renumber TSRMLS_DC)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
Bucket **arTmp;
|
|
|
|
Bucket *p;
|
|
|
|
int i, j;
|
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
2002-08-18 00:07:26 +08:00
|
|
|
if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
|
1999-04-08 02:10:10 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
2000-01-20 13:27:57 +08:00
|
|
|
arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
if (!arTmp) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
p = ht->pListHead;
|
|
|
|
i = 0;
|
|
|
|
while (p) {
|
|
|
|
arTmp[i] = p;
|
|
|
|
p = p->pListNext;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2001-09-19 18:25:04 +08:00
|
|
|
(*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
|
1999-04-08 02:10:10 +08:00
|
|
|
|
|
|
|
HANDLE_BLOCK_INTERRUPTIONS();
|
|
|
|
ht->pListHead = arTmp[0];
|
|
|
|
ht->pListTail = NULL;
|
|
|
|
ht->pInternalPointer = ht->pListHead;
|
|
|
|
|
2004-07-30 03:18:46 +08:00
|
|
|
arTmp[0]->pListLast = NULL;
|
|
|
|
if (i > 1) {
|
2005-04-25 16:21:15 +08:00
|
|
|
arTmp[0]->pListNext = arTmp[1];
|
|
|
|
for (j = 1; j < i-1; j++) {
|
|
|
|
arTmp[j]->pListLast = arTmp[j-1];
|
|
|
|
arTmp[j]->pListNext = arTmp[j+1];
|
|
|
|
}
|
2005-06-17 18:50:15 +08:00
|
|
|
arTmp[j]->pListLast = arTmp[j-1];
|
2005-04-25 16:21:15 +08:00
|
|
|
arTmp[j]->pListNext = NULL;
|
|
|
|
} else {
|
|
|
|
arTmp[0]->pListNext = NULL;
|
|
|
|
}
|
|
|
|
ht->pListTail = arTmp[i-1];
|
2004-07-30 03:18:46 +08:00
|
|
|
|
2000-01-20 13:27:57 +08:00
|
|
|
pefree(arTmp, ht->persistent);
|
1999-04-08 02:10:10 +08:00
|
|
|
HANDLE_UNBLOCK_INTERRUPTIONS();
|
|
|
|
|
|
|
|
if (renumber) {
|
|
|
|
p = ht->pListHead;
|
|
|
|
i=0;
|
|
|
|
while (p != NULL) {
|
|
|
|
p->nKeyLength = 0;
|
2001-10-04 22:18:52 +08:00
|
|
|
p->h = i++;
|
1999-04-08 02:10:10 +08:00
|
|
|
p = p->pListNext;
|
|
|
|
}
|
|
|
|
ht->nNextFreeElement = i;
|
|
|
|
zend_hash_rehash(ht);
|
|
|
|
}
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-30 12:54:16 +08:00
|
|
|
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
|
2000-06-03 18:34:19 +08:00
|
|
|
{
|
2006-02-07 04:37:11 +08:00
|
|
|
Bucket *p1, *p2 = NULL;
|
2000-06-03 18:34:19 +08:00
|
|
|
int result;
|
2000-06-05 05:59:49 +08:00
|
|
|
void *pData2;
|
2000-06-03 18:34:19 +08:00
|
|
|
|
|
|
|
IS_CONSISTENT(ht1);
|
|
|
|
IS_CONSISTENT(ht2);
|
|
|
|
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_PROTECT_RECURSION(ht1);
|
|
|
|
HASH_PROTECT_RECURSION(ht2);
|
|
|
|
|
2000-06-03 18:34:19 +08:00
|
|
|
result = ht1->nNumOfElements - ht2->nNumOfElements;
|
|
|
|
if (result!=0) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-03 18:34:19 +08:00
|
|
|
return result;
|
|
|
|
}
|
2000-06-05 05:59:49 +08:00
|
|
|
|
2000-06-03 18:34:19 +08:00
|
|
|
p1 = ht1->pListHead;
|
2000-06-05 05:59:49 +08:00
|
|
|
if (ordered) {
|
|
|
|
p2 = ht2->pListHead;
|
|
|
|
}
|
2000-06-03 18:34:19 +08:00
|
|
|
|
2000-06-05 05:59:49 +08:00
|
|
|
while (p1) {
|
|
|
|
if (ordered && !p2) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-05 05:59:49 +08:00
|
|
|
return 1; /* That's not supposed to happen */
|
|
|
|
}
|
|
|
|
if (ordered) {
|
|
|
|
if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */
|
|
|
|
result = p1->h - p2->h;
|
|
|
|
if (result!=0) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-05 05:59:49 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
} else { /* string indices */
|
|
|
|
result = p1->nKeyLength - p2->nKeyLength;
|
|
|
|
if (result!=0) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-05 05:59:49 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
result = memcmp(p1->arKey, p2->arKey, p1->nKeyLength);
|
|
|
|
if (result!=0) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-05 05:59:49 +08:00
|
|
|
return result;
|
|
|
|
}
|
2000-06-03 18:34:19 +08:00
|
|
|
}
|
2000-06-05 05:59:49 +08:00
|
|
|
pData2 = p2->pData;
|
|
|
|
} else {
|
|
|
|
if (p1->nKeyLength==0) { /* numeric index */
|
|
|
|
if (zend_hash_index_find(ht2, p1->h, &pData2)==FAILURE) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-05 05:59:49 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else { /* string index */
|
|
|
|
if (zend_hash_find(ht2, p1->arKey, p1->nKeyLength, &pData2)==FAILURE) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-05 05:59:49 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2000-06-03 18:34:19 +08:00
|
|
|
}
|
|
|
|
}
|
2001-09-19 18:25:04 +08:00
|
|
|
result = compar(p1->pData, pData2 TSRMLS_CC);
|
2000-06-03 18:34:19 +08:00
|
|
|
if (result!=0) {
|
2001-07-16 02:57:43 +08:00
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-03 18:34:19 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
p1 = p1->pListNext;
|
2000-06-05 05:59:49 +08:00
|
|
|
if (ordered) {
|
|
|
|
p2 = p2->pListNext;
|
|
|
|
}
|
2000-06-03 18:34:19 +08:00
|
|
|
}
|
2001-07-16 02:57:43 +08:00
|
|
|
|
|
|
|
HASH_UNPROTECT_RECURSION(ht1);
|
|
|
|
HASH_UNPROTECT_RECURSION(ht2);
|
2000-06-03 18:34:19 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2001-08-11 23:56:40 +08:00
|
|
|
Bucket *p, *res;
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
if (ht->nNumOfElements == 0 ) {
|
|
|
|
*pData=NULL;
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = p = ht->pListHead;
|
|
|
|
while ((p = p->pListNext)) {
|
|
|
|
if (flag) {
|
2001-09-19 18:25:04 +08:00
|
|
|
if (compar(&res, &p TSRMLS_CC) < 0) { /* max */
|
1999-04-08 02:10:10 +08:00
|
|
|
res = p;
|
|
|
|
}
|
|
|
|
} else {
|
2001-09-19 18:25:04 +08:00
|
|
|
if (compar(&res, &p TSRMLS_CC) > 0) { /* min */
|
1999-04-08 02:10:10 +08:00
|
|
|
res = p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*pData = res->pData;
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
2000-01-15 21:40:17 +08:00
|
|
|
IS_CONSISTENT(ht);
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
return ht->nNextFreeElement;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2001-05-17 01:22:01 +08:00
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
#if ZEND_DEBUG
|
2008-08-13 01:20:25 +08:00
|
|
|
void zend_hash_display_pListTail(const HashTable *ht)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
Bucket *p;
|
|
|
|
|
|
|
|
p = ht->pListTail;
|
|
|
|
while (p != NULL) {
|
2000-06-03 09:49:49 +08:00
|
|
|
zend_output_debug_string(0, "pListTail has key %s\n", p->arKey);
|
1999-04-08 02:10:10 +08:00
|
|
|
p = p->pListLast;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-13 01:20:25 +08:00
|
|
|
void zend_hash_display(const HashTable *ht)
|
1999-04-08 02:10:10 +08:00
|
|
|
{
|
|
|
|
Bucket *p;
|
|
|
|
uint i;
|
|
|
|
|
|
|
|
for (i = 0; i < ht->nTableSize; i++) {
|
|
|
|
p = ht->arBuckets[i];
|
|
|
|
while (p != NULL) {
|
2003-08-29 15:34:37 +08:00
|
|
|
zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
|
1999-04-08 02:10:10 +08:00
|
|
|
p = p->pNext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p = ht->pListTail;
|
|
|
|
while (p != NULL) {
|
2003-08-29 15:34:37 +08:00
|
|
|
zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
|
1999-04-08 02:10:10 +08:00
|
|
|
p = p->pListLast;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* tab-width: 4
|
|
|
|
* c-basic-offset: 4
|
2003-02-01 09:49:15 +08:00
|
|
|
* indent-tabs-mode: t
|
1999-04-08 02:10:10 +08:00
|
|
|
* End:
|
|
|
|
*/
|