779 linhas
23 KiB
C++
779 linhas
23 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 2.00 of the Zend license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.zend.com/license/2_00.txt. |
|
|
| If you did not receive a copy of the Zend license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@zend.com so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
#include "hphp/runtime/base/array_data.h"
|
|
|
|
#include "tbb/concurrent_hash_map.h"
|
|
|
|
#include "hphp/util/exception.h"
|
|
#include "hphp/runtime/base/array_init.h"
|
|
#include "hphp/runtime/base/array_iterator.h"
|
|
#include "hphp/runtime/base/type_conversions.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/runtime/base/complex_types.h"
|
|
#include "hphp/runtime/base/variable_serializer.h"
|
|
#include "hphp/runtime/base/runtime_option.h"
|
|
#include "hphp/runtime/base/macros.h"
|
|
#include "hphp/runtime/base/shared_map.h"
|
|
#include "hphp/runtime/base/policy_array.h"
|
|
#include "hphp/runtime/base/comparisons.h"
|
|
#include "hphp/runtime/vm/name_value_table_wrapper.h"
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static_assert(
|
|
sizeof(ArrayData) == 24,
|
|
"Performance is sensitive to sizeof(ArrayData)."
|
|
" Make sure you changed it with good reason and then update this assert.");
|
|
|
|
typedef tbb::concurrent_hash_map<const StringData *, ArrayData *,
|
|
StringDataHashCompare> ArrayDataMap;
|
|
static ArrayDataMap s_arrayDataMap;
|
|
|
|
ArrayData *ArrayData::GetScalarArray(ArrayData *arr,
|
|
const StringData *key /* = nullptr */) {
|
|
if (!key) {
|
|
key = StringData::GetStaticString(f_serialize(arr).get());
|
|
} else {
|
|
assert(key->isStatic());
|
|
assert(key->same(f_serialize(arr).get()));
|
|
}
|
|
ArrayDataMap::accessor acc;
|
|
if (s_arrayDataMap.insert(acc, key)) {
|
|
ArrayData *ad = arr->nonSmartCopy();
|
|
ad->setStatic();
|
|
ad->onSetEvalScalar();
|
|
acc->second = ad;
|
|
}
|
|
return acc->second;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static size_t VsizeNop(const ArrayData* ad) {
|
|
assert(false);
|
|
return ad->getSize();
|
|
}
|
|
|
|
// order: kVectorKind, kMixedKind, kSharedKind, kNvtwKind, kPolicyKind
|
|
extern const ArrayFunctions g_array_funcs = {
|
|
// release
|
|
{ &HphpArray::ReleaseVec, &HphpArray::Release,
|
|
&SharedMap::Release,
|
|
&NameValueTableWrapper::Release,
|
|
&PolicyArray::Release },
|
|
// nvGetInt
|
|
{ &HphpArray::NvGetIntVec, &HphpArray::NvGetInt,
|
|
&SharedMap::NvGetInt,
|
|
&NameValueTableWrapper::NvGetInt,
|
|
&PolicyArray::NvGetInt },
|
|
// nvGetStr
|
|
{ &HphpArray::NvGetStrVec, &HphpArray::NvGetStr,
|
|
&SharedMap::NvGetStr,
|
|
&NameValueTableWrapper::NvGetStr,
|
|
&PolicyArray::NvGetStr },
|
|
// nvGetKey
|
|
{ &HphpArray::NvGetKeyVec, &HphpArray::NvGetKey,
|
|
&SharedMap::NvGetKey,
|
|
&NameValueTableWrapper::NvGetKey,
|
|
&PolicyArray::NvGetKey },
|
|
// setInt
|
|
{ &HphpArray::SetIntVec, &HphpArray::SetInt,
|
|
&SharedMap::SetInt,
|
|
&NameValueTableWrapper::SetInt,
|
|
&PolicyArray::SetInt },
|
|
// setStr
|
|
{ &HphpArray::SetStrVec, &HphpArray::SetStr,
|
|
&SharedMap::SetStr,
|
|
&NameValueTableWrapper::SetStr,
|
|
&PolicyArray::SetStr },
|
|
// vsize
|
|
{ &VsizeNop, &VsizeNop,
|
|
&VsizeNop,
|
|
&NameValueTableWrapper::Vsize,
|
|
&VsizeNop },
|
|
// getValueRef
|
|
{ &HphpArray::GetValueRef, &HphpArray::GetValueRef,
|
|
&SharedMap::GetValueRef,
|
|
&NameValueTableWrapper::GetValueRef,
|
|
&PolicyArray::GetValueRef },
|
|
// noCopyOnWrite
|
|
{ false, false,
|
|
false,
|
|
true, // NameValueTableWrapper doesn't support COW.
|
|
false },
|
|
// isVectorData
|
|
{ &HphpArray::IsVectorDataVec, &HphpArray::IsVectorData,
|
|
&SharedMap::IsVectorData,
|
|
&NameValueTableWrapper::IsVectorData,
|
|
&PolicyArray::IsVectorData },
|
|
// existsInt
|
|
{ &HphpArray::ExistsIntVec, &HphpArray::ExistsInt,
|
|
&SharedMap::ExistsInt,
|
|
&NameValueTableWrapper::ExistsInt,
|
|
&PolicyArray::ExistsInt },
|
|
// existsStr
|
|
{ &HphpArray::ExistsStrVec, &HphpArray::ExistsStr,
|
|
&SharedMap::ExistsStr,
|
|
&NameValueTableWrapper::ExistsStr,
|
|
&PolicyArray::ExistsStr },
|
|
// lvalInt
|
|
{ &HphpArray::LvalIntVec, &HphpArray::LvalInt,
|
|
&SharedMap::LvalInt,
|
|
&NameValueTableWrapper::LvalInt,
|
|
&PolicyArray::LvalInt },
|
|
// lvalStr
|
|
{ &HphpArray::LvalStrVec, &HphpArray::LvalStr,
|
|
&SharedMap::LvalStr,
|
|
&NameValueTableWrapper::LvalStr,
|
|
&PolicyArray::LvalStr },
|
|
// lvalNew
|
|
{ &HphpArray::LvalNewVec, &HphpArray::LvalNew,
|
|
&SharedMap::LvalNew,
|
|
&NameValueTableWrapper::LvalNew,
|
|
&PolicyArray::LvalNew },
|
|
// setRefInt
|
|
{ &HphpArray::SetRefIntVec, &HphpArray::SetRefInt,
|
|
&SharedMap::SetRefInt,
|
|
&NameValueTableWrapper::SetRefInt,
|
|
&PolicyArray::SetRefInt },
|
|
// setRefStr
|
|
{ &HphpArray::SetRefStrVec, &HphpArray::SetRefStr,
|
|
&SharedMap::SetRefStr,
|
|
&NameValueTableWrapper::SetRefStr,
|
|
&PolicyArray::SetRefStr },
|
|
// addInt
|
|
{ &HphpArray::AddIntVec, &HphpArray::AddInt,
|
|
&SharedMap::SetInt, // reuse set
|
|
&NameValueTableWrapper::SetInt, // reuse set
|
|
&PolicyArray::AddInt },
|
|
// addStr
|
|
{ &HphpArray::AddStrVec, &HphpArray::AddStr,
|
|
&SharedMap::SetStr, // reuse set
|
|
&NameValueTableWrapper::SetStr, // reuse set
|
|
&PolicyArray::AddStr },
|
|
// addLvalInt
|
|
{ &HphpArray::AddLvalIntVec, &HphpArray::AddLvalInt,
|
|
&SharedMap::AddLvalInt,
|
|
&NameValueTableWrapper::AddLvalInt,
|
|
&PolicyArray::AddLvalInt },
|
|
// addLvalStr
|
|
{ &HphpArray::AddLvalStrVec, &HphpArray::AddLvalStr,
|
|
&SharedMap::AddLvalStr,
|
|
&NameValueTableWrapper::AddLvalStr,
|
|
&PolicyArray::AddLvalStr },
|
|
// removeInt
|
|
{ &HphpArray::RemoveIntVec, &HphpArray::RemoveInt,
|
|
&SharedMap::RemoveInt,
|
|
&NameValueTableWrapper::RemoveInt,
|
|
&PolicyArray::RemoveInt },
|
|
// removeStr
|
|
{ &HphpArray::RemoveStrVec, &HphpArray::RemoveStr,
|
|
&SharedMap::RemoveStr,
|
|
&NameValueTableWrapper::RemoveStr,
|
|
&PolicyArray::RemoveStr },
|
|
// iterBegin
|
|
{ &HphpArray::IterBegin, &HphpArray::IterBegin,
|
|
&SharedMap::IterBegin,
|
|
&NameValueTableWrapper::IterBegin,
|
|
&PolicyArray::IterBegin },
|
|
// iterEnd
|
|
{ &HphpArray::IterEnd, &HphpArray::IterEnd,
|
|
&SharedMap::IterEnd,
|
|
&NameValueTableWrapper::IterEnd,
|
|
&PolicyArray::IterEnd },
|
|
// iterAdvance
|
|
{ &HphpArray::IterAdvance, &HphpArray::IterAdvance,
|
|
&SharedMap::IterAdvance,
|
|
&NameValueTableWrapper::IterAdvance,
|
|
&PolicyArray::IterAdvance },
|
|
// iterRewind
|
|
{ &HphpArray::IterRewind, &HphpArray::IterRewind,
|
|
&SharedMap::IterRewind,
|
|
&NameValueTableWrapper::IterRewind,
|
|
&PolicyArray::IterRewind },
|
|
// validFullPos
|
|
{ &HphpArray::ValidFullPos, &HphpArray::ValidFullPos,
|
|
&SharedMap::ValidFullPos,
|
|
&NameValueTableWrapper::ValidFullPos,
|
|
&PolicyArray::ValidFullPos },
|
|
// advanceFullPos
|
|
{ &HphpArray::AdvanceFullPos, &HphpArray::AdvanceFullPos,
|
|
&SharedMap::AdvanceFullPos,
|
|
&NameValueTableWrapper::AdvanceFullPos,
|
|
&PolicyArray::AdvanceFullPos },
|
|
// escalateForSort
|
|
{ &HphpArray::EscalateForSort, &HphpArray::EscalateForSort,
|
|
&SharedMap::EscalateForSort,
|
|
&NameValueTableWrapper::EscalateForSort,
|
|
&PolicyArray::EscalateForSort },
|
|
// ksort
|
|
{ &HphpArray::Ksort, &HphpArray::Ksort,
|
|
&ArrayData::Ksort,
|
|
&NameValueTableWrapper::Ksort,
|
|
&ArrayData::Ksort },
|
|
// sort
|
|
{ &HphpArray::Sort, &HphpArray::Sort,
|
|
&ArrayData::Sort,
|
|
&NameValueTableWrapper::Sort,
|
|
&ArrayData::Sort },
|
|
// asort
|
|
{ &HphpArray::Asort, &HphpArray::Asort,
|
|
&ArrayData::Asort,
|
|
&NameValueTableWrapper::Asort,
|
|
&ArrayData::Asort },
|
|
// uksort
|
|
{ &HphpArray::Uksort, &HphpArray::Uksort,
|
|
&ArrayData::Uksort,
|
|
&NameValueTableWrapper::Uksort,
|
|
&ArrayData::Uksort },
|
|
// usort
|
|
{ &HphpArray::Usort, &HphpArray::Usort,
|
|
&ArrayData::Usort,
|
|
&NameValueTableWrapper::Usort,
|
|
&ArrayData::Usort },
|
|
// uasort
|
|
{ &HphpArray::Uasort, &HphpArray::Uasort,
|
|
&ArrayData::Uasort,
|
|
&NameValueTableWrapper::Uasort,
|
|
&ArrayData::Uasort },
|
|
// copy
|
|
{ &HphpArray::CopyVec, &HphpArray::Copy,
|
|
&SharedMap::Copy,
|
|
&NameValueTableWrapper::Copy,
|
|
&PolicyArray::Copy },
|
|
// copyWithStrongIterators
|
|
{ &HphpArray::CopyWithStrongIterators, &HphpArray::CopyWithStrongIterators,
|
|
&SharedMap::CopyWithStrongIterators,
|
|
&NameValueTableWrapper::CopyWithStrongIterators,
|
|
&PolicyArray::CopyWithStrongIterators },
|
|
// nonSmartCopy
|
|
{ &HphpArray::NonSmartCopy, &HphpArray::NonSmartCopy,
|
|
&ArrayData::NonSmartCopy,
|
|
&ArrayData::NonSmartCopy,
|
|
&PolicyArray::NonSmartCopy },
|
|
// append
|
|
{ &HphpArray::AppendVec, &HphpArray::Append,
|
|
&SharedMap::Append,
|
|
&NameValueTableWrapper::Append,
|
|
&PolicyArray::Append },
|
|
// appendRef
|
|
{ &HphpArray::AppendRefVec, &HphpArray::AppendRef,
|
|
&SharedMap::AppendRef,
|
|
&NameValueTableWrapper::AppendRef,
|
|
&PolicyArray::AppendRef },
|
|
// appendWithRef
|
|
{ &HphpArray::AppendWithRefVec, &HphpArray::AppendWithRef,
|
|
&SharedMap::AppendRef,
|
|
&NameValueTableWrapper::AppendRef,
|
|
&PolicyArray::AppendRef },
|
|
// plus
|
|
{ &HphpArray::Plus, &HphpArray::Plus,
|
|
&SharedMap::Plus,
|
|
&NameValueTableWrapper::Plus,
|
|
&PolicyArray::Plus },
|
|
// merge
|
|
{ &HphpArray::Merge, &HphpArray::Merge,
|
|
&SharedMap::Merge,
|
|
&NameValueTableWrapper::Merge,
|
|
&PolicyArray::Merge },
|
|
// pop
|
|
{ &HphpArray::PopVec, &HphpArray::Pop,
|
|
&SharedMap::Pop,
|
|
&NameValueTableWrapper::Pop,
|
|
&PolicyArray::Pop },
|
|
// dequeue
|
|
{ &HphpArray::Dequeue, &HphpArray::Dequeue,
|
|
&SharedMap::Dequeue,
|
|
&NameValueTableWrapper::Dequeue,
|
|
&PolicyArray::Dequeue },
|
|
// prepend
|
|
{ &HphpArray::Prepend, &HphpArray::Prepend,
|
|
&SharedMap::Prepend,
|
|
&NameValueTableWrapper::Prepend,
|
|
&PolicyArray::Prepend },
|
|
// renumber
|
|
{ &HphpArray::RenumberVec, &HphpArray::Renumber,
|
|
&SharedMap::Renumber,
|
|
&NameValueTableWrapper::Renumber,
|
|
&PolicyArray::Renumber },
|
|
// onSetEvalScalar
|
|
{ &HphpArray::OnSetEvalScalarVec, &HphpArray::OnSetEvalScalar,
|
|
&SharedMap::OnSetEvalScalar,
|
|
&NameValueTableWrapper::OnSetEvalScalar,
|
|
&PolicyArray::OnSetEvalScalar },
|
|
// escalate
|
|
{ &ArrayData::Escalate, &ArrayData::Escalate,
|
|
&SharedMap::Escalate,
|
|
&ArrayData::Escalate,
|
|
&PolicyArray::Escalate },
|
|
// getSharedVariant
|
|
{ &ArrayData::GetSharedVariant, &ArrayData::GetSharedVariant,
|
|
&SharedMap::GetSharedVariant,
|
|
&ArrayData::GetSharedVariant,
|
|
&ArrayData::GetSharedVariant },
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// In general, arrays can contain int-valued-strings, even though
|
|
// plain array access converts them to integers. non-int-string
|
|
// assersions should go upstream of the ArrayData api.
|
|
|
|
bool ArrayData::IsValidKey(CStrRef k) {
|
|
return IsValidKey(k.get());
|
|
}
|
|
|
|
bool ArrayData::IsValidKey(CVarRef k) {
|
|
return k.isInteger() ||
|
|
(k.isString() && IsValidKey(k.getStringData()));
|
|
}
|
|
|
|
// constructors/destructors
|
|
|
|
HOT_FUNC
|
|
ArrayData *ArrayData::Create() {
|
|
return ArrayInit((ssize_t)0).create();
|
|
}
|
|
|
|
HOT_FUNC
|
|
ArrayData *ArrayData::Create(CVarRef value) {
|
|
ArrayInit init(1);
|
|
init.set(value);
|
|
return init.create();
|
|
}
|
|
|
|
ArrayData *ArrayData::Create(CVarRef name, CVarRef value) {
|
|
ArrayInit init(1);
|
|
// There is no toKey() call on name.
|
|
init.set(name, value, true);
|
|
return init.create();
|
|
}
|
|
|
|
ArrayData *ArrayData::CreateRef(CVarRef value) {
|
|
ArrayInit init(1);
|
|
init.setRef(value);
|
|
return init.create();
|
|
}
|
|
|
|
ArrayData *ArrayData::CreateRef(CVarRef name, CVarRef value) {
|
|
ArrayInit init(1);
|
|
// There is no toKey() call on name.
|
|
init.setRef(name, value, true);
|
|
return init.create();
|
|
}
|
|
|
|
ArrayData *ArrayData::NonSmartCopy(const ArrayData*) {
|
|
throw FatalErrorException("nonSmartCopy not implemented.");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// reads
|
|
|
|
Object ArrayData::toObject() const {
|
|
return ObjectData::FromArray(const_cast<ArrayData*>(this));
|
|
}
|
|
|
|
int ArrayData::compare(const ArrayData *v2) const {
|
|
assert(v2);
|
|
|
|
auto const count1 = size();
|
|
auto const count2 = v2->size();
|
|
if (count1 < count2) return -1;
|
|
if (count1 > count2) return 1;
|
|
if (count1 == 0) return 0;
|
|
|
|
// prevent circular referenced objects/arrays or deep ones
|
|
DECLARE_THREAD_INFO; check_recursion(info);
|
|
|
|
for (ArrayIter iter(this); iter; ++iter) {
|
|
auto key = iter.first();
|
|
if (!v2->exists(key)) return 1;
|
|
auto value1 = iter.second();
|
|
auto value2 = v2->get(key);
|
|
if (HPHP::more(value1, value2)) return 1;
|
|
if (HPHP::less(value1, value2)) return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool ArrayData::equal(const ArrayData *v2, bool strict) const {
|
|
assert(v2);
|
|
|
|
auto const count1 = size();
|
|
auto const count2 = v2->size();
|
|
if (count1 != count2) return false;
|
|
if (count1 == 0) return true;
|
|
|
|
// prevent circular referenced objects/arrays or deep ones
|
|
DECLARE_THREAD_INFO; check_recursion(info);
|
|
|
|
if (strict) {
|
|
for (ArrayIter iter1(this), iter2(v2); iter1; ++iter1, ++iter2) {
|
|
assert(iter2);
|
|
if (!same(iter1.first(), iter2.first())
|
|
|| !same(iter1.second(), iter2.secondRef())) return false;
|
|
}
|
|
} else {
|
|
for (ArrayIter iter(this); iter; ++iter) {
|
|
Variant key(iter.first());
|
|
if (!v2->exists(key)) return false;
|
|
if (!tvEqual(*iter.second().asTypedValue(),
|
|
*v2->get(key).asTypedValue())) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// stack and queue operations
|
|
|
|
ArrayData *ArrayData::Pop(ArrayData* a, Variant &value) {
|
|
if (!a->empty()) {
|
|
ssize_t pos = a->iter_end();
|
|
value = a->getValue(pos);
|
|
return a->remove(a->getKey(pos), a->getCount() > 1);
|
|
}
|
|
value = uninit_null();
|
|
return a;
|
|
}
|
|
|
|
ArrayData *ArrayData::Dequeue(ArrayData* a, Variant &value) {
|
|
if (!a->empty()) {
|
|
auto const pos = a->iter_begin();
|
|
value = a->getValue(pos);
|
|
ArrayData *ret = a->remove(a->getKey(pos), a->getCount() > 1);
|
|
|
|
// In PHP, array_shift() will cause all numerically key-ed values re-keyed
|
|
ret->renumber();
|
|
return ret;
|
|
}
|
|
value = uninit_null();
|
|
return a;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// MutableArrayIter related functions
|
|
|
|
void ArrayData::newFullPos(FullPos &fp) {
|
|
assert(!fp.getContainer());
|
|
fp.setContainer(this);
|
|
fp.setNext(strongIterators());
|
|
setStrongIterators(&fp);
|
|
fp.m_pos = m_pos;
|
|
}
|
|
|
|
void ArrayData::freeFullPos(FullPos &fp) {
|
|
assert(strongIterators() && fp.getContainer() == this);
|
|
// search for fp in our list, then remove it. Usually its the first one.
|
|
FullPos* p = strongIterators();
|
|
if (p == &fp) {
|
|
setStrongIterators(p->getNext());
|
|
fp.setContainer(nullptr);
|
|
return;
|
|
}
|
|
for (; p->getNext(); p = p->getNext()) {
|
|
if (p->getNext() == &fp) {
|
|
p->setNext(p->getNext()->getNext());
|
|
fp.setContainer(nullptr);
|
|
return;
|
|
}
|
|
}
|
|
// If the strong iterator list was empty or if fp could not be
|
|
// found in the strong iterator list, then we are in a bad state
|
|
assert(false);
|
|
}
|
|
|
|
void ArrayData::freeStrongIterators() {
|
|
for (FullPosRange r(strongIterators()); !r.empty(); r.popFront()) {
|
|
r.front()->setContainer(nullptr);
|
|
}
|
|
setStrongIterators(nullptr);
|
|
}
|
|
|
|
void ArrayData::moveStrongIterators(ArrayData* dest, ArrayData* src) {
|
|
for (FullPosRange r(src->strongIterators()); !r.empty(); r.popFront()) {
|
|
r.front()->setContainer(dest);
|
|
}
|
|
// move pointer to list and flag in one copy
|
|
dest->m_strongIterators = src->m_strongIterators;
|
|
src->m_strongIterators = 0;
|
|
}
|
|
|
|
CVarRef ArrayData::endRef() {
|
|
if (m_pos != invalid_index) {
|
|
return getValueRef(iter_end());
|
|
}
|
|
throw FatalErrorException("invalid ArrayData::m_pos");
|
|
}
|
|
|
|
void ArrayData::Ksort(ArrayData*, int sort_flags, bool ascending) {
|
|
throw FatalErrorException("Unimplemented ArrayData::ksort");
|
|
}
|
|
|
|
void ArrayData::Sort(ArrayData*, int sort_flags, bool ascending) {
|
|
throw FatalErrorException("Unimplemented ArrayData::sort");
|
|
}
|
|
|
|
void ArrayData::Asort(ArrayData*, int sort_flags, bool ascending) {
|
|
throw FatalErrorException("Unimplemented ArrayData::asort");
|
|
}
|
|
|
|
void ArrayData::Uksort(ArrayData*, CVarRef cmp_function) {
|
|
throw FatalErrorException("Unimplemented ArrayData::uksort");
|
|
}
|
|
|
|
void ArrayData::Usort(ArrayData*, CVarRef cmp_function) {
|
|
throw FatalErrorException("Unimplemented ArrayData::usort");
|
|
}
|
|
|
|
void ArrayData::Uasort(ArrayData*, CVarRef cmp_function) {
|
|
throw FatalErrorException("Unimplemented ArrayData::uasort");
|
|
}
|
|
|
|
ArrayData* ArrayData::CopyWithStrongIterators(const ArrayData* ad) {
|
|
throw FatalErrorException("Unimplemented ArrayData::copyWithStrongIterators");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Default implementation of position-based iterations.
|
|
|
|
Variant ArrayData::reset() {
|
|
m_pos = iter_begin();
|
|
return m_pos != invalid_index ? getValue(m_pos) : Variant(false);
|
|
}
|
|
|
|
Variant ArrayData::prev() {
|
|
if (m_pos != invalid_index) {
|
|
m_pos = iter_rewind(m_pos);
|
|
if (m_pos != invalid_index) {
|
|
return getValue(m_pos);
|
|
}
|
|
}
|
|
return Variant(false);
|
|
}
|
|
|
|
Variant ArrayData::next() {
|
|
if (m_pos != invalid_index) {
|
|
m_pos = iter_advance(m_pos);
|
|
if (m_pos != invalid_index) {
|
|
return getValue(m_pos);
|
|
}
|
|
}
|
|
return Variant(false);
|
|
}
|
|
|
|
Variant ArrayData::end() {
|
|
m_pos = iter_end();
|
|
return m_pos != invalid_index ? getValue(m_pos) : Variant(false);
|
|
}
|
|
|
|
Variant ArrayData::key() const {
|
|
return m_pos != invalid_index ? getKey(m_pos) : uninit_null();
|
|
}
|
|
|
|
Variant ArrayData::value(int32_t &pos) const {
|
|
return pos != invalid_index ? getValue(pos) : Variant(false);
|
|
}
|
|
|
|
Variant ArrayData::current() const {
|
|
return m_pos != invalid_index ? getValue(m_pos) : Variant(false);
|
|
}
|
|
|
|
const StaticString
|
|
s_value("value"),
|
|
s_key("key");
|
|
|
|
Variant ArrayData::each() {
|
|
if (m_pos != invalid_index) {
|
|
ArrayInit ret(4);
|
|
Variant key(getKey(m_pos));
|
|
Variant value(getValue(m_pos));
|
|
ret.set(1, value);
|
|
ret.set(s_value, value);
|
|
ret.set(0, key);
|
|
ret.set(s_key, key);
|
|
m_pos = iter_advance(m_pos);
|
|
return ret.toVariant();
|
|
}
|
|
return Variant(false);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// helpers
|
|
|
|
void ArrayData::serializeImpl(VariableSerializer *serializer) const {
|
|
serializer->writeArrayHeader(size(), isVectorData());
|
|
for (ArrayIter iter(this); iter; ++iter) {
|
|
serializer->writeArrayKey(iter.first());
|
|
serializer->writeArrayValue(iter.secondRef());
|
|
}
|
|
serializer->writeArrayFooter();
|
|
}
|
|
|
|
void ArrayData::serialize(VariableSerializer *serializer,
|
|
bool skipNestCheck /* = false */) const {
|
|
if (size() == 0) {
|
|
serializer->writeArrayHeader(0, isVectorData());
|
|
serializer->writeArrayFooter();
|
|
return;
|
|
}
|
|
if (!skipNestCheck) {
|
|
if (serializer->incNestedLevel((void*)this)) {
|
|
serializer->writeOverflow((void*)this);
|
|
} else {
|
|
serializeImpl(serializer);
|
|
}
|
|
serializer->decNestedLevel((void*)this);
|
|
} else {
|
|
// If isObject, the array is temporary and we should not check or save
|
|
// its pointer.
|
|
serializeImpl(serializer);
|
|
}
|
|
}
|
|
|
|
bool ArrayData::hasInternalReference(PointerSet &vars,
|
|
bool ds /* = false */) const {
|
|
if (isSharedMap()) return false;
|
|
for (ArrayIter iter(this); iter; ++iter) {
|
|
CVarRef var = iter.secondRef();
|
|
if (var.isReferenced()) {
|
|
Variant *pvar = var.getRefData();
|
|
if (vars.find(pvar) != vars.end()) {
|
|
return true;
|
|
}
|
|
vars.insert(pvar);
|
|
}
|
|
if (var.isObject()) {
|
|
ObjectData *pobj = var.getObjectData();
|
|
if (vars.find(pobj) != vars.end()) {
|
|
return true;
|
|
}
|
|
vars.insert(pobj);
|
|
if (ds && pobj->instanceof(SystemLib::s_SerializableClass)) {
|
|
return true;
|
|
}
|
|
if (pobj->hasInternalReference(vars, ds)) {
|
|
return true;
|
|
}
|
|
} else if (var.isArray() &&
|
|
var.getArrayData()->hasInternalReference(vars, ds)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CVarRef ArrayData::get(CVarRef k, bool error) const {
|
|
assert(IsValidKey(k));
|
|
auto const cell = k.asCell();
|
|
return isIntKey(cell) ? get(getIntKey(cell), error)
|
|
: get(getStringKey(cell), error);
|
|
}
|
|
|
|
CVarRef ArrayData::getNotFound(int64_t k) {
|
|
raise_notice("Undefined index: %" PRId64, k);
|
|
return null_variant;
|
|
}
|
|
|
|
CVarRef ArrayData::getNotFound(const StringData* k) {
|
|
raise_notice("Undefined index: %s", k->data());
|
|
return null_variant;
|
|
}
|
|
|
|
CVarRef ArrayData::getNotFound(int64_t k, bool error) const {
|
|
return error && m_kind != kNvtwKind ? getNotFound(k) :
|
|
null_variant;
|
|
}
|
|
|
|
CVarRef ArrayData::getNotFound(const StringData* k, bool error) const {
|
|
return error && m_kind != kNvtwKind ? getNotFound(k) :
|
|
null_variant;
|
|
}
|
|
|
|
CVarRef ArrayData::getNotFound(CStrRef k) {
|
|
raise_notice("Undefined index: %s", k.data());
|
|
return null_variant;
|
|
}
|
|
|
|
CVarRef ArrayData::getNotFound(CVarRef k) {
|
|
raise_notice("Undefined index: %s", k.toString().data());
|
|
return null_variant;
|
|
}
|
|
|
|
void ArrayData::Renumber(ArrayData*) {
|
|
}
|
|
|
|
void ArrayData::OnSetEvalScalar(ArrayData*) {
|
|
assert(false);
|
|
}
|
|
|
|
ArrayData* ArrayData::Escalate(const ArrayData* ad) {
|
|
return const_cast<ArrayData*>(ad);
|
|
}
|
|
|
|
void ArrayData::dump() {
|
|
string out; dump(out); fwrite(out.c_str(), out.size(), 1, stdout);
|
|
}
|
|
|
|
void ArrayData::dump(std::string &out) {
|
|
VariableSerializer vs(VariableSerializer::Type::VarDump);
|
|
String ret(vs.serialize(Array(this), true));
|
|
out += "ArrayData(";
|
|
out += boost::lexical_cast<string>(_count);
|
|
out += "): ";
|
|
out += string(ret.data(), ret.size());
|
|
}
|
|
|
|
void ArrayData::dump(std::ostream &out) {
|
|
unsigned int i = 0;
|
|
for (ArrayIter iter(this); iter; ++iter, i++) {
|
|
VariableSerializer vs(VariableSerializer::Type::Serialize);
|
|
Variant key(iter.first());
|
|
out << i << " #### " << key.toString()->toCPPString() << " #### ";
|
|
Variant val(iter.second());
|
|
try {
|
|
String valS(vs.serialize(val, true));
|
|
out << valS->toCPPString();
|
|
} catch (const Exception &e) {
|
|
out << "Exception: " << e.what();
|
|
}
|
|
out << std::endl;
|
|
}
|
|
}
|
|
|
|
void ArrayData::getChildren(std::vector<TypedValue *> &out) {
|
|
if (isSharedMap()) {
|
|
SharedMap *sm = static_cast<SharedMap *>(this);
|
|
sm->getChildren(out);
|
|
return;
|
|
}
|
|
for (ssize_t pos = iter_begin();
|
|
pos != ArrayData::invalid_index;
|
|
pos = iter_advance(pos)) {
|
|
TypedValue *tv = nvGetValueRef(pos);
|
|
out.push_back(tv);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|