Annotation of embedaddon/php/Zend/zend_hash.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | Zend Engine                                                          |
                      4:    +----------------------------------------------------------------------+
                      5:    | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
                      6:    +----------------------------------------------------------------------+
                      7:    | This source file is subject to version 2.00 of the Zend license,     |
                      8:    | that is bundled with this package in the file LICENSE, and is        | 
                      9:    | available through the world-wide-web at the following url:           |
                     10:    | http://www.zend.com/license/2_00.txt.                                |
                     11:    | If you did not receive a copy of the Zend license and are unable to  |
                     12:    | obtain it through the world-wide-web, please send a note to          |
                     13:    | license@zend.com so we can mail you a copy immediately.              |
                     14:    +----------------------------------------------------------------------+
                     15:    | Authors: Andi Gutmans <andi@zend.com>                                |
                     16:    |          Zeev Suraski <zeev@zend.com>                                |
                     17:    +----------------------------------------------------------------------+
                     18: */
                     19: 
                     20: /* $Id: zend_hash.c 321634 2012-01-01 13:15:04Z felipe $ */
                     21: 
                     22: #include "zend.h"
                     23: #include "zend_compile.h"
                     24: 
                     25: #define CONNECT_TO_BUCKET_DLLIST(element, list_head)           \
                     26:        (element)->pNext = (list_head);                                                 \
                     27:        (element)->pLast = NULL;                                                                \
                     28:        if ((element)->pNext) {                                                                 \
                     29:                (element)->pNext->pLast = (element);                            \
                     30:        }
                     31: 
                     32: #define CONNECT_TO_GLOBAL_DLLIST(element, ht)                          \
                     33:        (element)->pListLast = (ht)->pListTail;                                 \
                     34:        (ht)->pListTail = (element);                                                    \
                     35:        (element)->pListNext = NULL;                                                    \
                     36:        if ((element)->pListLast != NULL) {                                             \
                     37:                (element)->pListLast->pListNext = (element);            \
                     38:        }                                                                                                               \
                     39:        if (!(ht)->pListHead) {                                                                 \
                     40:                (ht)->pListHead = (element);                                            \
                     41:        }                                                                                                               \
                     42:        if ((ht)->pInternalPointer == NULL) {                                   \
                     43:                (ht)->pInternalPointer = (element);                                     \
                     44:        }
                     45: 
                     46: #if ZEND_DEBUG
                     47: #define HT_OK                          0
                     48: #define HT_IS_DESTROYING       1
                     49: #define HT_DESTROYED           2
                     50: #define HT_CLEANING                    3
                     51: 
                     52: static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
                     53: {
                     54:        if (ht->inconsistent==HT_OK) {
                     55:                return;
                     56:        }
                     57:        switch (ht->inconsistent) {
                     58:                case HT_IS_DESTROYING:
                     59:                        zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
                     60:                        break;
                     61:                case HT_DESTROYED:
                     62:                        zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file, line, ht);
                     63:                        break;
                     64:                case HT_CLEANING:
                     65:                        zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file, line, ht);
                     66:                        break;
                     67:                default:
                     68:                        zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file, line, ht);
                     69:                        break;
                     70:        }
                     71:        zend_bailout();
                     72: }
                     73: #define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
                     74: #define SET_INCONSISTENT(n) ht->inconsistent = n;
                     75: #else
                     76: #define IS_CONSISTENT(a)
                     77: #define SET_INCONSISTENT(n)
                     78: #endif
                     79: 
                     80: #define HASH_PROTECT_RECURSION(ht)                                                                                                             \
                     81:        if ((ht)->bApplyProtection) {                                                                                                           \
                     82:                if ((ht)->nApplyCount++ >= 3) {                                                                                                 \
                     83:                        zend_error(E_ERROR, "Nesting level too deep - recursive dependency?");          \
                     84:                }                                                                                                                                                               \
                     85:        }
                     86: 
                     87: 
                     88: #define HASH_UNPROTECT_RECURSION(ht)                                                                                                   \
                     89:        if ((ht)->bApplyProtection) {                                                                                                           \
                     90:                (ht)->nApplyCount--;                                                                                                                    \
                     91:        }
                     92: 
                     93: 
                     94: #define ZEND_HASH_IF_FULL_DO_RESIZE(ht)                                \
                     95:        if ((ht)->nNumOfElements > (ht)->nTableSize) {  \
                     96:                zend_hash_do_resize(ht);                                        \
                     97:        }
                     98: 
                     99: static int zend_hash_do_resize(HashTable *ht);
                    100: 
                    101: ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
                    102: {
                    103:        return zend_inline_hash_func(arKey, nKeyLength);
                    104: }
                    105: 
                    106: 
                    107: #define UPDATE_DATA(ht, p, pData, nDataSize)                                                                                   \
                    108:        if (nDataSize == sizeof(void*)) {                                                                                                       \
                    109:                if ((p)->pData != &(p)->pDataPtr) {                                                                                             \
                    110:                        pefree_rel((p)->pData, (ht)->persistent);                                                                       \
                    111:                }                                                                                                                                                               \
                    112:                memcpy(&(p)->pDataPtr, pData, sizeof(void *));                                                                  \
                    113:                (p)->pData = &(p)->pDataPtr;                                                                                                    \
                    114:        } else {                                                                                                                                                        \
                    115:                if ((p)->pData == &(p)->pDataPtr) {                                                                                             \
                    116:                        (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);                        \
                    117:                        (p)->pDataPtr=NULL;                                                                                                                     \
                    118:                } else {                                                                                                                                                \
                    119:                        (p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent);   \
                    120:                        /* (p)->pDataPtr is already NULL so no need to initialize it */                         \
                    121:                }                                                                                                                                                               \
                    122:                memcpy((p)->pData, pData, nDataSize);                                                                                   \
                    123:        }
                    124: 
                    125: #define INIT_DATA(ht, p, pData, nDataSize);                                                            \
                    126:        if (nDataSize == sizeof(void*)) {                                                                       \
                    127:                memcpy(&(p)->pDataPtr, pData, sizeof(void *));                                  \
                    128:                (p)->pData = &(p)->pDataPtr;                                                                    \
                    129:        } else {                                                                                                                        \
                    130:                (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
                    131:                if (!(p)->pData) {                                                                                              \
                    132:                        pefree_rel(p, (ht)->persistent);                                                        \
                    133:                        return FAILURE;                                                                                         \
                    134:                }                                                                                                                               \
                    135:                memcpy((p)->pData, pData, nDataSize);                                                   \
                    136:                (p)->pDataPtr=NULL;                                                                                             \
                    137:        }
                    138: 
                    139: 
                    140: #if SUHOSIN_PATCH
                    141: #ifdef ZTS
                    142: static MUTEX_T zend_hash_dprot_mx_reader;
                    143: static MUTEX_T zend_hash_dprot_mx_writer;
                    144: static unsigned int zend_hash_dprot_reader;
                    145: #endif
                    146: static unsigned int zend_hash_dprot_counter;
                    147: static unsigned int zend_hash_dprot_curmax;
                    148: static dtor_func_t *zend_hash_dprot_table = NULL;
                    149: 
                    150: static void zend_hash_dprot_begin_read()
                    151: {
                    152: #ifdef ZTS
                    153:        tsrm_mutex_lock(zend_hash_dprot_mx_reader);
                    154:        if ((++(zend_hash_dprot_reader)) == 1) {
                    155:                tsrm_mutex_lock(zend_hash_dprot_mx_writer);
                    156:        }
                    157:        tsrm_mutex_unlock(zend_hash_dprot_mx_reader);
                    158: #endif
                    159: }
                    160: 
                    161: static void zend_hash_dprot_end_read()
                    162: {
                    163: #ifdef ZTS
                    164:        tsrm_mutex_lock(zend_hash_dprot_mx_reader);
                    165:        if ((--(zend_hash_dprot_reader)) == 0) {
                    166:                tsrm_mutex_unlock(zend_hash_dprot_mx_writer);
                    167:        }
                    168:        tsrm_mutex_unlock(zend_hash_dprot_mx_reader);
                    169: #endif
                    170: }
                    171: 
                    172: static void zend_hash_dprot_begin_write()
                    173: {
                    174: #ifdef ZTS
                    175:        tsrm_mutex_lock(zend_hash_dprot_mx_writer);
                    176: #endif
                    177: }
                    178: 
                    179: static void zend_hash_dprot_end_write()
                    180: {
                    181: #ifdef ZTS
                    182:        tsrm_mutex_unlock(zend_hash_dprot_mx_writer);
                    183: #endif
                    184: }
                    185: 
                    186: /*ZEND_API void zend_hash_dprot_dtor()
                    187: {
                    188: #ifdef ZTS
                    189:        tsrm_mutex_free(zend_hash_dprot_mx_reader);
                    190:        tsrm_mutex_free(zend_hash_dprot_mx_writer);
                    191: #endif 
                    192:        free(zend_hash_dprot_table);
                    193: }*/
                    194: 
                    195: static void zend_hash_add_destructor(dtor_func_t pDestructor)
                    196: {
                    197:        int left, right, mid;
                    198:        zend_bool found = 0;
                    199:        unsigned long value;
                    200:        
                    201:        if (pDestructor == NULL || pDestructor == ZVAL_PTR_DTOR || pDestructor == ZVAL_INTERNAL_PTR_DTOR
                    202:            || pDestructor == ZEND_FUNCTION_DTOR || pDestructor == ZEND_CLASS_DTOR) {
                    203:                return;
                    204:        }
                    205:        
                    206:        if (zend_hash_dprot_table == NULL) {
                    207: #ifdef ZTS
                    208:                zend_hash_dprot_mx_reader = tsrm_mutex_alloc();
                    209:                zend_hash_dprot_mx_writer = tsrm_mutex_alloc();
                    210:                zend_hash_dprot_reader = 0;
                    211: #endif 
                    212:                zend_hash_dprot_counter = 0;
                    213:                zend_hash_dprot_curmax = 256;
                    214:                zend_hash_dprot_table = (dtor_func_t *) malloc(256 * sizeof(dtor_func_t));
                    215:        }
                    216:        
                    217:        zend_hash_dprot_begin_write();
                    218: 
                    219:        if (zend_hash_dprot_counter == 0) {
                    220:                zend_hash_dprot_counter++;
                    221:                zend_hash_dprot_table[0] = pDestructor;
                    222:        } else {
                    223:                value = (unsigned long) pDestructor;
                    224:                left = 0;
                    225:                right = zend_hash_dprot_counter-1;
                    226:                mid = 0;
                    227:                
                    228:                while (left < right) {
                    229:                        mid = (right - left) >> 1;
                    230:                        mid += left;
                    231:                        if ((unsigned long)zend_hash_dprot_table[mid] == value) {
                    232:                                found = 1;
                    233:                                break;
                    234:                        }
                    235:                        if (value < (unsigned long)zend_hash_dprot_table[mid]) {
                    236:                                right = mid-1;
                    237:                        } else {
                    238:                                left = mid+1;
                    239:                        }
                    240:                }
                    241:                if ((unsigned long)zend_hash_dprot_table[left] == value) {
                    242:                        found = 1;
                    243:                }
                    244:                
                    245:                if (!found) {
                    246:                
                    247:                        if (zend_hash_dprot_counter >= zend_hash_dprot_curmax) {
                    248:                                zend_hash_dprot_curmax += 256;
                    249:                                zend_hash_dprot_table = (dtor_func_t *) realloc(zend_hash_dprot_table, zend_hash_dprot_curmax * sizeof(dtor_func_t));
                    250:                        }
                    251:                        
                    252:                        if ((unsigned long)zend_hash_dprot_table[left] < value) {
                    253:                                memmove(zend_hash_dprot_table+left+2, zend_hash_dprot_table+left+1, (zend_hash_dprot_counter-left-1)*sizeof(dtor_func_t));
                    254:                                zend_hash_dprot_table[left+1] = pDestructor;
                    255:                        } else {
                    256:                                memmove(zend_hash_dprot_table+left+1, zend_hash_dprot_table+left, (zend_hash_dprot_counter-left)*sizeof(dtor_func_t));
                    257:                                zend_hash_dprot_table[left] = pDestructor;
                    258:                        }
                    259: 
                    260:                        zend_hash_dprot_counter++;
                    261:                }
                    262:        }
                    263:        
                    264:        zend_hash_dprot_end_write();
                    265: }
                    266: 
                    267: static void zend_hash_check_destructor(dtor_func_t pDestructor)
                    268: {
                    269:        unsigned long value;
                    270:        
                    271:        if (pDestructor == NULL || pDestructor == ZVAL_PTR_DTOR || pDestructor == ZVAL_INTERNAL_PTR_DTOR
                    272: #ifdef ZEND_ENGINE_2
                    273:                || pDestructor == suhosin_zend_destroy_property_info_internal || pDestructor == suhosin_zend_destroy_property_info
                    274: #endif
                    275:            || pDestructor == ZEND_FUNCTION_DTOR || pDestructor == ZEND_CLASS_DTOR) {
                    276:                return;
                    277:        }
                    278: 
                    279:        zend_hash_dprot_begin_read();
                    280:        
                    281:        if (zend_hash_dprot_counter > 0) {
                    282:                int left, right, mid;
                    283:                zend_bool found = 0;
                    284:        
                    285:                value = (unsigned long) pDestructor;
                    286:                left = 0;
                    287:                right = zend_hash_dprot_counter-1;
                    288:                
                    289:                while (left < right) {
                    290:                        mid = (right - left) >> 1;
                    291:                        mid += left;
                    292:                        if ((unsigned long)zend_hash_dprot_table[mid] == value) {
                    293:                                found = 1;
                    294:                                break;
                    295:                        }
                    296:                        if (value < (unsigned long)zend_hash_dprot_table[mid]) {
                    297:                                right = mid-1;
                    298:                        } else {
                    299:                                left = mid+1;
                    300:                        }
                    301:                }
                    302:                if ((unsigned long)zend_hash_dprot_table[left] == value) {
                    303:                        found = 1;
                    304:                }
                    305:                
                    306:                if (!found) {
                    307:                        zend_hash_dprot_end_read();
                    308:                
                    309:                        zend_suhosin_log(S_MEMORY, "possible memory corruption detected - unknown Hashtable destructor");
                    310:                        if (SUHOSIN_CONFIG(SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR) == 0) {
                    311:                                _exit(1);
                    312:                        }
                    313:                        return;
                    314:                }
                    315:        
                    316:        } else {
                    317:                zend_hash_dprot_end_read();
                    318:        
                    319:                zend_suhosin_log(S_MEMORY, "possible memory corruption detected - unknown Hashtable destructor");
                    320:                if (SUHOSIN_CONFIG(SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR) == 0) {
                    321:                        _exit(1);
                    322:                }
                    323:                return;         
                    324:        }
                    325:        
                    326:        zend_hash_dprot_end_read();
                    327: }
                    328: 
                    329: #else
                    330: #define zend_hash_add_destructor(pDestructor) do {} while(0)
                    331: #define zend_hash_check_destructor(pDestructor) do {} while(0)
                    332: #endif
                    333: 
                    334: 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)
                    335: {
                    336:        uint i = 3;
                    337:        Bucket **tmp;
                    338: 
                    339:        SET_INCONSISTENT(HT_OK);
                    340: 
                    341:        if (nSize >= 0x80000000) {
                    342:                /* prevent overflow */
                    343:                ht->nTableSize = 0x80000000;
                    344:        } else {
                    345:                while ((1U << i) < nSize) {
                    346:                        i++;
                    347:                }
                    348:                ht->nTableSize = 1 << i;
                    349:        }
                    350: 
                    351:        ht->nTableMask = ht->nTableSize - 1;
                    352:        ht->pDestructor = pDestructor;
                    353:        zend_hash_add_destructor(pDestructor);
                    354:        ht->arBuckets = NULL;
                    355:        ht->pListHead = NULL;
                    356:        ht->pListTail = NULL;
                    357:        ht->nNumOfElements = 0;
                    358:        ht->nNextFreeElement = 0;
                    359:        ht->pInternalPointer = NULL;
                    360:        ht->persistent = persistent;
                    361:        ht->nApplyCount = 0;
                    362:        ht->bApplyProtection = 1;
                    363:        
                    364:        /* Uses ecalloc() so that Bucket* == NULL */
                    365:        if (persistent) {
                    366:                tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *));
                    367:                if (!tmp) {
                    368:                        return FAILURE;
                    369:                }
                    370:                ht->arBuckets = tmp;
                    371:        } else {
                    372:                tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *));
                    373:                if (tmp) {
                    374:                        ht->arBuckets = tmp;
                    375:                }
                    376:        }
                    377:        
                    378:        return SUCCESS;
                    379: }
                    380: 
                    381: 
                    382: 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)
                    383: {
                    384:        int retval = _zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent ZEND_FILE_LINE_CC);
                    385: 
                    386:        ht->bApplyProtection = bApplyProtection;
                    387:        return retval;
                    388: }
                    389: 
                    390: 
                    391: ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
                    392: {
                    393:        ht->bApplyProtection = bApplyProtection;
                    394: }
                    395: 
                    396: 
                    397: 
                    398: 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)
                    399: {
                    400:        ulong h;
                    401:        uint nIndex;
                    402:        Bucket *p;
                    403: 
                    404:        IS_CONSISTENT(ht);
                    405: 
                    406:        if (nKeyLength <= 0) {
                    407: #if ZEND_DEBUG
                    408:                ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
                    409: #endif
                    410:                return FAILURE;
                    411:        }
                    412: 
                    413:        h = zend_inline_hash_func(arKey, nKeyLength);
                    414:        nIndex = h & ht->nTableMask;
                    415: 
                    416:        p = ht->arBuckets[nIndex];
                    417:        while (p != NULL) {
                    418:                if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
                    419:                        if (!memcmp(p->arKey, arKey, nKeyLength)) {
                    420:                                if (flag & HASH_ADD) {
                    421:                                        return FAILURE;
                    422:                                }
                    423:                                HANDLE_BLOCK_INTERRUPTIONS();
                    424: #if ZEND_DEBUG
                    425:                                if (p->pData == pData) {
                    426:                                        ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
                    427:                                        HANDLE_UNBLOCK_INTERRUPTIONS();
                    428:                                        return FAILURE;
                    429:                                }
                    430: #endif
                    431:                                zend_hash_check_destructor(ht->pDestructor);
                    432:                                if (ht->pDestructor) {
                    433:                                        ht->pDestructor(p->pData);
                    434:                                }
                    435:                                UPDATE_DATA(ht, p, pData, nDataSize);
                    436:                                if (pDest) {
                    437:                                        *pDest = p->pData;
                    438:                                }
                    439:                                HANDLE_UNBLOCK_INTERRUPTIONS();
                    440:                                return SUCCESS;
                    441:                        }
                    442:                }
                    443:                p = p->pNext;
                    444:        }
                    445:        
                    446:        p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent);
                    447:        if (!p) {
                    448:                return FAILURE;
                    449:        }
                    450:        memcpy(p->arKey, arKey, nKeyLength);
                    451:        p->nKeyLength = nKeyLength;
                    452:        INIT_DATA(ht, p, pData, nDataSize);
                    453:        p->h = h;
                    454:        CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
                    455:        if (pDest) {
                    456:                *pDest = p->pData;
                    457:        }
                    458: 
                    459:        HANDLE_BLOCK_INTERRUPTIONS();
                    460:        CONNECT_TO_GLOBAL_DLLIST(p, ht);
                    461:        ht->arBuckets[nIndex] = p;
                    462:        HANDLE_UNBLOCK_INTERRUPTIONS();
                    463: 
                    464:        ht->nNumOfElements++;
                    465:        ZEND_HASH_IF_FULL_DO_RESIZE(ht);                /* If the Hash table is full, resize it */
                    466:        return SUCCESS;
                    467: }
                    468: 
                    469: 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)
                    470: {
                    471:        uint nIndex;
                    472:        Bucket *p;
                    473: 
                    474:        IS_CONSISTENT(ht);
                    475: 
                    476:        if (nKeyLength == 0) {
                    477:                return zend_hash_index_update(ht, h, pData, nDataSize, pDest);
                    478:        }
                    479: 
                    480:        nIndex = h & ht->nTableMask;
                    481:        
                    482:        p = ht->arBuckets[nIndex];
                    483:        while (p != NULL) {
                    484:                if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
                    485:                        if (!memcmp(p->arKey, arKey, nKeyLength)) {
                    486:                                if (flag & HASH_ADD) {
                    487:                                        return FAILURE;
                    488:                                }
                    489:                                HANDLE_BLOCK_INTERRUPTIONS();
                    490: #if ZEND_DEBUG
                    491:                                if (p->pData == pData) {
                    492:                                        ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
                    493:                                        HANDLE_UNBLOCK_INTERRUPTIONS();
                    494:                                        return FAILURE;
                    495:                                }
                    496: #endif
                    497:                                zend_hash_check_destructor(ht->pDestructor);
                    498:                                if (ht->pDestructor) {
                    499:                                        ht->pDestructor(p->pData);
                    500:                                }
                    501:                                UPDATE_DATA(ht, p, pData, nDataSize);
                    502:                                if (pDest) {
                    503:                                        *pDest = p->pData;
                    504:                                }
                    505:                                HANDLE_UNBLOCK_INTERRUPTIONS();
                    506:                                return SUCCESS;
                    507:                        }
                    508:                }
                    509:                p = p->pNext;
                    510:        }
                    511:        
                    512:        p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent);
                    513:        if (!p) {
                    514:                return FAILURE;
                    515:        }
                    516: 
                    517:        memcpy(p->arKey, arKey, nKeyLength);
                    518:        p->nKeyLength = nKeyLength;
                    519:        INIT_DATA(ht, p, pData, nDataSize);
                    520:        p->h = h;
                    521:        
                    522:        CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
                    523: 
                    524:        if (pDest) {
                    525:                *pDest = p->pData;
                    526:        }
                    527: 
                    528:        HANDLE_BLOCK_INTERRUPTIONS();
                    529:        ht->arBuckets[nIndex] = p;
                    530:        CONNECT_TO_GLOBAL_DLLIST(p, ht);
                    531:        HANDLE_UNBLOCK_INTERRUPTIONS();
                    532: 
                    533:        ht->nNumOfElements++;
                    534:        ZEND_HASH_IF_FULL_DO_RESIZE(ht);                /* If the Hash table is full, resize it */
                    535:        return SUCCESS;
                    536: }
                    537: 
                    538: 
                    539: ZEND_API int zend_hash_add_empty_element(HashTable *ht, const char *arKey, uint nKeyLength)
                    540: {
                    541:        void *dummy = (void *) 1;
                    542: 
                    543:        return zend_hash_add(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
                    544: }
                    545: 
                    546: 
                    547: 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)
                    548: {
                    549:        uint nIndex;
                    550:        Bucket *p;
                    551: 
                    552:        IS_CONSISTENT(ht);
                    553: 
                    554:        if (flag & HASH_NEXT_INSERT) {
                    555:                h = ht->nNextFreeElement;
                    556:        }
                    557:        nIndex = h & ht->nTableMask;
                    558: 
                    559:        p = ht->arBuckets[nIndex];
                    560:        while (p != NULL) {
                    561:                if ((p->nKeyLength == 0) && (p->h == h)) {
                    562:                        if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) {
                    563:                                return FAILURE;
                    564:                        }
                    565:                        HANDLE_BLOCK_INTERRUPTIONS();
                    566: #if ZEND_DEBUG
                    567:                        if (p->pData == pData) {
                    568:                                ZEND_PUTS("Fatal error in zend_hash_index_update: p->pData == pData\n");
                    569:                                HANDLE_UNBLOCK_INTERRUPTIONS();
                    570:                                return FAILURE;
                    571:                        }
                    572: #endif
                    573:                        zend_hash_check_destructor(ht->pDestructor);
                    574:                        if (ht->pDestructor) {
                    575:                                ht->pDestructor(p->pData);
                    576:                        }
                    577:                        UPDATE_DATA(ht, p, pData, nDataSize);
                    578:                        HANDLE_UNBLOCK_INTERRUPTIONS();
                    579:                        if ((long)h >= (long)ht->nNextFreeElement) {
                    580:                                ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
                    581:                        }
                    582:                        if (pDest) {
                    583:                                *pDest = p->pData;
                    584:                        }
                    585:                        return SUCCESS;
                    586:                }
                    587:                p = p->pNext;
                    588:        }
                    589:        p = (Bucket *) pemalloc_rel(sizeof(Bucket) - 1, ht->persistent);
                    590:        if (!p) {
                    591:                return FAILURE;
                    592:        }
                    593:        p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
                    594:        p->h = h;
                    595:        INIT_DATA(ht, p, pData, nDataSize);
                    596:        if (pDest) {
                    597:                *pDest = p->pData;
                    598:        }
                    599: 
                    600:        CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
                    601: 
                    602:        HANDLE_BLOCK_INTERRUPTIONS();
                    603:        ht->arBuckets[nIndex] = p;
                    604:        CONNECT_TO_GLOBAL_DLLIST(p, ht);
                    605:        HANDLE_UNBLOCK_INTERRUPTIONS();
                    606: 
                    607:        if ((long)h >= (long)ht->nNextFreeElement) {
                    608:                ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
                    609:        }
                    610:        ht->nNumOfElements++;
                    611:        ZEND_HASH_IF_FULL_DO_RESIZE(ht);
                    612:        return SUCCESS;
                    613: }
                    614: 
                    615: 
                    616: static int zend_hash_do_resize(HashTable *ht)
                    617: {
                    618:        Bucket **t;
                    619: 
                    620:        IS_CONSISTENT(ht);
                    621: 
                    622:        if ((ht->nTableSize << 1) > 0) {        /* Let's double the table size */
                    623:                t = (Bucket **) perealloc_recoverable(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent);
                    624:                if (t) {
                    625:                        HANDLE_BLOCK_INTERRUPTIONS();
                    626:                        ht->arBuckets = t;
                    627:                        ht->nTableSize = (ht->nTableSize << 1);
                    628:                        ht->nTableMask = ht->nTableSize - 1;
                    629:                        zend_hash_rehash(ht);
                    630:                        HANDLE_UNBLOCK_INTERRUPTIONS();
                    631:                        return SUCCESS;
                    632:                }
                    633:                return FAILURE;
                    634:        }
                    635:        return SUCCESS;
                    636: }
                    637: 
                    638: ZEND_API int zend_hash_rehash(HashTable *ht)
                    639: {
                    640:        Bucket *p;
                    641:        uint nIndex;
                    642: 
                    643:        IS_CONSISTENT(ht);
                    644: 
                    645:        memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
                    646:        p = ht->pListHead;
                    647:        while (p != NULL) {
                    648:                nIndex = p->h & ht->nTableMask;
                    649:                CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
                    650:                ht->arBuckets[nIndex] = p;
                    651:                p = p->pListNext;
                    652:        }
                    653:        return SUCCESS;
                    654: }
                    655: 
                    656: ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
                    657: {
                    658:        uint nIndex;
                    659:        Bucket *p;
                    660: 
                    661:        IS_CONSISTENT(ht);
                    662: 
                    663:        if (flag == HASH_DEL_KEY) {
                    664:                h = zend_inline_hash_func(arKey, nKeyLength);
                    665:        }
                    666:        nIndex = h & ht->nTableMask;
                    667: 
                    668:        p = ht->arBuckets[nIndex];
                    669:        while (p != NULL) {
                    670:                if ((p->h == h) 
                    671:                         && (p->nKeyLength == nKeyLength)
                    672:                         && ((p->nKeyLength == 0) /* Numeric index (short circuits the memcmp() check) */
                    673:                                 || !memcmp(p->arKey, arKey, nKeyLength))) { /* String index */
                    674:                        HANDLE_BLOCK_INTERRUPTIONS();
                    675:                        if (p == ht->arBuckets[nIndex]) {
                    676:                                ht->arBuckets[nIndex] = p->pNext;
                    677:                        } else {
                    678:                                p->pLast->pNext = p->pNext;
                    679:                        }
                    680:                        if (p->pNext) {
                    681:                                p->pNext->pLast = p->pLast;
                    682:                        }
                    683:                        if (p->pListLast != NULL) {
                    684:                                p->pListLast->pListNext = p->pListNext;
                    685:                        } else { 
                    686:                                /* Deleting the head of the list */
                    687:                                ht->pListHead = p->pListNext;
                    688:                        }
                    689:                        if (p->pListNext != NULL) {
                    690:                                p->pListNext->pListLast = p->pListLast;
                    691:                        } else {
                    692:                                ht->pListTail = p->pListLast;
                    693:                        }
                    694:                        if (ht->pInternalPointer == p) {
                    695:                                ht->pInternalPointer = p->pListNext;
                    696:                        }
                    697:                        zend_hash_check_destructor(ht->pDestructor);
                    698:                        if (ht->pDestructor) {
                    699:                                ht->pDestructor(p->pData);
                    700:                        }
                    701:                        if (p->pData != &p->pDataPtr) {
                    702:                                pefree(p->pData, ht->persistent);
                    703:                        }
                    704:                        pefree(p, ht->persistent);
                    705:                        HANDLE_UNBLOCK_INTERRUPTIONS();
                    706:                        ht->nNumOfElements--;
                    707:                        return SUCCESS;
                    708:                }
                    709:                p = p->pNext;
                    710:        }
                    711:        return FAILURE;
                    712: }
                    713: 
                    714: 
                    715: ZEND_API void zend_hash_destroy(HashTable *ht)
                    716: {
                    717:        Bucket *p, *q;
                    718: 
                    719:        IS_CONSISTENT(ht);
                    720: 
                    721:        SET_INCONSISTENT(HT_IS_DESTROYING);
                    722: 
                    723:        p = ht->pListHead;
                    724:        zend_hash_check_destructor(ht->pDestructor);
                    725:        while (p != NULL) {
                    726:                q = p;
                    727:                p = p->pListNext;
                    728:                if (ht->pDestructor) {
                    729:                        ht->pDestructor(q->pData);
                    730:                }
                    731:                if (q->pData != &q->pDataPtr) {
                    732:                        pefree(q->pData, ht->persistent);
                    733:                }
                    734:                pefree(q, ht->persistent);
                    735:        }
                    736:        pefree(ht->arBuckets, ht->persistent);
                    737: 
                    738:        SET_INCONSISTENT(HT_DESTROYED);
                    739: }
                    740: 
                    741: 
                    742: ZEND_API void zend_hash_clean(HashTable *ht)
                    743: {
                    744:        Bucket *p, *q;
                    745: 
                    746:        IS_CONSISTENT(ht);
                    747: 
                    748:        p = ht->pListHead;
                    749: 
                    750:        memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
                    751:        ht->pListHead = NULL;
                    752:        ht->pListTail = NULL;
                    753:        ht->nNumOfElements = 0;
                    754:        ht->nNextFreeElement = 0;
                    755:        ht->pInternalPointer = NULL;
                    756: 
                    757:        zend_hash_check_destructor(ht->pDestructor);
                    758:        while (p != NULL) {
                    759:                q = p;
                    760:                p = p->pListNext;
                    761:                if (ht->pDestructor) {
                    762:                        ht->pDestructor(q->pData);
                    763:                }
                    764:                if (q->pData != &q->pDataPtr) {
                    765:                        pefree(q->pData, ht->persistent);
                    766:                }
                    767:                pefree(q, ht->persistent);
                    768:        }
                    769: }
                    770: 
                    771: /* This function is used by the various apply() functions.
                    772:  * It deletes the passed bucket, and returns the address of the
                    773:  * next bucket.  The hash *may* be altered during that time, the
                    774:  * returned value will still be valid.
                    775:  */
                    776: static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p)
                    777: {
                    778:        Bucket *retval;
                    779: 
                    780:        HANDLE_BLOCK_INTERRUPTIONS();
                    781:        if (p->pLast) {
                    782:                p->pLast->pNext = p->pNext;
                    783:        } else {
                    784:                uint nIndex;
                    785: 
                    786:                nIndex = p->h & ht->nTableMask;
                    787:                ht->arBuckets[nIndex] = p->pNext;
                    788:        }
                    789:        if (p->pNext) {
                    790:                p->pNext->pLast = p->pLast;
                    791:        } else {
                    792:                /* Nothing to do as this list doesn't have a tail */
                    793:        }
                    794: 
                    795:        if (p->pListLast != NULL) {
                    796:                p->pListLast->pListNext = p->pListNext;
                    797:        } else { 
                    798:                /* Deleting the head of the list */
                    799:                ht->pListHead = p->pListNext;
                    800:        }
                    801:        if (p->pListNext != NULL) {
                    802:                p->pListNext->pListLast = p->pListLast;
                    803:        } else {
                    804:                ht->pListTail = p->pListLast;
                    805:        }
                    806:        if (ht->pInternalPointer == p) {
                    807:                ht->pInternalPointer = p->pListNext;
                    808:        }
                    809:        ht->nNumOfElements--;
                    810:        HANDLE_UNBLOCK_INTERRUPTIONS();
                    811: 
                    812:        zend_hash_check_destructor(ht->pDestructor);
                    813:        if (ht->pDestructor) {
                    814:                ht->pDestructor(p->pData);
                    815:        }
                    816:        if (p->pData != &p->pDataPtr) {
                    817:                pefree(p->pData, ht->persistent);
                    818:        }
                    819:        retval = p->pListNext;
                    820:        pefree(p, ht->persistent);
                    821: 
                    822:        return retval;
                    823: }
                    824: 
                    825: 
                    826: ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
                    827: {
                    828:        Bucket *p;
                    829: 
                    830:        IS_CONSISTENT(ht);
                    831: 
                    832:        p = ht->pListHead;
                    833:        zend_hash_check_destructor(ht->pDestructor);
                    834:        while (p != NULL) {
                    835:                p = zend_hash_apply_deleter(ht, p);
                    836:        }
                    837:        pefree(ht->arBuckets, ht->persistent);
                    838: 
                    839:        SET_INCONSISTENT(HT_DESTROYED);
                    840: }
                    841: 
                    842: ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
                    843: {
                    844:        Bucket *p;
                    845: 
                    846:        IS_CONSISTENT(ht);
                    847: 
                    848:        p = ht->pListTail;
                    849:        while (p != NULL) {
                    850:                zend_hash_apply_deleter(ht, p);
                    851:                p = ht->pListTail;
                    852:        }
                    853: 
                    854:        pefree(ht->arBuckets, ht->persistent);
                    855: 
                    856:        SET_INCONSISTENT(HT_DESTROYED);
                    857: }
                    858: 
                    859: /* This is used to recurse elements and selectively delete certain entries 
                    860:  * from a hashtable. apply_func() receives the data and decides if the entry 
                    861:  * should be deleted or recursion should be stopped. The following three 
                    862:  * return codes are possible:
                    863:  * ZEND_HASH_APPLY_KEEP   - continue
                    864:  * ZEND_HASH_APPLY_STOP   - stop iteration
                    865:  * ZEND_HASH_APPLY_REMOVE - delete the element, combineable with the former
                    866:  */
                    867: 
                    868: ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
                    869: {
                    870:        Bucket *p;
                    871: 
                    872:        IS_CONSISTENT(ht);
                    873: 
                    874:        HASH_PROTECT_RECURSION(ht);
                    875:        p = ht->pListHead;
                    876:        while (p != NULL) {
                    877:                int result = apply_func(p->pData TSRMLS_CC);
                    878:                
                    879:                if (result & ZEND_HASH_APPLY_REMOVE) {
                    880:                        p = zend_hash_apply_deleter(ht, p);
                    881:                } else {
                    882:                        p = p->pListNext;
                    883:                }
                    884:                if (result & ZEND_HASH_APPLY_STOP) {
                    885:                        break;
                    886:                }
                    887:        }
                    888:        HASH_UNPROTECT_RECURSION(ht);
                    889: }
                    890: 
                    891: 
                    892: ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument TSRMLS_DC)
                    893: {
                    894:        Bucket *p;
                    895: 
                    896:        IS_CONSISTENT(ht);
                    897: 
                    898:        HASH_PROTECT_RECURSION(ht);
                    899:        p = ht->pListHead;
                    900:        while (p != NULL) {
                    901:                int result = apply_func(p->pData, argument TSRMLS_CC);
                    902:                
                    903:                if (result & ZEND_HASH_APPLY_REMOVE) {
                    904:                        p = zend_hash_apply_deleter(ht, p);
                    905:                } else {
                    906:                        p = p->pListNext;
                    907:                }
                    908:                if (result & ZEND_HASH_APPLY_STOP) {
                    909:                        break;
                    910:                }
                    911:        }
                    912:        HASH_UNPROTECT_RECURSION(ht);
                    913: }
                    914: 
                    915: 
                    916: ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func_args_t apply_func, int num_args, ...)
                    917: {
                    918:        Bucket *p;
                    919:        va_list args;
                    920:        zend_hash_key hash_key;
                    921: 
                    922:        IS_CONSISTENT(ht);
                    923: 
                    924:        HASH_PROTECT_RECURSION(ht);
                    925: 
                    926:        p = ht->pListHead;
                    927:        while (p != NULL) {
                    928:                int result;
                    929:                va_start(args, num_args);
                    930:                hash_key.arKey = p->arKey;
                    931:                hash_key.nKeyLength = p->nKeyLength;
                    932:                hash_key.h = p->h;
                    933:                result = apply_func(p->pData TSRMLS_CC, num_args, args, &hash_key);
                    934: 
                    935:                if (result & ZEND_HASH_APPLY_REMOVE) {
                    936:                        p = zend_hash_apply_deleter(ht, p);
                    937:                } else {
                    938:                        p = p->pListNext;
                    939:                }
                    940:                if (result & ZEND_HASH_APPLY_STOP) {
                    941:                        va_end(args);
                    942:                        break;
                    943:                }
                    944:                va_end(args);
                    945:        }
                    946: 
                    947:        HASH_UNPROTECT_RECURSION(ht);
                    948: }
                    949: 
                    950: 
                    951: ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
                    952: {
                    953:        Bucket *p, *q;
                    954: 
                    955:        IS_CONSISTENT(ht);
                    956: 
                    957:        HASH_PROTECT_RECURSION(ht);
                    958:        p = ht->pListTail;
                    959:        while (p != NULL) {
                    960:                int result = apply_func(p->pData TSRMLS_CC);
                    961: 
                    962:                q = p;
                    963:                p = p->pListLast;
                    964:                if (result & ZEND_HASH_APPLY_REMOVE) {
                    965:                        zend_hash_apply_deleter(ht, q);
                    966:                }
                    967:                if (result & ZEND_HASH_APPLY_STOP) {
                    968:                        break;
                    969:                }
                    970:        }
                    971:        HASH_UNPROTECT_RECURSION(ht);
                    972: }
                    973: 
                    974: 
                    975: ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
                    976: {
                    977:        Bucket *p;
                    978:        void *new_entry;
                    979:        zend_bool setTargetPointer;
                    980: 
                    981:        IS_CONSISTENT(source);
                    982:        IS_CONSISTENT(target);
                    983: 
                    984:        setTargetPointer = !target->pInternalPointer;
                    985:        p = source->pListHead;
                    986:        while (p) {
                    987:                if (setTargetPointer && source->pInternalPointer == p) {
                    988:                        target->pInternalPointer = NULL;
                    989:                }
                    990:                if (p->nKeyLength) {
                    991:                        zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &new_entry);
                    992:                } else {
                    993:                        zend_hash_index_update(target, p->h, p->pData, size, &new_entry);
                    994:                }
                    995:                if (pCopyConstructor) {
                    996:                        pCopyConstructor(new_entry);
                    997:                }
                    998:                p = p->pListNext;
                    999:        }
                   1000:        if (!target->pInternalPointer) {
                   1001:                target->pInternalPointer = target->pListHead;
                   1002:        }
                   1003: }
                   1004: 
                   1005: 
                   1006: 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)
                   1007: {
                   1008:        Bucket *p;
                   1009:        void *t;
                   1010:        int mode = (overwrite?HASH_UPDATE:HASH_ADD);
                   1011: 
                   1012:        IS_CONSISTENT(source);
                   1013:        IS_CONSISTENT(target);
                   1014: 
                   1015:        p = source->pListHead;
                   1016:        while (p) {
                   1017:                if (p->nKeyLength>0) {
                   1018:                        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) {
                   1019:                                pCopyConstructor(t);
                   1020:                        }
                   1021:                } else {
                   1022:                        if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
                   1023:                                pCopyConstructor(t);
                   1024:                        }
                   1025:                }
                   1026:                p = p->pListNext;
                   1027:        }
                   1028:        target->pInternalPointer = target->pListHead;
                   1029: }
                   1030: 
                   1031: 
                   1032: static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
                   1033: {
                   1034:        zend_hash_key hash_key;
                   1035: 
                   1036:        hash_key.arKey = p->arKey;
                   1037:        hash_key.nKeyLength = p->nKeyLength;
                   1038:        hash_key.h = p->h;
                   1039:        return merge_checker_func(target, source_data, &hash_key, pParam);
                   1040: }
                   1041: 
                   1042: 
                   1043: 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)
                   1044: {
                   1045:        Bucket *p;
                   1046:        void *t;
                   1047: 
                   1048:        IS_CONSISTENT(source);
                   1049:        IS_CONSISTENT(target);
                   1050: 
                   1051:        p = source->pListHead;
                   1052:        while (p) {
                   1053:                if (zend_hash_replace_checker_wrapper(target, p->pData, p, pParam, pMergeSource)) {
                   1054:                        if (zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
                   1055:                                pCopyConstructor(t);
                   1056:                        }
                   1057:                }
                   1058:                p = p->pListNext;
                   1059:        }
                   1060:        target->pInternalPointer = target->pListHead;
                   1061: }
                   1062: 
                   1063: 
                   1064: ZEND_API ulong zend_get_hash_value(const char *arKey, uint nKeyLength)
                   1065: {
                   1066:        return zend_inline_hash_func(arKey, nKeyLength);
                   1067: }
                   1068: 
                   1069: 
                   1070: /* Returns SUCCESS if found and FAILURE if not. The pointer to the
                   1071:  * data is returned in pData. The reason is that there's no reason
                   1072:  * someone using the hash table might not want to have NULL data
                   1073:  */
                   1074: ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
                   1075: {
                   1076:        ulong h;
                   1077:        uint nIndex;
                   1078:        Bucket *p;
                   1079: 
                   1080:        IS_CONSISTENT(ht);
                   1081: 
                   1082:        h = zend_inline_hash_func(arKey, nKeyLength);
                   1083:        nIndex = h & ht->nTableMask;
                   1084: 
                   1085:        p = ht->arBuckets[nIndex];
                   1086:        while (p != NULL) {
                   1087:                if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
                   1088:                        if (!memcmp(p->arKey, arKey, nKeyLength)) {
                   1089:                                *pData = p->pData;
                   1090:                                return SUCCESS;
                   1091:                        }
                   1092:                }
                   1093:                p = p->pNext;
                   1094:        }
                   1095:        return FAILURE;
                   1096: }
                   1097: 
                   1098: 
                   1099: ZEND_API int zend_hash_quick_find(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void **pData)
                   1100: {
                   1101:        uint nIndex;
                   1102:        Bucket *p;
                   1103: 
                   1104:        if (nKeyLength==0) {
                   1105:                return zend_hash_index_find(ht, h, pData);
                   1106:        }
                   1107: 
                   1108:        IS_CONSISTENT(ht);
                   1109: 
                   1110:        nIndex = h & ht->nTableMask;
                   1111: 
                   1112:        p = ht->arBuckets[nIndex];
                   1113:        while (p != NULL) {
                   1114:                if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
                   1115:                        if (!memcmp(p->arKey, arKey, nKeyLength)) {
                   1116:                                *pData = p->pData;
                   1117:                                return SUCCESS;
                   1118:                        }
                   1119:                }
                   1120:                p = p->pNext;
                   1121:        }
                   1122:        return FAILURE;
                   1123: }
                   1124: 
                   1125: 
                   1126: ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
                   1127: {
                   1128:        ulong h;
                   1129:        uint nIndex;
                   1130:        Bucket *p;
                   1131: 
                   1132:        IS_CONSISTENT(ht);
                   1133: 
                   1134:        h = zend_inline_hash_func(arKey, nKeyLength);
                   1135:        nIndex = h & ht->nTableMask;
                   1136: 
                   1137:        p = ht->arBuckets[nIndex];
                   1138:        while (p != NULL) {
                   1139:                if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
                   1140:                        if (!memcmp(p->arKey, arKey, nKeyLength)) {
                   1141:                                return 1;
                   1142:                        }
                   1143:                }
                   1144:                p = p->pNext;
                   1145:        }
                   1146:        return 0;
                   1147: }
                   1148: 
                   1149: 
                   1150: ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h)
                   1151: {
                   1152:        uint nIndex;
                   1153:        Bucket *p;
                   1154: 
                   1155:        if (nKeyLength==0) {
                   1156:                return zend_hash_index_exists(ht, h);
                   1157:        }
                   1158: 
                   1159:        IS_CONSISTENT(ht);
                   1160: 
                   1161:        nIndex = h & ht->nTableMask;
                   1162: 
                   1163:        p = ht->arBuckets[nIndex];
                   1164:        while (p != NULL) {
                   1165:                if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
                   1166:                        if (!memcmp(p->arKey, arKey, nKeyLength)) {
                   1167:                                return 1;
                   1168:                        }
                   1169:                }
                   1170:                p = p->pNext;
                   1171:        }
                   1172:        return 0;
                   1173: 
                   1174: }
                   1175: 
                   1176: 
                   1177: ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
                   1178: {
                   1179:        uint nIndex;
                   1180:        Bucket *p;
                   1181: 
                   1182:        IS_CONSISTENT(ht);
                   1183: 
                   1184:        nIndex = h & ht->nTableMask;
                   1185: 
                   1186:        p = ht->arBuckets[nIndex];
                   1187:        while (p != NULL) {
                   1188:                if ((p->h == h) && (p->nKeyLength == 0)) {
                   1189:                        *pData = p->pData;
                   1190:                        return SUCCESS;
                   1191:                }
                   1192:                p = p->pNext;
                   1193:        }
                   1194:        return FAILURE;
                   1195: }
                   1196: 
                   1197: 
                   1198: ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h)
                   1199: {
                   1200:        uint nIndex;
                   1201:        Bucket *p;
                   1202: 
                   1203:        IS_CONSISTENT(ht);
                   1204: 
                   1205:        nIndex = h & ht->nTableMask;
                   1206: 
                   1207:        p = ht->arBuckets[nIndex];
                   1208:        while (p != NULL) {
                   1209:                if ((p->h == h) && (p->nKeyLength == 0)) {
                   1210:                        return 1;
                   1211:                }
                   1212:                p = p->pNext;
                   1213:        }
                   1214:        return 0;
                   1215: }
                   1216: 
                   1217: 
                   1218: ZEND_API int zend_hash_num_elements(const HashTable *ht)
                   1219: {
                   1220:        IS_CONSISTENT(ht);
                   1221: 
                   1222:        return ht->nNumOfElements;
                   1223: }
                   1224: 
                   1225: 
                   1226: ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
                   1227: {
                   1228:        ptr->pos = ht->pInternalPointer;
                   1229:        if (ht->pInternalPointer) {
                   1230:                ptr->h = ht->pInternalPointer->h;
                   1231:                return 1;
                   1232:        } else {
                   1233:                ptr->h = 0;
                   1234:                return 0;
                   1235:        }
                   1236: }
                   1237: 
                   1238: ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
                   1239: {
                   1240:        if (ptr->pos == NULL) {
                   1241:                ht->pInternalPointer = NULL;
                   1242:        } else if (ht->pInternalPointer != ptr->pos) {
                   1243:                Bucket *p;
                   1244: 
                   1245:                IS_CONSISTENT(ht);
                   1246:                p = ht->arBuckets[ptr->h & ht->nTableMask];
                   1247:                while (p != NULL) {
                   1248:                        if (p == ptr->pos) {
                   1249:                                ht->pInternalPointer = p;
                   1250:                                return 1;
                   1251:                        }
                   1252:                        p = p->pNext;
                   1253:                }
                   1254:                return 0;
                   1255:        }
                   1256:        return 1;
                   1257: }
                   1258: 
                   1259: ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
                   1260: {
                   1261:        IS_CONSISTENT(ht);
                   1262: 
                   1263:        if (pos)
                   1264:                *pos = ht->pListHead;
                   1265:        else
                   1266:                ht->pInternalPointer = ht->pListHead;
                   1267: }
                   1268: 
                   1269: 
                   1270: /* This function will be extremely optimized by remembering 
                   1271:  * the end of the list
                   1272:  */
                   1273: ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
                   1274: {
                   1275:        IS_CONSISTENT(ht);
                   1276: 
                   1277:        if (pos)
                   1278:                *pos = ht->pListTail;
                   1279:        else
                   1280:                ht->pInternalPointer = ht->pListTail;
                   1281: }
                   1282: 
                   1283: 
                   1284: ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
                   1285: {
                   1286:        HashPosition *current = pos ? pos : &ht->pInternalPointer;
                   1287: 
                   1288:        IS_CONSISTENT(ht);
                   1289: 
                   1290:        if (*current) {
                   1291:                *current = (*current)->pListNext;
                   1292:                return SUCCESS;
                   1293:        } else
                   1294:                return FAILURE;
                   1295: }
                   1296: 
                   1297: ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
                   1298: {
                   1299:        HashPosition *current = pos ? pos : &ht->pInternalPointer;
                   1300: 
                   1301:        IS_CONSISTENT(ht);
                   1302: 
                   1303:        if (*current) {
                   1304:                *current = (*current)->pListLast;
                   1305:                return SUCCESS;
                   1306:        } else
                   1307:                return FAILURE;
                   1308: }
                   1309: 
                   1310: 
                   1311: /* This function should be made binary safe  */
                   1312: 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)
                   1313: {
                   1314:        Bucket *p;
                   1315: 
                   1316:        p = pos ? (*pos) : ht->pInternalPointer;
                   1317: 
                   1318:        IS_CONSISTENT(ht);
                   1319: 
                   1320:        if (p) {
                   1321:                if (p->nKeyLength) {
                   1322:                        if (duplicate) {
                   1323:                                *str_index = estrndup(p->arKey, p->nKeyLength - 1);
                   1324:                        } else {
                   1325:                                *str_index = p->arKey;
                   1326:                        }
                   1327:                        if (str_length) {
                   1328:                                *str_length = p->nKeyLength;
                   1329:                        }
                   1330:                        return HASH_KEY_IS_STRING;
                   1331:                } else {
                   1332:                        *num_index = p->h;
                   1333:                        return HASH_KEY_IS_LONG;
                   1334:                }
                   1335:        }
                   1336:        return HASH_KEY_NON_EXISTANT;
                   1337: }
                   1338: 
                   1339: 
                   1340: ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
                   1341: {
                   1342:        Bucket *p;
                   1343: 
                   1344:        p = pos ? (*pos) : ht->pInternalPointer;
                   1345: 
                   1346:        IS_CONSISTENT(ht);
                   1347: 
                   1348:        if (p) {
                   1349:                if (p->nKeyLength) {
                   1350:                        return HASH_KEY_IS_STRING;
                   1351:                } else {
                   1352:                        return HASH_KEY_IS_LONG;
                   1353:                }
                   1354:        }
                   1355:        return HASH_KEY_NON_EXISTANT;
                   1356: }
                   1357: 
                   1358: 
                   1359: ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos)
                   1360: {
                   1361:        Bucket *p;
                   1362: 
                   1363:        p = pos ? (*pos) : ht->pInternalPointer;
                   1364: 
                   1365:        IS_CONSISTENT(ht);
                   1366: 
                   1367:        if (p) {
                   1368:                *pData = p->pData;
                   1369:                return SUCCESS;
                   1370:        } else {
                   1371:                return FAILURE;
                   1372:        }
                   1373: }
                   1374: 
                   1375: /* This function changes key of current element without changing elements'
                   1376:  * order. If element with target key already exists, it will be deleted first.
                   1377:  */
                   1378: 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)
                   1379: {
                   1380:        Bucket *p, *q;
                   1381: 
                   1382:        p = pos ? (*pos) : ht->pInternalPointer;
                   1383: 
                   1384:        IS_CONSISTENT(ht);
                   1385: 
                   1386:        zend_hash_check_destructor(ht->pDestructor);
                   1387:        if (p) {
                   1388:                if (key_type == HASH_KEY_IS_LONG) {
                   1389:                        str_length = 0;
                   1390:                        if (!p->nKeyLength && p->h == num_index) {
                   1391:                                return SUCCESS;
                   1392:                        }
                   1393: 
                   1394:                        q = ht->arBuckets[num_index & ht->nTableMask];
                   1395:                        while (q != NULL) {
                   1396:                                if (!q->nKeyLength && q->h == num_index) {
                   1397:                                        break;
                   1398:                                }
                   1399:                                q = q->pNext;
                   1400:                        }
                   1401:                } else if (key_type == HASH_KEY_IS_STRING) {
                   1402:                        ulong h;
                   1403: 
                   1404:                        if (p->nKeyLength == str_length &&
                   1405:                            memcmp(p->arKey, str_index, str_length) == 0) {
                   1406:                                return SUCCESS;
                   1407:                        }
                   1408: 
                   1409:                        h = zend_inline_hash_func(str_index, str_length);
                   1410:                        q = ht->arBuckets[h & ht->nTableMask];
                   1411: 
                   1412:                        while (q != NULL) {
                   1413:                                if (q->h == h && q->nKeyLength == str_length && 
                   1414:                                           memcmp(q->arKey, str_index, str_length) == 0) {
                   1415:                                        break;
                   1416:                                }
                   1417:                                q = q->pNext;
                   1418:                        }
                   1419:                } else {
                   1420:                        return FAILURE;
                   1421:                }
                   1422: 
                   1423:                HANDLE_BLOCK_INTERRUPTIONS();
                   1424: 
                   1425:                if (q) {
                   1426:                        if (mode != HASH_UPDATE_KEY_ANYWAY) {
                   1427:                                Bucket *r = p->pListLast;
                   1428:                                int found = HASH_UPDATE_KEY_IF_BEFORE;
                   1429:                                                
                   1430:                                while (r) {
                   1431:                                        if (r == q) {
                   1432:                                                found = HASH_UPDATE_KEY_IF_AFTER;
                   1433:                                                break;
                   1434:                                        }
                   1435:                                        r = r->pListLast;
                   1436:                                }
                   1437:                                if (mode & found) {
                   1438:                                        /* delete current bucket */
                   1439:                                        if (p == ht->arBuckets[p->h & ht->nTableMask]) {
                   1440:                                                ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
                   1441:                                        } else {
                   1442:                                                p->pLast->pNext = p->pNext;
                   1443:                                        }
                   1444:                                        if (p->pNext) {
                   1445:                                                p->pNext->pLast = p->pLast;
                   1446:                                        }
                   1447:                                        if (p->pListLast != NULL) {
                   1448:                                                p->pListLast->pListNext = p->pListNext;
                   1449:                                        } else { 
                   1450:                                                /* Deleting the head of the list */
                   1451:                                                ht->pListHead = p->pListNext;
                   1452:                                        }
                   1453:                                        if (p->pListNext != NULL) {
                   1454:                                                p->pListNext->pListLast = p->pListLast;
                   1455:                                        } else {
                   1456:                                                ht->pListTail = p->pListLast;
                   1457:                                        }
                   1458:                                        if (ht->pInternalPointer == p) {
                   1459:                                                ht->pInternalPointer = p->pListNext;
                   1460:                                        }
                   1461:                                        if (ht->pDestructor) {
                   1462:                                                ht->pDestructor(p->pData);
                   1463:                                        }
                   1464:                                        if (p->pData != &p->pDataPtr) {
                   1465:                                                pefree(p->pData, ht->persistent);
                   1466:                                        }
                   1467:                                        pefree(p, ht->persistent);
                   1468:                                        ht->nNumOfElements--;
                   1469:                                        HANDLE_UNBLOCK_INTERRUPTIONS();
                   1470:                                        return FAILURE;
                   1471:                                }
                   1472:                        }
                   1473:                        /* delete another bucket with the same key */
                   1474:                        if (q == ht->arBuckets[q->h & ht->nTableMask]) {
                   1475:                                ht->arBuckets[q->h & ht->nTableMask] = q->pNext;
                   1476:                        } else {
                   1477:                                q->pLast->pNext = q->pNext;
                   1478:                        }
                   1479:                        if (q->pNext) {
                   1480:                                q->pNext->pLast = q->pLast;
                   1481:                        }
                   1482:                        if (q->pListLast != NULL) {
                   1483:                                q->pListLast->pListNext = q->pListNext;
                   1484:                        } else { 
                   1485:                                /* Deleting the head of the list */
                   1486:                                ht->pListHead = q->pListNext;
                   1487:                        }
                   1488:                        if (q->pListNext != NULL) {
                   1489:                                q->pListNext->pListLast = q->pListLast;
                   1490:                        } else {
                   1491:                                ht->pListTail = q->pListLast;
                   1492:                        }
                   1493:                        if (ht->pInternalPointer == q) {
                   1494:                                ht->pInternalPointer = q->pListNext;
                   1495:                        }
                   1496:                        if (ht->pDestructor) {
                   1497:                                ht->pDestructor(q->pData);
                   1498:                        }
                   1499:                        if (q->pData != &q->pDataPtr) {
                   1500:                                pefree(q->pData, ht->persistent);
                   1501:                        }
                   1502:                        pefree(q, ht->persistent);
                   1503:                        ht->nNumOfElements--;
                   1504:                }
                   1505: 
                   1506:                if (p->pNext) {
                   1507:                        p->pNext->pLast = p->pLast;
                   1508:                }
                   1509:                if (p->pLast) {
                   1510:                        p->pLast->pNext = p->pNext;
                   1511:                } else {
                   1512:                        ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
                   1513:                }
                   1514: 
                   1515:                if (p->nKeyLength != str_length) {
                   1516:                        Bucket *q = (Bucket *) pemalloc(sizeof(Bucket) - 1 + str_length, ht->persistent);
                   1517: 
                   1518:                        q->nKeyLength = str_length;
                   1519:                        if (p->pData == &p->pDataPtr) {
                   1520:                                q->pData = &q->pDataPtr;
                   1521:                        } else {
                   1522:                                q->pData = p->pData;
                   1523:                        }
                   1524:                        q->pDataPtr = p->pDataPtr;
                   1525:                        q->pListNext = p->pListNext;
                   1526:                        q->pListLast = p->pListLast;
                   1527:                        if (q->pListNext) {
                   1528:                                p->pListNext->pListLast = q;
                   1529:                        } else {
                   1530:                                ht->pListTail = q;
                   1531:                        }
                   1532:                        if (q->pListLast) {
                   1533:                                p->pListLast->pListNext = q;
                   1534:                        } else {
                   1535:                                ht->pListHead = q;
                   1536:                        }
                   1537:                        if (ht->pInternalPointer == p) {
                   1538:                                ht->pInternalPointer = q;
                   1539:                        }
                   1540:                        if (pos) {
                   1541:                                *pos = q;
                   1542:                        }
                   1543:                        pefree(p, ht->persistent);
                   1544:                        p = q;
                   1545:                }
                   1546: 
                   1547:                if (key_type == HASH_KEY_IS_LONG) {
                   1548:                        p->h = num_index;
                   1549:                } else {
                   1550:                        memcpy(p->arKey, str_index, str_length);
                   1551:                        p->h = zend_inline_hash_func(str_index, str_length);
                   1552:                }
                   1553: 
                   1554:                CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[p->h & ht->nTableMask]);
                   1555:                ht->arBuckets[p->h & ht->nTableMask] = p;
                   1556:                HANDLE_UNBLOCK_INTERRUPTIONS();
                   1557: 
                   1558:                return SUCCESS;
                   1559:        } else {
                   1560:                return FAILURE;
                   1561:        }
                   1562: }
                   1563: 
                   1564: ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
                   1565:                                                        compare_func_t compar, int renumber TSRMLS_DC)
                   1566: {
                   1567:        Bucket **arTmp;
                   1568:        Bucket *p;
                   1569:        int i, j;
                   1570: 
                   1571:        IS_CONSISTENT(ht);
                   1572: 
                   1573:        if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
                   1574:                return SUCCESS;
                   1575:        }
                   1576:        arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
                   1577:        if (!arTmp) {
                   1578:                return FAILURE;
                   1579:        }
                   1580:        p = ht->pListHead;
                   1581:        i = 0;
                   1582:        while (p) {
                   1583:                arTmp[i] = p;
                   1584:                p = p->pListNext;
                   1585:                i++;
                   1586:        }
                   1587: 
                   1588:        (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
                   1589: 
                   1590:        HANDLE_BLOCK_INTERRUPTIONS();
                   1591:        ht->pListHead = arTmp[0];
                   1592:        ht->pListTail = NULL;
                   1593:        ht->pInternalPointer = ht->pListHead;
                   1594: 
                   1595:        arTmp[0]->pListLast = NULL;
                   1596:        if (i > 1) {
                   1597:                arTmp[0]->pListNext = arTmp[1];
                   1598:                for (j = 1; j < i-1; j++) {
                   1599:                        arTmp[j]->pListLast = arTmp[j-1];
                   1600:                        arTmp[j]->pListNext = arTmp[j+1];
                   1601:                }
                   1602:                arTmp[j]->pListLast = arTmp[j-1];
                   1603:                arTmp[j]->pListNext = NULL;
                   1604:        } else {
                   1605:                arTmp[0]->pListNext = NULL;
                   1606:        }
                   1607:        ht->pListTail = arTmp[i-1];
                   1608: 
                   1609:        pefree(arTmp, ht->persistent);
                   1610:        HANDLE_UNBLOCK_INTERRUPTIONS();
                   1611: 
                   1612:        if (renumber) {
                   1613:                p = ht->pListHead;
                   1614:                i=0;
                   1615:                while (p != NULL) {
                   1616:                        p->nKeyLength = 0;
                   1617:                        p->h = i++;
                   1618:                        p = p->pListNext;
                   1619:                }
                   1620:                ht->nNextFreeElement = i;
                   1621:                zend_hash_rehash(ht);
                   1622:        }
                   1623:        return SUCCESS;
                   1624: }
                   1625: 
                   1626: 
                   1627: ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
                   1628: {
                   1629:        Bucket *p1, *p2 = NULL;
                   1630:        int result;
                   1631:        void *pData2;
                   1632: 
                   1633:        IS_CONSISTENT(ht1);
                   1634:        IS_CONSISTENT(ht2);
                   1635: 
                   1636:        HASH_PROTECT_RECURSION(ht1); 
                   1637:        HASH_PROTECT_RECURSION(ht2); 
                   1638: 
                   1639:        result = ht1->nNumOfElements - ht2->nNumOfElements;
                   1640:        if (result!=0) {
                   1641:                HASH_UNPROTECT_RECURSION(ht1); 
                   1642:                HASH_UNPROTECT_RECURSION(ht2); 
                   1643:                return result;
                   1644:        }
                   1645: 
                   1646:        p1 = ht1->pListHead;
                   1647:        if (ordered) {
                   1648:                p2 = ht2->pListHead;
                   1649:        }
                   1650: 
                   1651:        while (p1) {
                   1652:                if (ordered && !p2) {
                   1653:                        HASH_UNPROTECT_RECURSION(ht1); 
                   1654:                        HASH_UNPROTECT_RECURSION(ht2); 
                   1655:                        return 1; /* That's not supposed to happen */
                   1656:                }
                   1657:                if (ordered) {
                   1658:                        if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */
                   1659:                                result = p1->h - p2->h;
                   1660:                                if (result!=0) {
                   1661:                                        HASH_UNPROTECT_RECURSION(ht1); 
                   1662:                                        HASH_UNPROTECT_RECURSION(ht2); 
                   1663:                                        return result;
                   1664:                                }
                   1665:                        } else { /* string indices */
                   1666:                                result = p1->nKeyLength - p2->nKeyLength;
                   1667:                                if (result!=0) {
                   1668:                                        HASH_UNPROTECT_RECURSION(ht1); 
                   1669:                                        HASH_UNPROTECT_RECURSION(ht2); 
                   1670:                                        return result;
                   1671:                                }
                   1672:                                result = memcmp(p1->arKey, p2->arKey, p1->nKeyLength);
                   1673:                                if (result!=0) {
                   1674:                                        HASH_UNPROTECT_RECURSION(ht1); 
                   1675:                                        HASH_UNPROTECT_RECURSION(ht2); 
                   1676:                                        return result;
                   1677:                                }
                   1678:                        }
                   1679:                        pData2 = p2->pData;
                   1680:                } else {
                   1681:                        if (p1->nKeyLength==0) { /* numeric index */
                   1682:                                if (zend_hash_index_find(ht2, p1->h, &pData2)==FAILURE) {
                   1683:                                        HASH_UNPROTECT_RECURSION(ht1); 
                   1684:                                        HASH_UNPROTECT_RECURSION(ht2); 
                   1685:                                        return 1;
                   1686:                                }
                   1687:                        } else { /* string index */
                   1688:                                if (zend_hash_quick_find(ht2, p1->arKey, p1->nKeyLength, p1->h, &pData2)==FAILURE) {
                   1689:                                        HASH_UNPROTECT_RECURSION(ht1); 
                   1690:                                        HASH_UNPROTECT_RECURSION(ht2); 
                   1691:                                        return 1;
                   1692:                                }
                   1693:                        }
                   1694:                }
                   1695:                result = compar(p1->pData, pData2 TSRMLS_CC);
                   1696:                if (result!=0) {
                   1697:                        HASH_UNPROTECT_RECURSION(ht1); 
                   1698:                        HASH_UNPROTECT_RECURSION(ht2); 
                   1699:                        return result;
                   1700:                }
                   1701:                p1 = p1->pListNext;
                   1702:                if (ordered) {
                   1703:                        p2 = p2->pListNext;
                   1704:                }
                   1705:        }
                   1706:        
                   1707:        HASH_UNPROTECT_RECURSION(ht1); 
                   1708:        HASH_UNPROTECT_RECURSION(ht2); 
                   1709:        return 0;
                   1710: }
                   1711: 
                   1712: 
                   1713: ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC)
                   1714: {
                   1715:        Bucket *p, *res;
                   1716: 
                   1717:        IS_CONSISTENT(ht);
                   1718: 
                   1719:        if (ht->nNumOfElements == 0 ) {
                   1720:                *pData=NULL;
                   1721:                return FAILURE;
                   1722:        }
                   1723: 
                   1724:        res = p = ht->pListHead;
                   1725:        while ((p = p->pListNext)) {
                   1726:                if (flag) {
                   1727:                        if (compar(&res, &p TSRMLS_CC) < 0) { /* max */
                   1728:                                res = p;
                   1729:                        }
                   1730:                } else {
                   1731:                        if (compar(&res, &p TSRMLS_CC) > 0) { /* min */
                   1732:                                res = p;
                   1733:                        }
                   1734:                }
                   1735:        }
                   1736:        *pData = res->pData;
                   1737:        return SUCCESS;
                   1738: }
                   1739: 
                   1740: ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
                   1741: {
                   1742:        IS_CONSISTENT(ht);
                   1743: 
                   1744:        return ht->nNextFreeElement;
                   1745: 
                   1746: }
                   1747: 
                   1748: 
                   1749: #if ZEND_DEBUG
                   1750: void zend_hash_display_pListTail(const HashTable *ht)
                   1751: {
                   1752:        Bucket *p;
                   1753: 
                   1754:        p = ht->pListTail;
                   1755:        while (p != NULL) {
                   1756:                zend_output_debug_string(0, "pListTail has key %s\n", p->arKey);
                   1757:                p = p->pListLast;
                   1758:        }
                   1759: }
                   1760: 
                   1761: void zend_hash_display(const HashTable *ht)
                   1762: {
                   1763:        Bucket *p;
                   1764:        uint i;
                   1765: 
                   1766:        for (i = 0; i < ht->nTableSize; i++) {
                   1767:                p = ht->arBuckets[i];
                   1768:                while (p != NULL) {
                   1769:                        zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
                   1770:                        p = p->pNext;
                   1771:                }
                   1772:        }
                   1773: 
                   1774:        p = ht->pListTail;
                   1775:        while (p != NULL) {
                   1776:                zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
                   1777:                p = p->pListLast;
                   1778:        }
                   1779: }
                   1780: #endif
                   1781: 
                   1782: /*
                   1783:  * Local variables:
                   1784:  * tab-width: 4
                   1785:  * c-basic-offset: 4
                   1786:  * indent-tabs-mode: t
                   1787:  * End:
                   1788:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>