ladybird/AK/COWVector.h
Ali Mohammad Pur 8003bde03d AK+LibRegex+LibWasm: Remove the non-const COWVector::operator[]
This was copying the vector behind our backs, let's remove it and make
the copying explicit by putting it behind COWVector::mutable_at().
This is a further 64% performance improvement on Wasm validation.
2024-03-12 17:10:47 +01:00

172 lines
3.3 KiB
C++

/*
* Copyright (c) 2021-2024, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
#include <AK/Vector.h>
namespace AK {
template<typename T>
class COWVector {
struct Detail : RefCounted<Detail> {
Vector<T> m_members;
};
public:
COWVector()
: m_detail(make_ref_counted<Detail>())
{
}
COWVector(std::initializer_list<T> entries)
: m_detail(make_ref_counted<Detail>())
{
for (auto& entry : entries)
m_detail->m_members.append(entry);
}
COWVector(COWVector const&) = default;
COWVector(COWVector&&) = default;
COWVector& operator=(COWVector const&) = default;
COWVector& operator=(COWVector&&) = default;
Vector<T> release() &&
{
if (m_detail->ref_count() == 1)
return exchange(m_detail->m_members, Vector<T>());
return m_detail->m_members;
}
void append(T const& value)
{
return append(T { value });
}
void append(T&& value)
{
copy();
m_detail->m_members.append(move(value));
}
void extend(Vector<T>&& values)
{
copy();
m_detail->m_members.extend(move(values));
}
void extend(Vector<T> const& values)
{
copy();
m_detail->m_members.extend(values);
}
void extend(COWVector<T> const& values)
{
copy();
m_detail->m_members.extend(values.m_detail->m_members);
}
void resize(size_t size)
{
copy();
m_detail->m_members.resize(size);
}
void ensure_capacity(size_t capacity)
{
if (m_detail->m_members.capacity() >= capacity)
return;
copy();
m_detail->m_members.ensure_capacity(capacity);
}
void prepend(T value)
{
copy();
m_detail->m_members.prepend(move(value));
}
template<typename... Args>
void empend(Args&&... args)
{
copy();
m_detail->m_members.empend(forward<Args>(args)...);
}
void clear()
{
if (m_detail->ref_count() > 1)
m_detail = make_ref_counted<Detail>();
else
m_detail->m_members.clear();
}
T& mutable_at(size_t index)
{
// We're handing out a mutable reference, so make sure we own the data exclusively.
copy();
return m_detail->m_members.at(index);
}
T const& at(size_t index) const
{
return m_detail->m_members.at(index);
}
T const& operator[](size_t index) const
{
return m_detail->m_members[index];
}
size_t capacity() const
{
return m_detail->m_members.capacity();
}
size_t size() const
{
return m_detail->m_members.size();
}
bool is_empty() const
{
return m_detail->m_members.is_empty();
}
T const& first() const
{
return m_detail->m_members.first();
}
T const& last() const
{
return m_detail->m_members.last();
}
private:
void copy()
{
if (m_detail->ref_count() <= 1)
return;
auto new_detail = make_ref_counted<Detail>();
new_detail->m_members = m_detail->m_members;
m_detail = new_detail;
}
NonnullRefPtr<Detail> m_detail;
};
}
#if USING_AK_GLOBALLY
using AK::COWVector;
#endif