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; }
StructArray* StructArray::CopyAndResizeIfNeeded( const StructArray* array, Shape* newShape ) { if (!array->shape()->transitionRequiresGrowth()) { auto ret = asStructArray(Copy(array)); ret->setShape(newShape); return ret; } auto const copy = asStructArray(Copy(array)); auto const ret = Grow(copy, newShape); Release(copy); return ret; }
void StructArray::NvGetKey(const ArrayData* ad, TypedValue* out, ssize_t pos) { const auto structArray = asStructArray(ad); auto str = const_cast<StringData*>(structArray->shape()->keyForOffset(pos)); out->m_type = KindOfPersistentString; out->m_data.pstr = str; }
void StructArray::OnSetEvalScalar(ArrayData* ad) { auto structArray = asStructArray(ad); auto ptr = structArray->data(); auto const stop = ptr + structArray->size(); for (; ptr != stop; ++ptr) { tvAsVariant(ptr).setEvalScalar(); } // All keys are already static strings. }
const TypedValue* StructArray::NvGetStr( const ArrayData* ad, const StringData* property ) { const auto structArray = asStructArray(ad); auto offset = structArray->shape()->offsetFor(property); if (offset == PropertyTable::kInvalidOffset) return nullptr; return &structArray->data()[offset]; }
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::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::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::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* StructArray::PlusEq(ArrayData* ad, const ArrayData* elems) { auto structArray = asStructArray(ad); auto const neededSize = structArray->size() + elems->size(); auto const mixedArray = ToMixedCopyReserve(structArray, neededSize); try { auto const ret = MixedArray::PlusEq(mixedArray, elems); assert(ret == mixedArray); assert(mixedArray->hasExactlyOneRef()); return ret; } catch (...) { MixedArray::Release(mixedArray); throw; } }
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* StructArray::CopyStatic(const ArrayData* ad) { auto structArray = asStructArray(ad); auto shape = structArray->shape(); auto ret = StructArray::createStatic(shape, structArray->size()); ret->m_pos = structArray->m_pos; auto const srcData = structArray->data(); auto const size = structArray->size(); auto const stop = srcData + size; auto targetData = ret->data(); for (auto ptr = srcData; ptr != stop; ++ptr, ++targetData) { tvDupFlattenVars(ptr, targetData, structArray); } assert(ret->isStatic()); return ret; }
void StructArray::Release(ArrayData* ad) { assert(ad->isRefCounted()); assert(ad->hasExactlyOneRef()); auto array = asStructArray(ad); auto const size = array->size(); auto const data = array->data(); auto const stop = data + size; for (auto ptr = data; ptr != stop; ++ptr) { tvRefcountedDecRef(ptr); } if (UNLIKELY(strong_iterators_exist())) { free_strong_iterators(ad); } auto const cap = array->capacity(); MM().objFree(array, sizeof(StructArray) + sizeof(TypedValue) * cap); }
bool StructArray::AdvanceMArrayIter(ArrayData* ad, MArrayIter& fp) { auto structArray = asStructArray(ad); if (fp.getResetFlag()) { fp.setResetFlag(false); fp.m_pos = 0; } else if (fp.m_pos == structArray->size()) { return false; } else { fp.m_pos = IterAdvance(structArray, fp.m_pos); } if (fp.m_pos == structArray->size()) { return false; } // We set ad's internal cursor to point to the next element // to conform with PHP5 behavior structArray->m_pos = IterAdvance(structArray, fp.m_pos); return true; }
void StructArray::ReleaseUncounted(ArrayData* ad) { assert(ad->isUncounted()); auto structArray = asStructArray(ad); auto const data = structArray->data(); auto const stop = data + structArray->size(); for (auto ptr = data; ptr != stop; ++ptr) { ReleaseUncountedTv(*ptr); } // We better not have strong iterators associated with uncounted // arrays. if (debug && UNLIKELY(strong_iterators_exist())) { for_each_strong_iterator([&] (const MIterTable::Ent& miEnt) { assert(miEnt.array != structArray); }); } std::free(structArray); }
ArrayData* StructArray::MakeUncounted(ArrayData* array) { auto structArray = asStructArray(array); // We don't need to copy the full capacity, since the array won't // change once it's uncounted. auto size = structArray->size(); StructArray* result = createUncounted(structArray->shape(), size); result->m_hdr.init(HeaderKind::Struct, UncountedValue); result->m_sizeAndPos = array->m_sizeAndPos; auto const srcData = structArray->data(); auto const stop = srcData + size; auto targetData = result->data(); for (auto ptr = srcData; ptr != stop; ++ptr, ++targetData) { auto srcVariant = MixedArray::CreateVarForUncountedArray(tvAsCVarRef(ptr)); tvCopy(*srcVariant.asTypedValue(), *targetData); } assert(result->m_pos == structArray->m_pos); assert(result->isUncounted()); return result; }
ArrayData* StructArray::Copy(const ArrayData* ad) { auto old = asStructArray(ad); auto shape = old->shape(); auto result = StructArray::createNoCopy(shape, shape->size()); result->m_pos = old->m_pos; assert(result->m_size == result->shape()->size()); assert(result->size() == old->size()); auto const srcData = old->data(); auto const stop = srcData + old->size(); auto targetData = result->data(); for (auto ptr = srcData; ptr != stop; ++ptr, ++targetData) { tvDupFlattenVars(ptr, targetData, old); } assert(result->m_size == result->shape()->size()); assert(result->hasExactlyOneRef()); return result; }
ArrayData* StructArray::ToDict(ArrayData* ad) { auto a = asStructArray(ad); auto mixed = ad->cowCheck() ? ToMixedCopy(a) : ToMixed(a); return MixedArray::ToDictInPlace(mixed); }
ArrayData* StructArray::Prepend(ArrayData* ad, const Variant& v, bool copy) { return MixedArray::Prepend(ToMixed(asStructArray(ad)), v, copy); }
ArrayData* StructArray::Dequeue(ArrayData* ad, Variant& value) { return MixedArray::Dequeue(ToMixed(asStructArray(ad))->asArrayData(), value); }
ArrayData* StructArray::Merge(ArrayData* ad, const ArrayData* elems) { auto structArray = asStructArray(ad); auto const neededSize = structArray->m_size + elems->size(); auto const mixedArray = ToMixedCopyReserve(structArray, neededSize); return MixedArray::ArrayMergeGeneric(mixedArray, elems); }
ssize_t StructArray::IterEnd(const ArrayData* ad) { return asStructArray(ad)->size(); }
ssize_t StructArray::IterAdvance(const ArrayData* ad, ssize_t pos) { if (pos < asStructArray(ad)->size()) { ++pos; } return pos; }
ArrayData* StructArray::ZAppend(ArrayData* ad, RefData* v, int64_t* key_ptr) { return MixedArray::ZAppend(ToMixedCopy(asStructArray(ad)), v, key_ptr); }
ArrayData* StructArray::ZSetStr(ArrayData* ad, StringData* k, RefData* v) { return MixedArray::ZSetStr(ToMixedCopy(asStructArray(ad)), k, v); }
ArrayData* StructArray::ZSetInt(ArrayData* ad, int64_t k, RefData* v) { return MixedArray::ZSetInt(ToMixedCopy(asStructArray(ad)), k, v); }
ArrayData* StructArray::EscalateForSort(ArrayData* ad, SortFunction) { return ToMixedCopy(asStructArray(ad)); }
ssize_t StructArray::IterLast(const ArrayData* ad) { const auto structArray = asStructArray(ad); return structArray->size() ? structArray->size() - 1 : 0; }
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); }
ssize_t StructArray::IterRewind(const ArrayData* ad, ssize_t pos) { if (pos > 0) { return pos - 1; } return asStructArray(ad)->size(); }