static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement) { uint32_t idx, nIndex; Bucket *p; if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) { HT_SET_DATA_ADDR(ht, &uninitialized_bucket); return; } if (ht->nNumUsed == 0) { efree(HT_GET_DATA_ADDR(ht)); ht->nTableMask = HT_MIN_MASK; HT_SET_DATA_ADDR(ht, &uninitialized_bucket); ht->u.flags &= ~HASH_FLAG_INITIALIZED; return; } if (ht->u.flags & HASH_FLAG_PACKED) { void *data = HT_GET_DATA_ADDR(ht); zend_accel_store(data, HT_USED_SIZE(ht)); HT_SET_DATA_ADDR(ht, data); } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 2) { /* compact table */ void *old_data = HT_GET_DATA_ADDR(ht); Bucket *old_buckets = ht->arData; uint32_t hash_size; if (ht->nNumUsed <= HT_MIN_SIZE) { hash_size = HT_MIN_SIZE; } else { hash_size = (uint32_t)(-(int32_t)ht->nTableMask); while (hash_size >> 1 > ht->nNumUsed) { hash_size >>= 1; } } ht->nTableMask = (uint32_t)(-(int32_t)hash_size); ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ HT_SET_DATA_ADDR(ht, ZCG(mem)); ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE((hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket)))); HT_HASH_RESET(ht); memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket)); efree(old_data); for (idx = 0; idx < ht->nNumUsed; idx++) { p = ht->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; /* persist bucket and key */ if (p->key) { zend_accel_store_interned_string(p->key); } /* persist the data itself */ pPersistElement(&p->val); nIndex = p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); } return; } else {
static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_string *key, zval *pData, uint32_t flag ZEND_FILE_LINE_DC) { zend_ulong h; uint32_t nIndex; uint32_t idx; Bucket *p; IS_CONSISTENT(ht); if (UNEXPECTED(ht->nTableMask == 0)) { CHECK_INIT(ht, 0); goto add_to_hash; } else if (ht->u.flags & HASH_FLAG_PACKED) { zend_hash_packed_to_hash(ht); } else if ((flag & HASH_ADD_NEW) == 0) { p = zend_hash_find_bucket(ht, key); if (p) { zval *data; if (flag & HASH_ADD) { return NULL; } ZEND_ASSERT(&p->val != pData); data = &p->val; if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) { data = Z_INDIRECT_P(data); } HANDLE_BLOCK_INTERRUPTIONS(); if (ht->pDestructor) { ht->pDestructor(data); } ZVAL_COPY_VALUE(data, pData); HANDLE_UNBLOCK_INTERRUPTIONS(); return data; } } ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */ add_to_hash: HANDLE_BLOCK_INTERRUPTIONS(); idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = idx; } p = ht->arData + idx; p->h = h = zend_string_hash_val(key); p->key = key; zend_string_addref(key); ZVAL_COPY_VALUE(&p->val, pData); nIndex = h & ht->nTableMask; Z_NEXT(p->val) = ht->arHash[nIndex]; ht->arHash[nIndex] = idx; HANDLE_UNBLOCK_INTERRUPTIONS(); return &p->val; }
static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce) { uint idx; Bucket *p, *q; zend_ulong nIndex; zend_op_array *new_entry; ht->nTableSize = source->nTableSize; ht->nTableMask = source->nTableMask; ht->nNumUsed = 0; ht->nNumOfElements = source->nNumOfElements; ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = ZEND_FUNCTION_DTOR; ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED); ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX; if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) { ht->arHash = (uint32_t*)&uninitialized_bucket; return; } ZEND_ASSERT(!(source->u.flags & HASH_FLAG_PACKED)); ht->arData = (Bucket *) emalloc(ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t))); ht->arHash = (uint32_t *)(ht->arData + ht->nTableSize); memset(ht->arHash, INVALID_IDX, sizeof(uint32_t) * ht->nTableSize); for (idx = 0; idx < source->nNumUsed; idx++) { p = source->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; nIndex = p->h & ht->nTableMask; /* Insert into hash collision list */ q = ht->arData + ht->nNumUsed; Z_NEXT(q->val) = ht->arHash[nIndex]; ht->arHash[nIndex] = ht->nNumUsed++; /* Initialize key */ q->h = p->h; ZEND_ASSERT(p->key != NULL); q->key = zend_clone_str(p->key); /* Copy data */ ZVAL_PTR(&q->val, ARENA_REALLOC(Z_PTR(p->val))); new_entry = (zend_op_array*)Z_PTR(q->val); if ((void*)new_entry->scope >= ZCG(current_persistent_script)->arena_mem && (void*)new_entry->scope < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) { new_entry->scope = ARENA_REALLOC(new_entry->scope); /* update prototype */ if (new_entry->prototype) { new_entry->prototype = ARENA_REALLOC(new_entry->prototype); } } } }
static zend_always_inline int apc_array_dup_element(apc_context_t *ctxt, HashTable *source, HashTable *target, uint32_t idx, Bucket *p, Bucket *q, int packed, int static_keys, int with_holes) { zval *data = &p->val; if (with_holes) { if (!packed && Z_TYPE_INFO_P(data) == IS_INDIRECT) { data = Z_INDIRECT_P(data); } if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) { return 0; } } else if (!packed) { /* INDIRECT element may point to UNDEF-ined slots */ if (Z_TYPE_INFO_P(data) == IS_INDIRECT) { data = Z_INDIRECT_P(data); if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) { return 0; } } } do { if (Z_OPT_REFCOUNTED_P(data)) { if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1 && (Z_TYPE_P(Z_REFVAL_P(data)) != IS_ARRAY || Z_ARRVAL_P(Z_REFVAL_P(data)) != source)) { data = Z_REFVAL_P(data); if (!Z_OPT_REFCOUNTED_P(data)) { break; } } } } while (0); my_copy_zval(&q->val, data, ctxt); q->h = p->h; if (packed) { q->key = NULL; } else { uint32_t nIndex; q->key = p->key; if (!static_keys && q->key) { if (ctxt->copy == APC_COPY_IN) { q->key = apc_pstrcpy(q->key, ctxt->pool); } else q->key = p->key; } nIndex = q->h | target->nTableMask; Z_NEXT(q->val) = HT_HASH(target, nIndex); HT_HASH(target, nIndex) = HT_IDX_TO_HASH(idx); } return 1; }
static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_uint_t h) { uint nIndex; uint idx; Bucket *p; nIndex = h & ht->nTableMask; idx = ht->arHash[nIndex]; while (idx != INVALID_IDX) { ZEND_ASSERT(idx < ht->nTableSize); p = ht->arData + idx; if (p->h == h && !p->key) { return p; } idx = Z_NEXT(p->val); } return NULL; }
static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ulong h, const char *str, size_t size, HashTable *interned_strings) { uint32_t nIndex; uint32_t idx; Bucket *p; nIndex = h | interned_strings->nTableMask; idx = HT_HASH(interned_strings, nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(interned_strings, idx); if ((p->h == h) && (ZSTR_LEN(p->key) == size)) { if (!memcmp(ZSTR_VAL(p->key), str, size)) { return p->key; } } idx = Z_NEXT(p->val); } return NULL; }
static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht, const char *str, int len, zend_uint_t h) { uint nIndex; uint idx; Bucket *p; nIndex = h & ht->nTableMask; idx = ht->arHash[nIndex]; while (idx != INVALID_IDX) { ZEND_ASSERT(idx < ht->nTableSize); p = ht->arData + idx; if ((p->h == h) && p->key && (p->key->len == len) && !memcmp(p->key->val, str, len)) { return p; } idx = Z_NEXT(p->val); } return NULL; }
static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) { zend_uint_t h; uint nIndex; uint idx; Bucket *p; h = STR_HASH_VAL(key); nIndex = h & ht->nTableMask; idx = ht->arHash[nIndex]; while (idx != INVALID_IDX) { p = ht->arData + idx; if ((p->key == key) || /* check for the the same interned string */ (p->h == h && p->key && p->key->len == key->len && memcmp(p->key->val, key->val, key->len) == 0)) { return p; } idx = Z_NEXT(p->val); } return NULL; }
static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_class_entry *old_ce) { uint idx; Bucket *p, *q; zend_ulong nIndex; zend_property_info *prop_info; ht->nTableSize = source->nTableSize; ht->nTableMask = source->nTableMask; ht->nNumUsed = 0; ht->nNumOfElements = source->nNumOfElements; ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = NULL; ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED); ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX; if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) { ht->arHash = (uint32_t*)&uninitialized_bucket; return; } ZEND_ASSERT(!(source->u.flags & HASH_FLAG_PACKED)); ht->arData = (Bucket *) emalloc(ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t))); ht->arHash = (uint32_t*)(ht->arData + ht->nTableSize); memset(ht->arHash, INVALID_IDX, sizeof(uint32_t) * ht->nTableSize); for (idx = 0; idx < source->nNumUsed; idx++) { p = source->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; nIndex = p->h & ht->nTableMask; /* Insert into hash collision list */ q = ht->arData + ht->nNumUsed; Z_NEXT(q->val) = ht->arHash[nIndex]; ht->arHash[nIndex] = ht->nNumUsed++; /* Initialize key */ q->h = p->h; ZEND_ASSERT(p->key != NULL); q->key = zend_clone_str(p->key); /* Copy data */ prop_info = ARENA_REALLOC(Z_PTR(p->val)); ZVAL_PTR(&q->val, prop_info); if (prop_info->ce == old_ce || (prop_info->flags & ZEND_ACC_SHADOW)) { /* Copy constructor */ prop_info->name = zend_clone_str(prop_info->name); if (prop_info->doc_comment) { if (ZCG(accel_directives).load_comments) { prop_info->doc_comment = zend_string_dup(prop_info->doc_comment, 0); } else { prop_info->doc_comment = NULL; } } prop_info->ce = ARENA_REALLOC(prop_info->ce); } else if ((void*)prop_info->ce >= ZCG(current_persistent_script)->arena_mem && (void*)prop_info->ce < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) { prop_info->ce = ARENA_REALLOC(prop_info->ce); } } }
static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind) { uint idx; Bucket *p, *q, *r; zend_ulong nIndex; ht->nTableSize = source->nTableSize; ht->nTableMask = source->nTableMask; ht->nNumUsed = 0; ht->nNumOfElements = source->nNumOfElements; ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = ZVAL_PTR_DTOR; ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED) | HASH_FLAG_APPLY_PROTECTION; ht->arData = NULL; ht->arHash = NULL; ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX; if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) { ht->arHash = (uint32_t*)&uninitialized_bucket; return; } if (source->u.flags & HASH_FLAG_PACKED) { ht->u.flags |= HASH_FLAG_PACKED; ht->arData = (Bucket *) emalloc(ht->nTableSize * sizeof(Bucket)); ht->arHash = (uint32_t*)&uninitialized_bucket; for (idx = 0; idx < source->nNumUsed; idx++) { p = source->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; nIndex = p->h & ht->nTableMask; r = ht->arData + ht->nNumUsed; q = ht->arData + p->h; while (r != q) { ZVAL_UNDEF(&r->val); r++; } ht->nNumUsed = p->h + 1; /* Initialize key */ q->h = p->h; q->key = NULL; /* Copy data */ ZVAL_COPY_VALUE(&q->val, &p->val); zend_clone_zval(&q->val, bind); } } else { ht->arData = (Bucket *) emalloc(ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t))); ht->arHash = (uint32_t*)(ht->arData + ht->nTableSize); memset(ht->arHash, INVALID_IDX, sizeof(uint32_t) * ht->nTableSize); for (idx = 0; idx < source->nNumUsed; idx++) { p = source->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; nIndex = p->h & ht->nTableMask; /* Insert into hash collision list */ q = ht->arData + ht->nNumUsed; Z_NEXT(q->val) = ht->arHash[nIndex]; ht->arHash[nIndex] = ht->nNumUsed++; /* Initialize key */ q->h = p->h; if (!p->key) { q->key = NULL; } else { q->key = zend_clone_str(p->key); } /* Copy data */ ZVAL_COPY_VALUE(&q->val, &p->val); zend_clone_zval(&q->val, bind); } } }
static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind) { uint idx; Bucket *p, *q, *r; zend_ulong nIndex; ht->nTableSize = source->nTableSize; ht->nTableMask = source->nTableMask; ht->nNumUsed = 0; ht->nNumOfElements = source->nNumOfElements; ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = ZVAL_PTR_DTOR; ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED) | HASH_FLAG_APPLY_PROTECTION; ht->nInternalPointer = source->nNumOfElements ? 0 : HT_INVALID_IDX; if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) { ht->arData = source->arData; return; } if (source->u.flags & HASH_FLAG_PACKED) { ht->u.flags |= HASH_FLAG_PACKED; HT_SET_DATA_ADDR(ht, (Bucket *) emalloc(HT_SIZE(ht))); HT_HASH_RESET_PACKED(ht); for (idx = 0; idx < source->nNumUsed; idx++) { p = source->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; nIndex = p->h | ht->nTableMask; r = ht->arData + ht->nNumUsed; q = ht->arData + p->h; while (r != q) { ZVAL_UNDEF(&r->val); r++; } ht->nNumUsed = p->h + 1; /* Initialize key */ q->h = p->h; q->key = NULL; /* Copy data */ ZVAL_COPY_VALUE(&q->val, &p->val); zend_clone_zval(&q->val, bind); } } else { HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); HT_HASH_RESET(ht); for (idx = 0; idx < source->nNumUsed; idx++) { p = source->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; nIndex = p->h | ht->nTableMask; /* Insert into hash collision list */ q = ht->arData + ht->nNumUsed; Z_NEXT(q->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(ht->nNumUsed++); /* Initialize key */ q->h = p->h; if (!p->key) { q->key = NULL; } else { q->key = zend_clone_str(p->key); } /* Copy data */ ZVAL_COPY_VALUE(&q->val, &p->val); zend_clone_zval(&q->val, bind); } } }
static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement) { uint32_t idx, nIndex; Bucket *p; HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; ht->pDestructor = NULL; ht->nInternalPointer = 0; if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); } else { HT_SET_DATA_ADDR(ht, &uninitialized_bucket); } return; } if (ht->nNumUsed == 0) { efree(HT_GET_DATA_ADDR(ht)); ht->nTableMask = HT_MIN_MASK; if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); } else { HT_SET_DATA_ADDR(ht, &uninitialized_bucket); } HT_FLAGS(ht) |= HASH_FLAG_UNINITIALIZED; return; } if (HT_FLAGS(ht) & HASH_FLAG_PACKED) { void *data = HT_GET_DATA_ADDR(ht); data = zend_shared_memdup_free(data, HT_USED_SIZE(ht)); HT_SET_DATA_ADDR(ht, data); } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { /* compact table */ void *old_data = HT_GET_DATA_ADDR(ht); Bucket *old_buckets = ht->arData; uint32_t hash_size; if (ht->nNumUsed <= HT_MIN_SIZE) { hash_size = HT_MIN_SIZE * 2; } else { hash_size = (uint32_t)(-(int32_t)ht->nTableMask); while (hash_size >> 2 > ht->nNumUsed) { hash_size >>= 1; } } ht->nTableMask = (uint32_t)(-(int32_t)hash_size); ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ HT_SET_DATA_ADDR(ht, ZCG(mem)); ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE((hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket)))); HT_HASH_RESET(ht); memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket)); efree(old_data); for (idx = 0; idx < ht->nNumUsed; idx++) { p = ht->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; /* persist bucket and key */ if (p->key) { zend_accel_store_interned_string(p->key); } /* persist the data itself */ pPersistElement(&p->val); nIndex = p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); } return; } else {