ALWAYS_INLINE void BaseSet::addImpl(StringData *key) { if (!raw) { mutate(); } strhash_t h = key->hash(); auto p = findForInsert(key, h); assert(p); if (validPos(*p)) { return; } if (UNLIKELY(isFull())) { makeRoom(); p = findForInsert(key, h); } auto& e = allocElm(p); // This increments the string's refcount twice, once for // the key and once for the value e.setStrKey(key, h); cellDup(make_tv<KindOfString>(key), e.data); updateIntLikeStrKeys(key); if (!raw) { ++m_version; } }
ALWAYS_INLINE void BaseMap::setImpl(StringData* key, const TypedValue* val) { if (!raw) { mutate(); } assert(val->m_type != KindOfRef); assert(canMutateBuffer()); retry: strhash_t h = key->hash(); auto* p = findForInsert(key, h); assert(p); if (validPos(*p)) { auto& e = data()[*p]; TypedValue old = e.data; cellDup(*val, e.data); tvRefcountedDecRef(old); return; } if (UNLIKELY(isFull())) { makeRoom(); goto retry; } if (!raw) { ++m_version; } auto& e = allocElm(p); cellDup(*val, e.data); e.setStrKey(key, h); updateIntLikeStrKeys(key); }
void BaseMap::addAllImpl(const Variant& iterable) { if (iterable.isNull()) return; VMRegGuard _; decltype(cap()) oldCap = 0; bool ok = IterateKV( *iterable.asTypedValue(), [&](ArrayData* adata) { auto sz = adata->size(); if (!sz) return true; if (!m_size) { if (adata->isMixed()) { replaceArray(adata); updateIntLikeStrKeys(); return true; } } else { oldCap = cap(); // assume minimal collisions } reserve(m_size + sz); mutateAndBump(); return false; }, [this](const TypedValue* key, const TypedValue* value) { setRaw(tvAsCVarRef(key), tvAsCVarRef(value)); }, [this](ObjectData* coll) { switch (coll->collectionType()) { case CollectionType::Map: case CollectionType::Set: { if (m_size) break; auto hc = static_cast<HashCollection*>(coll); replaceArray(hc->arrayData()); setIntLikeStrKeys(BaseMap::intLikeStrKeys(hc)); return true; } case CollectionType::Pair: mutateAndBump(); break; default: break; } return false; }, [this](const TypedValue* key, const TypedValue* value) { set(tvAsCVarRef(key), tvAsCVarRef(value)); }); if (UNLIKELY(!ok)) { throw_invalid_collection_parameter(); } // ... and shrink back if that was incorrect if (oldCap) shrinkIfCapacityTooHigh(oldCap); }
typename std::enable_if< std::is_base_of<BaseSet, TSet>::value, Object>::type BaseSet::php_skip(const Variant& n) { if (!n.isInteger()) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter n must be an integer"); } int64_t len = n.toInt64(); if (len <= 0) { // We know the resulting Set will simply be a copy of this Set, // so we can just call Clone() and return early here. return Object::attach(TSet::Clone(this)); } auto set = req::make<TSet>(); if (len >= m_size) { // We know the resulting Set will be empty, so we can return // early here. return Object{std::move(set)}; } size_t sz = size_t(m_size) - size_t(len); assert(sz); set->reserve(sz); set->setSize(sz); set->setPosLimit(sz); uint32_t frPos = nthElmPos(len); auto table = set->hashTab(); auto mask = set->tableMask(); for (uint32_t toPos = 0; toPos < sz; ++toPos, ++frPos) { while (isTombstone(frPos)) { assert(frPos + 1 < posLimit()); ++frPos; } auto& toE = set->data()[toPos]; dupElm(data()[frPos], toE); *findForNewInsert(table, mask, toE.probe()) = toPos; if (toE.hasIntKey()) { set->updateNextKI(toE.ikey); } else { assert(toE.hasStrKey()); set->updateIntLikeStrKeys(toE.skey); } } return Object{std::move(set)}; }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::php_take(const Variant& n) { if (!n.isInteger()) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter n must be an integer"); } int64_t len = n.toInt64(); if (len >= int64_t(m_size)) { // We know the resulting Map will simply be a copy of this Map, // so we can just call Clone() and return early here. return Object::attach(TMap::Clone(this)); } auto map = req::make<TMap>(); if (len <= 0) { // We know the resulting Map will be empty, so we can return // early here. return Object{std::move(map)}; } size_t sz = size_t(len); map->reserve(sz); map->setSize(sz); map->setPosLimit(sz); auto table = map->hashTab(); auto mask = map->tableMask(); for (uint32_t frPos = 0, toPos = 0; toPos < sz; ++toPos, ++frPos) { frPos = skipTombstonesNoBoundsCheck(frPos); auto& toE = map->data()[toPos]; dupElm(data()[frPos], toE); *findForNewInsert(table, mask, toE.probe()) = toPos; if (toE.hasIntKey()) { map->updateNextKI(toE.ikey); } else { assert(toE.hasStrKey()); map->updateIntLikeStrKeys(toE.skey); } } return Object{std::move(map)}; }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::php_slice(const Variant& start, const Variant& len) { int64_t istart; int64_t ilen; if (!start.isInteger() || (istart = start.toInt64()) < 0) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter start must be a non-negative integer"); } if (!len.isInteger() || (ilen = len.toInt64()) < 0) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter len must be a non-negative integer"); } size_t skipAmt = std::min<size_t>(istart, m_size); size_t sz = std::min<size_t>(ilen, size_t(m_size) - skipAmt); auto map = req::make<TMap>(); map->reserve(sz); map->setSize(sz); map->setPosLimit(sz); uint32_t frPos = nthElmPos(skipAmt); auto table = map->hashTab(); auto mask = map->tableMask(); for (uint32_t toPos = 0; toPos < sz; ++toPos, ++frPos) { frPos = skipTombstonesNoBoundsCheck(frPos); auto& toE = map->data()[toPos]; dupElm(data()[frPos], toE); *findForNewInsert(table, mask, toE.probe()) = toPos; if (toE.hasIntKey()) { map->updateNextKI(toE.ikey); } else { assert(toE.hasStrKey()); map->updateIntLikeStrKeys(toE.skey); } } return Object{std::move(map)}; }