Skip to content

Commit fcd6bcb

Browse files
committed
Refactor hashmap to allow for filter sizing.
1 parent aa23b69 commit fcd6bcb

7 files changed

Lines changed: 203 additions & 109 deletions

File tree

include/bitcoin/database/impl/primitives/hashhead.ipp

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ namespace database {
3232
TEMPLATE
3333
CLASS::hashhead(storage& head, size_t bits) NOEXCEPT
3434
: file_(head),
35-
buckets_(system::power2<integer>(bits)),
36-
mask_(system::unmask_right<integer>(bits))
35+
buckets_(system::power2<bucket_integer>(bits)),
36+
mask_(system::unmask_right<bucket_integer>(bits))
3737
{
3838
}
3939

@@ -110,8 +110,8 @@ inline Link CLASS::index(const Key& key) const NOEXCEPT
110110
BC_ASSERT_MSG(mask_ < max_size_t, "insufficient domain");
111111
BC_ASSERT_MSG(is_nonzero(buckets_), "hash table requires buckets");
112112

113-
const auto index = possible_narrow_cast<integer>(keys::hash<Key>(key));
114-
return bit_and<integer>(mask_, index);
113+
const auto index = possible_narrow_cast<bucket_integer>(keys::hash<Key>(key));
114+
return bit_and<bucket_integer>(mask_, index);
115115
}
116116

117117
TEMPLATE
@@ -128,25 +128,27 @@ inline Link CLASS::top(const Link& index) const NOEXCEPT
128128
if (is_null(raw))
129129
return {};
130130

131-
if constexpr (Align)
131+
if constexpr (aligned)
132132
{
133133
// Reads full padded word.
134134
// xcode clang++16 does not support C++20 std::atomic_ref.
135-
////const std::atomic_ref<integer> head(unsafe_byte_cast<integer>(raw));
136-
const auto& head = *pointer_cast<std::atomic<integer>>(raw);
135+
////const std::atomic_ref<bucket_integer> head(unsafe_byte_cast<bucket_integer>(raw));
136+
const auto& head = *pointer_cast<std::atomic<bucket_integer>>(raw);
137137

138138
// Acquire is necessary to synchronize with push release.
139139
// Relaxed would miss next updates, so acquire is optimal.
140140
return head.load(std::memory_order_acquire);
141141
}
142142
else
143143
{
144-
const auto& head = to_array<size_>(raw);
144+
const auto& head = to_array<bucket_size>(raw);
145145
mutex_.lock_shared();
146146
const auto top = head;
147147
mutex_.unlock_shared();
148148
return top;
149149
}
150+
151+
// TODO: return terminal if filtered.
150152
}
151153

152154
TEMPLATE
@@ -159,25 +161,33 @@ inline bool CLASS::push(const Link& current, bytes& next,
159161
TEMPLATE
160162
inline bool CLASS::push(const Link& current, bytes& next,
161163
const Link& index) NOEXCEPT
164+
{
165+
bool collision{};
166+
return push(collision, current, next, index);
167+
}
168+
169+
TEMPLATE
170+
inline bool CLASS::push(bool& collision, const Link& current, bytes& next,
171+
const Link& index) NOEXCEPT
162172
{
163173
using namespace system;
164174
const auto raw = file_.get_raw(link_to_position(index));
165175
if (is_null(raw))
166176
return false;
167177

168-
if constexpr (Align)
178+
if constexpr (aligned)
169179
{
170180
// Writes full padded word (0x00 fill).
171181
// xcode clang++16 does not support C++20 std::atomic_ref.
172-
////const std::atomic_ref<integer> head(unsafe_byte_cast<integer>(raw));
173-
auto& head = *pointer_cast<std::atomic<integer>>(raw);
182+
////const std::atomic_ref<bucket_integer> head(unsafe_byte_cast<bucket_integer>(raw));
183+
auto& head = *pointer_cast<std::atomic<bucket_integer>>(raw);
174184
auto top = head.load(std::memory_order_acquire);
175185
do
176186
{
177187
// Compiler could order this after head.store, which would expose key
178188
// to search before next entry is linked. Thread fence imposes order.
179189
// A release fence ensures that all prior writes (like next) are
180-
// completed before any subsequent atomic store.
190+
// completed before any subsequent atomic store.
181191
next = Link{ top };
182192
std::atomic_thread_fence(std::memory_order_release);
183193
}
@@ -186,13 +196,18 @@ inline bool CLASS::push(const Link& current, bytes& next,
186196
}
187197
else
188198
{
189-
auto& head = to_array<size_>(raw);
199+
auto& head = to_array<bucket_size>(raw);
190200
mutex_.lock();
191201
next = head;
192202
head = current;
193203
mutex_.unlock();
194204
}
195205

206+
// TODO: set collision when unfiltered or fingerprint matches filter.
207+
collision = false;
208+
209+
// The returned next is set to prevous head, which is where collisions may
210+
// be resolved to duplicate or not, when 'collision' is set to true.
196211
return true;
197212
}
198213

include/bitcoin/database/impl/primitives/hashmap.ipp

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,14 @@ Key CLASS::get_key(const Link& link) NOEXCEPT
205205
}
206206

207207
TEMPLATE
208-
template <typename Element, if_equal<Element::size, Size>>
208+
ELEMENT_CONSTRAINT
209209
inline bool CLASS::find(const Key& key, Element& element) const NOEXCEPT
210210
{
211211
return !find_link(key, element).is_terminal();
212212
}
213213

214214
TEMPLATE
215-
template <typename Element, if_equal<Element::size, Size>>
215+
ELEMENT_CONSTRAINT
216216
inline Link CLASS::find_link(const Key& key, Element& element) const NOEXCEPT
217217
{
218218
// This override avoids duplicated memory_ptr construct in get(first()).
@@ -225,7 +225,7 @@ inline Link CLASS::find_link(const Key& key, Element& element) const NOEXCEPT
225225
}
226226

227227
TEMPLATE
228-
template <typename Element, if_equal<Element::size, Size>>
228+
ELEMENT_CONSTRAINT
229229
inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT
230230
{
231231
// This override is the normal form.
@@ -234,7 +234,7 @@ inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT
234234

235235
// static
236236
TEMPLATE
237-
template <typename Element, if_equal<Element::size, Size>>
237+
ELEMENT_CONSTRAINT
238238
inline bool CLASS::get(const memory_ptr& ptr, const Link& link,
239239
Element& element) NOEXCEPT
240240
{
@@ -243,7 +243,7 @@ inline bool CLASS::get(const memory_ptr& ptr, const Link& link,
243243

244244
// static
245245
TEMPLATE
246-
template <typename Element, if_equal<Element::size, Size>>
246+
ELEMENT_CONSTRAINT
247247
inline bool CLASS::get(const iterator& it, Element& element) NOEXCEPT
248248
{
249249
// This override avoids deadlock when holding iterator to the same table.
@@ -252,16 +252,17 @@ inline bool CLASS::get(const iterator& it, Element& element) NOEXCEPT
252252

253253
// static
254254
TEMPLATE
255-
template <typename Element, if_equal<Element::size, Size>>
255+
ELEMENT_CONSTRAINT
256256
inline bool CLASS::get(const iterator& it, const Link& link,
257257
Element& element) NOEXCEPT
258258
{
259259
// This override avoids deadlock when holding iterator to the same table.
260260
return read(it.get(), link, element);
261261
}
262262

263+
// static
263264
TEMPLATE
264-
template <typename Element, if_equal<Element::size, Size>>
265+
ELEMENT_CONSTRAINT
265266
bool CLASS::set(const memory_ptr& ptr, const Link& link, const Key& key,
266267
const Element& element) NOEXCEPT
267268
{
@@ -296,15 +297,15 @@ bool CLASS::set(const memory_ptr& ptr, const Link& link, const Key& key,
296297
}
297298

298299
TEMPLATE
299-
template <typename Element, if_equal<Element::size, Size>>
300+
ELEMENT_CONSTRAINT
300301
bool CLASS::set(const Link& link, const Key& key,
301302
const Element& element) NOEXCEPT
302303
{
303304
return set(get_memory(), link, key, element);
304305
}
305306

306307
TEMPLATE
307-
template <typename Element, if_equal<Element::size, Size>>
308+
ELEMENT_CONSTRAINT
308309
inline Link CLASS::set_link(const Key& key, const Element& element) NOEXCEPT
309310
{
310311
Link link{};
@@ -315,7 +316,7 @@ inline Link CLASS::set_link(const Key& key, const Element& element) NOEXCEPT
315316
}
316317

317318
TEMPLATE
318-
template <typename Element, if_equal<Element::size, Size>>
319+
ELEMENT_CONSTRAINT
319320
inline bool CLASS::set_link(Link& link, const Key& key,
320321
const Element& element) NOEXCEPT
321322
{
@@ -324,7 +325,7 @@ inline bool CLASS::set_link(Link& link, const Key& key,
324325
}
325326

326327
TEMPLATE
327-
template <typename Element, if_equal<Element::size, Size>>
328+
ELEMENT_CONSTRAINT
328329
inline Link CLASS::put_link(const Key& key, const Element& element) NOEXCEPT
329330
{
330331
Link link{};
@@ -335,7 +336,7 @@ inline Link CLASS::put_link(const Key& key, const Element& element) NOEXCEPT
335336
}
336337

337338
TEMPLATE
338-
template <typename Element, if_equal<Element::size, Size>>
339+
ELEMENT_CONSTRAINT
339340
inline bool CLASS::put_link(Link& link, const Key& key,
340341
const Element& element) NOEXCEPT
341342
{
@@ -344,14 +345,14 @@ inline bool CLASS::put_link(Link& link, const Key& key,
344345
}
345346

346347
TEMPLATE
347-
template <typename Element, if_equal<Element::size, Size>>
348+
ELEMENT_CONSTRAINT
348349
inline bool CLASS::put(const Key& key, const Element& element) NOEXCEPT
349350
{
350351
return !put_link(key, element).is_terminal();
351352
}
352353

353354
TEMPLATE
354-
template <typename Element, if_equal<Element::size, Size>>
355+
ELEMENT_CONSTRAINT
355356
inline bool CLASS::put(const Link& link, const Key& key,
356357
const Element& element) NOEXCEPT
357358
{
@@ -360,53 +361,63 @@ inline bool CLASS::put(const Link& link, const Key& key,
360361
}
361362

362363
TEMPLATE
363-
template <typename Element, if_equal<Element::size, Size>>
364+
ELEMENT_CONSTRAINT
364365
inline bool CLASS::put(const memory_ptr& ptr, const Link& link, const Key& key,
365366
const Element& element) NOEXCEPT
366367
{
367368
return write(ptr, link, key, element);
368369
}
369370

370371
TEMPLATE
371-
bool CLASS::commit(const memory_ptr& ptr, const Link& link,
372-
const Key& key) NOEXCEPT
372+
ELEMENT_CONSTRAINT
373+
inline bool CLASS::put(bool& duplicate, const memory_ptr& ptr,
374+
const Link& link, const Key& key, const Element& element) NOEXCEPT
373375
{
374-
using namespace system;
375-
if (!ptr)
376+
Link previous_head{};
377+
if (!write(previous_head, ptr, link, key, element))
376378
return false;
377379

378-
// get element offset (fault)
379-
const auto offset = ptr->offset(body::link_to_position(link));
380-
if (is_null(offset))
381-
return false;
380+
duplicate = !first(ptr, previous_head, key).is_terminal();
381+
return true;
382+
}
382383

383-
//// Set element search key.
384-
//unsafe_array_cast<uint8_t, key_size>(std::next(offset,
385-
// Link::size)) = key;
384+
TEMPLATE
385+
inline Link CLASS::commit_link(const Link& link, const Key& key) NOEXCEPT
386+
{
387+
if (!commit(link, key))
388+
return {};
386389

387-
// Commit element to search index (terminal is a valid bucket index).
388-
auto& next = unsafe_array_cast<uint8_t, Link::size>(offset);
389-
return head_.push(link, next, head_.index(key));
390+
return link;
390391
}
391392

392393
TEMPLATE
393-
bool CLASS::commit(const Link& link, const Key& key) NOEXCEPT
394+
inline bool CLASS::commit(const Link& link, const Key& key) NOEXCEPT
394395
{
395396
return commit(get_memory(), link, key);
396397
}
397398

398399
TEMPLATE
399-
inline Link CLASS::commit_link(const Link& link, const Key& key) NOEXCEPT
400+
bool CLASS::commit(const memory_ptr& ptr, const Link& link,
401+
const Key& key) NOEXCEPT
400402
{
401-
if (!commit(link, key))
402-
return {};
403+
using namespace system;
404+
if (!ptr)
405+
return false;
403406

404-
return link;
407+
// get element offset (fault)
408+
const auto offset = ptr->offset(body::link_to_position(link));
409+
if (is_null(offset))
410+
return false;
411+
412+
// Commit element to search index (terminal is a valid bucket index).
413+
auto& next = unsafe_array_cast<uint8_t, Link::size>(offset);
414+
return head_.push(link, next, head_.index(key));
405415
}
406416

407-
// protected/static
417+
// protected
408418
// ----------------------------------------------------------------------------
409419

420+
// static
410421
TEMPLATE
411422
Link CLASS::first(const memory_ptr& ptr, const Link& link,
412423
const Key& key) NOEXCEPT
@@ -435,8 +446,9 @@ Link CLASS::first(const memory_ptr& ptr, const Link& link,
435446
return next;
436447
}
437448

449+
// static
438450
TEMPLATE
439-
template <typename Element, if_equal<Element::size, Size>>
451+
ELEMENT_CONSTRAINT
440452
bool CLASS::read(const memory_ptr& ptr, const Link& link,
441453
Element& element) NOEXCEPT
442454
{
@@ -462,12 +474,12 @@ bool CLASS::read(const memory_ptr& ptr, const Link& link,
462474
reader source{ stream };
463475
source.skip_bytes(index_size);
464476

465-
if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(Size * element.count());) }
477+
if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(RowSize * element.count());) }
466478
return element.from_data(source);
467479
}
468480

469481
TEMPLATE
470-
template <typename Element, if_equal<Element::size, Size>>
482+
ELEMENT_CONSTRAINT
471483
bool CLASS::write(const memory_ptr& ptr, const Link& link, const Key& key,
472484
const Element& element) NOEXCEPT
473485
{
@@ -500,6 +512,50 @@ bool CLASS::write(const memory_ptr& ptr, const Link& link, const Key& key,
500512
return element.to_data(sink) && head_.push(link, next, head_.index(key));
501513
}
502514

515+
TEMPLATE
516+
ELEMENT_CONSTRAINT
517+
bool CLASS::write(Link& previous, const memory_ptr& ptr, const Link& link,
518+
const Key& key, const Element& element) NOEXCEPT
519+
{
520+
using namespace system;
521+
if (!ptr || link.is_terminal())
522+
return false;
523+
524+
const auto start = body::link_to_position(link);
525+
if (is_limited<ptrdiff_t>(start))
526+
return false;
527+
528+
const auto size = ptr->size();
529+
const auto position = possible_narrow_and_sign_cast<ptrdiff_t>(start);
530+
if (position > size)
531+
return false;
532+
533+
const auto offset = ptr->offset(start);
534+
if (is_null(offset))
535+
return false;
536+
537+
// iostream.flush is a nop (direct copy).
538+
iostream stream{ offset, size - position };
539+
finalizer sink{ stream };
540+
sink.skip_bytes(Link::size);
541+
keys::write(sink, key);
542+
543+
// Commit element to body.
544+
if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) }
545+
auto& next = unsafe_array_cast<uint8_t, Link::size>(offset);
546+
if (!element.to_data(sink))
547+
return false;
548+
549+
// Commit element to search (terminal is a valid bucket index).
550+
bool collision{};
551+
if (!head_.push(collision, link, next, head_.index(key)))
552+
return false;
553+
554+
// If filter collision set previous stack head for conflict resolution.
555+
previous = collision ? next : Link::terminal;
556+
return true;
557+
}
558+
503559
} // namespace database
504560
} // namespace libbitcoin
505561

0 commit comments

Comments
 (0)