HOT_FUNC Variant ArrayIter::second() { if (hasVector()) { c_Vector* vec = getVector(); if (UNLIKELY(m_versionNumber != vec->getVersionNumber())) { throw_collection_modified(); } return tvAsCVarRef(vec->at(m_pos)); } if (hasMap()) { c_Map* mp = getMap(); if (UNLIKELY(m_versionNumber != mp->getVersionNumber())) { throw_collection_modified(); } return mp->iter_value(m_pos); } if (hasStableMap()) { c_StableMap* smp = getStableMap(); if (UNLIKELY(m_versionNumber != smp->getVersionNumber())) { throw_collection_modified(); } return smp->iter_value(m_pos); } if (hasObject()) { ObjectData* obj = getObject(); return obj->o_invoke(s_current, Array()); } assert(hasArrayData()); assert(m_pos != ArrayData::invalid_index); const ArrayData* ad = getArrayData(); assert(ad); return ad->getValue(m_pos); }
void ArrayIter::secondHelper(Variant & v) { if (hasVector()) { c_Vector* vec = getVector(); if (UNLIKELY(m_versionNumber != vec->getVersionNumber())) { throw_collection_modified(); } v = tvAsCVarRef(vec->at(m_pos)); return; } if (hasMap()) { c_Map* mp = getMap(); if (UNLIKELY(m_versionNumber != mp->getVersionNumber())) { throw_collection_modified(); } v = mp->iter_value(m_pos); return; } if (hasStableMap()) { c_StableMap* smp = getStableMap(); if (UNLIKELY(m_versionNumber != smp->getVersionNumber())) { throw_collection_modified(); } v = smp->iter_value(m_pos); return; } assert(hasObject()); ObjectData* obj = getObject(); v = obj->o_invoke(s_current, Array()); }
void ArrayIter::nextHelper() { if (hasVector()) { m_pos++; return; } if (hasMap()) { assert(m_pos != 0); c_Map* mp = getMap(); if (UNLIKELY(m_versionNumber != mp->getVersionNumber())) { throw_collection_modified(); } m_pos = mp->iter_next(m_pos); return; } if (hasStableMap()) { assert(m_pos != 0); c_StableMap* smp = getStableMap(); if (UNLIKELY(m_versionNumber != smp->getVersionNumber())) { throw_collection_modified(); } m_pos = smp->iter_next(m_pos); return; } assert(hasObject()); ObjectData* obj = getObject(); obj->o_invoke(s_next, Array()); }
void BaseVector::filterwithkey(BaseVector* bvec, CVarRef callback) { CallCtx ctx; vm_decode_function(callback, nullptr, false, ctx); if (!ctx.func) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Parameter must be a valid callback")); throw e; } uint sz = m_size; for (uint i = 0; i < sz; ++i) { Variant ret; int32_t version = m_version; TypedValue args[2] = { make_tv<KindOfInt64>(i), m_data[i] }; g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 2, args); if (UNLIKELY(version != m_version)) { throw_collection_modified(); } if (ret.toBoolean()) { bvec->add(&m_data[i]); } } }
void BaseVector::mapwithkey(BaseVector* bvec, CVarRef callback) { CallCtx ctx; vm_decode_function(callback, nullptr, false, ctx); if (!ctx.func) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Parameter must be a valid callback")); throw e; } uint sz = m_size; bvec->reserve(sz); for (uint i = 0; i < sz; ++i) { TypedValue* tv = &bvec->m_data[i]; int32_t version = m_version; TypedValue args[2] = { make_tv<KindOfInt64>(i), m_data[i] }; g_vmContext->invokeFuncFew(tv, ctx, 2, args); if (UNLIKELY(version != m_version)) { tvRefcountedDecRef(tv); throw_collection_modified(); } ++bvec->m_size; } }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::php_takeWhile(const Variant& fn) { CallCtx ctx; vm_decode_function(fn, 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)}; int32_t version UNUSED; if (std::is_same<c_Map, TMap>::value) { version = m_version; } for (ssize_t pos = iter_begin(); iter_valid(pos); pos = iter_next(pos)) { auto* e = iter_elm(pos); bool b = invokeAndCastToBool(ctx, 1, &e->data); if (std::is_same<c_Map, TMap>::value) { if (UNLIKELY(version != m_version)) { throw_collection_modified(); } } if (!b) break; e = iter_elm(pos); if (e->hasIntKey()) { map->set(e->ikey, &e->data); } else { assert(e->hasStrKey()); map->set(e->skey, &e->data); } } return Object{std::move(map)}; }
Object BaseMap::php_retain(const Variant& callback) { CallCtx ctx; vm_decode_function(callback, nullptr, false, ctx); if (!ctx.func) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter must be a valid callback"); } auto size = m_size; if (!size) { return Object{this}; } 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); 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; int32_t version = m_version; bool b = invokeAndCastToBool(ctx, argc, argv); if (UNLIKELY(version != m_version)) { throw_collection_modified(); } if (b) { continue; } mutateAndBump(); version = m_version; e = iter_elm(pos); ssize_t pp = (e->hasIntKey() ? findForRemove(e->ikey) : findForRemove(e->skey, e->skey->hash())); eraseNoCompact(pp); if (UNLIKELY(version != m_version)) { throw_collection_modified(); } } assert(m_size <= size); compactOrShrinkIfDensityTooLow(); return Object{this}; }
Variant ArrayIter::firstHelper() { if (hasVector()) { return m_pos; } if (hasMap()) { assert(m_pos != 0); c_Map* mp = getMap(); if (UNLIKELY(m_versionNumber != mp->getVersionNumber())) { throw_collection_modified(); } return mp->iter_key(m_pos); } if (hasStableMap()) { assert(m_pos != 0); c_StableMap* smp = getStableMap(); if (UNLIKELY(m_versionNumber != smp->getVersionNumber())) { throw_collection_modified(); } return smp->iter_key(m_pos); } assert(hasObject()); ObjectData* obj = getObject(); return obj->o_invoke(s_key, Array()); }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::php_filter(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)); map->mutate(); int32_t version = m_version; 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); 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; bool b = invokeAndCastToBool(ctx, argc, argv); if (UNLIKELY(version != m_version)) { throw_collection_modified(); } if (!b) continue; e = iter_elm(pos); if (e->hasIntKey()) { map->set(e->ikey, &e->data); } else { assert(e->hasStrKey()); map->set(e->skey, &e->data); } } return Object(std::move(map)); }
typename std::enable_if< std::is_base_of<BaseSet, TSet>::value, Object>::type BaseSet::php_skipWhile(const Variant& fn) { CallCtx ctx; vm_decode_function(fn, 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)); // we don't reserve(), because we don't know how selective fn will be set->mutate(); int32_t version UNUSED; if (std::is_same<c_Set, TSet>::value) { version = m_version; } uint32_t used = posLimit(); uint32_t i = 0; for (; i < used; ++i) { if (isTombstone(i)) continue; Elm& e = data()[i]; bool b = invokeAndCastToBool(ctx, 1, &e.data); if (std::is_same<c_Set, TSet>::value) { if (UNLIKELY(version != m_version)) { throw_collection_modified(); } } if (!b) break; } for (; i < used; ++i) { if (isTombstone(i)) continue; Elm& e = data()[i]; if (e.hasIntKey()) { set->addRaw(e.data.m_data.num); } else { assert(e.hasStrKey()); set->addRaw(e.data.m_data.pstr); } } return Object(std::move(set)); }
typename std::enable_if< std::is_base_of<BaseSet, TSet>::value, Object>::type BaseSet::php_filter(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)); // we don't reserve(), because we don't know how selective callback will be set->mutate(); int32_t version = m_version; 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); if (useKey) { argv[0] = e->data; } argv[argc-1] = e->data; bool b = invokeAndCastToBool(ctx, argc, argv); if (UNLIKELY(version != m_version)) { throw_collection_modified(); } if (!b) continue; e = iter_elm(pos); if (e->hasIntKey()) { set->addRaw(e->data.m_data.num); } else { assert(e->hasStrKey()); set->addRaw(e->data.m_data.pstr); } } return Object(std::move(set)); }
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)}; }
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)}; }