Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/hashtable.h
9906 views
1
// Copyright 2022 The Manifold Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
#pragma once
15
#include <stdint.h>
16
17
#include <atomic>
18
19
#include "utils.h"
20
#include "vec.h"
21
22
namespace {
23
using hash_fun_t = uint64_t(uint64_t);
24
inline constexpr uint64_t kOpen = std::numeric_limits<uint64_t>::max();
25
26
template <typename T>
27
T AtomicCAS(T& target, T compare, T val) {
28
std::atomic<T>& tar = reinterpret_cast<std::atomic<T>&>(target);
29
tar.compare_exchange_strong(compare, val, std::memory_order_acq_rel);
30
return compare;
31
}
32
33
template <typename T>
34
void AtomicStore(T& target, T val) {
35
std::atomic<T>& tar = reinterpret_cast<std::atomic<T>&>(target);
36
// release is good enough, although not really something general
37
tar.store(val, std::memory_order_release);
38
}
39
40
template <typename T>
41
T AtomicLoad(const T& target) {
42
const std::atomic<T>& tar = reinterpret_cast<const std::atomic<T>&>(target);
43
// acquire is good enough, although not general
44
return tar.load(std::memory_order_acquire);
45
}
46
47
} // namespace
48
49
namespace manifold {
50
51
template <typename V, hash_fun_t H = hash64bit>
52
class HashTableD {
53
public:
54
HashTableD(Vec<uint64_t>& keys, Vec<V>& values, std::atomic<size_t>& used,
55
uint32_t step = 1)
56
: step_{step}, keys_{keys}, values_{values}, used_{used} {}
57
58
int Size() const { return keys_.size(); }
59
60
bool Full() const {
61
return used_.load(std::memory_order_relaxed) * 2 >
62
static_cast<size_t>(Size());
63
}
64
65
void Insert(uint64_t key, const V& val) {
66
uint32_t idx = H(key) & (Size() - 1);
67
while (1) {
68
if (Full()) return;
69
uint64_t& k = keys_[idx];
70
const uint64_t found = AtomicCAS(k, kOpen, key);
71
if (found == kOpen) {
72
used_.fetch_add(1, std::memory_order_relaxed);
73
values_[idx] = val;
74
return;
75
}
76
if (found == key) return;
77
idx = (idx + step_) & (Size() - 1);
78
}
79
}
80
81
V& operator[](uint64_t key) {
82
uint32_t idx = H(key) & (Size() - 1);
83
while (1) {
84
const uint64_t k = AtomicLoad(keys_[idx]);
85
if (k == key || k == kOpen) {
86
return values_[idx];
87
}
88
idx = (idx + step_) & (Size() - 1);
89
}
90
}
91
92
const V& operator[](uint64_t key) const {
93
uint32_t idx = H(key) & (Size() - 1);
94
while (1) {
95
const uint64_t k = AtomicLoad(keys_[idx]);
96
if (k == key || k == kOpen) {
97
return values_[idx];
98
}
99
idx = (idx + step_) & (Size() - 1);
100
}
101
}
102
103
uint64_t KeyAt(int idx) const { return AtomicLoad(keys_[idx]); }
104
V& At(int idx) { return values_[idx]; }
105
const V& At(int idx) const { return values_[idx]; }
106
107
private:
108
uint32_t step_;
109
VecView<uint64_t> keys_;
110
VecView<V> values_;
111
std::atomic<size_t>& used_;
112
};
113
114
template <typename V, hash_fun_t H = hash64bit>
115
class HashTable {
116
public:
117
HashTable(size_t size, uint32_t step = 1)
118
: keys_{size == 0 ? 0 : 1_uz << (int)ceil(log2(size)), kOpen},
119
values_{size == 0 ? 0 : 1_uz << (int)ceil(log2(size)), {}},
120
step_(step) {}
121
122
HashTable(const HashTable& other)
123
: keys_(other.keys_), values_(other.values_), step_(other.step_) {
124
used_.store(other.used_.load());
125
}
126
127
HashTable& operator=(const HashTable& other) {
128
if (this == &other) return *this;
129
keys_ = other.keys_;
130
values_ = other.values_;
131
used_.store(other.used_.load());
132
step_ = other.step_;
133
return *this;
134
}
135
136
HashTableD<V, H> D() { return {keys_, values_, used_, step_}; }
137
138
int Entries() const { return used_.load(std::memory_order_relaxed); }
139
140
size_t Size() const { return keys_.size(); }
141
142
bool Full() const {
143
return used_.load(std::memory_order_relaxed) * 2 > Size();
144
}
145
146
double FilledFraction() const {
147
return static_cast<double>(used_.load(std::memory_order_relaxed)) / Size();
148
}
149
150
Vec<V>& GetValueStore() { return values_; }
151
152
static uint64_t Open() { return kOpen; }
153
154
private:
155
Vec<uint64_t> keys_;
156
Vec<V> values_;
157
std::atomic<size_t> used_ = 0;
158
uint32_t step_;
159
};
160
} // namespace manifold
161
162