dec99505a6
This diff refactors some of the VM's logic for iterators (with a focus on
mutable iteration), delivering several improvements:
1) MIterCtx was renamed to MArrayIter, and the m_key and m_val fields
were eliminated.
2) Eliminated the need for MArrayIter to dynamically allocate a
MutableArrayIter object, and removed other layers of indirection as
well.
3) Reduced the size of HPHP::VM::Iter from 64 bytes down to 32 bytes.
4) Removed the "if (siPastEnd())" check when adding a new element to an
HphpArray or a ZendArray.
5) Moved all of the iterator logic into a single .cpp file.
This diff reworks FullPos's to point to current element instead of pointing
to the next element. It also splits up the IterFree instruction into two
instructions (IterFree and MIterFree). These changes allowed various logic
to be simplified and data structures to be reduced in size. There is
definitely more opportunity for refactoring, but I know the JIT helpers for
iteration have been carefully tuned and so I'll leave further refactoring
for future diffs.
Finally, I spent a little time cleaning up the bytecode spec a bit, mostly
with respect to iteration.
734 linhas
20 KiB
C++
734 linhas
20 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
#define INLINE_VARIANT_HELPER 1 // for selected inlining
|
|
|
|
#include <runtime/base/array/vector_array.h>
|
|
#include <runtime/base/array/array_iterator.h>
|
|
#include <runtime/base/externals.h>
|
|
#include <runtime/base/runtime_option.h>
|
|
#include <util/util.h>
|
|
|
|
namespace HPHP {
|
|
|
|
StaticEmptyVectorArray StaticEmptyVectorArray::s_theEmptyVectorArray;
|
|
|
|
IMPLEMENT_SMART_ALLOCATION_HOT(VectorArray);
|
|
|
|
#ifdef DEBUGGING_SMART_ALLOCATOR
|
|
#define DECLARE_ALLOCATOR(a, T, I)
|
|
#define NEWALLOC(a) new
|
|
#define DEALLOC(a, e, T) delete e;
|
|
#else
|
|
#define DECLARE_ALLOCATOR(a, T, I) \
|
|
SmartAllocator<T, SmartAllocatorImpl::I> *a = \
|
|
T::AllocatorType::getNoCheck();
|
|
#define NEWALLOC(a) new (a)
|
|
#define DEALLOC(a, e, T) (e->~T(),a->dealloc(e))
|
|
#endif
|
|
ssize_t VectorArray::vsize() const { not_reached(); }
|
|
|
|
HOT_FUNC_HPHP
|
|
ssize_t VectorArray::iter_begin() const {
|
|
if (!m_size) return VectorArray::invalid_index;
|
|
return 0;
|
|
}
|
|
|
|
ssize_t VectorArray::iter_end() const {
|
|
if (!m_size) return VectorArray::invalid_index;
|
|
return m_size - 1;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
ssize_t VectorArray::iter_advance(ssize_t prev) const {
|
|
assert(prev >= 0 && prev < (ssize_t)m_size);
|
|
ssize_t next = prev + 1;
|
|
if (next >= m_size) return VectorArray::invalid_index;
|
|
return next;
|
|
}
|
|
|
|
ssize_t VectorArray::iter_rewind(ssize_t prev) const {
|
|
assert(prev >= 0 && prev < (ssize_t)m_size);
|
|
ssize_t next = prev - 1;
|
|
if (next < 0) return VectorArray::invalid_index;
|
|
return next;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
Variant VectorArray::getKey(ssize_t pos) const {
|
|
assert(pos >= 0 && pos < m_size);
|
|
return pos;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
Variant VectorArray::getValue(ssize_t pos) const {
|
|
assert(pos >= 0 && pos < m_size);
|
|
return tvAsCVarRef(&m_elems[pos]);
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
CVarRef VectorArray::getValueRef(ssize_t pos) const {
|
|
assert(pos >= 0 && pos < m_size);
|
|
return tvAsCVarRef(&m_elems[pos]);
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
Variant VectorArray::value(ssize_t &pos) const {
|
|
if (pos >= 0 && pos < m_size) {
|
|
return tvAsCVarRef(&m_elems[pos]);
|
|
}
|
|
pos = VectorArray::invalid_index;
|
|
return false;
|
|
}
|
|
|
|
Variant VectorArray::key() const {
|
|
if (m_pos >= 0 && m_pos < m_size) {
|
|
return m_pos;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Variant VectorArray::current() const {
|
|
if (m_pos >= 0 && m_pos < m_size) {
|
|
return tvAsCVarRef(&m_elems[m_pos]);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Variant VectorArray::reset() {
|
|
ssize_t pos = 0;
|
|
Variant v = VectorArray::value(pos);
|
|
if (m_pos != pos) m_pos = pos;
|
|
return v;
|
|
}
|
|
Variant VectorArray::prev() {
|
|
ssize_t pos = m_pos - 1;
|
|
Variant v = VectorArray::value(pos);
|
|
if (m_pos != pos) m_pos = pos;
|
|
return v;
|
|
}
|
|
Variant VectorArray::next() {
|
|
ssize_t pos = m_pos + 1;
|
|
Variant v = VectorArray::value(pos);
|
|
if (m_pos != pos) m_pos = pos;
|
|
return v;
|
|
}
|
|
Variant VectorArray::end() {
|
|
ssize_t pos = (ssize_t)m_size - 1;
|
|
Variant v = VectorArray::value(pos);
|
|
if (m_pos != pos) m_pos = pos;
|
|
return v;
|
|
}
|
|
|
|
void VectorArray::alloc(uint size) {
|
|
if (size <= FixedSize) {
|
|
m_capacity = FixedSize;
|
|
m_elems = m_fixed;
|
|
m_allocMode = kInline;
|
|
return;
|
|
}
|
|
uint cap = Util::nextPower2(size);
|
|
m_capacity = cap;
|
|
if (!m_nonsmart) {
|
|
m_elems = (TypedValue*) smart_malloc(cap * sizeof(TypedValue));
|
|
m_allocMode = kSmart;
|
|
} else {
|
|
m_elems = (TypedValue*) malloc(cap * sizeof(TypedValue));
|
|
m_allocMode = kMalloc;
|
|
}
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
VectorArray::VectorArray(uint capacity /* = 0 */) {
|
|
m_size = 0;
|
|
alloc(capacity);
|
|
m_pos = ArrayData::invalid_index;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
VectorArray::VectorArray(const VectorArray *src, uint start /* = 0 */,
|
|
uint size /* = 0 */) : ArrayData(src) {
|
|
assert(src);
|
|
assert(size == 0 || (size == src->m_size - 1L && size > 0));
|
|
assert(!src->strongIterators());
|
|
assert(m_pos == src->m_pos);
|
|
m_size = size ? size : src->m_size;
|
|
alloc(m_size);
|
|
for (uint i = 0; i < m_size; i++) {
|
|
Variant& to = tvAsUninitializedVariant(&m_elems[i]);
|
|
CVarRef fm = tvAsCVarRef(&src->m_elems[i + start]);
|
|
to.constructWithRefHelper(fm, src);
|
|
}
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
VectorArray::VectorArray(uint size, const Variant *values[]) {
|
|
assert(size > 0);
|
|
m_size = size;
|
|
alloc(size);
|
|
for (uint i = 0; i < size; i++) {
|
|
Variant& to = tvAsUninitializedVariant(&m_elems[i]);
|
|
to.constructValHelper(*values[i]);
|
|
}
|
|
assert(m_pos == 0);
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
VectorArray::~VectorArray() {
|
|
uint size = m_size;
|
|
for (uint i = 0; i < size; i++) {
|
|
tvAsVariant(&m_elems[i]).~Variant();
|
|
}
|
|
if (m_allocMode == kSmart) {
|
|
smart_free(m_elems);
|
|
} else if (m_allocMode == kMalloc) {
|
|
free(m_elems);
|
|
}
|
|
}
|
|
|
|
// This constructor is for nonSmartCopy()
|
|
VectorArray::VectorArray(const VectorArray *src, bool sma /* ignored */) :
|
|
ArrayData(src, kArrayData, /*nonsmart*/true) {
|
|
assert(src);
|
|
assert(!src->strongIterators());
|
|
m_size = src->m_size;
|
|
if (m_size <= FixedSize) {
|
|
m_capacity = FixedSize;
|
|
m_elems = m_fixed;
|
|
m_allocMode = kInline;
|
|
} else {
|
|
m_capacity = Util::nextPower2(m_size);
|
|
m_elems = (TypedValue*) malloc(m_capacity * sizeof(TypedValue));
|
|
m_allocMode = kMalloc;
|
|
}
|
|
for (uint i = 0, n = m_size; i < n; i++) {
|
|
assert(src->m_elems[i].m_type != KindOfRef &&
|
|
src->m_elems[i].m_type != KindOfObject);
|
|
Variant& to = tvAsUninitializedVariant(&m_elems[i]);
|
|
CVarRef fm = tvAsCVarRef(&src->m_elems[i]);
|
|
to.constructWithRefHelper(fm, src);
|
|
}
|
|
assert(src->m_pos == 0 && m_pos == 0);
|
|
}
|
|
|
|
void VectorArray::grow(uint newSize) {
|
|
assert(newSize > FixedSize);
|
|
m_capacity = Util::nextPower2(newSize);
|
|
if (!m_nonsmart) {
|
|
assert(m_allocMode == kInline || m_allocMode == kSmart);
|
|
if (m_allocMode == kInline) {
|
|
m_elems = (TypedValue*)smart_malloc(m_capacity * sizeof(TypedValue));
|
|
memcpy(m_elems, m_fixed, m_size * sizeof(TypedValue));
|
|
m_allocMode = kSmart;
|
|
} else {
|
|
m_elems = (TypedValue*)smart_realloc(m_elems,
|
|
m_capacity * sizeof(TypedValue));
|
|
}
|
|
} else if (m_allocMode == kInline) {
|
|
m_elems = (TypedValue*)malloc(m_capacity * sizeof(TypedValue));
|
|
memcpy(m_elems, m_fixed, m_size * sizeof(TypedValue));
|
|
m_allocMode = kMalloc;
|
|
} else {
|
|
assert(m_allocMode == kMalloc);
|
|
m_elems = (TypedValue*)realloc(m_elems, m_capacity * sizeof(TypedValue));
|
|
}
|
|
}
|
|
|
|
inline void ALWAYS_INLINE VectorArray::checkSize(uint n /* = 1 */) {
|
|
assert(n >= 1);
|
|
assert(m_size <= m_capacity);
|
|
uint newSize = m_size + n;
|
|
if (UNLIKELY(m_capacity < newSize)) {
|
|
grow(newSize);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do unsigned comparison to cheaply exclude k < 0
|
|
*/
|
|
inline ALWAYS_INLINE bool inRange(int64_t k, uint size) {
|
|
return size_t(k) < size_t(size);
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
bool VectorArray::exists(int64 k) const {
|
|
return inRange(k, m_size);
|
|
}
|
|
|
|
bool VectorArray::exists(const StringData* k) const {
|
|
return false;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
CVarRef VectorArray::get(int64 k, bool error /* = false */) const {
|
|
if (LIKELY(inRange(k, m_size))) {
|
|
return tvAsCVarRef(&m_elems[k]);
|
|
}
|
|
return error ? getNotFound(k) : null_variant;
|
|
}
|
|
|
|
CVarRef VectorArray::get(const StringData* k, bool error /* = false */) const {
|
|
return error ? getNotFound(k) : null_variant;
|
|
}
|
|
|
|
ssize_t VectorArray::getIndex(int64 k) const {
|
|
if (k >= 0 && k < m_size) return k;
|
|
return ArrayData::invalid_index;
|
|
}
|
|
|
|
ssize_t VectorArray::getIndex(const StringData* k) const {
|
|
return ArrayData::invalid_index;
|
|
}
|
|
|
|
ZendArray *VectorArray::escalateToNonEmptyZendArray() const {
|
|
assert(m_size);
|
|
ZendArray *ret;
|
|
ZendArray::Bucket *p[256], **pp;
|
|
if (LIKELY(m_size < 256)) {
|
|
pp = p;
|
|
} else {
|
|
pp =
|
|
(ZendArray::Bucket **)malloc(sizeof(ZendArray::Bucket *) * (m_size + 1));
|
|
}
|
|
DECLARE_ALLOCATOR(a, ZendArray::Bucket, Bucket);
|
|
for (int64 i = 0; i < m_size; i++) {
|
|
CVarRef v = tvAsCVarRef(&m_elems[i]);
|
|
pp[i] = NEWALLOC(a) ZendArray::Bucket(i, withRefBind(v));
|
|
}
|
|
pp[m_size] = NULL;
|
|
ret = NEW(ZendArray)(m_size, m_size, pp);
|
|
if (UNLIKELY(pp != p)) free(pp);
|
|
if (m_pos != ArrayData::invalid_index) {
|
|
ret->setPosition(ret->getIndex(m_pos));
|
|
} else {
|
|
ret->setPosition(0);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
ZendArray *VectorArray::escalateToZendArray() const {
|
|
if (m_size == 0) {
|
|
assert(m_pos == ArrayData::invalid_index);
|
|
return NEW(ZendArray)();
|
|
}
|
|
return escalateToNonEmptyZendArray();
|
|
}
|
|
|
|
inline void ALWAYS_INLINE VectorArray::checkInsertIterator(ssize_t pos) {
|
|
if (m_pos == ArrayData::invalid_index) m_pos = pos;
|
|
}
|
|
|
|
ArrayData *VectorArray::lvalNew(Variant *&ret, bool copy) {
|
|
if (UNLIKELY(copy)) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
a->VectorArray::lvalNew(ret, false);
|
|
return a;
|
|
}
|
|
uint index = m_size;
|
|
checkSize();
|
|
Variant& v = tvAsUninitializedVariant(&m_elems[index]);
|
|
v.setUninitNull();
|
|
ret = &v;
|
|
checkInsertIterator((ssize_t)index);
|
|
m_size++;
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::lval(int64 k, Variant *&ret, bool copy,
|
|
bool checkExist /* = false */) {
|
|
ret = inRange(k, m_size) ? &tvAsVariant(&m_elems[k]) : NULL;
|
|
if (ret == NULL && k != m_size) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->addLvalImpl(k, &ret, false);
|
|
return a;
|
|
}
|
|
if (LIKELY(!copy)) {
|
|
if (ret) return NULL;
|
|
assert(m_size == k);
|
|
checkSize();
|
|
Variant& v = tvAsUninitializedVariant(&m_elems[k]);
|
|
v.setUninitNull();
|
|
ret = &v;
|
|
checkInsertIterator((ssize_t)k);
|
|
m_size++;
|
|
return NULL;
|
|
}
|
|
if (checkExist && ret && (ret->isReferenced() || ret->isObject())) {
|
|
return NULL;
|
|
}
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
if (ret) {
|
|
Variant& v = tvAsVariant(&a->m_elems[k]);
|
|
ret = &v;
|
|
assert(ret);
|
|
return a;
|
|
}
|
|
assert(m_size == k);
|
|
a->VectorArray::lvalNew(ret, false);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::lval(StringData* k, Variant *&ret, bool copy,
|
|
bool checkExist /* = false */) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->addLvalImpl(k, k->hash(), &ret);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::lvalPtr(StringData* k, Variant *&ret, bool copy,
|
|
bool create) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->lvalPtr(k, ret, false, create);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::lvalPtr(int64 k, Variant *&ret, bool copy,
|
|
bool create) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->lvalPtr(k, ret, false, create);
|
|
return a;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
ArrayData *VectorArray::set(int64 k, CVarRef v, bool copy) {
|
|
if (inRange(k, m_size)) {
|
|
if (copy) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
tvAsVariant(&a->m_elems[k]).assignVal(v);
|
|
return a;
|
|
}
|
|
tvAsVariant(&m_elems[k]).assignVal(v);
|
|
return NULL;
|
|
}
|
|
if (k == m_size) return VectorArray::append(v, copy);
|
|
ZendArray *a = escalateToZendArray();
|
|
a->add(k, v, false);
|
|
return a;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
ArrayData *VectorArray::set(StringData* k, CVarRef v, bool copy) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->add(k, v, false);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::setRef(int64 k, CVarRef v, bool copy) {
|
|
if (UNLIKELY(copy)) {
|
|
if (inRange(k, m_size) || k == m_size) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
a->VectorArray::setRef(k, v, false);
|
|
return a;
|
|
}
|
|
} else {
|
|
if (inRange(k, m_size)) {
|
|
tvAsVariant(&m_elems[k]).assignRef(v);
|
|
return NULL;
|
|
} else if (k == m_size) {
|
|
checkSize();
|
|
tvAsUninitializedVariant(&m_elems[k]).constructRefHelper(v);
|
|
checkInsertIterator((ssize_t)k);
|
|
m_size++;
|
|
return NULL;
|
|
}
|
|
}
|
|
ZendArray *a = escalateToZendArray();
|
|
a->updateRef(k, v);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::setRef(StringData* k, CVarRef v, bool copy) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->updateRef(k, v);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::copy() const {
|
|
return NEW(VectorArray)(this);
|
|
}
|
|
|
|
ArrayData *VectorArray::nonSmartCopy() const {
|
|
return new VectorArray(this, false);
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
ArrayData *VectorArray::append(CVarRef v, bool copy) {
|
|
uint index = m_size;
|
|
if (copy) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
a->checkSize();
|
|
tvAsUninitializedVariant(&a->m_elems[index]).constructValHelper(v);
|
|
a->checkInsertIterator((ssize_t)index);
|
|
a->m_size++;
|
|
return a;
|
|
}
|
|
checkSize();
|
|
tvAsUninitializedVariant(&m_elems[index]).constructValHelper(v);
|
|
checkInsertIterator((ssize_t)index);
|
|
m_size++;
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::appendRef(CVarRef v, bool copy) {
|
|
if (UNLIKELY(copy)) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
a->VectorArray::appendRef(v, false);
|
|
return a;
|
|
}
|
|
uint index = m_size;
|
|
checkSize();
|
|
tvAsUninitializedVariant(&m_elems[index]).constructRefHelper(v);
|
|
checkInsertIterator((ssize_t)index);
|
|
m_size++;
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::appendWithRef(CVarRef v, bool copy) {
|
|
if (UNLIKELY(copy)) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
a->VectorArray::appendWithRef(v, false);
|
|
return a;
|
|
}
|
|
uint index = m_size;
|
|
checkSize();
|
|
Variant& to = tvAsUninitializedVariant(&m_elems[index]);
|
|
to.setUninitNull();
|
|
to.setWithRef(v);
|
|
checkInsertIterator((ssize_t)index);
|
|
m_size++;
|
|
return NULL;
|
|
}
|
|
|
|
HOT_FUNC_HPHP
|
|
ArrayData *VectorArray::append(const ArrayData *elems, ArrayOp op, bool copy) {
|
|
if (UNLIKELY(!elems->isVectorArray())) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->append(elems, op, false);
|
|
return a;
|
|
}
|
|
if (UNLIKELY(copy)) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
a->VectorArray::append(elems, op, false);
|
|
return a;
|
|
}
|
|
assert(dynamic_cast<const VectorArray *>(elems));
|
|
const VectorArray *velems = static_cast<const VectorArray *>(elems);
|
|
if (op == Plus) {
|
|
if (velems->m_size > m_size) {
|
|
checkSize(velems->m_size - m_size);
|
|
for (uint i = m_size; i < velems->m_size; i++) {
|
|
Variant& to = tvAsUninitializedVariant(&m_elems[i]);
|
|
CVarRef fm = tvAsCVarRef(&velems->m_elems[i]);
|
|
to.constructWithRefHelper(fm, 0);
|
|
}
|
|
checkInsertIterator((ssize_t)m_size);
|
|
m_size = velems->m_size;
|
|
}
|
|
} else {
|
|
assert(op == Merge);
|
|
if (velems->m_size > 0) {
|
|
checkSize(velems->m_size);
|
|
for (uint i = m_size; i < m_size + velems->m_size; i++) {
|
|
Variant& to = tvAsUninitializedVariant(&m_elems[i]);
|
|
CVarRef fm = tvAsCVarRef(&velems->m_elems[i - m_size]);
|
|
to.constructWithRefHelper(fm, 0);
|
|
}
|
|
checkInsertIterator((ssize_t)m_size);
|
|
m_size += velems->m_size;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::pop(Variant &value) {
|
|
if (UNLIKELY(!m_size)) {
|
|
value.setNull();
|
|
return NULL;
|
|
}
|
|
if (UNLIKELY(getCount() > 1)) {
|
|
value = tvAsCVarRef(&m_elems[m_size - 1]);
|
|
if (m_size == 1) {
|
|
return StaticEmptyVectorArray::Get();
|
|
}
|
|
VectorArray *a = NEW(VectorArray)(this, 0, m_size - 1);
|
|
a->m_pos = (ssize_t)0;
|
|
return a;
|
|
}
|
|
ssize_t pos = m_size - 1;
|
|
value = tvAsCVarRef(&m_elems[pos]);
|
|
tvAsVariant(&m_elems[pos]).~Variant();
|
|
assert(m_size && pos == m_size - 1L);
|
|
m_size--;
|
|
// To match PHP-like semantics, the pop operation resets the array's
|
|
// internal iterator
|
|
m_pos = m_size ? (ssize_t)0 : ArrayData::invalid_index;
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::add(int64 k, CVarRef v, bool copy) {
|
|
assert(!exists(k));
|
|
if (k == m_size) return VectorArray::append(v, copy);
|
|
ZendArray *a = escalateToZendArray();
|
|
a->add(k, v, false);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::add(StringData* k, CVarRef v, bool copy) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->add(k, v, false);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::addLval(int64 k, Variant *&ret, bool copy) {
|
|
assert(!exists(k));
|
|
if (k != m_size) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->addLval(k, ret, false);
|
|
return a;
|
|
}
|
|
uint index = m_size;
|
|
if (UNLIKELY(copy)) {
|
|
VectorArray *a = NEW(VectorArray)(this);
|
|
a->VectorArray::addLval(k, ret, false);
|
|
return a;
|
|
}
|
|
checkSize();
|
|
Variant& v = tvAsUninitializedVariant(&m_elems[index]);
|
|
v.setUninitNull();
|
|
ret = &v;
|
|
checkInsertIterator((ssize_t)index);
|
|
m_size++;
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::addLval(StringData* k, Variant *&ret, bool copy) {
|
|
ZendArray *a = escalateToZendArray();
|
|
a->addLval(k, ret, false);
|
|
return a;
|
|
}
|
|
|
|
ArrayData *VectorArray::remove(int64 k, bool copy) {
|
|
if (!inRange(k, m_size)) return NULL;
|
|
if (k != m_size - 1L) {
|
|
ArrayData *a = escalateToNonEmptyZendArray();
|
|
a->remove(k, false);
|
|
return a;
|
|
}
|
|
// k == m_size-1
|
|
if (copy) {
|
|
if (k == 0) return StaticEmptyVectorArray::Get();
|
|
VectorArray *a = NEW(VectorArray)(this, 0, m_size - 1);
|
|
if (a->m_pos == m_size - 1) a->m_pos = ArrayData::invalid_index;
|
|
return a;
|
|
}
|
|
assert(m_size > 0 && k == m_size - 1);
|
|
tvAsCVarRef(&m_elems[k]).~Variant();
|
|
if (m_pos == k) m_pos = ArrayData::invalid_index;
|
|
assert(m_size && k == m_size - 1L);
|
|
m_size--;
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::remove(const StringData* k, bool copy) {
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::prepend(CVarRef v, bool copy) {
|
|
if (UNLIKELY(m_size == m_capacity)) {
|
|
ZendArray *a = escalateToNonEmptyZendArray();
|
|
ArrayData *aa UNUSED = a->prepend(v, false);
|
|
assert(!aa);
|
|
return a;
|
|
}
|
|
if (UNLIKELY(copy)) {
|
|
ArrayData *a = UNLIKELY(m_size >= FixedSize && Util::isPowerOfTwo(m_size)) ?
|
|
// in this case, we would escalate in the capacity check anyway
|
|
static_cast<ArrayData*>(escalateToNonEmptyZendArray()) :
|
|
static_cast<ArrayData*>(NEW(VectorArray)(this));
|
|
ArrayData *aa UNUSED = a->prepend(v, false);
|
|
assert(!aa);
|
|
return a;
|
|
}
|
|
checkSize();
|
|
for (uint i = m_size; i > 0; i--) {
|
|
// copying TV's by value, intentionally not refcounting.
|
|
m_elems[i] = m_elems[i-1];
|
|
}
|
|
tvAsUninitializedVariant(&m_elems[0]).constructValHelper(v);
|
|
m_size++;
|
|
// To match PHP-like semantics, the prepend operation resets the array's
|
|
// internal iterator
|
|
m_pos = (ssize_t)0;
|
|
return NULL;
|
|
}
|
|
|
|
ArrayData *VectorArray::dequeue(Variant &value) {
|
|
if (UNLIKELY(!m_size)) {
|
|
value.setNull();
|
|
return NULL;
|
|
}
|
|
if (UNLIKELY(getCount() > 1)) {
|
|
value = tvAsCVarRef(&m_elems[0]);
|
|
if (m_size == 1) {
|
|
return StaticEmptyVectorArray::Get();
|
|
}
|
|
VectorArray *a = NEW(VectorArray)(this, 1, m_size - 1);
|
|
a->m_pos = (ssize_t)0;
|
|
return a;
|
|
}
|
|
value = tvAsCVarRef(&m_elems[0]);
|
|
m_size--;
|
|
tvAsVariant(&m_elems[0]).~Variant();
|
|
for (uint i = 0; i < m_size; i++) {
|
|
// TypedValue copy without refcounting.
|
|
m_elems[i] = m_elems[i+1];
|
|
}
|
|
// To match PHP-like semantics, the dequeue operation resets the array's
|
|
// internal iterator
|
|
m_pos = m_size ? (ssize_t)0 : ArrayData::invalid_index;
|
|
return NULL;
|
|
}
|
|
|
|
void VectorArray::onSetEvalScalar() {
|
|
for (uint i = 0; i < m_size; i++) {
|
|
tvAsVariant(&m_elems[i]).setEvalScalar();
|
|
}
|
|
}
|
|
|
|
CVarRef VectorArray::currentRef() {
|
|
assert(m_pos >= 0 && m_pos < m_size);
|
|
return tvAsCVarRef(&m_elems[m_pos]);
|
|
}
|
|
|
|
CVarRef VectorArray::endRef() {
|
|
assert(m_pos >= 0 && m_pos < m_size);
|
|
return tvAsCVarRef(&m_elems[m_size - 1]);
|
|
}
|
|
|
|
ArrayData *VectorArray::escalate(bool mutableIteration /* = false */) const {
|
|
if (mutableIteration) {
|
|
return escalateToZendArray();
|
|
}
|
|
return const_cast<VectorArray *>(this);
|
|
}
|
|
|
|
ArrayData* VectorArray::escalateForSort() {
|
|
return escalateToZendArray();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|