ArrayData* PackedArray::SetInt(ArrayData* adIn, int64_t k, const Variant& v, bool copy) { assert(checkInvariants(adIn)); // Right now SetInt is used for the AddInt entry point also. This // first branch is the only thing we'd be able to omit if we were // doing AddInt. if (size_t(k) < adIn->m_size) { auto const ad = copy ? Copy(adIn) : adIn; auto& dst = *tvToCell(&packedData(ad)[k]); cellSet(*v.asCell(), dst); // TODO(#3888164): we should restructure things so we don't have to // check KindOfUninit here. if (UNLIKELY(dst.m_type == KindOfUninit)) { dst.m_type = KindOfNull; } return ad; } // Setting the int at the size of the array can keep it in packed // mode---it's the same as an append. if (size_t(k) == adIn->m_size) return Append(adIn, v, copy); // On the promote-to-mixed path, we can use addVal since we know the // key can't exist. auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); return mixed->addVal(k, v); }
ArrayData* PackedArray::SetRefStr(ArrayData* adIn, StringData* k, Variant& v, bool copy) { auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); // todo t2606310: key can't exist. use add/findForNewInsert return mixed->updateRef(k, v); }
ArrayData* PackedArray::SetStr(ArrayData* adIn, StringData* k, const Variant& v, bool copy) { // We must convert to mixed, but can call addVal since the key must // not exist. auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); return mixed->addVal(k, v); }
ArrayData* PackedArray::LvalStr(ArrayData* adIn, StringData* key, Variant*& ret, bool copy) { // We have to promote. We know the key doesn't exist, but aren't // making use of that fact yet. TODO(#2606310). auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); return mixed->addLvalImpl(key, ret); }
ArrayData* StructArray::AppendWithRef( ArrayData* ad, const Variant& v, bool copy ) { auto structArray = asStructArray(ad); auto mixedArray = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return MixedArray::AppendWithRef(mixedArray->asArrayData(), v, false); }
ArrayData* StructArray::LvalInt( ArrayData* ad, int64_t k, Variant*& ret, bool copy ) { auto structArray = asStructArray(ad); auto mixedArray = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return mixedArray->addLvalImpl(k, ret); }
ArrayData* StructArray::SetRefStr( ArrayData* ad, StringData* k, Variant& v, bool copy ) { auto structArray = asStructArray(ad); auto mixedArray = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return MixedArray::SetRefStr(mixedArray->asArrayData(), k, v, false); }
ArrayData* StructArray::SetStr( ArrayData* ad, StringData* k, Cell v, bool copy ) { auto structArray = asStructArray(ad); auto shape = structArray->shape(); auto result = structArray; auto offset = shape->offsetFor(k); bool isNewProperty = offset == PropertyTable::kInvalidOffset; auto convertToMixedAndAdd = [&]() { auto mixed = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return mixed->addValNoAsserts(k, v); }; if (isNewProperty) { StringData* staticKey; // We don't support adding non-static strings yet. if (k->isStatic()) { staticKey = k; } else { staticKey = lookupStaticString(k); if (!staticKey) return convertToMixedAndAdd(); } auto newShape = shape->transition(staticKey); if (!newShape) return convertToMixedAndAdd(); result = copy ? CopyAndResizeIfNeeded(structArray, newShape) : ResizeIfNeeded(structArray, newShape); offset = result->shape()->offsetFor(staticKey); assert(offset != PropertyTable::kInvalidOffset); TypedValue* dst = &result->data()[offset]; // TODO(#3888164): we should restructure things so we don't have to // check KindOfUninit here. if (UNLIKELY(v.m_type == KindOfUninit)) v = make_tv<KindOfNull>(); cellDup(v, *dst); return result; } if (copy) { result = asStructArray(Copy(structArray)); } assert(offset != PropertyTable::kInvalidOffset); TypedValue* dst = &result->data()[offset]; if (UNLIKELY(v.m_type == KindOfUninit)) v = make_tv<KindOfNull>(); cellSet(v, *tvToCell(dst)); return result; }
ArrayData* StructArray::RemoveStr( ArrayData* ad, const StringData* k, bool copy ) { auto structArray = asStructArray(ad); if (structArray->shape()->hasOffsetFor(k)) { auto const mixed = copy ? ToMixedCopy(structArray) : ToMixed(structArray); auto pos = mixed->findForRemove(k, k->hash()); if (validPos(pos)) mixed->erase(pos); return mixed; } return copy ? Copy(structArray) : structArray; }
ArrayData* PackedArray::RemoveInt(ArrayData* adIn, int64_t k, bool copy) { assert(checkInvariants(adIn)); if (size_t(k) < adIn->m_size) { // Escalate to mixed for correctness; unset preserves m_nextKI. // // TODO(#2606310): if we're removing the /last/ element, we // probably could stay packed, but this needs to be verified. auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); auto pos = mixed->findForRemove(k, false); if (validPos(pos)) mixed->erase(pos); return mixed; } // Key doesn't exist---we're still packed. return copy ? Copy(adIn) : adIn; }
ArrayData* PackedArray::LvalNew(ArrayData* adIn, Variant*& ret, bool copy) { assert(checkInvariants(adIn)); auto const ad = copy ? CopyAndResizeIfNeeded(adIn) : ResizeIfNeeded(adIn); if (UNLIKELY(!ad)) { auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(ad); return MixedArray::LvalNew(mixed, ret, copy); } if (ad->m_pos == ArrayData::invalid_index) { ad->m_pos = ad->m_size; } auto& tv = packedData(ad)[ad->m_size++]; tv.m_type = KindOfNull; ret = &tvAsVariant(&tv); return ad; }
ArrayData* PackedArray::Append(ArrayData* adIn, const Variant& v, bool copy) { assert(checkInvariants(adIn)); auto const ad = copy ? CopyAndResizeIfNeeded(adIn) : ResizeIfNeeded(adIn); if (UNLIKELY(!ad)) { auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); return MixedArray::Append(mixed, v, copy); } if (ad->m_pos == ArrayData::invalid_index) { ad->m_pos = ad->m_size; } auto& dst = packedData(ad)[ad->m_size++]; cellDup(*v.asCell(), dst); // TODO(#3888164): restructure this so we don't need KindOfUninit checks. if (dst.m_type == KindOfUninit) dst.m_type = KindOfNull; return ad; }
ArrayData* PackedArray::SetRefInt(ArrayData* adIn, int64_t k, Variant& v, bool copy) { assert(checkInvariants(adIn)); if (size_t(k) == adIn->m_size) return AppendRef(adIn, v, copy); if (size_t(k) < adIn->m_size) { auto const ad = copy ? Copy(adIn) : adIn; tvBind(v.asRef(), &packedData(ad)[k]); return ad; } // todo t2606310: key can't exist. use add/findForNewInsert auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); mixed->updateRef(k, v); return mixed; }
ArrayData* PackedArray::AppendRef(ArrayData* adIn, Variant& v, bool copy) { assert(checkInvariants(adIn)); auto const ad = copy ? CopyAndResizeIfNeeded(adIn) : ResizeIfNeeded(adIn); if (UNLIKELY(!ad)) { auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); return MixedArray::AppendRef(mixed, v, copy); } if (ad->m_pos == ArrayData::invalid_index) { ad->m_pos = ad->m_size; } auto& dst = packedData(ad)[ad->m_size++]; dst.m_data.pref = v.asRef()->m_data.pref; dst.m_type = KindOfRef; dst.m_data.pref->incRefCount(); return ad; }
ArrayData* PackedArray::AppendWithRef(ArrayData* adIn, const Variant& v, bool copy) { assert(checkInvariants(adIn)); auto const ad = copy ? CopyAndResizeIfNeeded(adIn) : ResizeIfNeeded(adIn); if (UNLIKELY(!ad)) { auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); // XXX: constness return MixedArray::AppendRef(mixed, const_cast<Variant&>(v), copy); } if (ad->m_pos == ArrayData::invalid_index) { ad->m_pos = ad->m_size; } auto& dst = packedData(ad)[ad->m_size++]; dst.m_type = KindOfNull; tvAsVariant(&dst).setWithRef(v); return ad; }
ArrayData* StructArray::LvalStr( ArrayData* ad, StringData* property, Variant*& ret, bool copy ) { auto structArray = asStructArray(ad); auto shape = structArray->shape(); auto offset = shape->offsetFor(property); if (offset != PropertyTable::kInvalidOffset) { auto const result = asStructArray( copy ? Copy(structArray) : structArray); ret = &tvAsVariant(&result->data()[offset]); return result; } auto convertToMixedAndAdd = [&]() { auto mixed = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return mixed->addLvalImpl(property, ret); }; // We don't support adding non-static strings yet. StringData* staticKey; if (property->isStatic()) { staticKey = property; } else { staticKey = lookupStaticString(property); if (!staticKey) return convertToMixedAndAdd(); } auto newShape = shape->transition(staticKey); if (!newShape) return convertToMixedAndAdd(); auto result = copy ? CopyAndResizeIfNeeded(structArray, newShape) : ResizeIfNeeded(structArray, newShape); assert(newShape->hasOffsetFor(staticKey)); offset = newShape->offsetFor(staticKey); tvWriteNull(&result->data()[offset]); ret = &tvAsVariant(&result->data()[offset]); return result; }
ArrayData* PackedArray::EscalateForSort(ArrayData* ad, SortFunction sf) { if (ad->m_size <= 1) { return ad; } if (sf == SORTFUNC_KSORT) { return ad; // trivial for packed arrays. } if (isSortFamily(sf)) { // sort/rsort/usort if (UNLIKELY(ad->hasMultipleRefs())) { auto ret = PackedArray::Copy(ad); assert(ret->hasExactlyOneRef()); return ret; } else { return ad; } } assert(checkInvariants(ad)); auto ret = ToMixedCopy(ad); assert(ret->hasExactlyOneRef()); return ret; }
ArrayData* PackedArray::LvalInt(ArrayData* adIn, int64_t k, Variant*& ret, bool copy) { assert(checkInvariants(adIn)); if (LIKELY(size_t(k) < adIn->m_size)) { auto const ad = copy ? Copy(adIn) : adIn; ret = &tvAsVariant(&packedData(ad)[k]); return ad; } // We can stay packed if the index is m_size, and the operation does // the same thing as LvalNew. if (size_t(k) == adIn->m_size) return LvalNew(adIn, ret, copy); // Promote-to-mixed path, we know the key is new and should be using // findForNewInsert but aren't yet TODO(#2606310). auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); return mixed->addLvalImpl(k, ret); }
ArrayData* StructArray::LvalNew(ArrayData* ad, Variant*& ret, bool copy) { auto structArray = asStructArray(ad); auto mixedArray = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return MixedArray::LvalNew(mixedArray->asArrayData(), ret, false); }
ArrayData* StructArray::ZSetInt(ArrayData* ad, int64_t k, RefData* v) { return MixedArray::ZSetInt(ToMixedCopy(asStructArray(ad)), k, v); }
ArrayData* StructArray::SetInt(ArrayData* ad, int64_t k, Cell v, bool copy) { auto structArray = asStructArray(ad); auto mixedArray = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return mixedArray->addVal(k, v); }
ArrayData* StructArray::ZAppend(ArrayData* ad, RefData* v, int64_t* key_ptr) { return MixedArray::ZAppend(ToMixedCopy(asStructArray(ad)), v, key_ptr); }
ArrayData* StructArray::EscalateForSort(ArrayData* ad, SortFunction) { return ToMixedCopy(asStructArray(ad)); }
ArrayData* PackedArray::EscalateForSort(ArrayData* ad) { // Note: ToMixedCopy also grows so we have !isFull. We could use // ToMixedCopyReserve? assert(checkInvariants(ad)); return ToMixedCopy(ad); }
ArrayData* StructArray::ZSetStr(ArrayData* ad, StringData* k, RefData* v) { return MixedArray::ZSetStr(ToMixedCopy(asStructArray(ad)), k, v); }
ArrayData* PackedArray::ZSetInt(ArrayData* ad, int64_t k, RefData* v) { assert(checkInvariants(ad)); return MixedArray::ZSetInt(ToMixedCopy(ad), k, v); }
ArrayData* StructArray::Append(ArrayData* ad, Cell v, bool copy) { auto structArray = asStructArray(ad); auto mixedArray = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return MixedArray::Append(mixedArray->asArrayData(), v, false); }
ArrayData* PackedArray::ZSetStr(ArrayData* ad, StringData* k, RefData* v) { assert(checkInvariants(ad)); return MixedArray::ZSetStr(ToMixedCopy(ad), k, v); }
ArrayData* StructArray::ToDict(ArrayData* ad) { auto a = asStructArray(ad); auto mixed = ad->cowCheck() ? ToMixedCopy(a) : ToMixed(a); return MixedArray::ToDictInPlace(mixed); }
ArrayData* PackedArray::ZAppend(ArrayData* ad, RefData* v) { assert(checkInvariants(ad)); return MixedArray::ZAppend(ToMixedCopy(ad), v); }