ArrayData* PackedArray::Dequeue(ArrayData* adIn, Variant& value) { assert(checkInvariants(adIn)); auto const ad = adIn->hasMultipleRefs() ? Copy(adIn) : adIn; // To conform to PHP behavior, we invalidate all strong iterators when an // element is removed from the beginning of the array. if (UNLIKELY(strong_iterators_exist())) { free_strong_iterators(ad); } if (UNLIKELY(ad->m_size == 0)) { value = uninit_null(); ad->m_pos = ArrayData::invalid_index; return ad; } // This is O(N), but so is Dequeue on a mixed array, because it // needs to renumber keys. So it makes sense to stay packed. auto n = ad->m_size - 1; auto const data = packedData(ad); value = std::move(tvAsVariant(data)); // no incref+decref std::memmove(data, data + 1, n * sizeof *data); ad->m_size = n; ad->m_pos = n > 0 ? 0 : ArrayData::invalid_index; return ad; }
ArrayData* StructArray::CopyWithStrongIterators(const ArrayData* ad) { auto const cpy = Copy(ad); if (LIKELY(strong_iterators_exist())) { // This returns its first argument just so we can tail call it. return move_strong_iterators(cpy, const_cast<ArrayData*>(ad)); } return cpy; }
void PackedArray::Sort(ArrayData* ad, int sort_flags, bool ascending) { assert(ad->isPacked()); if (ad->m_size <= 1) { return; } assert(!ad->hasMultipleRefs()); auto a = ad; if (UNLIKELY(strong_iterators_exist())) { free_strong_iterators(a); } SortFlavor flav = preSort(ad); a->m_pos = 0; auto data_begin = packedData(ad); auto data_end = data_begin + a->m_size; CALL_SORT(TVAccessor); }
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); }
NEVER_INLINE void PackedArray::Release(ArrayData* ad) { assert(checkInvariants(ad)); assert(ad->isRefCounted()); auto const size = ad->m_size; auto const data = packedData(ad); 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 = ad->m_packedCap; MM().objFreeLogged(ad, sizeof(ArrayData) + sizeof(TypedValue) * cap); }
NEVER_INLINE ArrayData* PackedArray::Grow(ArrayData* old) { assert(checkInvariants(old)); assert(old->m_size == old->m_packedCap); DEBUG_ONLY auto const oldPos = old->m_pos; auto const oldCap = old->m_packedCap; auto const cap = oldCap * 2; if (UNLIKELY(cap >= kMaxPackedCap)) return nullptr; auto const ad = static_cast<ArrayData*>( MM().objMallocLogged(sizeof(ArrayData) + cap * sizeof(TypedValue)) ); auto const oldSize = old->m_size; auto const oldPosUnsigned = uint64_t{static_cast<uint32_t>(old->m_pos)}; ad->m_kindAndSize = uint64_t{oldSize} << 32 | cap; ad->m_posAndCount = oldPosUnsigned; if (UNLIKELY(strong_iterators_exist())) { move_strong_iterators(ad, old); } // Steal the old array payload. At the time of this writing, it was // better not to reuse the memcpy return value here because gcc had // `ad' in a callee saved register anyway. The reg-to-reg move was // smaller than subtracting sizeof(ArrayData) from rax to return. old->m_size = 0; std::memcpy(packedData(ad), packedData(old), oldSize * sizeof(TypedValue)); // TODO(#2926276): it would be good to refactor callers to expect // our refcount to start at 1. assert(ad->m_kind == ArrayData::kPackedKind); assert(ad->m_pos == oldPos); assert(ad->m_count == 0); assert(ad->m_packedCap == cap); assert(ad->m_size == oldSize); assert(checkInvariants(ad)); return ad; }
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); }
StructArray* StructArray::Grow(StructArray* old, Shape* newShape) { assert(old->shape()->transitionRequiresGrowth()); auto result = StructArray::create(newShape, old->data(), old->shape()->size()); result->m_size = newShape->size(); if (UNLIKELY(strong_iterators_exist())) { move_strong_iterators(result, old); } old->m_size = 0; if (debug) { // For debug builds, set m_pos to 0 as well to make the // asserts in checkInvariants() happy. old->m_pos = 0; } assert(result->hasExactlyOneRef()); return result; }
bool PackedArray::Usort(ArrayData* ad, const Variant& cmp_function) { assert(ad->isPacked()); if (ad->m_size <= 1) { return true; } assert(!ad->hasMultipleRefs()); if (UNLIKELY(strong_iterators_exist())) { free_strong_iterators(ad); } ElmUCompare<TVAccessor> comp; CallCtx ctx; CallerFrame cf; vm_decode_function(cmp_function, cf(), false, ctx); if (!ctx.func) { return false; } comp.ctx = &ctx; auto const data = packedData(ad); Sort::sort(data, data + ad->m_size, comp); return true; }
ArrayData* PackedArray::Prepend(ArrayData* adIn, const Variant& v, bool copy) { assert(checkInvariants(adIn)); auto const ad = adIn->hasMultipleRefs() ? CopyAndResizeIfNeeded(adIn) : ResizeIfNeeded(adIn); // To conform to PHP behavior, we invalidate all strong iterators when an // element is added to the beginning of the array. if (UNLIKELY(strong_iterators_exist())) { free_strong_iterators(ad); } auto const size = ad->m_size; auto const data = packedData(ad); std::memmove(data + 1, data, sizeof *data * size); // TODO(#3888164): constructValHelper is making KindOfUninit checks. tvAsUninitializedVariant(&data[0]).constructValHelper(v); ad->m_size = size + 1; ad->m_pos = 0; return ad; }
ArrayData* PackedArray::Pop(ArrayData* adIn, Variant& value) { assert(checkInvariants(adIn)); auto const ad = adIn->hasMultipleRefs() ? Copy(adIn) : adIn; if (UNLIKELY(ad->m_size == 0)) { value = uninit_null(); ad->m_pos = ArrayData::invalid_index; return ad; } auto const oldSize = ad->m_size; auto& tv = packedData(ad)[oldSize - 1]; value = tvAsCVarRef(&tv); if (UNLIKELY(strong_iterators_exist())) { adjustMArrayIter(ad, oldSize - 1); } auto const oldType = tv.m_type; auto const oldDatum = tv.m_data.num; ad->m_size = oldSize - 1; ad->m_pos = oldSize - 1 > 0 ? 0 : ArrayData::invalid_index; tvRefcountedDecRefHelper(oldType, oldDatum); return ad; }