blob: 99f6bf6aa0c1b68c56bb821feb2925e77efce384 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2018 Crimson AS <info@crimson.no>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv4estable_p.h"
#include "qv4object_p.h"
using namespace QV4;
// The ES spec requires that Map/Set be implemented using a data structure that
// is a little different from most; it requires nonlinear access, and must also
// preserve the order of insertion of items in a deterministic way.
//
// This class implements those requirements, except for fast access: that
// will be addressed in a followup patch.
ESTable::ESTable()
: m_capacity(8)
{
m_keys = (Value*)malloc(m_capacity * sizeof(Value));
m_values = (Value*)malloc(m_capacity * sizeof(Value));
memset(m_keys, 0, m_capacity);
memset(m_values, 0, m_capacity);
}
ESTable::~ESTable()
{
free(m_keys);
free(m_values);
m_size = 0;
m_capacity = 0;
m_keys = nullptr;
m_values = nullptr;
}
void ESTable::markObjects(MarkStack *s, bool isWeakMap)
{
for (uint i = 0; i < m_size; ++i) {
if (!isWeakMap)
m_keys[i].mark(s);
m_values[i].mark(s);
}
}
// Pretends that there's nothing in the table. Doesn't actually free memory, as
// it will almost certainly be reused again anyway.
void ESTable::clear()
{
m_size = 0;
}
// Update the table to contain \a value for a given \a key. The key is
// normalized, as required by the ES spec.
void ESTable::set(const Value &key, const Value &value)
{
for (uint i = 0; i < m_size; ++i) {
if (m_keys[i].sameValueZero(key)) {
m_values[i] = value;
return;
}
}
if (m_capacity == m_size) {
uint oldCap = m_capacity;
m_capacity *= 2;
m_keys = (Value*)realloc(m_keys, m_capacity * sizeof(Value));
m_values = (Value*)realloc(m_values, m_capacity * sizeof(Value));
memset(m_keys + oldCap, 0, m_capacity - oldCap);
memset(m_values + oldCap, 0, m_capacity - oldCap);
}
Value nk = key;
if (nk.isDouble()) {
if (nk.doubleValue() == 0 && std::signbit(nk.doubleValue()))
nk = Value::fromDouble(+0);
}
m_keys[m_size] = nk;
m_values[m_size] = value;
m_size++;
}
// Returns true if the table contains \a key, false otherwise.
bool ESTable::has(const Value &key) const
{
for (uint i = 0; i < m_size; ++i) {
if (m_keys[i].sameValueZero(key))
return true;
}
return false;
}
// Fetches the value for the given \a key, and if \a hasValue is passed in,
// it is set depending on whether or not the given key was found.
ReturnedValue ESTable::get(const Value &key, bool *hasValue) const
{
for (uint i = 0; i < m_size; ++i) {
if (m_keys[i].sameValueZero(key)) {
if (hasValue)
*hasValue = true;
return m_values[i].asReturnedValue();
}
}
if (hasValue)
*hasValue = false;
return Encode::undefined();
}
// Removes the given \a key from the table
bool ESTable::remove(const Value &key)
{
bool found = false;
uint idx = 0;
for (; idx < m_size; ++idx) {
if (m_keys[idx].sameValueZero(key)) {
found = true;
break;
}
}
if (found == true) {
memmove(m_keys + idx, m_keys + idx + 1, (m_size - idx)*sizeof(Value));
memmove(m_values + idx, m_values + idx + 1, (m_size - idx)*sizeof(Value));
m_size--;
}
return found;
}
// Returns the size of the table. Note that the size may not match the underlying allocation.
uint ESTable::size() const
{
return m_size;
}
// Retrieves a key and value for a given \a idx, and places them in \a key and
// \a value. They must be valid pointers.
void ESTable::iterate(uint idx, Value *key, Value *value)
{
Q_ASSERT(idx < m_size);
Q_ASSERT(key);
Q_ASSERT(value);
*key = m_keys[idx];
*value = m_values[idx];
}
void ESTable::removeUnmarkedKeys()
{
uint idx = 0;
uint toIdx = 0;
for (; idx < m_size; ++idx) {
Q_ASSERT(m_keys[idx].isObject());
Object &o = static_cast<Object &>(m_keys[idx]);
if (o.d()->isMarked()) {
m_keys[toIdx] = m_keys[idx];
m_values[toIdx] = m_values[idx];
++toIdx;
}
}
m_size = toIdx;
}