diff options
Diffstat (limited to 'Zend/zend_hash.c')
-rw-r--r-- | Zend/zend_hash.c | 1163 |
1 files changed, 1163 insertions, 0 deletions
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c new file mode 100644 index 0000000000..4cb93c9c69 --- /dev/null +++ b/Zend/zend_hash.c @@ -0,0 +1,1163 @@ +/* + +----------------------------------------------------------------------+ + | 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->arKey == NULL) { + 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->arKey == NULL) { + 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->arKey) { + 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: + */ |