MixedArray* StructArray::ToMixed(StructArray* old) { auto const oldSize = old->size(); auto const ad = ToMixedHeader(oldSize + 1); auto const srcData = old->data(); auto shape = old->shape(); memset(ad->hashTab(), static_cast<uint8_t>(MixedArray::Empty), sizeof(int32_t) * ad->hashSize()); for (auto i = 0; i < oldSize; ++i) { auto key = const_cast<StringData*>(shape->keyForOffset(i)); auto& e = ad->addKeyAndGetElem(key); tvCopy(srcData[i], e.data); } old->m_size = 0; ad->m_pos = old->m_pos; if (debug) { // For debug builds, set m_pos to 0 as well to make the // asserts in checkInvariants() happy. old->m_pos = 0; } assert(ad->checkInvariants()); assert(!ad->isFull()); assert(ad->hasExactlyOneRef()); return ad; }
/** * postSort() runs after the sort has been performed. For HphpArray, postSort() * handles rebuilding the hash. Also, if resetKeys is true, postSort() will * renumber the keys 0 thru n-1. */ void HphpArray::postSort(bool resetKeys) { assert(m_size > 0); auto const ht = hashTab(); initHash(ht, hashSize()); m_hLoad = 0; if (resetKeys) { for (uint32_t pos = 0; pos < m_used; ++pos) { auto& e = data()[pos]; if (e.hasStrKey()) decRefStr(e.key); e.setIntKey(pos); ht[pos] = pos; } m_nextKI = m_size; } else { auto mask = m_tableMask; auto data = this->data(); for (uint32_t pos = 0; pos < m_used; ++pos) { auto& e = data[pos]; auto ei = findForNewInsert(ht, mask, e.hasIntKey() ? e.ikey : e.hash()); *ei = pos; } } m_hLoad = m_size; }
/* * Creating a single-element mixed array with a integer key. The * value is already incref'd. */ std::pair<ArrayData*,TypedValue*> EmptyArray::MakeMixed(int64_t key, TypedValue val) { auto const ad = smartAllocArray(MixedArray::SmallScale); InitMixed(ad, 0/*count*/, 1/*size*/, (key >= 0) ? key + 1 : 0); auto const data = ad->data(); auto const hash = reinterpret_cast<int32_t*>(data + MixedArray::SmallSize); assert(ad->hashSize() == MixedArray::SmallHashSize); auto const emptyVal = int64_t{MixedArray::Empty}; reinterpret_cast<int64_t*>(hash)[0] = emptyVal; reinterpret_cast<int64_t*>(hash)[1] = emptyVal; auto const mask = MixedArray::SmallMask; hash[key & mask] = 0; data[0].setIntKey(key); auto& lval = data[0].data; lval.m_data = val.m_data; lval.m_type = val.m_type; assert(ad->kind() == ArrayData::kMixedKind); assert(ad->m_size == 1); assert(ad->m_pos == 0); assert(ad->getCount() == 0); assert(ad->m_scale == MixedArray::SmallScale); assert(ad->m_used == 1); assert(ad->checkInvariants()); return { ad, &lval }; }
STORAGE_SET *aliasAuxDataStore(ALIAS_AUX_DATA *data) { // if we have no hashtable, return an empty set if(data->aliases == NULL || hashSize(data->aliases) == 0) return new_storage_set(); STORAGE_SET *set = new_storage_set(); STORAGE_SET_LIST *list = new_storage_list(); HASH_ITERATOR *hash_i = newHashIterator(data->aliases); const char *name = NULL; const char *cmd = NULL; store_list(set, "aliases", list); ITERATE_HASH(name, cmd, hash_i) { STORAGE_SET *alias_set = new_storage_set(); store_string(alias_set, "key", name); store_string(alias_set, "val", hashIteratorCurrentVal(hash_i)); storage_list_put(list, alias_set); }
MixedArray* StructArray::ToMixedCopy(const StructArray* old) { auto const oldSize = old->size(); auto const ad = ToMixedHeader(oldSize + 1); auto const srcData = old->data(); auto shape = old->shape(); memset(ad->hashTab(), static_cast<uint8_t>(MixedArray::Empty), sizeof(int32_t) * ad->hashSize()); for (auto i = 0; i < oldSize; ++i) { auto key = const_cast<StringData*>(shape->keyForOffset(i)); auto& e = ad->addKeyAndGetElem(key); tvDupFlattenVars(&srcData[i], &e.data, old); } ad->m_pos = old->m_pos; assert(ad->checkInvariants()); assert(!ad->isFull()); assert(ad->hasExactlyOneRef()); return ad; }
typename std::enable_if< std::is_base_of<BaseSet, TSet>::value, Object>::type BaseSet::php_map(const Variant& callback) const { VMRegGuard _; CallCtx ctx; vm_decode_function(callback, nullptr, false, ctx); if (!ctx.func) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter must be a valid callback"); } auto set = req::make<TSet>(); if (!m_size) return Object{std::move(set)}; assert(posLimit() != 0); assert(hashSize() > 0); assert(set->arrayData() == staticEmptyMixedArray()); auto oldCap = set->cap(); set->reserve(posLimit()); // presume minimum collisions ... assert(set->canMutateBuffer()); constexpr int64_t argc = useKey ? 2 : 1; TypedValue argv[argc]; for (ssize_t pos = iter_begin(); iter_valid(pos); pos = iter_next(pos)) { auto e = iter_elm(pos); TypedValue tvCbRet; int32_t pVer = m_version; if (useKey) { argv[0] = e->data; } argv[argc-1] = e->data; g_context->invokeFuncFew(&tvCbRet, ctx, argc, argv); // Now that tvCbRet is live, make sure to decref even if we throw. SCOPE_EXIT { tvRefcountedDecRef(&tvCbRet); }; if (UNLIKELY(m_version != pVer)) throw_collection_modified(); set->addRaw(&tvCbRet); } // ... and shrink back if that was incorrect set->shrinkIfCapacityTooHigh(oldCap); return Object{std::move(set)}; }
/** Set the extra info (if any) */ virtual void finalizeWithExtraInfo(uint8 * outBuffer, const uint8 * extra, const uint32 extraLen) { uint8 hashArray[OutputSize + 64] = {0}; uint32 tmpSize = InputSize + 4 + extraLen; uint8 hashTmpArray[InputSize + 4 + BaseHasher::DigestSize] = {0}; uint8 * hashTmp = hashTmpArray; if ((extraLen && extra) || hasher.hashSize() > sizeof(hashTmpArray)) { hashTmp = new uint8[max(tmpSize, hasher.hashSize())]; if (!hashTmp) { if (outBuffer) memset(outBuffer, 0, hashSize()); return; // Far more error to expect when stack or heap is exhausted } memset(hashTmp, 0, tmpSize); } uint32 d = (OutputSize + hasher.hashSize() - 1) / hasher.hashSize(); for (uint32 i = 0; i < d; i++) { // Computing the inner hash memcpy(hashTmp, hashInput, InputSize); uint32 swappedI = Swap32(i); memcpy(&hashTmp[InputSize], &swappedI, sizeof(swappedI)); // Big endian if (extra && extraLen) memcpy(&hashTmp[InputSize + 4], extra, extraLen); // Hash such string now hasher.Start(); hasher.Hash(hashTmp, tmpSize); // Overwrite the data with the hash hasher.Finalize(hashTmp); memcpy(&hashArray[i * hasher.hashSize()], hashTmp, hasher.hashSize()); memset(hashTmp, 0, tmpSize); } if ((extraLen && extra) || hasher.hashSize() > sizeof(hashTmpArray)) delete[] hashTmp; if (outBuffer) memcpy(outBuffer, hashArray, OutputSize); }
HashCollection::Elm& HashCollection::allocElmFront(MixedArray::Inserter ei) { assert(MixedArray::isValidIns(ei) && !MixedArray::isValidPos(*ei)); assert(m_size <= posLimit() && posLimit() < cap()); // Move the existing elements to make element slot 0 available. memmove(data() + 1, data(), posLimit() * sizeof(Elm)); incPosLimit(); // Update the hashtable to reflect the fact that everything was // moved over one position auto* hash = hashTab(); auto* hashEnd = hash + hashSize(); for (; hash != hashEnd; ++hash) { if (validPos(*hash)) { ++(*hash); } } // Set the hash entry we found to point to element slot 0. (*ei) = 0; // Adjust m_pos so that is points at this new first element. arrayData()->m_pos = 0; // Adjust size to reflect that we're adding a new element. incSize(); // Store the value into element slot 0. return data()[0]; }
int raceCount() { return hashSize(race_table); }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::php_map(const Variant& callback) const { VMRegGuard _; CallCtx ctx; vm_decode_function(callback, nullptr, false, ctx); if (!ctx.func) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter must be a valid callback"); } auto map = req::make<TMap>(); if (!m_size) return Object{std::move(map)}; assert(posLimit() != 0); assert(hashSize() > 0); assert(map->arrayData() == staticEmptyMixedArray()); map->m_arr = MixedArray::asMixed(MixedArray::MakeReserveMixed(cap())); map->setIntLikeStrKeys(intLikeStrKeys()); wordcpy(map->hashTab(), hashTab(), hashSize()); { uint32_t used = posLimit(); int32_t version = m_version; uint32_t i = 0; // When the loop below finishes or when an exception is thrown, // make sure that posLimit() get set to the correct value and // that m_pos gets set to point to the first element. SCOPE_EXIT { map->setPosLimit(i); map->arrayData()->m_pos = map->nthElmPos(0); }; constexpr int64_t argc = useKey ? 2 : 1; TypedValue argv[argc]; for (; i < used; ++i) { const Elm& e = data()[i]; Elm& ne = map->data()[i]; if (isTombstone(i)) { ne.data.m_type = e.data.m_type; continue; } TypedValue* tv = &ne.data; if (useKey) { if (e.hasIntKey()) { argv[0].m_type = KindOfInt64; argv[0].m_data.num = e.ikey; } else { argv[0].m_type = KindOfString; argv[0].m_data.pstr = e.skey; } } argv[argc-1] = e.data; g_context->invokeFuncFew(tv, ctx, argc, argv); if (UNLIKELY(version != m_version)) { tvRefcountedDecRef(tv); throw_collection_modified(); } if (e.hasStrKey()) { e.skey->incRefCount(); } ne.ikey = e.ikey; ne.data.hash() = e.data.hash(); map->incSize(); // Needed so that the new elements are accounted for when GC scanning. map->incPosLimit(); } } return Object{std::move(map)}; }