ladybird/Libraries/LibWeb/IndexedDB/Internal/Index.cpp

171 lines
5.1 KiB
C++
Raw Normal View History

2025-04-01 13:26:13 -03:00
/*
* Copyright (c) 2025, stelar7 <dudedbz@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/QuickSort.h>
2025-04-01 13:26:13 -03:00
#include <LibWeb/IndexedDB/Internal/Index.h>
#include <LibWeb/IndexedDB/Internal/MutationLog.h>
2025-04-01 13:26:13 -03:00
#include <LibWeb/IndexedDB/Internal/ObjectStore.h>
namespace Web::IndexedDB {
GC_DEFINE_ALLOCATOR(Index);
Index::~Index() = default;
GC::Ref<Index> Index::create(JS::Realm& realm, GC::Ref<ObjectStore> store, String const& name, KeyPath const& key_path, bool unique, bool multi_entry)
2025-04-01 13:26:13 -03:00
{
return realm.create<Index>(store, name, key_path, unique, multi_entry);
}
Index::Index(GC::Ref<ObjectStore> store, String const& name, KeyPath const& key_path, bool unique, bool multi_entry)
2025-04-01 13:26:13 -03:00
: m_object_store(store)
, m_name(name)
2025-04-01 13:26:13 -03:00
, m_unique(unique)
, m_multi_entry(multi_entry)
, m_key_path(key_path)
{
store->index_set().set(name, *this);
2025-04-01 13:26:13 -03:00
}
void Index::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_object_store);
for (auto& record : m_records) {
visitor.visit(record.key);
visitor.visit(record.value);
}
}
void Index::set_name(String name)
{
// NOTE: Update the key in the map so it still matches the name
auto old_value = m_object_store->index_set().take(m_name).release_value();
m_object_store->index_set().set(name, old_value);
m_name = move(name);
}
bool Index::has_record_with_key(GC::Ref<Key> key)
{
auto index = m_records.find_if([&key](auto const& record) {
return Key::equals(record.key, key);
});
return index != m_records.end();
}
// https://w3c.github.io/IndexedDB/#index-referenced-value
HTML::SerializationRecord const& Index::referenced_value(IndexRecord const& index_record) const
{
// Records in an index are said to have a referenced value.
// This is the value of the record in the indexs referenced object store which has a key equal to the indexs records value.
return *m_object_store
->records()
.first_matching([&](auto const& store_record) {
return Key::equals(store_record.key, index_record.value);
})
.value()
.value;
}
void Index::clear_records()
{
auto deleted = move(m_records);
if (auto log = m_object_store->mutation_log(); log && !deleted.is_empty())
log->note_index_records_deleted(*this, move(deleted));
}
2025-05-13 17:18:14 -03:00
Optional<IndexRecord&> Index::first_in_range(GC::Ref<IDBKeyRange> range)
{
return m_records.first_matching([&](auto const& record) {
return range->is_in_range(record.key);
});
}
2025-05-13 17:41:18 -03:00
GC::ConservativeVector<IndexRecord> Index::first_n_in_range(GC::Ref<IDBKeyRange> range, Optional<WebIDL::UnsignedLong> count)
{
GC::ConservativeVector<IndexRecord> records(range->heap());
for (auto const& record : m_records) {
if (range->is_in_range(record.key))
records.append(record);
if (count.has_value() && records.size() >= *count)
break;
}
return records;
}
GC::ConservativeVector<IndexRecord> Index::last_n_in_range(GC::Ref<IDBKeyRange> range, Optional<WebIDL::UnsignedLong> count)
{
GC::ConservativeVector<IndexRecord> records(range->heap());
for (auto const& record : m_records.in_reverse()) {
if (range->is_in_range(record.key))
records.append(record);
if (count.has_value() && records.size() >= *count)
2025-05-13 17:41:18 -03:00
break;
}
return records;
}
2025-05-13 17:54:08 -03:00
u64 Index::count_records_in_range(GC::Ref<IDBKeyRange> range)
{
u64 count = 0;
for (auto const& record : m_records) {
if (range->is_in_range(record.key))
++count;
}
return count;
}
void Index::store_a_record(IndexRecord const& record)
{
if (auto log = m_object_store->mutation_log())
log->note_index_record_stored(*this, record);
m_records.append(record);
// NOTE: The record is stored in indexs list of records such that the list is sorted primarily on the records keys, and secondarily on the records values, in ascending order.
AK::quick_sort(m_records, [](auto const& a, auto const& b) {
auto key_comparison = Key::compare_two_keys(a.key, b.key);
if (key_comparison != 0)
return key_comparison < 0;
return Key::compare_two_keys(a.value, b.value) < 0;
});
}
void Index::remove_record(IndexRecord const& record)
{
m_records.remove_first_matching([&](auto const& existing) {
return Key::equals(existing.key, record.key) && Key::equals(existing.value, record.value);
});
}
void Index::remove_records_with_value_in_range(GC::Ref<IDBKeyRange> range)
{
auto log = m_object_store->mutation_log();
Vector<IndexRecord> removed_records;
for (size_t i = 0; i < m_records.size();) {
auto const& record = m_records[i];
if (range->is_in_range(record.value)) {
auto removed_record = m_records.take(i);
if (log)
removed_records.append(removed_record);
continue;
}
i++;
}
if (!removed_records.is_empty())
log->note_index_records_deleted(*this, move(removed_records));
}
2025-04-01 13:26:13 -03:00
}