Annotation of embedaddon/php/Zend/zend_hash.c, revision 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>