2025-03-24 16:29:33 -03:00
/*
* Copyright ( c ) 2025 , stelar7 < dudedbz @ gmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2026-03-21 01:33:25 -03:00
# include <AK/BinarySearch.h>
2026-03-05 17:58:36 -03:00
# include <AK/Math.h>
2025-04-11 06:34:52 -03:00
# include <LibWeb/IndexedDB/IDBKeyRange.h>
2026-03-05 19:08:56 -03:00
# include <LibWeb/IndexedDB/Internal/MutationLog.h>
2025-03-24 16:29:33 -03:00
# include <LibWeb/IndexedDB/Internal/ObjectStore.h>
2026-05-11 04:02:49 -03:00
# include <LibWeb/IndexedDB/Internal/RecordRange.h>
2025-03-24 16:29:33 -03:00
namespace Web : : IndexedDB {
GC_DEFINE_ALLOCATOR ( ObjectStore ) ;
ObjectStore : : ~ ObjectStore ( ) = default ;
2025-03-24 17:18:26 -03:00
GC : : Ref < ObjectStore > ObjectStore : : create ( JS : : Realm & realm , GC : : Ref < Database > database , String name , bool auto_increment , Optional < KeyPath > const & key_path )
2025-03-24 16:29:33 -03:00
{
2025-03-24 17:18:26 -03:00
return realm . create < ObjectStore > ( database , name , auto_increment , key_path ) ;
}
2026-03-05 19:08:56 -03:00
size_t ObjectStore : : mutation_log_position ( ) const
{
if ( ! m_mutation_log )
return 0 ;
return m_mutation_log - > position ( ) ;
}
void ObjectStore : : revert_mutations_from ( size_t position )
{
if ( m_mutation_log )
m_mutation_log - > revert_from ( * this , position ) ;
}
2025-03-24 17:18:26 -03:00
ObjectStore : : ObjectStore ( GC : : Ref < Database > database , String name , bool auto_increment , Optional < KeyPath > const & key_path )
: m_database ( database )
, m_name ( move ( name ) )
, m_key_path ( key_path )
{
database - > add_object_store ( * this ) ;
if ( auto_increment )
m_key_generator = KeyGenerator { } ;
}
void ObjectStore : : visit_edges ( Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_database ) ;
2025-04-01 13:26:13 -03:00
visitor . visit ( m_indexes ) ;
2026-03-05 19:08:56 -03:00
visitor . visit ( m_mutation_log ) ;
2025-04-11 06:34:52 -03:00
for ( auto & record : m_records ) {
visitor . visit ( record . key ) ;
}
}
void ObjectStore : : remove_records_in_range ( GC : : Ref < IDBKeyRange > range )
{
2026-03-21 01:33:49 -03:00
if ( m_records . is_empty ( ) )
return ;
// Since records are sorted by key, records in range form a contiguous block.
2026-05-11 04:02:49 -03:00
auto record_range = record_range_for_key_range ( m_records , range ) ;
2026-03-21 01:33:49 -03:00
2026-05-11 04:02:49 -03:00
if ( record_range . start < record_range . end ) {
2026-03-21 01:33:49 -03:00
if ( m_mutation_log ) {
Vector < ObjectStoreRecord > deleted ;
2026-05-11 04:02:49 -03:00
deleted . ensure_capacity ( record_range . end - record_range . start ) ;
for ( size_t i = record_range . start ; i < record_range . end ; + + i )
2026-03-21 01:33:49 -03:00
deleted . append ( move ( m_records [ i ] ) ) ;
m_mutation_log - > note_records_deleted ( move ( deleted ) ) ;
}
2026-05-11 04:02:49 -03:00
m_records . remove ( record_range . start , record_range . end - record_range . start ) ;
2026-03-05 19:08:56 -03:00
}
}
void ObjectStore : : remove_record_with_key ( GC : : Ref < Key > key )
{
2026-05-11 04:01:09 -03:00
size_t index = 0 ;
auto * record = AK : : binary_search ( m_records , key , & index , [ ] ( auto const & needle , auto const & record ) {
return Key : : compare_two_keys ( needle , record . key ) ;
2025-04-11 06:34:52 -03:00
} ) ;
2026-05-11 04:01:09 -03:00
if ( record )
m_records . remove ( index ) ;
2025-03-24 16:29:33 -03:00
}
2025-04-11 06:38:29 -03:00
bool ObjectStore : : has_record_with_key ( GC : : Ref < Key > key )
{
2026-03-21 01:33:25 -03:00
return binary_search ( m_records , key , nullptr , [ ] ( auto const & needle , auto const & record ) - > int {
return Key : : compare_two_keys ( needle , record . key ) ;
} ) ! = nullptr ;
2025-04-11 06:38:29 -03:00
}
2026-03-21 01:35:29 -03:00
void ObjectStore : : store_a_record ( ObjectStoreRecord record )
2025-04-11 06:38:29 -03:00
{
2026-03-05 19:08:56 -03:00
if ( m_mutation_log )
m_mutation_log - > note_record_stored ( record . key ) ;
2025-04-11 06:38:29 -03:00
// NOTE: The record is stored in the object store’ s list of records such that the list is sorted according to the key of the records in ascending order.
2026-05-11 04:02:49 -03:00
if ( m_records . is_empty ( ) | | Key : : compare_two_keys ( m_records . last ( ) . key , record . key ) < = 0 ) {
m_records . append ( move ( record ) ) ;
return ;
2026-03-21 01:33:00 -03:00
}
2026-05-11 04:02:49 -03:00
m_records . insert ( first_record_index_with_key_at_or_after ( m_records , record . key , false ) , move ( record ) ) ;
2025-04-11 06:38:29 -03:00
}
2025-04-28 10:51:35 -03:00
u64 ObjectStore : : count_records_in_range ( GC : : Ref < IDBKeyRange > range )
{
2026-05-11 04:02:49 -03:00
auto record_range = record_range_for_key_range ( m_records , range ) ;
return record_range . end - record_range . start ;
2025-04-28 10:51:35 -03:00
}
2025-07-09 06:49:05 -03:00
Optional < ObjectStoreRecord & > ObjectStore : : first_in_range ( GC : : Ref < IDBKeyRange > range )
2025-04-28 10:55:46 -03:00
{
2026-05-11 04:02:49 -03:00
auto record_range = record_range_for_key_range ( m_records , range ) ;
if ( record_range . start = = record_range . end )
return { } ;
return m_records [ record_range . start ] ;
2025-04-28 10:55:46 -03:00
}
2025-05-08 05:04:43 -03:00
void ObjectStore : : clear_records ( )
{
2026-03-05 19:08:56 -03:00
auto deleted_records = move ( m_records ) ;
if ( m_mutation_log & & ! deleted_records . is_empty ( ) )
m_mutation_log - > note_records_deleted ( deleted_records ) ;
2025-05-08 05:04:43 -03:00
}
2026-03-05 17:58:36 -03:00
// https://w3c.github.io/IndexedDB/#generate-a-key
ErrorOr < u64 > ObjectStore : : generate_a_key ( )
{
// 1. Let generator be store's key generator.
auto & generator = key_generator ( ) ;
// 2. Let key be generator's current number.
auto key = generator . current_number ( ) ;
// 3. If key is greater than 2^53 (9007199254740992), then return failure.
if ( key > static_cast < u64 > ( MAX_KEY_GENERATOR_VALUE ) )
return Error : : from_string_literal ( " Key is greater than 2^53 while trying to generate a key " ) ;
// 4. Increase generator's current number by 1.
2026-03-05 19:08:56 -03:00
if ( m_mutation_log )
m_mutation_log - > note_key_generator_changed ( key ) ;
2026-03-05 17:58:36 -03:00
generator . increment ( 1 ) ;
// 5. Return key.
return key ;
}
// https://w3c.github.io/IndexedDB/#possibly-update-the-key-generator
void ObjectStore : : possibly_update_the_key_generator ( GC : : Ref < Key > key )
{
// 1. If the type of key is not number, abort these steps.
if ( key - > type ( ) ! = Key : : KeyType : : Number )
return ;
// 2. Let value be the value of key.
auto value = key - > value_as_double ( ) ;
// 3. Set value to the minimum of value and 2^53 (9007199254740992).
value = min ( value , MAX_KEY_GENERATOR_VALUE ) ;
// 4. Set value to the largest integer not greater than value.
value = AK : : floor ( value ) ;
// 5. Let generator be store's key generator.
auto & generator = key_generator ( ) ;
// 6. If value is greater than or equal to generator's current number, then set generator's current number to value + 1.
2026-03-05 19:08:56 -03:00
if ( value > = static_cast < double > ( generator . current_number ( ) ) ) {
if ( m_mutation_log )
m_mutation_log - > note_key_generator_changed ( generator . current_number ( ) ) ;
2026-03-05 17:58:36 -03:00
generator . set ( static_cast < u64 > ( value + 1 ) ) ;
2026-03-05 19:08:56 -03:00
}
2026-03-05 17:58:36 -03:00
}
2025-07-09 06:49:05 -03:00
GC : : ConservativeVector < ObjectStoreRecord > ObjectStore : : first_n_in_range ( GC : : Ref < IDBKeyRange > range , Optional < WebIDL : : UnsignedLong > count )
2025-05-08 18:36:18 -03:00
{
2026-05-19 15:55:11 -03:00
GC : : ConservativeVector < ObjectStoreRecord > records ;
2026-05-11 04:02:49 -03:00
auto record_range = record_range_for_key_range ( m_records , range ) ;
for ( size_t i = record_range . start ; i < record_range . end ; + + i ) {
records . append ( m_records [ i ] ) ;
2025-05-08 18:36:18 -03:00
if ( count . has_value ( ) & & records . size ( ) > = * count )
break ;
}
return records ;
}
2025-07-09 06:52:44 -03:00
GC : : ConservativeVector < ObjectStoreRecord > ObjectStore : : last_n_in_range ( GC : : Ref < IDBKeyRange > range , Optional < WebIDL : : UnsignedLong > count )
{
2026-05-19 15:55:11 -03:00
GC : : ConservativeVector < ObjectStoreRecord > records ;
2026-05-11 04:02:49 -03:00
auto record_range = record_range_for_key_range ( m_records , range ) ;
for ( size_t i = record_range . end ; i > record_range . start ; ) {
- - i ;
records . append ( m_records [ i ] ) ;
2025-07-09 06:52:44 -03:00
if ( count . has_value ( ) & & records . size ( ) > = * count )
break ;
}
return records ;
}
2025-03-24 16:29:33 -03:00
}