php-src/Zend/zend_hash.c
Zeev Suraski b0bfa458b5 * Fix all hash checks that checked Bucket.arKey for NULL, when it was changed
to char[1], these checks should have been changed to Bucket.nKeyLength==0
* Support runtime declaration of functions.  I ended up changing the grammar
  to catch top level functions vs. nested functions.  The reason is simple -
  if we don't have functions properly declared at compile-time, function calls
  cannot be resolved at compile time, and have to be resolved at runtime, which
  ends up being much much slower (without the optimizer, that is).
  It's no biggy though, the grammar change isn't that bad.
1999-05-15 15:47:24 +00:00

1164 lines
25 KiB
C

/*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998, 1999 Andi Gutmans, Zeev Suraski |
+----------------------------------------------------------------------+
| This source file is subject to the Zend license, that is bundled |
| with this package in the file LICENSE. If you did not receive a |
| copy of the Zend license, please mail us at zend@zend.com so we can |
| send you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
+----------------------------------------------------------------------+
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "zend.h"
#define HANDLE_NUMERIC(key,length,func) { \
register char *tmp=key; \
\
if ((*tmp>='0' && *tmp<='9')) do { /* possibly a numeric index */ \
char *end=tmp+length-1; \
ulong idx; \
\
if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */ \
break; \
} \
while (tmp<end) { \
if (!(*tmp>='0' && *tmp<='9')) { \
break; \
} \
tmp++; \
} \
if (tmp==end && *tmp=='\0') { /* a numeric index */ \
idx = strtol(key,NULL,10); \
if (idx!=LONG_MAX) { \
return func; \
} \
} \
} while(0); \
}
/* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */
static uint PrimeNumbers[] =
{5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793, 2097397, 4194103, 8388857, 16777447, 33554201, 67108961, 134217487, 268435697, 536870683, 1073741621, 2147483399};
static int if_full_do_resize(HashTable *ht);
static int zend_hash_rehash(HashTable *ht);
static uint nNumPrimeNumbers = sizeof(PrimeNumbers) / sizeof(ulong);
ZEND_API ulong hashpjw(char *arKey, uint nKeyLength)
{
ulong h = 0, g;
char *arEnd=arKey+nKeyLength;
while (arKey < arEnd) {
h = (h << 4) + *arKey++;
if ((g = (h & 0xF0000000))) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
return h;
}
ZEND_API int zend_hash_init(HashTable *ht, uint nSize, ulong(*pHashFunction) (char *arKey, uint nKeyLength), void (*pDestructor) (void *pData),int persistent)
{
uint i;
for (i = 0; i < nNumPrimeNumbers; i++) {
if (nSize <= PrimeNumbers[i]) {
nSize = PrimeNumbers[i];
ht->nHashSizeIndex = i;
break;
}
}
if (i == nNumPrimeNumbers) { /* This shouldn't really happen unless the ask for a ridiculous size */
nSize = PrimeNumbers[i - 1];
ht->nHashSizeIndex = i - 1;
}
/* Uses ecalloc() so that Bucket* == NULL */
ht->arBuckets = (Bucket **) pecalloc(nSize, sizeof(Bucket *), persistent);
if (!ht->arBuckets) {
return FAILURE;
}
if (pHashFunction == NULL) {
ht->pHashFunction = hashpjw;
} else {
ht->pHashFunction = pHashFunction;
}
ht->pDestructor = pDestructor;
ht->nTableSize = nSize;
ht->pListHead = NULL;
ht->pListTail = NULL;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->pInternalPointer = NULL;
ht->persistent = persistent;
return SUCCESS;
}
ZEND_API int zend_hash_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag)
{
ulong h;
uint nIndex;
Bucket *p;
if (nKeyLength <= 0) {
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
#endif
return FAILURE;
}
HANDLE_NUMERIC(arKey,nKeyLength,zend_hash_index_update_or_next_insert(ht,idx,pData,nDataSize,pDest,flag));
h = ht->pHashFunction(arKey, nKeyLength);
nIndex = h % ht->nTableSize;
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);
}
if (flag & HASH_ADD_PTR) {
if (!p->pDataPtr) {
efree(p->pData);
}
p->pDataPtr = pData;
p->pData = &p->pDataPtr;
} else {
if (p->pDataPtr) {
p->pData = (void *) emalloc(nDataSize);
p->pDataPtr=NULL;
}
memcpy(p->pData, pData, nDataSize);
}
if (pDest) {
*pDest = p->pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
}
p = p->pNext;
}
p = (Bucket *) pemalloc(sizeof(Bucket)-1+nKeyLength,ht->persistent);
if (!p) {
return FAILURE;
}
memcpy(p->arKey, arKey, nKeyLength);
p->nKeyLength = nKeyLength;
if (flag & HASH_ADD_PTR) {
p->pDataPtr = pData;
p->pData = &p->pDataPtr;
} else {
p->pData = (void *) pemalloc(nDataSize,ht->persistent);
if (!p->pData) {
pefree(p,ht->persistent);
pefree(p->arKey,ht->persistent);
return FAILURE;
}
memcpy(p->pData, pData, nDataSize);
p->pDataPtr=NULL;
}
p->h = h;
p->bIsPointer = 0;
p->pNext = ht->arBuckets[nIndex];
if (pDest) {
*pDest = p->pData;
}
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pInternalPointer == NULL) {
ht->pInternalPointer = p;
}
ht->arBuckets[nIndex] = p;
/* Setup the double linked list */
p->pListLast = ht->pListTail;
ht->pListTail = p;
p->pListNext = NULL;
if (p->pListLast != NULL) {
p->pListLast->pListNext = p;
}
if (!ht->pListHead) {
ht->pListHead = p;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements++;
if_full_do_resize(ht); /* If the Hash table is full, resize it */
return SUCCESS;
}
ZEND_API int zend_hash_quick_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag)
{
uint nIndex;
Bucket *p;
if (nKeyLength <= 0) {
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
#endif
return FAILURE;
}
nIndex = h % ht->nTableSize;
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);
}
if (flag & HASH_ADD_PTR) {
if (!p->pDataPtr) {
efree(p->pData);
}
p->pDataPtr = pData;
p->pData = &p->pDataPtr;
} else {
if (p->pDataPtr) {
p->pData = (void *) emalloc(nDataSize);
p->pDataPtr=NULL;
}
memcpy(p->pData, pData, nDataSize);
}
if (pDest) {
*pDest = p->pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
}
p = p->pNext;
}
p = (Bucket *) pemalloc(sizeof(Bucket)-1+nKeyLength,ht->persistent);
if (!p) {
return FAILURE;
}
memcpy(p->arKey, arKey, nKeyLength);
p->nKeyLength = nKeyLength;
if (flag & HASH_ADD_PTR) {
p->pDataPtr = pData;
p->pData = &p->pDataPtr;
} else {
p->pData = (void *) pemalloc(nDataSize,ht->persistent);
if (!p->pData) {
pefree(p,ht->persistent);
pefree(p->arKey,ht->persistent);
return FAILURE;
}
memcpy(p->pData, pData, nDataSize);
p->pDataPtr=NULL;
}
p->h = h;
p->bIsPointer = 0;
p->pNext = ht->arBuckets[nIndex];
if (pDest) {
*pDest = p->pData;
}
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pInternalPointer == NULL) {
ht->pInternalPointer = p;
}
ht->arBuckets[nIndex] = p;
/* Setup the double linked list */
p->pListLast = ht->pListTail;
ht->pListTail = p;
p->pListNext = NULL;
if (p->pListLast != NULL) {
p->pListLast->pListNext = p;
}
if (!ht->pListHead) {
ht->pListHead = p;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements++;
if_full_do_resize(ht); /* If the Hash table is full, resize it */
return SUCCESS;
}
ZEND_API int zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag)
{
uint nIndex;
Bucket *p;
if (flag & HASH_NEXT_INSERT) {
h = ht->nNextFreeElement;
}
nIndex = h % ht->nTableSize;
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);
}
if (flag & HASH_ADD_PTR) {
if (!p->pDataPtr) {
efree(p->pData);
}
p->pDataPtr = pData;
p->pData = &p->pDataPtr;
} else {
if (p->pDataPtr) {
p->pData = (void *) emalloc(nDataSize);
p->pDataPtr=NULL;
}
memcpy(p->pData, pData, nDataSize);
}
HANDLE_UNBLOCK_INTERRUPTIONS();
if (h >= ht->nNextFreeElement) {
ht->nNextFreeElement = h + 1;
}
if (pDest) {
*pDest = p->pData;
}
return SUCCESS;
}
p = p->pNext;
}
p = (Bucket *) pemalloc(sizeof(Bucket)-1,ht->persistent);
if (!p) {
return FAILURE;
}
p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
p->h = h;
if (flag & HASH_ADD_PTR) {
p->pDataPtr = pData;
p->pData = &p->pDataPtr;
} else {
p->pData = (void *) pemalloc(nDataSize,ht->persistent);
if (!p->pData) {
pefree(p,ht->persistent);
return FAILURE;
}
memcpy(p->pData, pData, nDataSize);
p->pDataPtr=NULL;
}
p->bIsPointer = 0;
if (pDest) {
*pDest = p->pData;
}
p->pNext = ht->arBuckets[nIndex];
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pInternalPointer == NULL) {
ht->pInternalPointer = p;
}
ht->arBuckets[nIndex] = p;
/* Setup the double linked list */
p->pListLast = ht->pListTail;
ht->pListTail = p;
p->pListNext = NULL;
if (p->pListLast != NULL) {
p->pListLast->pListNext = p;
}
if (!ht->pListHead) {
ht->pListHead = p;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
if (h >= ht->nNextFreeElement) {
ht->nNextFreeElement = h + 1;
}
ht->nNumOfElements++;
if_full_do_resize(ht);
return SUCCESS;
}
ZEND_API int zend_hash_pointer_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData)
{
ulong h;
uint nIndex;
Bucket *p;
if (nKeyLength <= 0) {
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
#endif
return FAILURE;
}
HANDLE_NUMERIC(arKey,nKeyLength,zend_hash_pointer_index_update_or_next_insert(ht,idx,pData,HASH_UPDATE));
h = ht->pHashFunction(arKey, nKeyLength);
nIndex = h % ht->nTableSize;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
if (!memcmp(p->arKey, arKey, nKeyLength)) {
#if ZEND_DEBUG
if (p->pData == pData) {
ZEND_PUTS("Fatal error in zend_hash_pointer_update: p->pData == pData\n");
return FAILURE;
}
#endif
HANDLE_BLOCK_INTERRUPTIONS();
if (!p->bIsPointer && ht->pDestructor) {
ht->pDestructor(p->pData);
pefree(p->pData,ht->persistent);
}
p->pData = pData;
p->bIsPointer = 1;
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
}
p = p->pNext;
}
p = (Bucket *) pemalloc(sizeof(Bucket)-1+nKeyLength,ht->persistent);
if (!p) {
return FAILURE;
}
p->nKeyLength = nKeyLength;
p->pData = pData;
p->h = h;
p->bIsPointer = 1;
memcpy(p->arKey, arKey, nKeyLength);
p->pNext = ht->arBuckets[nIndex];
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pInternalPointer == NULL) {
ht->pInternalPointer = p;
}
ht->arBuckets[nIndex] = p;
/* Setup the double linked list */
p->pListLast = ht->pListTail;
ht->pListTail = p;
p->pListNext = NULL;
if (p->pListLast != NULL) {
p->pListLast->pListNext = p;
}
if (!ht->pListHead) {
ht->pListHead = p;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements++;
if_full_do_resize(ht); /* If the Hash table is full, resize it */
return SUCCESS;
}
ZEND_API int zend_hash_pointer_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, int flag)
{
uint nIndex;
Bucket *p;
if (flag & HASH_NEXT_INSERT) {
h = ht->nNextFreeElement;
}
nIndex = h % ht->nTableSize;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->nKeyLength == 0) && (p->h == h)) {
if (flag & HASH_NEXT_INSERT) {
return FAILURE;
}
#if ZEND_DEBUG
if (p->pData == pData) {
ZEND_PUTS("Fatal error in zend_hash_pointer_update: p->pData == pData\n");
return FAILURE;
}
#endif
HANDLE_BLOCK_INTERRUPTIONS();
if (!p->bIsPointer && ht->pDestructor) {
ht->pDestructor(p->pData);
pefree(p->pData,ht->persistent);
}
p->pData = pData;
p->bIsPointer = 1;
if (h >= ht->nNextFreeElement) {
ht->nNextFreeElement = h + 1;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
p = p->pNext;
}
p = (Bucket *) pemalloc(sizeof(Bucket)-1,ht->persistent);
if (!p) {
return FAILURE;
}
p->nKeyLength = 0;
p->pData = pData;
p->h = h;
p->bIsPointer = 1;
p->pNext = ht->arBuckets[nIndex];
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pInternalPointer == NULL) {
ht->pInternalPointer = p;
}
ht->arBuckets[nIndex] = p;
/* Setup the double linked list */
p->pListLast = ht->pListTail;
ht->pListTail = p;
p->pListNext = NULL;
if (p->pListLast != NULL) {
p->pListLast->pListNext = p;
}
if (!ht->pListHead) {
ht->pListHead = p;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements++;
if (h >= ht->nNextFreeElement) {
ht->nNextFreeElement = h + 1;
}
if_full_do_resize(ht); /* If the Hash table is full, resize it */
return SUCCESS;
}
ZEND_API int zend_hash_is_pointer(HashTable *ht, char *arKey, uint nKeyLength)
{
ulong h;
uint nIndex;
Bucket *p;
if (nKeyLength <= 0) {
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: Can't check for empty key\n");
#endif
return FAILURE;
}
HANDLE_NUMERIC(arKey,nKeyLength,zend_hash_index_is_pointer(ht, idx));
h = ht->pHashFunction(arKey, nKeyLength);
nIndex = h % ht->nTableSize;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
if (!memcmp(p->arKey, arKey, nKeyLength)) {
return (p->bIsPointer);
}
}
p = p->pNext;
}
return 0;
}
ZEND_API int zend_hash_index_is_pointer(HashTable *ht, ulong h)
{
uint nIndex;
Bucket *p;
nIndex = h % ht->nTableSize;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->nKeyLength == 0) && (p->h == h)) {
return (p->bIsPointer);
}
p = p->pNext;
}
return 0;
}
static int if_full_do_resize(HashTable *ht)
{
Bucket **t;
if ((ht->nNumOfElements > ht->nTableSize) && (ht->nHashSizeIndex < nNumPrimeNumbers - 1)) { /* Let's double the table
size */
t = (Bucket **) perealloc(ht->arBuckets, PrimeNumbers[ht->nHashSizeIndex + 1] * sizeof(Bucket *),ht->persistent);
if (t) {
HANDLE_BLOCK_INTERRUPTIONS();
ht->arBuckets = t;
ht->nTableSize = PrimeNumbers[ht->nHashSizeIndex + 1];
ht->nHashSizeIndex++;
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
return FAILURE;
}
return SUCCESS;
}
static int zend_hash_rehash(HashTable *ht)
{
Bucket *p;
uint nIndex;
memset(ht->arBuckets, 0, PrimeNumbers[ht->nHashSizeIndex] * sizeof(Bucket *));
p = ht->pListHead;
while (p != NULL) {
nIndex = p->h % ht->nTableSize;
p->pNext = ht->arBuckets[nIndex];
ht->arBuckets[nIndex] = p;
p = p->pListNext;
}
return SUCCESS;
}
ZEND_API int zend_hash_del_key_or_index(HashTable *ht, char *arKey, uint nKeyLength, ulong h, int flag)
{
uint nIndex;
Bucket *p, *t = NULL; /* initialize just to shut gcc up with -Wall */
if (flag == HASH_DEL_KEY) {
HANDLE_NUMERIC(arKey,nKeyLength,zend_hash_del_key_or_index(ht,arKey,nKeyLength,idx,HASH_DEL_INDEX));
h = ht->pHashFunction(arKey, nKeyLength);
}
nIndex = h % ht->nTableSize;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->h == h) && ((p->nKeyLength == 0) || /* Numeric index */
((p->nKeyLength == nKeyLength) && (!memcmp(p->arKey, arKey, nKeyLength))))) {
HANDLE_BLOCK_INTERRUPTIONS();
if (p == ht->arBuckets[nIndex]) {
ht->arBuckets[nIndex] = p->pNext;
} else {
t->pNext = p->pNext;
}
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 (!p->bIsPointer) {
if (ht->pDestructor) {
ht->pDestructor(p->pData);
}
if (!p->pDataPtr) {
pefree(p->pData,ht->persistent);
}
}
if (ht->pInternalPointer == p) {
ht->pInternalPointer = p->pListNext;
}
pefree(p,ht->persistent);
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements--;
return SUCCESS;
}
t = p;
p = p->pNext;
}
return FAILURE;
}
ZEND_API void zend_hash_destroy(HashTable *ht)
{
Bucket *p, *q;
p = ht->pListHead;
while (p != NULL) {
q = p;
p = p->pListNext;
if (!q->bIsPointer) {
if (ht->pDestructor) {
ht->pDestructor(q->pData);
}
if (!q->pDataPtr && q->pData) {
pefree(q->pData,ht->persistent);
}
}
pefree(q,ht->persistent);
}
pefree(ht->arBuckets,ht->persistent);
}
ZEND_API void zend_hash_clean(HashTable *ht)
{
Bucket *p, *q;
p = ht->pListHead;
while (p != NULL) {
q = p;
p = p->pListNext;
if (!q->bIsPointer) {
if (ht->pDestructor) {
ht->pDestructor(q->pData);
}
if (!q->pDataPtr && q->pData) {
pefree(q->pData,ht->persistent);
}
}
pefree(q,ht->persistent);
}
memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
ht->pListHead = NULL;
ht->pListTail = NULL;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->pInternalPointer = NULL;
}
/* This is used to selectively delete certain entries from a hashtable.
* destruct() receives the data and decides if the entry should be deleted
* or not
*/
ZEND_API void zend_hash_apply(HashTable *ht,int (*destruct) (void *))
{
Bucket *p, *q;
p = ht->pListHead;
while (p != NULL) {
q = p;
p = p->pListNext;
if (destruct(q->pData)) {
if (q->nKeyLength==0) {
zend_hash_index_del(ht, q->h);
} else {
zend_hash_del(ht,q->arKey,q->nKeyLength);
}
}
}
}
ZEND_API void zend_hash_apply_with_argument(HashTable *ht,int (*destruct) (void *, void *), void *argument)
{
Bucket *p, *q;
p = ht->pListHead;
while (p != NULL) {
q = p;
p = p->pListNext;
if (destruct(q->pData, argument)) {
if (q->nKeyLength == 0) {
zend_hash_index_del(ht, q->h);
} else {
zend_hash_del(ht,q->arKey,q->nKeyLength);
}
}
}
}
ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, void (*pCopyConstructor) (void *pData), void *tmp, uint size)
{
Bucket *p;
p = source->pListHead;
while (p) {
memcpy(tmp, p->pData, size);
if (pCopyConstructor) {
pCopyConstructor(tmp);
}
if (p->nKeyLength) {
zend_hash_update(target, p->arKey, p->nKeyLength, tmp, size, NULL);
} else {
zend_hash_index_update(target, p->h, tmp, size, NULL);
}
p = p->pListNext;
}
target->pInternalPointer = target->pListHead;
}
ZEND_API void zend_hash_merge(HashTable *target, HashTable *source, void (*pCopyConstructor) (void *pData), void *tmp, uint size)
{
Bucket *p;
void *t;
p = source->pListHead;
while (p) {
memcpy(tmp, p->pData, size);
if (p->nKeyLength>0) {
if (zend_hash_add(target, p->arKey, p->nKeyLength, tmp, size, &t)==SUCCESS && pCopyConstructor) {
pCopyConstructor(t);
}
} else {
if (!zend_hash_index_exists(target, p->h) && zend_hash_index_update(target, p->h, tmp, size, &t)==SUCCESS && pCopyConstructor) {
pCopyConstructor(t);
}
}
p = p->pListNext;
}
target->pInternalPointer = target->pListHead;
}
ZEND_API ulong zend_get_hash_value(HashTable *ht, char *arKey, uint nKeyLength)
{
return ht->pHashFunction(arKey, nKeyLength);
}
/* 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
*/
ZEND_API int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength, void **pData)
{
ulong h;
uint nIndex;
Bucket *p;
HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_find(ht,idx,pData));
h = ht->pHashFunction(arKey, nKeyLength);
nIndex = h % ht->nTableSize;
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;
}
ZEND_API int zend_hash_quick_find(HashTable *ht, char *arKey, uint nKeyLength, ulong h, void **pData)
{
uint nIndex;
Bucket *p;
nIndex = h % ht->nTableSize;
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;
}
ZEND_API int zend_hash_exists(HashTable *ht, char *arKey, uint nKeyLength)
{
ulong h;
uint nIndex;
Bucket *p;
HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_exists(ht,idx));
h = ht->pHashFunction(arKey, nKeyLength);
nIndex = h % ht->nTableSize;
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;
}
ZEND_API int zend_hash_index_find(HashTable *ht, ulong h, void **pData)
{
uint nIndex;
Bucket *p;
nIndex = h % ht->nTableSize;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->h == h) && (p->nKeyLength == 0)) {
*pData = p->pData;
return SUCCESS;
}
p = p->pNext;
}
return FAILURE;
}
ZEND_API int zend_hash_index_exists(HashTable *ht, ulong h)
{
uint nIndex;
Bucket *p;
nIndex = h % ht->nTableSize;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->h == h) && (p->nKeyLength == 0)) {
return 1;
}
p = p->pNext;
}
return 0;
}
ZEND_API int zend_hash_num_elements(HashTable *ht)
{
return ht->nNumOfElements;
}
ZEND_API void zend_hash_internal_pointer_reset(HashTable *ht)
{
ht->pInternalPointer = ht->pListHead;
}
/* This function will be extremely optimized by remembering
* the end of the list
*/
ZEND_API void zend_hash_internal_pointer_end(HashTable *ht)
{
ht->pInternalPointer = ht->pListTail;
}
ZEND_API void zend_hash_move_forward(HashTable *ht)
{
if (ht->pInternalPointer) {
ht->pInternalPointer = ht->pInternalPointer->pListNext;
}
}
ZEND_API void zend_hash_move_backwards(HashTable *ht)
{
if (ht->pInternalPointer) {
ht->pInternalPointer = ht->pInternalPointer->pListLast;
}
}
/* This function should be made binary safe */
ZEND_API int zend_hash_get_current_key(HashTable *ht, char **str_index, ulong *num_index)
{
Bucket *p = ht->pInternalPointer;
if (p) {
if (p->nKeyLength) {
*str_index = (char *) pemalloc(p->nKeyLength,ht->persistent);
memcpy(*str_index, p->arKey, p->nKeyLength);
return HASH_KEY_IS_STRING;
} else {
*num_index = p->h;
return HASH_KEY_IS_LONG;
}
}
return HASH_KEY_NON_EXISTANT;
}
ZEND_API int zend_hash_get_current_data(HashTable *ht, void **pData)
{
Bucket *p = ht->pInternalPointer;
if (p) {
*pData = p->pData;
return SUCCESS;
} else {
return FAILURE;
}
}
ZEND_API int zend_hash_sort(HashTable *ht, int (*compar) (const void *, const void *), int renumber)
{
Bucket **arTmp;
Bucket *p;
int i, j;
if (ht->nNumOfElements <= 1) { /* Doesn't require sorting */
return SUCCESS;
}
arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *),ht->persistent);
if (!arTmp) {
return FAILURE;
}
p = ht->pListHead;
i = 0;
while (p) {
arTmp[i] = p;
p = p->pListNext;
i++;
}
qsort((void *) arTmp, i, sizeof(Bucket *), compar);
HANDLE_BLOCK_INTERRUPTIONS();
ht->pListHead = arTmp[0];
ht->pListTail = NULL;
ht->pInternalPointer = ht->pListHead;
for (j = 0; j < i; j++) {
if (ht->pListTail) {
ht->pListTail->pListNext = arTmp[j];
}
arTmp[j]->pListLast = ht->pListTail;
arTmp[j]->pListNext = NULL;
ht->pListTail = arTmp[j];
}
pefree(arTmp,ht->persistent);
HANDLE_UNBLOCK_INTERRUPTIONS();
if (renumber) {
p = ht->pListHead;
i=0;
while (p != NULL) {
p->nKeyLength = 0;
p->h = i++;
p = p->pListNext;
}
ht->nNextFreeElement = i;
zend_hash_rehash(ht);
}
return SUCCESS;
}
ZEND_API int zend_hash_minmax(HashTable *ht, int (*compar) (const void *, const void *), int flag, void **pData)
{
Bucket *p,*res;
if (ht->nNumOfElements == 0 ) {
*pData=NULL;
return FAILURE;
}
res = p = ht->pListHead;
while ((p = p->pListNext)) {
if (flag) {
if (compar(&res,&p) < 0) { /* max */
res = p;
}
} else {
if (compar(&res,&p) > 0) { /* min */
res = p;
}
}
}
*pData = res->pData;
return SUCCESS;
}
ZEND_API ulong zend_hash_next_free_element(HashTable *ht)
{
return ht->nNextFreeElement;
}
#if ZEND_DEBUG
void zend_hash_display_pListTail(HashTable *ht)
{
Bucket *p;
p = ht->pListTail;
while (p != NULL) {
zend_printf("pListTail has key %s\n", p->arKey);
p = p->pListLast;
}
}
void zend_hash_display(HashTable *ht)
{
Bucket *p;
uint i;
for (i = 0; i < ht->nTableSize; i++) {
p = ht->arBuckets[i];
while (p != NULL) {
zend_printf("%s <==> 0x%X\n", p->arKey, p->h);
p = p->pNext;
}
}
p = ht->pListTail;
while (p != NULL) {
zend_printf("%s <==> 0x%X\n", p->arKey, p->h);
p = p->pListLast;
}
}
#endif
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/