16bool insist_no_rehashing =
false;
18bool special_case_quicksort_two_elements =
true;
21bool partition_hard_boundary =
false;
26static
void add_wasm_algo_args()
34 "--insist-no-rehashing",
35 "insist that no rehashing occurs",
36 [](
bool){ options::insist_no_rehashing =
true; }
41 "--no-special-case-quicksort-two-elements",
42 "disable special case handling for sorting two elements using quicksort",
43 [](
bool){ options::special_case_quicksort_two_elements =
false; }
48 "--partition-hard-boundary",
49 "partition data using a hard boundary, i.e. pivot element strictly splits the data; "
50 "otherwise, a soft boundary is used, i.e. boundary can be anywhere in the data range equal "
52 [](
bool){ options::partition_hard_boundary =
true; }
63template<
bool CmpPredicated,
bool IsGlobal>
66 static_assert(IsGlobal,
"quicksort on local buffers is not yet supported");
79 auto partition = [&](U32x1 _begin, U32x1 _end,
const Environment &env_pivot) -> U32x1 {
84 U32x1 last = end - 1U;
88 auto env_begin = [&](){
95 auto env_last = [&](){
102 swap(begin, last, env_begin, env_last);
109 for (
auto &e : buffer.
schema()) {
110 M_insist(env_begin.template is<NChar>(e.id) == env_last.template is<NChar>(e.id),
111 "either both or none of the entries must be `NChar`s");
112 if (not env_begin.template is<NChar>(e.id)) {
114 auto tmp = env_begin.extract(e.id);
115 env_begin.add(e.id, env_last.extract(e.id));
116 env_last.add(e.id, std::move(tmp));
121 Boolx1 begin_cmp_pivot =
122 options::partition_hard_boundary ? compare<CmpPredicated>(env_begin, env_pivot, order) < 0
123 : compare<CmpPredicated>(env_begin, env_pivot, order) <= 0;
124 Boolx1 last_ge_pivot = compare<CmpPredicated>(env_last, env_pivot, order) >= 0;
126 begin += begin_cmp_pivot.to<uint32_t>();
127 end -= last_ge_pivot.to<uint32_t>();
145 U32x1 last = end - 1U;
147 Boolx1 cmp = options::special_case_quicksort_two_elements ? end - begin > 2U : end - begin >= 2U;
152 auto env_begin = [&](){
159 auto env_mid = [&](){
166 auto env_last = [&](){
173 Boolx1 begin_le_mid = compare<CmpPredicated>(env_begin, env_mid, order) <= 0;
174 Boolx1 begin_le_last = compare<CmpPredicated>(env_begin, env_last, order) <= 0;
175 Boolx1 mid_le_last = compare<CmpPredicated>(env_mid, env_last, order) <= 0;
177 IF (begin_le_last.clone()) {
178 IF (mid_le_last.clone()) {
179 swap(begin, mid, env_begin, env_mid);
181 swap(begin, last.clone(), env_begin, env_last);
186 IF (not begin_le_last) {
187 swap(begin, last.clone(), env_begin, env_last);
190 swap(begin, mid, env_begin, env_mid);
196 auto env_pivot = [&](){
203 mid = partition(begin + 1U, end, env_pivot);
204 swap(pivot, mid - 1U, env_pivot);
207 IF (end - mid >= 2U) {
215 if (options::special_case_quicksort_two_elements) {
216 IF (end - begin == 2U) {
218 auto env_begin = [&](){
225 auto env_last = [&](){
232 Boolx1 begin_gt_last = compare<CmpPredicated>(env_begin, env_last, order) > 0;
234 swap(begin, last, env_begin, env_last);
262requires signed_integral<T>
266requires std::floating_point<T>
and (
sizeof(T) == 4)
270requires std::floating_point<T>
and (
sizeof(T) == 8)
274requires std::same_as<T, bool>
286 res ^=
res >> uint64_t(31);
287 res *= uint64_t(0x7fb5d329728ea185UL);
288 res ^=
res >> uint64_t(27);
289 res *= uint64_t(0x81dadef4bc2dd44dUL);
290 res ^=
res >> uint64_t(33);
299 Wasm_insist(not bytes.clone().is_nullptr(),
"cannot compute hash of nullptr");
304 WHILE (ptr != bytes + num_bytes.make_signed()
and U8x1(*ptr).to<
bool>()) {
306 h *= uint64_t(0x100000001b3UL);
318 h = uint64_t(1UL << 63);
323 for (int32_t i = 0; i != _str.
length(); ++i) {
325 Charx1 c = *(str + i);
326 h |= c.to<uint64_t>();
331 h =
fnv_1a(_str.to<
void*>().to<uint8_t*>(), U32x1(_str.
length()));
342 M_insist(values.size() != 0,
"cannot compute hash of an empty sequence of values");
345 if (values.size() == 1) {
349 [](
auto) -> U64x1 {
M_unreachable(
"SIMDfication currently not supported"); },
350 [](std::monostate) -> U64x1 {
M_unreachable(
"invalid variant"); }
351 }, values.front().second);
355 uint64_t total_size_in_bits = 0;
356 for (
const auto &p : values) {
358 [&]<
typename T>(
const Expr<T> &val) ->
void { total_size_in_bits += p.first->size(); },
359 [&](
const NChar &val) ->
void { total_size_in_bits += 8 * val.length(); },
360 [](
auto&) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
361 [](std::monostate&) ->
void {
M_unreachable(
"invalid variant"); }
366 if (total_size_in_bits <= 64) {
368 for (
auto &p : values) {
370 [&]<
typename T>(
Expr<T> _val) ->
void {
371 h <<= p.first->size();
372 if (_val.can_be_null()) {
373 auto [val,
is_null] = _val.split();
382 auto val = _val.insist_not_null();
386 [&](
NChar _val) ->
void {
387 IF (_val.clone().is_null()) {
388 uint64_t len_in_bits = 8 * _val.length();
392 for (int32_t i = 0; i != _val.length(); ++i) {
394 Charx1 c = *(val + i);
395 h |= c.to<uint64_t>();
399 [](
auto) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
400 [](std::monostate) ->
void {
M_unreachable(
"invalid variant"); }
407 U64x1
m(0xc6a4a7935bd1e995UL);
411 for (
auto &p : values) {
413 [&]<
typename T>(
Expr<T> val) ->
void {
420 h = h * uint64_t(5UL) + uint64_t(0xe6546b64UL);
423 [](
auto) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
424 [](std::monostate) ->
void {
M_unreachable(
"invalid variant"); }
427 h ^= uint64_t(values.size());
437std::pair<HashTable::size_t, HashTable::size_t>
443 return { initial_offset_in_bytes, initial_max_alignment_in_bytes };
446 std::size_t
indices[types.size()];
450 std::stable_sort(
indices,
indices + types.size(), [&](std::size_t left, std::size_t right) {
451 return types[left]->alignment() > types[right]->alignment();
455 offsets_in_bytes.resize(types.size());
458 for (std::size_t idx = 0; idx != types.size(); ++idx) {
459 const auto sorted_idx =
indices[idx];
460 offsets_in_bytes[sorted_idx] = current_offset_in_bytes;
461 current_offset_in_bytes += (types[sorted_idx]->size() + 7) / 8;
462 max_alignment_in_bytes =
463 std::max<HashTable::offset_t>(max_alignment_in_bytes, (types[sorted_idx]->alignment() + 7) / 8);
467 if (
const auto rem = current_offset_in_bytes % max_alignment_in_bytes; rem)
468 current_offset_in_bytes += max_alignment_in_bytes - rem;
469 return { current_offset_in_bytes, max_alignment_in_bytes };
475template<
bool IsGlobal>
477 uint32_t initial_capacity)
480 std::vector<const Type*> types;
481 bool has_nullable =
false;
487 for (
auto &e :
schema_.get()) {
488 types.push_back(e.type);
489 has_nullable |= e.nullable();
498 std::vector<HashTable::offset_t> offsets;
514 const auto capacity_init = ceil_to_pow_2(initial_capacity);
515 const auto mask_init = capacity_init - 1U;
516 const auto high_watermark_absolute_init = capacity_init;
517 if constexpr (IsGlobal) {
520 storage_.high_watermark_absolute_.init(high_watermark_absolute_init);
522 mask_.emplace(mask_init);
527template<
bool IsGlobal>
530 if constexpr (IsGlobal) {
535 Wasm_insist(storage_.address_ <= it
and it < end,
"bucket out-of-bounds");
537 WHILE (not bucket_it.is_nullptr()) {
539 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>());
542 it += int32_t(
sizeof(uint32_t));
546 Module::Allocator().deallocate(storage_.address_, (storage_.mask_ + 1U) * uint32_t(
sizeof(uint32_t)));
549 for (
auto it = dummy_allocations_.rbegin(); it != dummy_allocations_.rend(); ++it)
551 if (predication_dummy_) {
554 "predication dummy must always contain an empty collision list");
557 WHILE (not bucket_it.is_nullptr()) {
559 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).to<uint32_t*>());
568template<
bool IsGlobal>
571 M_insist(not address_,
"must not call `setup()` twice");
572 M_insist(not num_entries_,
"must not call `setup()` twice");
576 num_entries_.emplace();
577 if constexpr (IsGlobal) {
578 M_insist(not mask_,
"must not call `setup()` twice");
579 M_insist(not high_watermark_absolute_,
"must not call `setup()` twice");
581 high_watermark_absolute_.emplace();
584 M_insist(
bool(high_watermark_absolute_));
588 if constexpr (IsGlobal) {
590 *mask_ = storage_.mask_;
591 *num_entries_ = storage_.num_entries_;
592 *high_watermark_absolute_ = storage_.high_watermark_absolute_;
595 if constexpr (IsGlobal) {
596 IF (storage_.address_.is_nullptr()) {
603 *address_ = storage_.address_;
614template<
bool IsGlobal>
617 M_insist(
bool(address_),
"must call `setup()` before");
618 M_insist(
bool(mask_),
"must call `setup()` before");
619 M_insist(
bool(num_entries_),
"must call `setup()` before");
620 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
622 if constexpr (not IsGlobal) {
625 WHILE (it != end()) {
626 Wasm_insist(begin() <= it
and it < end(),
"bucket out-of-bounds");
628 WHILE (not bucket_it.is_nullptr()) {
630 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>());
633 it += int32_t(
sizeof(uint32_t));
640 for (
auto it = dummy_allocations_.rbegin(); it != dummy_allocations_.rend(); ++it)
642 if (predication_dummy_) {
645 "predication dummy must always contain an empty collision list");
648 WHILE (not bucket_it.is_nullptr()) {
650 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).to<uint32_t*>());
659 if constexpr (IsGlobal) {
660 storage_.address_ = *address_;
661 storage_.mask_ = *mask_;
662 storage_.num_entries_ = *num_entries_;
663 storage_.high_watermark_absolute_ = *high_watermark_absolute_;
669 num_entries_.reset();
670 high_watermark_absolute_.reset();
673template<
bool IsGlobal>
677 WHILE (it != end()) {
681 WHILE (not bucket_it.is_nullptr()) {
683 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).to<uint32_t*>());
687 *(it + ptr_offset_in_bytes_).
template to<uint32_t*>() = 0
U;
688 it += int32_t(
sizeof(uint32_t));
692template<
bool IsGlobal>
695 M_insist(
bool(mask_),
"must call `setup()` before");
696 M_insist(key.size() == key_indices_.size(),
697 "provided number of key elements does not match hash table's number of key indices");
700 std::vector<std::pair<const Type*, SQL_t>> values;
701 values.reserve(key_indices_.size());
702 auto key_it = key.begin();
703 for (
auto k : key_indices_)
704 values.emplace_back(schema_.get()[k].type, std::move(*key_it++));
710 U32x1 bucket_idx =
hash.to<uint32_t>() bitand *mask_;
712 Wasm_insist(begin() <= bucket.clone()
and bucket.clone() < end(),
"bucket out-of-bounds");
716template<
bool IsGlobal>
720 std::optional<Boolx1> pred;
723 pred.emplace(env.extract_predicate<_Boolx1>().is_true_and_not_null());
724 if (not predication_dummy_)
727 M_insist(not pred or predication_dummy_);
731 pred ?
Select(*pred, hash_to_bucket(std::move(key)), *predication_dummy_)
732 : hash_to_bucket(std::move(key))
738template<
bool IsGlobal>
741 M_insist(
bool(num_entries_),
"must call `setup()` before");
742 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
745 IF (*num_entries_ == *high_watermark_absolute_) {
747 update_high_watermark();
750 return emplace_without_rehashing(std::move(key));
753template<
bool IsGlobal>
756 M_insist(
bool(num_entries_),
"must call `setup()` before");
757 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
759 Wasm_insist(*num_entries_ < *high_watermark_absolute_);
762 std::optional<Var<Boolx1>> pred;
765 pred = env.extract_predicate<_Boolx1>().is_true_and_not_null();
766 if (not predication_dummy_)
767 create_predication_dummy();
769 M_insist(not pred or predication_dummy_);
773 pred ?
Select(*pred, hash_to_bucket(
clone(key)), *predication_dummy_)
774 : hash_to_bucket(
clone(key))
781 *(entry + ptr_offset_in_bytes_).
template to<uint32_t*>() = *bucket.to<uint32_t*>();
782 *bucket.to<uint32_t*>() = pred ?
Select(*pred, entry.to<uint32_t>(), *bucket.to<uint32_t*>())
783 : entry.to<uint32_t>();
786 *num_entries_ += pred ? pred->to<uint32_t>() : U32x1(1);
789 insert_key(entry, std::move(key));
792 return value_entry(entry);
795template<
bool IsGlobal>
798 M_insist(
bool(num_entries_),
"must call `setup()` before");
799 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
802 IF (*num_entries_ == *high_watermark_absolute_) {
804 update_high_watermark();
806 Wasm_insist(*num_entries_ < *high_watermark_absolute_);
809 std::optional<Var<Boolx1>> pred;
812 pred = env.extract_predicate<_Boolx1>().is_true_and_not_null();
813 if (not predication_dummy_)
814 create_predication_dummy();
816 M_insist(not pred or predication_dummy_);
820 pred ?
Select(*pred, hash_to_bucket(
clone(key)), *predication_dummy_)
821 : hash_to_bucket(
clone(key))
827 BLOCK(insert_entry) {
828 IF (bucket_it.is_nullptr()) {
829 bucket_it = bucket - ptr_offset_in_bytes_;
832 GOTO(equal_key(bucket_it,
clone(key)), insert_entry);
834 Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>())
836 BREAK(next_bucket_it.is_nullptr());
837 bucket_it = next_bucket_it;
841 Wasm_insist(
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>()).is_nullptr());
843 Wasm_insist(*pred or bucket_it == bucket - ptr_offset_in_bytes_,
844 "predication dummy must always contain an empty collision list");
847 entry_inserted =
true;
853 *(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>() = pred ?
Select(*pred, entry.to<uint32_t>(), 0
U)
854 : entry.to<uint32_t>();
860 *num_entries_ += pred ? pred->to<uint32_t>() : U32x1(1);
863 insert_key(entry, std::move(key));
869 return { value_entry(bucket_it), entry_inserted };
872template<
bool IsGlobal>
876 bucket_hint ? *bucket_hint : compute_bucket(
clone(key));
880 WHILE (not bucket_it.is_nullptr()) {
881 BREAK(equal_key(bucket_it, std::move(key)));
882 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>());
886 Boolx1 key_found = not bucket_it.is_nullptr();
889 return { value_entry(bucket_it), key_found };
892template<
bool IsGlobal>
897 WHILE (it != end()) {
898 Wasm_insist(begin() <= it
and it < end(),
"bucket out-of-bounds");
900 WHILE (not bucket_it.is_nullptr()) {
902 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>());
904 it += int32_t(
sizeof(uint32_t));
908template<
bool IsGlobal>
910 bool predicated,
hint_t bucket_hint)
const
913 bucket_hint ? *bucket_hint : compute_bucket(
clone(key));
917 WHILE (not bucket_it.is_nullptr()) {
922 IF (equal_key(bucket_it, std::move(key))) {
926 bucket_it =
Ptr<void>(*(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>());
930template<
bool IsGlobal>
935 entry =
Module::Allocator().allocate(entry_size_in_bytes_, entry_max_alignment_in_bytes_);
938 dummy_allocations_.emplace_back(entry, entry_size_in_bytes_);
944template<
bool IsGlobal>
949 for (std::size_t i = 0; i < key_indices_.size(); ++i) {
952 auto &e = schema_.get()[key_indices_[i]];
953 const auto off = entry_offsets_in_bytes_[key_indices_[i]];
954 auto compare_equal = [&]<
typename T>() {
955 using type =
typename T::type;
956 M_insist(std::holds_alternative<T>(key[i]));
959 entry.clone() + null_bitmap_offset_in_bytes_, key_indices_[i]);
960 res =
res and ref == *std::get_if<T>(&key[i]);
963 res =
res and ref == *std::get_if<T>(&key[i]);
967 [&](
const Boolean&) { compare_equal.template operator()<_Boolx1>(); },
971 case Numeric::N_Decimal:
974 case 8: compare_equal.template operator()<_I8x1 >();
break;
975 case 16: compare_equal.template operator()<_I16x1>();
break;
976 case 32: compare_equal.template operator()<_I32x1>();
break;
977 case 64: compare_equal.template operator()<_I64x1>();
break;
980 case Numeric::N_Float:
982 compare_equal.template
operator()<_Floatx1>();
984 compare_equal.template operator()<_Doublex1>();
988 M_insist(std::holds_alternative<NChar>(key[i]));
989 NChar val((entry.clone() + off).template to<char*>(), e.nullable(), &cs);
992 res =
res and ref == *std::get_if<NChar>(&key[i]);
995 res =
res and ref == *std::get_if<NChar>(&key[i]);
998 [&](
const Date&) { compare_equal.template operator()<_I32x1>(); },
999 [&](
const DateTime&) { compare_equal.template operator()<_I64x1>(); },
1009template<
bool IsGlobal>
1012 for (std::size_t i = 0; i < key_indices_.size(); ++i) {
1014 if (std::find(key_indices_.begin(), key_indices_.begin() + i, key_indices_[i]) != key_indices_.begin() + i) {
1019 auto &e = schema_.get()[key_indices_[i]];
1020 const auto off = entry_offsets_in_bytes_[key_indices_[i]];
1021 auto insert = [&]<
typename T>() {
1022 using type =
typename T::type;
1023 M_insist(std::holds_alternative<T>(key[i]));
1026 entry.clone() + null_bitmap_offset_in_bytes_, key_indices_[i]);
1027 ref = *std::get_if<T>(&key[i]);
1030 ref = *std::get_if<T>(&key[i]);
1034 [&](
const Boolean&) {
insert.template operator()<_Boolx1>(); },
1037 case Numeric::N_Int:
1038 case Numeric::N_Decimal:
1041 case 8:
insert.template operator()<_I8x1 >();
break;
1042 case 16:
insert.template operator()<_I16x1>();
break;
1043 case 32:
insert.template operator()<_I32x1>();
break;
1044 case 64:
insert.template operator()<_I64x1>();
break;
1047 case Numeric::N_Float:
1049 insert.template
operator()<_Floatx1>();
1051 insert.template operator()<_Doublex1>();
1055 M_insist(std::holds_alternative<NChar>(key[i]));
1056 NChar val((entry.clone() + off).template to<char*>(), e.nullable(), &cs);
1058 reference_t<NChar> ref(val, entry.clone() + null_bitmap_offset_in_bytes_, key_indices_[i]);
1059 ref = *std::get_if<NChar>(&key[i]);
1062 ref = *std::get_if<NChar>(&key[i]);
1065 [&](
const Date&) {
insert.template operator()<_I32x1>(); },
1074template<
bool IsGlobal>
1079 for (std::size_t i = 0; i < value_indices_.size(); ++i) {
1082 auto &e = schema_.
get()[value_indices_[i]];
1083 const auto off = entry_offsets_in_bytes_[value_indices_[i]];
1084 auto add = [&]<
typename T>() {
1085 using type =
typename T::type;
1088 entry.clone() + null_bitmap_offset_in_bytes_, value_indices_[i]);
1089 value_entry.
add(e.id, std::move(ref));
1092 value_entry.
add(e.id, std::move(ref));
1096 [&](
const Boolean&) { add.template operator()<_Boolx1>(); },
1099 case Numeric::N_Int:
1100 case Numeric::N_Decimal:
1103 case 8: add.template operator()<_I8x1 >();
break;
1104 case 16: add.template operator()<_I16x1>();
break;
1105 case 32: add.template operator()<_I32x1>();
break;
1106 case 64: add.template operator()<_I64x1>();
break;
1109 case Numeric::N_Float:
1111 add.template
operator()<_Floatx1>();
1113 add.template operator()<_Doublex1>();
1117 NChar val((entry.clone() + off).template to<char*>(), e.nullable(), &cs);
1119 reference_t<NChar> ref(val, entry.clone() + null_bitmap_offset_in_bytes_, value_indices_[i]);
1120 value_entry.
add(e.id, std::move(ref));
1123 value_entry.
add(e.id, std::move(ref));
1126 [&](
const Date&) { add.template operator()<_I32x1>(); },
1127 [&](
const DateTime&) { add.template operator()<_I64x1>(); },
1137template<
bool IsGlobal>
1142 for (std::size_t i = 0; i < key_indices_.size() + value_indices_.size(); ++i) {
1143 const bool is_key = i < key_indices_.size();
1145 if (is_key
and std::find(key_indices_.begin(), key_indices_.begin() + i, key_indices_[i]) != key_indices_.begin() + i)
1148 const auto idx = is_key ? key_indices_[i] : value_indices_[i - key_indices_.size()];
1149 auto &e = schema_.
get()[idx];
1150 const auto off = entry_offsets_in_bytes_[idx];
1151 auto add = [&]<
typename T>() {
1152 using type =
typename T::type;
1155 entry.clone() + null_bitmap_offset_in_bytes_, idx);
1156 _entry.
add(e.id, std::move(ref));
1159 _entry.
add(e.id, std::move(ref));
1163 [&](
const Boolean&) { add.template operator()<_Boolx1>(); },
1166 case Numeric::N_Int:
1167 case Numeric::N_Decimal:
1170 case 8: add.template operator()<_I8x1 >();
break;
1171 case 16: add.template operator()<_I16x1>();
break;
1172 case 32: add.template operator()<_I32x1>();
break;
1173 case 64: add.template operator()<_I64x1>();
break;
1176 case Numeric::N_Float:
1178 add.template
operator()<_Floatx1>();
1180 add.template operator()<_Doublex1>();
1184 NChar val((entry.clone() + off).template to<char*>(), e.nullable(), &cs);
1187 _entry.
add(e.id, std::move(ref));
1190 _entry.
add(e.id, std::move(ref));
1193 [&](
const Date&) { add.template operator()<_I32x1>(); },
1194 [&](
const DateTime&) { add.template operator()<_I64x1>(); },
1204template<
bool IsGlobal>
1207 if (options::insist_no_rehashing)
1208 Throw(exception::unreachable,
"rehashing must not occur");
1210 auto emit_rehash = [
this](){
1213 M_insist(
bool(address_),
"must call `setup()` before");
1214 M_insist(
bool(mask_),
"must call `setup()` before");
1221 *mask_ = (*mask_ << 1U) + 1U;
1231 WHILE (it != end_old) {
1232 Wasm_insist(begin_old <= it
and it < end_old,
"bucket out-of-bounds");
1234 WHILE (not bucket_it.is_nullptr()) {
1235 auto e_old = entry(it);
1238 std::vector<SQL_t> key;
1239 for (
auto k : key_indices_) {
1241 [&](
auto &&r) ->
void { key.emplace_back(r); },
1242 [](std::monostate) ->
void {
M_unreachable(
"invalid reference"); },
1243 }, e_old.get(schema_.get()[k].id));
1247 const Var<Ptr<void>> bucket(hash_to_bucket(std::move(key)));
1253 *(bucket_it + ptr_offset_in_bytes_).
template to<uint32_t*>() = *bucket.to<uint32_t*>();
1254 *bucket.to<uint32_t*>() = bucket_it.to<uint32_t>();
1257 bucket_it = tmp.val();
1261 it += int32_t(
sizeof(uint32_t));
1269 if constexpr (IsGlobal) {
1272 auto old_address = std::exchange(address_, std::nullopt);
1273 auto old_mask = std::exchange(mask_, std::nullopt);
1280 address_.emplace(storage_.address_);
1281 mask_.emplace(storage_.mask_);
1286 storage_.address_ = *address_;
1287 storage_.mask_ = *mask_;
1291 rehash_ = std::move(rehash);
1294 std::exchange(address_, std::move(old_address));
1295 std::exchange(mask_, std::move(old_mask));
1299 storage_.address_ = *address_;
1300 storage_.mask_ = *mask_;
1307 *address_ = storage_.address_;
1308 *mask_ = storage_.mask_;
1335 "provided number of key elements does not match hash table's number of key indices");
1338 std::vector<std::pair<const Type*, SQL_t>> values;
1340 auto key_it = key.begin();
1342 values.emplace_back(
schema_.get()[k].type, std::move(*key_it++));
1348 U32x1 bucket_idx =
hash.to<uint32_t>() bitand
mask();
1354template<
bool IsGlobal,
bool ValueInPlace>
1356 std::vector<HashTable::index_t> key_indices,
1357 uint32_t initial_capacity)
1360 std::vector<const Type*> types;
1361 bool has_nullable =
false;
1366 if constexpr (ValueInPlace) {
1368 for (
auto &e :
schema_.get()) {
1369 types.push_back(e.type);
1370 has_nullable |= e.nullable();
1379 std::vector<HashTable::offset_t> offsets;
1387 layout_.null_bitmap_offset_in_bytes_ = offsets.back();
1392 layout_.entry_offsets_in_bytes_ = std::vector<HashTable::offset_t>(std::next(offsets.begin()), offsets.end());
1395 for (std::size_t i = 0; i <
schema_.get().num_entries(); ++i) {
1398 types.push_back(
schema_.get()[i].type);
1399 has_nullable |=
schema_.get()[i].nullable();
1411 std::vector<HashTable::offset_t> offsets;
1419 layout_.keys_null_bitmap_offset_in_bytes_ = offsets.back();
1424 layout_.ptr_offset_in_bytes_ = offsets.back();
1425 layout_.key_offsets_in_bytes_ =
1426 std::vector<HashTable::offset_t>(std::next(offsets.begin()), std::prev(offsets.end()));
1430 has_nullable =
false;
1431 for (std::size_t i = 0; i <
schema_.get().num_entries(); ++i) {
1434 types.push_back(
schema_.get()[i].type);
1435 has_nullable |=
schema_.get()[i].nullable();
1444 std::tie(
layout_.values_size_in_bytes_,
layout_.values_max_alignment_in_bytes_) =
1448 layout_.values_null_bitmap_offset_in_bytes_ = offsets.back();
1449 layout_.value_offsets_in_bytes_ =
1450 std::vector<HashTable::offset_t>(offsets.begin(), std::prev(offsets.end()));
1453 std::tie(
layout_.values_size_in_bytes_,
layout_.values_max_alignment_in_bytes_) =
1459 M_insist(initial_capacity < std::numeric_limits<uint32_t>::max(),
1460 "incremented initial capacity would exceed data type");
1463 const auto capacity_init = std::max<uint32_t>(4, ceil_to_pow_2(initial_capacity));
1464 const auto mask_init = capacity_init - 1U;
1465 const auto high_watermark_absolute_init = capacity_init - 1U;
1466 if constexpr (IsGlobal) {
1469 storage_.high_watermark_absolute_.init(high_watermark_absolute_init);
1471 mask_.emplace(mask_init);
1476template<
bool IsGlobal,
bool ValueInPlace>
1479 if constexpr (IsGlobal) {
1480 if constexpr (not ValueInPlace) {
1483 const Var<Ptr<void>> end(storage_.address_ + ((storage_.mask_ + 1U) * entry_size_in_bytes_).make_signed());
1485 Wasm_insist(storage_.address_ <= it
and it < end,
"entry out-of-bounds");
1486 IF (reference_count(it) !=
ref_t(0)) {
1488 layout_.values_size_in_bytes_);
1490 it += int32_t(entry_size_in_bytes_);
1495 Module::Allocator().deallocate(storage_.address_, (storage_.mask_ + 1U) * entry_size_in_bytes_);
1498 for (
auto it = dummy_allocations_.rbegin(); it != dummy_allocations_.rend(); ++it)
1503template<
bool IsGlobal,
bool ValueInPlace>
1506 M_insist(not address_,
"must not call `setup()` twice");
1507 M_insist(not num_entries_,
"must not call `setup()` twice");
1511 num_entries_.emplace();
1512 if constexpr (IsGlobal) {
1513 M_insist(not mask_,
"must not call `setup()` twice");
1514 M_insist(not high_watermark_absolute_,
"must not call `setup()` twice");
1516 high_watermark_absolute_.emplace();
1519 M_insist(
bool(high_watermark_absolute_));
1523 if constexpr (IsGlobal) {
1525 *mask_ = storage_.mask_;
1526 *num_entries_ = storage_.num_entries_;
1527 *high_watermark_absolute_ = storage_.high_watermark_absolute_;
1530 if constexpr (IsGlobal) {
1531 IF (storage_.address_.is_nullptr()) {
1533 *address_ =
Module::Allocator().allocate(size_in_bytes(), entry_max_alignment_in_bytes_);
1538 *address_ = storage_.address_;
1542 *address_ =
Module::Allocator().allocate(size_in_bytes(), entry_max_alignment_in_bytes_);
1549template<
bool IsGlobal,
bool ValueInPlace>
1552 M_insist(
bool(address_),
"must call `setup()` before");
1553 M_insist(
bool(mask_),
"must call `setup()` before");
1554 M_insist(
bool(num_entries_),
"must call `setup()` before");
1555 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
1557 if constexpr (not IsGlobal) {
1558 if constexpr (not ValueInPlace) {
1561 WHILE (it != end()) {
1562 Wasm_insist(begin() <= it
and it < end(),
"entry out-of-bounds");
1563 IF (reference_count(it) !=
ref_t(0)) {
1565 layout_.values_size_in_bytes_);
1567 it += int32_t(entry_size_in_bytes_);
1575 for (
auto it = dummy_allocations_.rbegin(); it != dummy_allocations_.rend(); ++it)
1580 if constexpr (IsGlobal) {
1581 storage_.address_ = *address_;
1582 storage_.mask_ = *mask_;
1583 storage_.num_entries_ = *num_entries_;
1584 storage_.high_watermark_absolute_ = *high_watermark_absolute_;
1590 num_entries_.reset();
1591 high_watermark_absolute_.reset();
1594template<
bool IsGlobal,
bool ValueInPlace>
1598 std::optional<Boolx1> pred;
1601 pred.emplace(env.extract_predicate<_Boolx1>().is_true_and_not_null());
1602 if (not predication_dummy_)
1605 M_insist(not pred or predication_dummy_);
1609 pred ?
Select(*pred, hash_to_bucket(std::move(key)), *predication_dummy_)
1610 : hash_to_bucket(std::move(key))
1616template<
bool IsGlobal,
bool ValueInPlace>
1619 M_insist(
bool(num_entries_),
"must call `setup()` before");
1620 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
1623 IF (*num_entries_ == *high_watermark_absolute_) {
1625 update_high_watermark();
1628 auto slot = emplace_without_rehashing(std::move(key));
1630 if constexpr (ValueInPlace) {
1632 return value_entry(slot);
1636 Module::Allocator().allocate(layout_.values_size_in_bytes_, layout_.values_max_alignment_in_bytes_);
1637 *(slot + layout_.ptr_offset_in_bytes_).
template to<uint32_t*>() = ptr.clone().to<uint32_t>();
1640 return value_entry(ptr);
1644template<
bool IsGlobal,
bool ValueInPlace>
1647 M_insist(
bool(num_entries_),
"must call `setup()` before");
1648 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
1650 Wasm_insist(*num_entries_ < *high_watermark_absolute_);
1653 std::optional<Var<Boolx1>> pred;
1656 pred = env.extract_predicate<_Boolx1>().is_true_and_not_null();
1657 if (not predication_dummy_)
1658 create_predication_dummy();
1660 M_insist(not pred or predication_dummy_);
1664 pred ?
Select(*pred, hash_to_bucket(
clone(key)), *predication_dummy_)
1665 : hash_to_bucket(
clone(key))
1672 Ptr<void> _slot = probing_strategy().skip_slots(bucket, refs);
1673 Wasm_insist(begin() <= _slot.clone()
and _slot.clone() < end(),
"slot out-of-bounds");
1679 Wasm_insist(refs <= *num_entries_,
"probing strategy has to find unoccupied slot if there is one");
1680 slot = probing_strategy().advance_to_next_slot(slot, refs);
1681 Wasm_insist(begin() <= slot
and slot < end(),
"slot out-of-bounds");
1685 reference_count(bucket) = refs +
ref_t(1);
1691 *num_entries_ += pred ? pred->to<uint32_t>() : U32x1(1);
1692 Wasm_insist(*num_entries_ < capacity(),
"at least one entry must always be unoccupied for lookups");
1695 insert_key(slot, std::move(key));
1700template<
bool IsGlobal,
bool ValueInPlace>
1701std::pair<HashTable::entry_t, Boolx1>
1704 M_insist(
bool(num_entries_),
"must call `setup()` before");
1705 M_insist(
bool(high_watermark_absolute_),
"must call `setup()` before");
1708 IF (*num_entries_ == *high_watermark_absolute_) {
1710 update_high_watermark();
1712 Wasm_insist(*num_entries_ < *high_watermark_absolute_);
1715 std::optional<Var<Boolx1>> pred;
1718 pred = env.extract_predicate<_Boolx1>().is_true_and_not_null();
1719 if (not predication_dummy_)
1720 create_predication_dummy();
1722 M_insist(not pred or predication_dummy_);
1726 pred ?
Select(*pred, hash_to_bucket(
clone(key)), *predication_dummy_)
1727 : hash_to_bucket(
clone(key))
1736 BLOCK(insert_entry) {
1738 GOTO(equal_key(slot,
clone(key)), insert_entry);
1740 Wasm_insist(refs <= *num_entries_,
"probing strategy has to find unoccupied slot if there is one");
1741 slot = probing_strategy().advance_to_next_slot(slot, refs);
1742 Wasm_insist(begin() <= slot
and slot < end(),
"slot out-of-bounds");
1746 Wasm_insist(*pred or refs ==
ref_t(0),
"predication dummy must always be unoccupied");
1749 entry_inserted =
true;
1752 Wasm_insist(reference_count(bucket) <= refs,
"reference count must increase if unoccupied slot is found");
1753 reference_count(bucket) = refs +
ref_t(1);
1759 *num_entries_ += pred ? pred->to<uint32_t>() : U32x1(1);
1760 Wasm_insist(*num_entries_ < capacity(),
"at least one entry must always be unoccupied for lookups");
1763 insert_key(slot, std::move(key));
1765 if constexpr (not ValueInPlace) {
1768 Module::Allocator().allocate(layout_.values_size_in_bytes_, layout_.values_max_alignment_in_bytes_);
1769 *(slot + layout_.ptr_offset_in_bytes_).
template to<uint32_t*>() = ptr.clone().to<uint32_t>();
1775 dummy_allocations_.emplace_back(ptr_, layout_.values_size_in_bytes_);
1784 if constexpr (not ValueInPlace) {
1786 slot = *(slot + layout_.ptr_offset_in_bytes_).
template to<uint32_t*>();
1790 return { value_entry(slot), entry_inserted };
1793template<
bool IsGlobal,
bool ValueInPlace>
1797 M_insist(
bool(num_entries_),
"must call `setup()` before");
1800 bucket_hint ? *bucket_hint : compute_bucket(
clone(key));
1808 WHILE (steps != refs) {
1809 Wasm_insist(reference_count(slot) !=
ref_t(0),
"slot in bucket list must be occupied");
1810 BREAK(equal_key(slot, std::move(key)));
1812 Wasm_insist(steps <= *num_entries_,
"probing strategy has to find unoccupied slot if there is one");
1813 slot = probing_strategy().advance_to_next_slot(slot, steps);
1814 Wasm_insist(begin() <= slot
and slot < end(),
"slot out-of-bounds");
1818 Boolx1 key_found = steps != refs;
1820 if constexpr (not ValueInPlace) {
1822 slot = *(slot + layout_.ptr_offset_in_bytes_).
template to<uint32_t*>();
1826 return { value_entry(slot), key_found };
1829template<
bool IsGlobal,
bool ValueInPlace>
1834 WHILE (it != end()) {
1835 Wasm_insist(begin() <= it
and it < end(),
"entry out-of-bounds");
1836 IF (reference_count(it) !=
ref_t(0)) {
1839 it += int32_t(entry_size_in_bytes_);
1843template<
bool IsGlobal,
bool ValueInPlace>
1847 hint_t bucket_hint)
const
1849 M_insist(
bool(num_entries_),
"must call `setup()` before");
1852 bucket_hint ? *bucket_hint : compute_bucket(
clone(key));
1860 WHILE (steps != refs) {
1861 Wasm_insist(reference_count(slot) !=
ref_t(0),
"slot in bucket list must be occupied");
1866 IF (equal_key(slot, std::move(key))) {
1871 Wasm_insist(steps <= *num_entries_,
"probing strategy has to find unoccupied slot if there is one");
1872 slot = probing_strategy().advance_to_next_slot(slot, steps);
1873 Wasm_insist(begin() <= slot
and slot < end(),
"slot out-of-bounds");
1877template<
bool IsGlobal,
bool ValueInPlace>
1880 if constexpr (ValueInPlace) {
1883 slot =
Module::Allocator().allocate(entry_size_in_bytes_, entry_max_alignment_in_bytes_);
1886 dummy_allocations_.emplace_back(slot, entry_size_in_bytes_);
1893 ptr =
Module::Allocator().allocate(layout_.values_size_in_bytes_, layout_.values_max_alignment_in_bytes_);
1896 dummy_allocations_.emplace_back(ptr, layout_.values_size_in_bytes_);
1903template<
bool IsGlobal,
bool ValueInPlace>
1908 const auto off_null_bitmap =
M_CONSTEXPR_COND(ValueInPlace, layout_.null_bitmap_offset_in_bytes_,
1909 layout_.keys_null_bitmap_offset_in_bytes_);
1910 for (std::size_t i = 0; i < key_indices_.size(); ++i) {
1913 auto &e = schema_.get()[key_indices_[i]];
1914 const auto idx = [&](){
1915 if constexpr (ValueInPlace) {
1916 return key_indices_[i];
1918 std::size_t count = 0;
1919 for (
index_t idx = 0; idx != key_indices_[i]; ++idx)
1920 count +=
contains(key_indices_, idx);
1924 const auto off =
M_CONSTEXPR_COND(ValueInPlace, layout_.entry_offsets_in_bytes_[idx],
1925 layout_.key_offsets_in_bytes_[idx]);
1926 auto compare_equal = [&]<
typename T>() {
1927 using type =
typename T::type;
1928 M_insist(std::holds_alternative<T>(key[i]));
1930 const_reference_t<T> ref((slot.clone() + off).template to<type*>(), slot.clone() + off_null_bitmap, idx);
1931 res =
res and ref == *std::get_if<T>(&key[i]);
1934 res =
res and ref == *std::get_if<T>(&key[i]);
1938 [&](
const Boolean&) { compare_equal.template operator()<_Boolx1>(); },
1941 case Numeric::N_Int:
1942 case Numeric::N_Decimal:
1945 case 8: compare_equal.template operator()<_I8x1 >();
break;
1946 case 16: compare_equal.template operator()<_I16x1>();
break;
1947 case 32: compare_equal.template operator()<_I32x1>();
break;
1948 case 64: compare_equal.template operator()<_I64x1>();
break;
1951 case Numeric::N_Float:
1953 compare_equal.template
operator()<_Floatx1>();
1955 compare_equal.template operator()<_Doublex1>();
1959 M_insist(std::holds_alternative<NChar>(key[i]));
1960 NChar val((slot.clone() + off).template to<char*>(), e.nullable(), &cs);
1963 res =
res and ref == *std::get_if<NChar>(&key[i]);
1966 res =
res and ref == *std::get_if<NChar>(&key[i]);
1969 [&](
const Date&) { compare_equal.template operator()<_I32x1>(); },
1970 [&](
const DateTime&) { compare_equal.template operator()<_I64x1>(); },
1980template<
bool IsGlobal,
bool ValueInPlace>
1983 const auto off_null_bitmap =
M_CONSTEXPR_COND(ValueInPlace, layout_.null_bitmap_offset_in_bytes_,
1984 layout_.keys_null_bitmap_offset_in_bytes_);
1985 for (std::size_t i = 0; i < key_indices_.size(); ++i) {
1987 if (std::find(key_indices_.begin(), key_indices_.begin() + i, key_indices_[i]) != key_indices_.begin() + i) {
1992 auto &e = schema_.get()[key_indices_[i]];
1993 const auto idx = [&](){
1994 if constexpr (ValueInPlace) {
1995 return key_indices_[i];
1997 std::size_t count = 0;
1998 for (
index_t idx = 0; idx != key_indices_[i]; ++idx)
1999 count +=
contains(key_indices_, idx);
2003 const auto off =
M_CONSTEXPR_COND(ValueInPlace, layout_.entry_offsets_in_bytes_[idx],
2004 layout_.key_offsets_in_bytes_[idx]);
2005 auto insert = [&]<
typename T>() {
2006 using type =
typename T::type;
2007 M_insist(std::holds_alternative<T>(key[i]));
2009 reference_t<T> ref((slot.clone() + off).template to<type*>(), slot.clone() + off_null_bitmap, idx);
2010 ref = *std::get_if<T>(&key[i]);
2013 ref = *std::get_if<T>(&key[i]);
2017 [&](
const Boolean&) {
insert.template operator()<_Boolx1>(); },
2020 case Numeric::N_Int:
2021 case Numeric::N_Decimal:
2024 case 8:
insert.template operator()<_I8x1 >();
break;
2025 case 16:
insert.template operator()<_I16x1>();
break;
2026 case 32:
insert.template operator()<_I32x1>();
break;
2027 case 64:
insert.template operator()<_I64x1>();
break;
2030 case Numeric::N_Float:
2032 insert.template
operator()<_Floatx1>();
2034 insert.template operator()<_Doublex1>();
2038 M_insist(std::holds_alternative<NChar>(key[i]));
2039 NChar val((slot.clone() + off).template to<char*>(), e.nullable(), &cs);
2042 ref = *std::get_if<NChar>(&key[i]);
2045 ref = *std::get_if<NChar>(&key[i]);
2048 [&](
const Date&) {
insert.template operator()<_I32x1>(); },
2057template<
bool IsGlobal,
bool ValueInPlace>
2062 const auto off_null_bitmap =
M_CONSTEXPR_COND(ValueInPlace, layout_.null_bitmap_offset_in_bytes_,
2063 layout_.values_null_bitmap_offset_in_bytes_);
2064 for (std::size_t i = 0; i < value_indices_.size(); ++i) {
2067 auto &e = schema_.get()[value_indices_[i]];
2068 const auto idx = [&](){
2069 if constexpr (ValueInPlace) {
2070 return value_indices_[i];
2072 std::size_t count = 0;
2073 for (
index_t idx = 0; idx != value_indices_[i]; ++idx)
2074 count +=
contains(value_indices_, idx);
2078 const auto off =
M_CONSTEXPR_COND(ValueInPlace, layout_.entry_offsets_in_bytes_[idx],
2079 layout_.value_offsets_in_bytes_[idx]);
2080 auto add = [&]<
typename T>() {
2081 using type =
typename T::type;
2083 reference_t<T> ref((ptr.clone() + off).template to<type*>(), ptr.clone() + off_null_bitmap, idx);
2084 value_entry.
add(e.id, std::move(ref));
2087 value_entry.
add(e.id, std::move(ref));
2091 [&](
const Boolean&) { add.template operator()<_Boolx1>(); },
2094 case Numeric::N_Int:
2095 case Numeric::N_Decimal:
2098 case 8: add.template operator()<_I8x1 >();
break;
2099 case 16: add.template operator()<_I16x1>();
break;
2100 case 32: add.template operator()<_I32x1>();
break;
2101 case 64: add.template operator()<_I64x1>();
break;
2104 case Numeric::N_Float:
2106 add.template
operator()<_Floatx1>();
2108 add.template operator()<_Doublex1>();
2112 NChar val((ptr.clone() + off).template to<char*>(), e.nullable(), &cs);
2115 value_entry.
add(e.id, std::move(ref));
2118 value_entry.
add(e.id, std::move(ref));
2121 [&](
const Date&) { add.template operator()<_I32x1>(); },
2122 [&](
const DateTime&) { add.template operator()<_I64x1>(); },
2132template<
bool IsGlobal,
bool ValueInPlace>
2137 std::unique_ptr<Ptr<void>>
value;
2138 if constexpr (not ValueInPlace) {
2139 const Var<Ptr<void>> value_(*(slot.clone() + layout_.ptr_offset_in_bytes_).template to<uint32_t*>());
2140 value = std::make_unique<Ptr<void>>(value_);
2144 auto off_null_bitmap =
M_CONSTEXPR_COND(ValueInPlace, layout_.null_bitmap_offset_in_bytes_,
2145 layout_.keys_null_bitmap_offset_in_bytes_);
2146 for (std::size_t i = 0; i < key_indices_.size() + value_indices_.size(); ++i) {
2147 if constexpr (not ValueInPlace) {
2148 if (i == key_indices_.size()) {
2152 off_null_bitmap = layout_.values_null_bitmap_offset_in_bytes_;
2156 const bool is_key = i < key_indices_.size();
2158 if (is_key
and std::find(key_indices_.begin(), key_indices_.begin() + i, key_indices_[i]) != key_indices_.begin() + i)
2161 const auto schema_idx = is_key ? key_indices_[i] : value_indices_[i - key_indices_.size()];
2162 auto &e = schema_.get()[schema_idx];
2163 const auto idx = [&](){
2164 if constexpr (ValueInPlace) {
2167 std::size_t count = 0;
2168 for (
index_t idx = 0; idx != schema_idx; ++idx)
2169 count += is_key ?
contains(key_indices_, idx) :
contains(value_indices_, idx);
2175 layout_.entry_offsets_in_bytes_[idx],
2176 is_key ? layout_.key_offsets_in_bytes_[idx] : layout_.value_offsets_in_bytes_[idx]);
2177 auto add = [&]<
typename T>() {
2178 using type =
typename T::type;
2180 const_reference_t<T> ref((ptr->clone() + off).template to<type*>(), ptr->clone() + off_null_bitmap, idx);
2181 entry.
add(e.id, std::move(ref));
2184 entry.
add(e.id, std::move(ref));
2188 [&](
const Boolean&) { add.template operator()<_Boolx1>(); },
2191 case Numeric::N_Int:
2192 case Numeric::N_Decimal:
2195 case 8: add.template operator()<_I8x1 >();
break;
2196 case 16: add.template operator()<_I16x1>();
break;
2197 case 32: add.template operator()<_I32x1>();
break;
2198 case 64: add.template operator()<_I64x1>();
break;
2201 case Numeric::N_Float:
2203 add.template
operator()<_Floatx1>();
2205 add.template operator()<_Doublex1>();
2209 NChar val((ptr->clone() + off).template to<char*>(), e.nullable(), &cs);
2212 entry.
add(e.id, std::move(ref));
2215 entry.
add(e.id, std::move(ref));
2218 [&](
const Date&) { add.template operator()<_I32x1>(); },
2219 [&](
const DateTime&) { add.template operator()<_I64x1>(); },
2230template<
bool IsGlobal,
bool ValueInPlace>
2233 if (options::insist_no_rehashing)
2234 Throw(exception::unreachable,
"rehashing must not occur");
2236 auto emit_rehash = [
this](){
2239 M_insist(
bool(address_),
"must call `setup()` before");
2240 M_insist(
bool(mask_),
"must call `setup()` before");
2241 M_insist(
bool(num_entries_),
"must call `setup()` before");
2248 *mask_ = (*mask_ << 1U) + 1U;
2251 *address_ =
Module::Allocator().allocate(size_in_bytes(), entry_max_alignment_in_bytes_);
2258 const Var<U32x1> num_entries_old(*num_entries_);
2266 WHILE (it != end_old) {
2267 Wasm_insist(begin_old <= it
and it < end_old,
"entry out-of-bounds");
2268 IF (reference_count(it) !=
ref_t(0)) {
2269 auto e_old = entry(it);
2272 std::vector<SQL_t> key;
2273 for (
auto k : key_indices_) {
2275 [&](
auto &&r) ->
void { key.emplace_back(r); },
2276 [](std::monostate) ->
void {
M_unreachable(
"invalid reference"); },
2277 }, e_old.get(schema_.get()[k].id));
2281 auto slot = emplace_without_rehashing(std::move(key));
2283 if constexpr (ValueInPlace) {
2285 auto e_new = value_entry(slot);
2288 for (
auto v : value_indices_) {
2289 auto id = schema_.get()[v].id;
2291 [&]<sql_type
T>(
reference_t<T> &&r) ->
void { r = e_old.template extract<T>(
id); },
2292 [](std::monostate) ->
void {
M_unreachable(
"invalid reference"); },
2293 }, e_new.extract(
id));
2298 *(slot + layout_.ptr_offset_in_bytes_).
template to<uint32_t*>() =
2299 *(it + layout_.ptr_offset_in_bytes_).
template to<uint32_t*>();
2304 it += int32_t(entry_size_in_bytes_);
2308 Wasm_insist(*num_entries_ == num_entries_old,
"number of entries of old and new hash table do not match");
2316 if constexpr (IsGlobal) {
2319 auto old_address = std::exchange(address_, std::nullopt);
2320 auto old_mask = std::exchange(mask_, std::nullopt);
2321 auto old_num_entries = std::exchange(num_entries_, std::nullopt);
2322 auto old_high_watermark_absolute = std::exchange(high_watermark_absolute_, std::nullopt);
2328 address_.emplace(storage_.address_);
2329 mask_.emplace(storage_.mask_);
2330 num_entries_.emplace(storage_.num_entries_);
2331 high_watermark_absolute_.emplace(storage_.high_watermark_absolute_);
2336 storage_.address_ = *address_;
2337 storage_.mask_ = *mask_;
2338 storage_.num_entries_ = *num_entries_;
2339 storage_.high_watermark_absolute_ = *high_watermark_absolute_;
2342 num_entries_.reset();
2343 high_watermark_absolute_.reset();
2345 rehash_ = std::move(rehash);
2348 std::exchange(address_, std::move(old_address));
2349 std::exchange(mask_, std::move(old_mask));
2350 std::exchange(num_entries_, std::move(old_num_entries));
2351 std::exchange(high_watermark_absolute_, std::move(old_high_watermark_absolute));
2355 storage_.address_ = *address_;
2356 storage_.mask_ = *mask_;
2357 storage_.num_entries_ = *num_entries_;
2358 storage_.high_watermark_absolute_ = *high_watermark_absolute_;
2365 *address_ = storage_.address_;
2366 *mask_ = storage_.mask_;
2367 *num_entries_ = storage_.num_entries_;
2368 *high_watermark_absolute_ = storage_.high_watermark_absolute_;
2394 current_step.discard();
2403 auto skips_cloned = skips.clone();
2404 U32x1 slots_skipped = (skips_cloned * (skips + 1U)) >> 1U;
2405 U32x1 slots_skipped_mod = slots_skipped bitand
ht_.
mask();
__attribute__((constructor(202))) static void register_interpreter()
void insert(std::vector< const ast::Expr * > &exprs, const std::vector< const ast::Designator * > &insertions)
Like std::vector::insert() but adds only those elements of insertions which are not already contained...
U64x1 reinterpret_to_U64(m::wasm::PrimitiveExpr< T > value)
#define FUNCTION(NAME, TYPE)
void add(const char *group_name, const char *short_name, const char *long_name, const char *description, Callback &&callback)
Adds a new group option to the ArgParser.
#define M_unreachable(MSG)
#define M_CONSTEXPR_COND(COND, IF_TRUE, IF_FALSE)
template void quicksort< true >(GlobalBuffer &, const std::vector< SortingOperator::order_type > &)
U64x1 str_hash(NChar str)
Hashes the string str.
template void quicksort< false >(GlobalBuffer &, const std::vector< SortingOperator::order_type > &)
typename detail::var_helper< T >::type Var
Local variable.
auto make_unsigned()
Conversion of a PrimitiveExpr<T, L> to a PrimitiveExpr<std::make_unsigned_t<T>, L>.
auto make_signed()
Conversion of a PrimitiveExpr<T, L> to a PrimitiveExpr<std::make_signed_t<T>, L>.
void GOTO(const Block &block)
Jumps to the end of block.
U64x1 murmur3_bit_mix(U64x1 bits)
Mixes the bits of bits using the Murmur3 algorithm.
void quicksort(Buffer< IsGlobal > &buffer, const std::vector< SortingOperator::order_type > &order)
Sorts the buffer buffer using the quicksort algorithm and a branchless binary partition algorithm.
and
Constructs a new PrimitiveExpr from a constant value.
Bool< L > is_null(SQL_t &variant)
auto Select(C &&_cond, T &&_tru, U &&_fals)
void discard()
Discards this.
PrimitiveExpr< uint64_t, L > L L L L U
for(std::size_t idx=1;idx< num_vectors;++idx) res.emplace((vectors_[idx].bitmask()<< uint32_t(idx *vector_type return * res
void CONTINUE(std::size_t level=1)
U64x1 fnv_1a(Ptr< U8x1 > bytes, U32x1 num_bytes)
Hashes num_bytes bytes of bytes using the FNV-1a algorithm.
std::pair<::wasm::Expression *, std::list< std::shared_ptr< Bit > > > move()
Moves the underlying Binaryen ::wasm::Expression and the referenced bits out of this.
U64x1 murmur3_64a_hash(std::vector< std::pair< const Type *, SQL_t > > values)
Hashes the elements of values where the first element is the type of the value to hash and the second...
PrimitiveExpr< uint64_t, L > hash() and(L
PrimitiveExpr clone() const
Creates and returns a deep copy of this.
void BREAK(std::size_t level=1)
static constexpr std::size_t num_simd_lanes
the number of SIMD lanes of the represented expression, i.e. 1 for scalar and at least 2 for vectori...
bool M_EXPORT contains(const H &haystack, const N &needle)
Checks whether haystack contains needle.
void swap(PlanTableBase< Actual > &first, PlanTableBase< Actual > &second)
auto visit(Callable &&callable, Base &obj, m::tag< Callable > &&=m::tag< Callable >())
Generic implementation to visit a class hierarchy, with similar syntax as std::visit.
command-line options for the HeuristicSearchPlanEnumerator
The catalog contains all Databases and keeps track of all meta information of the database system.
static Catalog & Get()
Return a reference to the single Catalog instance.
m::ArgParser & arg_parser()
The type of character strings, both fixed length and varying length.
The numeric type represents integer and floating-point types of different precision and scale.
Implements push-based evaluation of a pipeline in the plan.
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
static Pooled< Bitmap > Get_Bitmap(category_t category, std::size_t length)
Returns a Bitmap type of the given category and length.
static Pooled< Numeric > Get_Integer(category_t category, unsigned num_bytes)
Returns a Numeric type for integrals of given category and num_bytes bytes.
Buffers tuples by materializing them into memory.
buffer_load_proxy_t< IsGlobal > create_load_proxy(param_t tuple_value_schema=param_t(), param_t tuple_addr_schema=param_t()) const
Creates and returns a proxy object to load value tuples of schema tuple_value_schema (default: entire...
const Schema & schema() const
Returns the schema of the buffer.
void setup_base_address()
Performs the setup of the local base address of this buffer by reading it from the global backup.
void teardown_base_address()
Performs the teardown of the local base address of this buffer by destroying it but without storing i...
buffer_swap_proxy_t< IsGlobal > create_swap_proxy(param_t tuple_schema=param_t()) const
Creates and returns a proxy object to swap tuples of schema tuple_schema (default: entire tuples) in ...
U32x1 size() const
Returns the current size of the buffer.
void rehash()
Performs rehashing, i.e.
HashTable::offset_t null_bitmap_offset_in_bytes_
offset of NULL bitmap; only specified if at least one entry is nullable
std::optional< Var< U32x1 > > high_watermark_absolute_
maximum number of entries before growing the hash table is required
void setup() override
Performs the setup of all local variables of the hash table (by reading them from the global backups ...
void clear() override
Clears the hash table.
std::pair< entry_t, Boolx1 > find(std::vector< SQL_t > key, hint_t bucket_hint) override
Tries to find an entry with key key in the hash table.
void for_each_in_equal_range(std::vector< SQL_t > key, callback_t Pipeline, bool predicated, hint_t bucket_hint) const override
Calls Pipeline for each entry with key key in the hash table, where the key comparison is performed p...
void for_each(callback_t Pipeline) const override
Calls Pipeline for each entry contained in the hash table.
entry_t value_entry(Ptr< void > entry) const
Returns a handle for the entry at address entry which may be used to write the values of the correspo...
HashTable::offset_t ptr_offset_in_bytes_
offset of pointer to next entry in linked collision list
HashTable::size_t entry_size_in_bytes_
entry size in bytes
Boolx1 equal_key(Ptr< void > entry, std::vector< SQL_t > key) const
Compares the key of the entry at address entry with key and returns true iff they are equal.
std::pair< entry_t, Boolx1 > try_emplace(std::vector< SQL_t > key) override
If no entry with key key already exists, inserts one into the hash table, i.e.
entry_t emplace_without_rehashing(std::vector< SQL_t > key)
Inserts an entry into the hash table with key key regardless whether it already exists,...
void teardown() override
Performs the teardown of all local variables of the hash table (by storing them into the global backu...
HashTable::size_t entry_max_alignment_in_bytes_
alignment requirement in bytes of a single entry
std::vector< HashTable::offset_t > entry_offsets_in_bytes_
entry offsets, i.e.
void insert_key(Ptr< void > entry, std::vector< SQL_t > key)
Inserts the key key into the entry at address entry.
chained_hash_table_storage< IsGlobal > storage_
if IsGlobal, contains backups for address, capacity, number of entries, and absolute high watermark
Ptr< void > compute_bucket(std::vector< SQL_t > key) const override
Computes the bucket for key key.
entry_t dummy_entry() override
Returns a handle to a newly created dummy entry which may be used to write the values for this entry.
const_entry_t entry(Ptr< void > entry) const
Returns a handle for the entry at address entry which may be used to read both the keys and the value...
std::optional< Var< U32x1 > > mask_
mask of hash table, i.e. number of buckets / collision lists minus 1; always a power of 2 minus 1
Ptr< void > hash_to_bucket(std::vector< SQL_t > key) const
Returns the bucket address for the key key by hashing it.
std::conditional_t< IsGlobal, Global< T >, Var< T > > var_t
variable type dependent on whether the hash table should be globally usable
ChainedHashTable(const Schema &schema, std::vector< HashTable::index_t > key_indices, uint32_t initial_capacity)
Creates a chained hash table with schema schema, keys at key_indices, and an initial capacity for ini...
entry_t emplace(std::vector< SQL_t > key) override
Inserts an entry into the hash table with key key regardless whether it already exists,...
Environment & env()
Returns the current Environment.
static CodeGenContext & Get()
Scope scoped_environment()
Creates a new, scoped Environment.
Binds Schema::Identifiers to Expr<T>s.
void add_predicate(SQL_boolean_t &&pred)
Adds the predicate pred to the predication predicate.
Helper struct as proxy to access a hash table entry.
value_t get(const Schema::Identifier &id) const
Returns the copied entry for identifier id.
void add(Schema::Identifier id, the_reference< T, IsConst > &&ref)
Adds a mapping from identifier id to reference ref.
Helper struct as proxy to access a single value (inclusive NULL bit) of a hash table entry.
Hash table to hash key-value pairs in memory.
std::vector< index_t > key_indices_
keys of hash table
std::vector< index_t > value_indices_
values of hash table
std::function< void(const_entry_t)> callback_t
std::optional< Ptr< void > > hint_t
std::reference_wrapper< const Schema > schema_
schema of hash table
std::pair< size_t, size_t > set_byte_offsets(std::vector< offset_t > &offsets_in_bytes, const std::vector< const Type * > &types, offset_t initial_offset_in_bytes=0, offset_t initial_max_alignment_in_bytes=1)
Sets the byte offsets of an entry containing values of types types in offsets_in_bytes with the start...
Ptr< void > skip_slots(Ptr< void > bucket, U32x1 skips) const override
Returns the address of the skips -th (starting with index 0) slot in the bucket starting at bucket.
Ptr< void > advance_to_next_slot(Ptr< void > slot, U32x1 current_step) const override
Returns the address of the current_step -th slot (starting with index 0) of a bucket which follows th...
std::size_t length() const
const OpenAddressingHashTableBase & ht_
open addressing hash table which uses this probing strategy
HashTable::offset_t refs_offset_in_bytes_
offset in bytes for reference counter
HashTable::size_t entry_size_in_bytes() const
Returns the size in bytes of a single entry in the hash table.
virtual Ptr< void > begin() const =0
Returns the address of the first entry.
HashTable::size_t entry_size_in_bytes_
entry size in bytes
Reference< ref_t > reference_count(Ptr< void > entry) const
Returns a Reference to the reference counter for the entry at address entry.
U32x1 size_in_bytes() const
Returns the overall size in bytes of the hash table.
virtual U32x1 mask() const =0
Returns the mask of the hash table, i.e.
void clear() override
Clears the hash table.
virtual Ptr< void > end() const =0
Returns the address of the past-the-end entry.
Ptr< void > hash_to_bucket(std::vector< SQL_t > key) const
Returns the bucket address for the key key by hashing it.
uint32_t ref_t
4 bytes for reference counting
HashTable::size_t entry_max_alignment_in_bytes_
alignment requirement in bytes of a single entry
U32x1 capacity() const
Returns the capacity of the hash table.
Boolx1 equal_key(Ptr< void > slot, std::vector< SQL_t > key) const
Compares the key of the slot at address slot with key and returns true iff they are equal.
std::optional< Var< U32x1 > > high_watermark_absolute_
maximum number of entries before growing the hash table is required
const_entry_t entry(Ptr< void > slot) const
Returns a handle for the slot at address slot which may be used to read both the keys and the values ...
open_addressing_hash_table_storage< IsGlobal > storage_
if IsGlobal, contains backups for address, capacity, number of entries, and absolute high watermark
void for_each(callback_t Pipeline) const override
Calls Pipeline for each entry contained in the hash table.
void teardown() override
Performs the teardown of all local variables of the hash table (by storing them into the global backu...
void rehash()
Performs rehashing, i.e.
std::optional< Var< U32x1 > > mask_
mask of hash table; always a power of 2 minus 1, i.e. 0b0..01..1
std::pair< entry_t, Boolx1 > try_emplace(std::vector< SQL_t > key) override
If no entry with key key already exists, inserts one into the hash table, i.e.
entry_t value_entry(Ptr< void > ptr) const
Returns a handle which may be used to write the values of the corresponding entry.
void setup() override
Performs the setup of all local variables of the hash table (by reading them from the global backups ...
std::pair< entry_t, Boolx1 > find(std::vector< SQL_t > key, hint_t bucket_hint) override
Tries to find an entry with key key in the hash table.
Ptr< void > emplace_without_rehashing(std::vector< SQL_t > key)
Inserts an entry into the hash table with key key regardless whether it already exists,...
Ptr< void > compute_bucket(std::vector< SQL_t > key) const override
Computes the bucket for key key.
entry_t emplace(std::vector< SQL_t > key) override
Inserts an entry into the hash table with key key regardless whether it already exists,...
void insert_key(Ptr< void > slot, std::vector< SQL_t > key)
Inserts the key key into the slot at address slot.
std::conditional_t< IsGlobal, Global< T >, Var< T > > var_t
variable type dependent on whether the hash table should be globally usable
entry_t dummy_entry() override
Returns a handle to a newly created dummy entry which may be used to write the values for this entry.
~OpenAddressingHashTable()
void for_each_in_equal_range(std::vector< SQL_t > key, callback_t Pipeline, bool predicated, hint_t bucket_hint) const override
Calls Pipeline for each entry with key key in the hash table, where the key comparison is performed p...
OpenAddressingHashTable(const Schema &schema, std::vector< HashTable::index_t > key_indices, uint32_t initial_capacity)
Creates an open addressing hash table with schema schema, keys at key_indices, and an initial capacit...
open_addressing_hash_table_layout< ValueInPlace > layout_
layout of hash table
Ptr< void > skip_slots(Ptr< void > bucket, U32x1 skips) const override
Returns the address of the skips -th (starting with index 0) slot in the bucket starting at bucket.
Ptr< void > advance_to_next_slot(Ptr< void > slot, U32x1 current_step) const override
Returns the address of the current_step -th slot (starting with index 0) of a bucket which follows th...