/* * Copyright (c) 2025, stelar7 * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include namespace Web::IndexedDB { GC_DEFINE_ALLOCATOR(Index); Index::~Index() = default; GC::Ref Index::create(JS::Realm& realm, GC::Ref store, String const& name, KeyPath const& key_path, bool unique, bool multi_entry) { return realm.create(store, name, key_path, unique, multi_entry); } Index::Index(GC::Ref store, String const& name, KeyPath const& key_path, bool unique, bool multi_entry) : m_object_store(store) , m_name(name) , m_unique(unique) , m_multi_entry(multi_entry) , m_key_path(key_path) { store->index_set().set(name, *this); } 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) { 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 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 index’s referenced object store which has a key equal to the index’s record’s 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() { m_records.clear(); } Optional Index::first_in_range(GC::Ref range) { return m_records.first_matching([&](auto const& record) { return range->is_in_range(record.key); }); } GC::ConservativeVector Index::first_n_in_range(GC::Ref range, Optional count) { GC::ConservativeVector 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; } u64 Index::count_records_in_range(GC::Ref 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) { m_records.append(record); // NOTE: The record is stored in index’s 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_records_with_value_in_range(GC::Ref range) { m_records.remove_all_matching([&](auto const& record) { return range->is_in_range(record.value); }); } }