620 lines
17 KiB
C++
620 lines
17 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** This file is part of the QtCore module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#ifndef QV4ARRAY_H
|
|
#define QV4ARRAY_H
|
|
|
|
#include <QtCore/qmap.h>
|
|
#include <qmljs_value.h>
|
|
#include <qv4propertydescriptor.h>
|
|
#include <assert.h>
|
|
|
|
#ifdef Q_MAP_DEBUG
|
|
#include <QtCore/qdebug.h>
|
|
#endif
|
|
|
|
#include <new>
|
|
|
|
namespace QQmlJS {
|
|
namespace VM {
|
|
|
|
struct SparseArray;
|
|
|
|
class ArrayElementLessThan
|
|
{
|
|
public:
|
|
inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn)
|
|
: m_context(context), thisObject(thisObject), m_comparefn(comparefn) {}
|
|
|
|
bool operator()(const PropertyDescriptor &v1, const PropertyDescriptor &v2) const;
|
|
|
|
private:
|
|
ExecutionContext *m_context;
|
|
Object *thisObject;
|
|
Value m_comparefn;
|
|
};
|
|
|
|
|
|
struct SparseArrayNode
|
|
{
|
|
quintptr p;
|
|
SparseArrayNode *left;
|
|
SparseArrayNode *right;
|
|
uint size_left;
|
|
uint value;
|
|
|
|
enum Color { Red = 0, Black = 1 };
|
|
enum { Mask = 3 }; // reserve the second bit as well
|
|
|
|
const SparseArrayNode *nextNode() const;
|
|
SparseArrayNode *nextNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->nextNode()); }
|
|
const SparseArrayNode *previousNode() const;
|
|
SparseArrayNode *previousNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->previousNode()); }
|
|
|
|
Color color() const { return Color(p & 1); }
|
|
void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; }
|
|
SparseArrayNode *parent() const { return reinterpret_cast<SparseArrayNode *>(p & ~Mask); }
|
|
void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); }
|
|
|
|
uint key() const {
|
|
uint k = size_left;
|
|
const SparseArrayNode *n = this;
|
|
while (SparseArrayNode *p = n->parent()) {
|
|
if (p && p->right == n)
|
|
k += p->size_left;
|
|
n = p;
|
|
}
|
|
return k;
|
|
}
|
|
|
|
SparseArrayNode *copy(SparseArray *d) const;
|
|
|
|
SparseArrayNode *lowerBound(uint key);
|
|
SparseArrayNode *upperBound(uint key);
|
|
};
|
|
|
|
|
|
inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey)
|
|
{
|
|
SparseArrayNode *n = this;
|
|
SparseArrayNode *last = 0;
|
|
while (n) {
|
|
if (akey <= n->size_left) {
|
|
last = n;
|
|
n = n->left;
|
|
} else {
|
|
akey -= n->size_left;
|
|
n = n->right;
|
|
}
|
|
}
|
|
return last;
|
|
}
|
|
|
|
|
|
inline SparseArrayNode *SparseArrayNode::upperBound(uint akey)
|
|
{
|
|
SparseArrayNode *n = this;
|
|
SparseArrayNode *last = 0;
|
|
while (n) {
|
|
if (akey < n->size_left) {
|
|
last = n;
|
|
n = n->left;
|
|
} else {
|
|
akey -= n->size_left;
|
|
n = n->right;
|
|
}
|
|
}
|
|
return last;
|
|
}
|
|
|
|
|
|
|
|
struct Q_CORE_EXPORT SparseArray
|
|
{
|
|
SparseArray();
|
|
~SparseArray() {
|
|
if (root())
|
|
freeTree(header.left, Q_ALIGNOF(SparseArrayNode));
|
|
}
|
|
|
|
SparseArray(const SparseArray &other);
|
|
private:
|
|
SparseArray &operator=(const SparseArray &other);
|
|
|
|
int numEntries;
|
|
SparseArrayNode header;
|
|
SparseArrayNode *mostLeftNode;
|
|
|
|
void rotateLeft(SparseArrayNode *x);
|
|
void rotateRight(SparseArrayNode *x);
|
|
void rebalance(SparseArrayNode *x);
|
|
void recalcMostLeftNode();
|
|
|
|
SparseArrayNode *root() const { return header.left; }
|
|
|
|
void deleteNode(SparseArrayNode *z);
|
|
|
|
|
|
public:
|
|
SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left);
|
|
void freeTree(SparseArrayNode *root, int alignment);
|
|
|
|
SparseArrayNode *findNode(uint akey) const;
|
|
|
|
uint pop_front();
|
|
void push_front(uint at);
|
|
uint pop_back(uint len);
|
|
void push_back(uint at, uint len);
|
|
|
|
QList<int> keys() const;
|
|
|
|
const SparseArrayNode *end() const { return &header; }
|
|
SparseArrayNode *end() { return &header; }
|
|
const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); }
|
|
SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); }
|
|
|
|
SparseArrayNode *erase(SparseArrayNode *n);
|
|
|
|
SparseArrayNode *lowerBound(uint key);
|
|
const SparseArrayNode *lowerBound(uint key) const;
|
|
SparseArrayNode *upperBound(uint key);
|
|
const SparseArrayNode *upperBound(uint key) const;
|
|
SparseArrayNode *insert(uint akey);
|
|
|
|
// STL compatibility
|
|
typedef uint key_type;
|
|
typedef int mapped_type;
|
|
typedef qptrdiff difference_type;
|
|
typedef int size_type;
|
|
|
|
#ifdef Q_MAP_DEBUG
|
|
void dump() const;
|
|
#endif
|
|
};
|
|
|
|
inline SparseArrayNode *SparseArray::findNode(uint akey) const
|
|
{
|
|
SparseArrayNode *n = root();
|
|
|
|
while (n) {
|
|
if (akey == n->size_left) {
|
|
return n;
|
|
} else if (akey < n->size_left) {
|
|
n = n->left;
|
|
} else {
|
|
akey -= n->size_left;
|
|
n = n->right;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
inline uint SparseArray::pop_front()
|
|
{
|
|
uint idx = UINT_MAX ;
|
|
|
|
SparseArrayNode *n = findNode(0);
|
|
if (n) {
|
|
idx = n->value;
|
|
deleteNode(n);
|
|
// adjust all size_left indices on the path to leftmost item by 1
|
|
SparseArrayNode *n = root();
|
|
while (n) {
|
|
n->size_left -= 1;
|
|
n = n->left;
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
inline void SparseArray::push_front(uint value)
|
|
{
|
|
// adjust all size_left indices on the path to leftmost item by 1
|
|
SparseArrayNode *n = root();
|
|
while (n) {
|
|
n->size_left += 1;
|
|
n = n->left;
|
|
}
|
|
n = insert(0);
|
|
n->value = value;
|
|
}
|
|
|
|
inline uint SparseArray::pop_back(uint len)
|
|
{
|
|
uint idx = UINT_MAX;
|
|
if (!len)
|
|
return idx;
|
|
|
|
SparseArrayNode *n = findNode(len - 1);
|
|
if (n) {
|
|
idx = n->value;
|
|
deleteNode(n);
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
inline void SparseArray::push_back(uint index, uint len)
|
|
{
|
|
SparseArrayNode *n = insert(len);
|
|
n->value = index;
|
|
}
|
|
|
|
#ifdef Q_MAP_DEBUG
|
|
|
|
void SparseArray::dump() const
|
|
{
|
|
const_iterator it = begin();
|
|
qDebug() << "map dump:";
|
|
while (it != end()) {
|
|
const SparseArrayNode *n = it.i;
|
|
int depth = 0;
|
|
while (n && n != root()) {
|
|
++depth;
|
|
n = n->parent();
|
|
}
|
|
QByteArray space(4*depth, ' ');
|
|
qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right
|
|
<< it.key() << it.value();
|
|
++it;
|
|
}
|
|
qDebug() << "---------";
|
|
}
|
|
#endif
|
|
|
|
|
|
inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n)
|
|
{
|
|
if (n == end())
|
|
return n;
|
|
|
|
SparseArrayNode *next = n->nextNode();
|
|
deleteNode(n);
|
|
return next;
|
|
}
|
|
|
|
inline QList<int> SparseArray::keys() const
|
|
{
|
|
QList<int> res;
|
|
res.reserve(numEntries);
|
|
SparseArrayNode *n = mostLeftNode;
|
|
while (n != end()) {
|
|
res.append(n->key());
|
|
n = n->nextNode();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const
|
|
{
|
|
const SparseArrayNode *lb = root()->lowerBound(akey);
|
|
if (!lb)
|
|
lb = end();
|
|
return lb;
|
|
}
|
|
|
|
|
|
inline SparseArrayNode *SparseArray::lowerBound(uint akey)
|
|
{
|
|
SparseArrayNode *lb = root()->lowerBound(akey);
|
|
if (!lb)
|
|
lb = end();
|
|
return lb;
|
|
}
|
|
|
|
|
|
inline const SparseArrayNode *SparseArray::upperBound(uint akey) const
|
|
{
|
|
const SparseArrayNode *ub = root()->upperBound(akey);
|
|
if (!ub)
|
|
ub = end();
|
|
return ub;
|
|
}
|
|
|
|
|
|
inline SparseArrayNode *SparseArray::upperBound(uint akey)
|
|
{
|
|
SparseArrayNode *ub = root()->upperBound(akey);
|
|
if (!ub)
|
|
ub = end();
|
|
return ub;
|
|
}
|
|
|
|
|
|
class Array
|
|
{
|
|
uint len;
|
|
PropertyDescriptor *lengthProperty;
|
|
union {
|
|
uint freeList;
|
|
uint offset;
|
|
};
|
|
QVector<PropertyDescriptor> values;
|
|
SparseArray *sparse;
|
|
|
|
void fillDescriptor(PropertyDescriptor *pd, Value v)
|
|
{
|
|
pd->type = PropertyDescriptor::Data;
|
|
pd->writable = PropertyDescriptor::Enabled;
|
|
pd->enumberable = PropertyDescriptor::Enabled;
|
|
pd->configurable = PropertyDescriptor::Enabled;
|
|
pd->value = v;
|
|
}
|
|
|
|
uint allocValue() {
|
|
uint idx = freeList;
|
|
if (values.size() <= (int)freeList)
|
|
values.resize(++freeList);
|
|
else
|
|
freeList = values.at(freeList).value.integerValue();
|
|
return idx;
|
|
}
|
|
|
|
uint allocValue(Value v) {
|
|
uint idx = allocValue();
|
|
PropertyDescriptor *pd = &values[idx];
|
|
fillDescriptor(pd, v);
|
|
return idx;
|
|
}
|
|
void freeValue(int idx) {
|
|
PropertyDescriptor &pd = values[idx];
|
|
pd.type = PropertyDescriptor::Generic;
|
|
pd.value.tag = Value::_Undefined_Type;
|
|
pd.value.int_32 = freeList;
|
|
freeList = idx;
|
|
}
|
|
|
|
PropertyDescriptor *descriptor(uint index) {
|
|
PropertyDescriptor *pd = values.data() + index;
|
|
if (!sparse)
|
|
pd += offset;
|
|
return pd;
|
|
}
|
|
const PropertyDescriptor *descriptor(uint index) const {
|
|
const PropertyDescriptor *pd = values.data() + index;
|
|
if (!sparse)
|
|
pd += offset;
|
|
return pd;
|
|
}
|
|
|
|
void getHeadRoom() {
|
|
assert(!sparse && !offset);
|
|
offset = qMax(values.size() >> 2, 16);
|
|
QVector<PropertyDescriptor> newValues(values.size() + offset);
|
|
memcpy(newValues.data() + offset, values.data(), values.size()*sizeof(PropertyDescriptor));
|
|
values = newValues;
|
|
}
|
|
|
|
public:
|
|
Array() : len(0), lengthProperty(0), offset(0), sparse(0) {}
|
|
Array(const Array &other);
|
|
~Array() { delete sparse; }
|
|
void initSparse();
|
|
|
|
uint length() const { return len; }
|
|
bool setLength(uint newLen);
|
|
|
|
void setLengthProperty(PropertyDescriptor *pd) { lengthProperty = pd; }
|
|
PropertyDescriptor *getLengthProperty() { return lengthProperty; }
|
|
|
|
void setLengthUnchecked(uint l) {
|
|
len = l;
|
|
if (lengthProperty)
|
|
lengthProperty->value = Value::fromUInt32(l);
|
|
}
|
|
|
|
PropertyDescriptor *insert(uint index) {
|
|
PropertyDescriptor *pd;
|
|
if (!sparse && (index < 0x1000 || index < len + (len >> 2))) {
|
|
if (index + offset >= (uint)values.size()) {
|
|
values.resize(offset + index + 1);
|
|
for (uint i = len + 1; i < index; ++i) {
|
|
values[i].type = PropertyDescriptor::Generic;
|
|
values[i].value.tag = Value::_Undefined_Type;
|
|
}
|
|
}
|
|
pd = descriptor(index);
|
|
} else {
|
|
initSparse();
|
|
SparseArrayNode *n = sparse->insert(index);
|
|
if (n->value == UINT_MAX)
|
|
n->value = allocValue();
|
|
pd = descriptor(n->value);
|
|
}
|
|
if (index >= len)
|
|
setLengthUnchecked(index + 1);
|
|
return pd;
|
|
}
|
|
|
|
void set(uint index, const PropertyDescriptor *pd) {
|
|
*insert(index) = *pd;
|
|
}
|
|
|
|
void set(uint index, Value value) {
|
|
PropertyDescriptor *pd = insert(index);
|
|
fillDescriptor(pd, value);
|
|
}
|
|
|
|
bool deleteIndex(uint index) {
|
|
if (index >= len)
|
|
return true;
|
|
PropertyDescriptor *pd = 0;
|
|
if (!sparse) {
|
|
pd = at(index);
|
|
} else {
|
|
SparseArrayNode *n = sparse->findNode(index);
|
|
if (n)
|
|
pd = descriptor(n->value);
|
|
}
|
|
if (!pd || pd->type == PropertyDescriptor::Generic)
|
|
return true;
|
|
if (!pd->isConfigurable())
|
|
return false;
|
|
pd->type = PropertyDescriptor::Generic;
|
|
pd->value.tag = Value::_Undefined_Type;
|
|
if (sparse) {
|
|
pd->value.int_32 = freeList;
|
|
freeList = pd - values.constData();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
PropertyDescriptor *at(uint index) {
|
|
if (!sparse) {
|
|
if (index >= values.size() - offset)
|
|
return 0;
|
|
return values.data() + index - offset;
|
|
} else {
|
|
SparseArrayNode *n = sparse->findNode(index);
|
|
if (!n)
|
|
return 0;
|
|
return values.data() + n->value;
|
|
}
|
|
}
|
|
|
|
const PropertyDescriptor *at(uint index) const {
|
|
if (!sparse) {
|
|
if (index >= values.size() - offset)
|
|
return 0;
|
|
return values.data() + index + offset;
|
|
} else {
|
|
SparseArrayNode *n = sparse->findNode(index);
|
|
if (!n)
|
|
return 0;
|
|
return values.data() + n->value;
|
|
}
|
|
}
|
|
|
|
void getCollectables(QVector<Object *> &objects) const;
|
|
|
|
void push_front(Value v) {
|
|
if (!sparse) {
|
|
if (!offset)
|
|
getHeadRoom();
|
|
|
|
PropertyDescriptor pd;
|
|
fillDescriptor(&pd, v);
|
|
--offset;
|
|
values[offset] = pd;
|
|
} else {
|
|
uint idx = allocValue(v);
|
|
sparse->push_front(idx);
|
|
}
|
|
setLengthUnchecked(len + 1);
|
|
}
|
|
PropertyDescriptor *front() {
|
|
PropertyDescriptor *pd = 0;
|
|
if (!sparse) {
|
|
if (len)
|
|
pd = values.data() + offset;
|
|
} else {
|
|
SparseArrayNode *n = sparse->findNode(0);
|
|
if (n)
|
|
pd = descriptor(n->value);
|
|
}
|
|
if (pd && pd->type == PropertyDescriptor::Generic)
|
|
return 0;
|
|
return pd;
|
|
}
|
|
void pop_front() {
|
|
if (!len)
|
|
return;
|
|
if (!sparse) {
|
|
++offset;
|
|
} else {
|
|
uint idx = sparse->pop_front();
|
|
freeValue(idx);
|
|
}
|
|
setLengthUnchecked(len - 1);
|
|
}
|
|
void push_back(Value v) {
|
|
if (!sparse) {
|
|
PropertyDescriptor pd;
|
|
fillDescriptor(&pd, v);
|
|
values.append(pd);
|
|
} else {
|
|
uint idx = allocValue(v);
|
|
sparse->push_back(idx, len);
|
|
}
|
|
setLengthUnchecked(len + 1);
|
|
}
|
|
PropertyDescriptor *back() {
|
|
PropertyDescriptor *pd = 0;
|
|
if (!sparse) {
|
|
if (len)
|
|
pd = values.data() + offset + len;
|
|
} else {
|
|
SparseArrayNode *n = sparse->findNode(len - 1);
|
|
if (n)
|
|
pd = descriptor(n->value);
|
|
}
|
|
if (pd && pd->type == PropertyDescriptor::Generic)
|
|
return 0;
|
|
return pd;
|
|
}
|
|
void pop_back() {
|
|
if (!len)
|
|
return;
|
|
if (!sparse) {
|
|
values.resize(values.size() - 1);
|
|
} else {
|
|
uint idx = sparse->pop_back(len);
|
|
if (idx != UINT_MAX)
|
|
freeValue(idx);
|
|
}
|
|
setLengthUnchecked(len - 1);
|
|
}
|
|
|
|
SparseArrayNode *sparseLowerBound(uint idx) { return sparse ? sparse->lowerBound(idx) : 0; }
|
|
SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; }
|
|
SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; }
|
|
|
|
void concat(const Array &other);
|
|
void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn);
|
|
void splice(double start, double deleteCount, const QVector<Value> &, Array &);
|
|
Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o);
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
#endif // QMAP_H
|