Serenity Operating System
at master 452 lines 14 kB view raw
1/* 2 * Copyright (c) 2021, Jan de Visser <jan@de-visser.net> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibSQL/HashIndex.h> 8#include <LibSQL/Heap.h> 9#include <LibSQL/Key.h> 10#include <LibSQL/Serializer.h> 11 12namespace SQL { 13 14HashDirectoryNode::HashDirectoryNode(HashIndex& index, u32 node_number, size_t offset) 15 : IndexNode(index.node_pointer(node_number)) 16 , m_hash_index(index) 17 , m_node_number(node_number) 18 , m_offset(offset) 19{ 20} 21 22HashDirectoryNode::HashDirectoryNode(HashIndex& index, u32 pointer) 23 : IndexNode(pointer) 24 , m_hash_index(index) 25{ 26} 27 28void HashDirectoryNode::deserialize(Serializer& serializer) 29{ 30 dbgln_if(SQL_DEBUG, "Deserializing Hash Directory Node"); 31 m_hash_index.m_global_depth = serializer.deserialize<u32>(); 32 auto size = serializer.deserialize<u32>(); 33 dbgln_if(SQL_DEBUG, "Global Depth {}, #Bucket pointers {}", m_hash_index.global_depth(), size); 34 auto next_node = serializer.deserialize<u32>(); 35 if (next_node) { 36 dbgln_if(SQL_DEBUG, "Next node {}", next_node); 37 m_hash_index.m_nodes.append(next_node); 38 } else { 39 dbgln_if(SQL_DEBUG, "This is the last directory node"); 40 m_is_last = true; 41 } 42 for (auto ix = 0u; ix < size; ix++) { 43 auto bucket_pointer = serializer.deserialize<u32>(); 44 auto local_depth = serializer.deserialize<u32>(); 45 dbgln_if(SQL_DEBUG, "--Index {} bucket pointer {} local depth {}", ix, bucket_pointer, local_depth); 46 m_hash_index.append_bucket(ix, local_depth, bucket_pointer); 47 } 48} 49 50void HashDirectoryNode::serialize(Serializer& serializer) const 51{ 52 dbgln_if(SQL_DEBUG, "Serializing directory node #{}. Offset {}", m_node_number, m_offset); 53 serializer.serialize<u32>((u32)m_hash_index.global_depth()); 54 serializer.serialize<u32>(number_of_pointers()); 55 dbgln_if(SQL_DEBUG, "Global depth {}, #bucket pointers {}", m_hash_index.global_depth(), number_of_pointers()); 56 57 u32 next_node; 58 if (m_node_number < (m_hash_index.m_nodes.size() - 1)) { 59 next_node = m_hash_index.m_nodes[m_node_number + 1]; 60 dbgln_if(SQL_DEBUG, "Next directory node pointer {}", next_node); 61 } else { 62 next_node = 0u; 63 dbgln_if(SQL_DEBUG, "This is the last directory node"); 64 } 65 66 serializer.serialize<u32>(next_node); 67 for (auto ix = 0u; ix < number_of_pointers(); ix++) { 68 auto& bucket = m_hash_index.m_buckets[m_offset + ix]; 69 dbgln_if(SQL_DEBUG, "Bucket index #{} pointer {} local depth {} size {}", ix, bucket->pointer(), bucket->local_depth(), bucket->size()); 70 serializer.serialize<u32>(bucket->pointer()); 71 serializer.serialize<u32>(bucket->local_depth()); 72 } 73} 74 75HashBucket::HashBucket(HashIndex& hash_index, u32 index, u32 local_depth, u32 pointer) 76 : IndexNode(pointer) 77 , m_hash_index(hash_index) 78 , m_local_depth(local_depth) 79 , m_index(index) 80{ 81} 82 83void HashBucket::serialize(Serializer& serializer) const 84{ 85 dbgln_if(SQL_DEBUG, "Serializing bucket: pointer {}, index #{}, local depth {} size {}", 86 pointer(), index(), local_depth(), size()); 87 serializer.serialize<u32>(local_depth()); 88 serializer.serialize<u32>(size()); 89 for (auto& key : m_entries) { 90 serializer.serialize<Key>(key); 91 } 92} 93 94void HashBucket::deserialize(Serializer& serializer) 95{ 96 if (m_inflated || !pointer()) 97 return; 98 dbgln_if(SQL_DEBUG, "Inflating Hash Bucket {}", pointer()); 99 m_local_depth = serializer.deserialize<u32>(); 100 dbgln_if(SQL_DEBUG, "Bucket Local Depth {}", m_local_depth); 101 auto size = serializer.deserialize<u32>(); 102 dbgln_if(SQL_DEBUG, "Bucket has {} keys", size); 103 for (auto ix = 0u; ix < size; ix++) { 104 auto key = serializer.deserialize<Key>(m_hash_index.descriptor()); 105 dbgln_if(SQL_DEBUG, "Key {}: {}", ix, key.to_deprecated_string()); 106 m_entries.append(key); 107 } 108 m_inflated = true; 109} 110 111size_t HashBucket::length() const 112{ 113 size_t len = 2 * sizeof(u32); 114 for (auto& key : m_entries) { 115 len += key.length(); 116 } 117 return len; 118} 119 120Optional<u32> HashBucket::get(Key& key) 121{ 122 auto optional_index = find_key_in_bucket(key); 123 if (optional_index.has_value()) { 124 auto& k = m_entries[optional_index.value()]; 125 key.set_pointer(k.pointer()); 126 return k.pointer(); 127 } 128 return {}; 129} 130 131bool HashBucket::insert(Key const& key) 132{ 133 if (!m_inflated) 134 m_hash_index.serializer().deserialize_block_to(pointer(), *this); 135 if (find_key_in_bucket(key).has_value()) { 136 return false; 137 } 138 if ((length() + key.length()) > BLOCKSIZE) { 139 dbgln_if(SQL_DEBUG, "Adding key {} would make length exceed block size", key.to_deprecated_string()); 140 return false; 141 } 142 m_entries.append(key); 143 m_hash_index.serializer().serialize_and_write(*this); 144 return true; 145} 146 147Optional<size_t> HashBucket::find_key_in_bucket(Key const& key) 148{ 149 for (auto ix = 0u; ix < size(); ix++) { 150 auto& k = entries()[ix]; 151 if (k == key) { 152 return ix; 153 } 154 } 155 return {}; 156} 157 158HashBucket const* HashBucket::next_bucket() 159{ 160 for (auto ix = m_index + 1; ix < m_hash_index.size(); ix++) { 161 auto bucket = m_hash_index.get_bucket_by_index(ix); 162 m_hash_index.serializer().deserialize_block_to<HashBucket>(bucket->pointer(), *bucket); 163 if (bucket->size()) 164 return bucket; 165 } 166 return nullptr; 167} 168 169HashBucket const* HashBucket::previous_bucket() 170{ 171 for (auto ix = m_index - 1; ix > 0; ix--) { 172 auto bucket = m_hash_index.get_bucket_by_index(ix); 173 if (bucket->pointer()) 174 return bucket; 175 } 176 return nullptr; 177} 178 179Vector<Key> const& HashBucket::entries() 180{ 181 if (!m_inflated) 182 m_hash_index.serializer().deserialize_block_to(pointer(), *this); 183 return m_entries; 184} 185 186Key const& HashBucket::operator[](size_t ix) 187{ 188 if (!m_inflated) 189 m_hash_index.serializer().deserialize_block_to(pointer(), *this); 190 VERIFY(ix < size()); 191 return m_entries[ix]; 192} 193 194Key const& HashBucket::operator[](size_t ix) const 195{ 196 VERIFY(ix < m_entries.size()); 197 return m_entries[ix]; 198} 199 200void HashBucket::list_bucket() 201{ 202 warnln("Bucket #{} size {} local depth {} pointer {}{}", 203 index(), size(), local_depth(), pointer(), (pointer() ? "" : " (VIRTUAL)")); 204 for (auto& key : entries()) { 205 warnln(" {} hash {}", key.to_deprecated_string(), key.hash()); 206 } 207} 208 209HashIndex::HashIndex(Serializer& serializer, NonnullRefPtr<TupleDescriptor> const& descriptor, u32 first_node) 210 : Index(serializer, descriptor, true, first_node) 211 , m_nodes() 212 , m_buckets() 213{ 214 if (!first_node) { 215 set_pointer(new_record_pointer()); 216 } 217 if (serializer.has_block(first_node)) { 218 u32 pointer = first_node; 219 do { 220 VERIFY(serializer.has_block(pointer)); 221 auto node = serializer.deserialize_block<HashDirectoryNode>(pointer, *this, pointer); 222 if (node.is_last()) 223 break; 224 pointer = m_nodes.last(); // FIXME Ugly 225 } while (pointer); 226 } else { 227 auto bucket = append_bucket(0u, 1u, new_record_pointer()); 228 bucket->m_inflated = true; 229 serializer.serialize_and_write(*bucket); 230 bucket = append_bucket(1u, 1u, new_record_pointer()); 231 bucket->m_inflated = true; 232 serializer.serialize_and_write(*bucket); 233 m_nodes.append(first_node); 234 write_directory_to_write_ahead_log(); 235 } 236} 237 238HashBucket* HashIndex::get_bucket(u32 index) 239{ 240 VERIFY(index < m_buckets.size()); 241 auto divisor = size() / 2; 242 while (!m_buckets[index]->pointer()) { 243 VERIFY(divisor > 1); 244 index = index % divisor; 245 divisor /= 2; 246 } 247 auto& bucket = m_buckets[index]; 248 return bucket; 249} 250 251HashBucket* HashIndex::get_bucket_for_insert(Key const& key) 252{ 253 auto key_hash = key.hash(); 254 255 do { 256 dbgln_if(SQL_DEBUG, "HashIndex::get_bucket_for_insert({}) bucket {} of {}", key.to_deprecated_string(), key_hash % size(), size()); 257 auto bucket = get_bucket(key_hash % size()); 258 if (bucket->length() + key.length() < BLOCKSIZE) { 259 return bucket; 260 } 261 dbgln_if(SQL_DEBUG, "Bucket is full (bucket size {}/length {} key length {}). Expanding directory", bucket->size(), bucket->length(), key.length()); 262 263 // We previously doubled the directory but the target bucket is 264 // still at an older depth. Create new buckets at the current global 265 // depth and allocate the contents of the existing buckets to the 266 // newly created ones: 267 while (bucket->local_depth() < global_depth()) { 268 auto base_index = bucket->index(); 269 auto step = 1 << (global_depth() - bucket->local_depth()); 270 auto total_moved = 0; 271 for (auto ix = base_index + step; ix < size(); ix += step) { 272 auto& sub_bucket = m_buckets[ix]; 273 sub_bucket->set_local_depth(bucket->local_depth() + 1); 274 auto moved = 0; 275 for (auto entry_index = (int)bucket->m_entries.size() - 1; entry_index >= 0; entry_index--) { 276 if (bucket->m_entries[entry_index].hash() % size() == ix) { 277 if (!sub_bucket->pointer()) { 278 sub_bucket->set_pointer(new_record_pointer()); 279 } 280 sub_bucket->insert(bucket->m_entries.take(entry_index)); 281 moved++; 282 } 283 } 284 if (moved > 0) { 285 dbgln_if(SQL_DEBUG, "Moved {} entries from bucket #{} to #{}", moved, base_index, ix); 286 serializer().serialize_and_write(*sub_bucket); 287 } 288 total_moved += moved; 289 } 290 if (total_moved) 291 dbgln_if(SQL_DEBUG, "Redistributed {} entries from bucket #{}", total_moved, base_index); 292 else 293 dbgln_if(SQL_DEBUG, "Nothing redistributed from bucket #{}", base_index); 294 bucket->set_local_depth(bucket->local_depth() + 1); 295 serializer().serialize_and_write(*bucket); 296 write_directory_to_write_ahead_log(); 297 298 auto bucket_after_redistribution = get_bucket(key_hash % size()); 299 if (bucket_after_redistribution->length() + key.length() < BLOCKSIZE) 300 return bucket_after_redistribution; 301 } 302 expand(); 303 } while (true); 304 VERIFY_NOT_REACHED(); 305} 306 307void HashIndex::expand() 308{ 309 auto sz = size(); 310 dbgln_if(SQL_DEBUG, "Expanding directory from {} to {} buckets", sz, 2 * sz); 311 for (auto i = 0u; i < sz; i++) { 312 auto bucket = get_bucket(i); 313 bucket = append_bucket(sz + i, bucket->local_depth(), 0u); 314 bucket->m_inflated = true; 315 } 316 m_global_depth++; 317 write_directory_to_write_ahead_log(); 318} 319 320void HashIndex::write_directory_to_write_ahead_log() 321{ 322 auto num_nodes_required = (size() / HashDirectoryNode::max_pointers_in_node()) + 1; 323 while (m_nodes.size() < num_nodes_required) 324 m_nodes.append(new_record_pointer()); 325 326 size_t offset = 0u; 327 size_t num_node = 0u; 328 while (offset < size()) { 329 HashDirectoryNode node(*this, num_node, offset); 330 serializer().serialize_and_write(node); 331 offset += node.number_of_pointers(); 332 } 333} 334 335HashBucket* HashIndex::append_bucket(u32 index, u32 local_depth, u32 pointer) 336{ 337 m_buckets.append(make<HashBucket>(*this, index, local_depth, pointer)); 338 return m_buckets.last(); 339} 340 341HashBucket* HashIndex::get_bucket_by_index(u32 index) 342{ 343 if (index >= size()) 344 return nullptr; 345 return m_buckets[index]; 346} 347 348Optional<u32> HashIndex::get(Key& key) 349{ 350 auto hash = key.hash(); 351 auto bucket_index = hash % size(); 352 dbgln_if(SQL_DEBUG, "HashIndex::get({}) bucket_index {}", key.to_deprecated_string(), bucket_index); 353 auto bucket = get_bucket(bucket_index); 354 if constexpr (SQL_DEBUG) 355 bucket->list_bucket(); 356 return bucket->get(key); 357} 358 359bool HashIndex::insert(Key const& key) 360{ 361 dbgln_if(SQL_DEBUG, "HashIndex::insert({})", key.to_deprecated_string()); 362 auto bucket = get_bucket_for_insert(key); 363 bucket->insert(key); 364 if constexpr (SQL_DEBUG) 365 bucket->list_bucket(); 366 return true; 367} 368 369HashIndexIterator HashIndex::begin() 370{ 371 return HashIndexIterator(get_bucket(0)); 372} 373 374HashIndexIterator HashIndex::end() 375{ 376 return HashIndexIterator::end(); 377} 378 379HashIndexIterator HashIndex::find(Key const& key) 380{ 381 auto hash = key.hash(); 382 auto bucket_index = hash % size(); 383 auto bucket = get_bucket(bucket_index); 384 auto optional_index = bucket->find_key_in_bucket(key); 385 if (!optional_index.has_value()) 386 return end(); 387 return HashIndexIterator(bucket, optional_index.value()); 388} 389 390void HashIndex::list_hash() 391{ 392 warnln("Number of buckets: {} (Global depth {})", size(), global_depth()); 393 warn("Directory pointer(s): "); 394 for (auto ptr : m_nodes) { 395 warn("{}, ", ptr); 396 } 397 warnln(); 398 399 bool first_bucket = true; 400 for (auto& bucket : m_buckets) { 401 if (first_bucket) { 402 first_bucket = false; 403 } 404 bucket->list_bucket(); 405 } 406} 407 408HashIndexIterator::HashIndexIterator(HashBucket const* bucket, size_t index) 409 : m_current(bucket) 410 , m_index(index) 411{ 412 VERIFY(!m_current || !index || (index < m_current->size())); 413 while (m_current && (m_current->size() == 0)) { 414 m_current = m_current->next_bucket(); 415 m_index = 0; 416 } 417} 418 419HashIndexIterator HashIndexIterator::next() 420{ 421 if (is_end()) 422 return *this; 423 if (m_index < (m_current->size() - 1)) 424 return HashIndexIterator(m_current.ptr(), m_index + 1); 425 return HashIndexIterator(m_current->next_bucket()); 426} 427 428HashIndexIterator HashIndexIterator::previous() 429{ 430 TODO(); 431} 432 433bool HashIndexIterator::operator==(HashIndexIterator const& other) const 434{ 435 if (is_end()) 436 return other.is_end(); 437 if (other.is_end()) 438 return false; 439 VERIFY(&other.m_current->hash_index() == &m_current->hash_index()); 440 return (m_current.ptr() == other.m_current.ptr()) && (m_index == other.m_index); 441} 442 443bool HashIndexIterator::operator==(Key const& other) const 444{ 445 if (is_end()) 446 return false; 447 if (other.is_null()) 448 return false; 449 return (**this).compare(other); 450} 451 452}