Serenity Operating System
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}