Skip to content

Commit 2d721bb

Browse files
committed
Reduce reliance on dryad dependency
1 parent fc0cec9 commit 2d721bb

File tree

18 files changed

+478
-66
lines changed

18 files changed

+478
-66
lines changed

include/openvic-dataloader/Error.hpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
#include <cstdint>
44
#include <string_view>
55

6+
#include <openvic-dataloader/detail/SymbolIntern.hpp>
67
#include <openvic-dataloader/detail/Utility.hpp>
78

89
#include <dryad/abstract_node.hpp>
910
#include <dryad/node.hpp>
10-
#include <dryad/symbol.hpp>
1111

1212
namespace ovdl {
1313
template<typename>
@@ -51,12 +51,7 @@ namespace ovdl::error {
5151
LastAnnotation = SecondaryAnnotation,
5252
};
5353

54-
struct ErrorSymbolInterner {
55-
struct SymbolId;
56-
using index_type = std::uint32_t;
57-
using symbol_type = dryad::symbol<SymbolId, index_type>;
58-
using symbol_interner_type = dryad::symbol_interner<SymbolId, char, index_type>;
59-
};
54+
struct ErrorSymbolInterner : SymbolIntern {};
6055

6156
static constexpr std::string_view get_kind_name(ErrorKind kind) {
6257
switch (kind) {
@@ -70,7 +65,7 @@ namespace ovdl::error {
7065
}
7166

7267
struct Error : dryad::abstract_node_all<ErrorKind> {
73-
const char* message(const ErrorSymbolInterner::symbol_interner_type& symbols) const { return _message.c_str(symbols); }
68+
const char* message(const ErrorSymbolInterner::symbol_interner_type& symbols) const { return _message.c_str(); }
7469

7570
protected:
7671
DRYAD_ABSTRACT_NODE_CTOR(Error);

include/openvic-dataloader/csv/Parser.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
#include <openvic-dataloader/detail/Encoding.hpp>
1212
#include <openvic-dataloader/detail/ErrorRange.hpp>
1313

14-
#include <dryad/node.hpp>
15-
1614
namespace ovdl::csv {
1715
class Parser final : public detail::BasicParser {
1816
public:

include/openvic-dataloader/detail/ErrorRange.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
#include <utility>
44

5-
#include <dryad/node.hpp>
6-
75
namespace ovdl::detail {
86
template<typename ErrorRoot>
97
using error_range = decltype(std::declval<const ErrorRoot*>()->errors());
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <type_traits>
5+
6+
namespace ovdl::detail {
7+
/// FNV-1a 64 bit hash.
8+
class DefaultHash {
9+
static constexpr std::uint64_t fnv_basis = 14695981039346656037ull;
10+
static constexpr std::uint64_t fnv_prime = 1099511628211ull;
11+
12+
public:
13+
explicit DefaultHash() : _hash(fnv_basis) {}
14+
15+
DefaultHash(DefaultHash&&) = default;
16+
DefaultHash& operator=(DefaultHash&&) = default;
17+
18+
~DefaultHash() = default;
19+
20+
DefaultHash&& hash_bytes(const unsigned char* ptr, std::size_t size) {
21+
for (auto i = 0u; i != size; ++i) {
22+
_hash ^= ptr[i];
23+
_hash *= fnv_prime;
24+
}
25+
return static_cast<std ::remove_reference_t<decltype(*this)>&&>(*this);
26+
}
27+
28+
template<typename T>
29+
requires std::is_scalar_v<T>
30+
DefaultHash&& hash_scalar(T value) {
31+
static_assert(!std::is_floating_point_v<T>,
32+
"you shouldn't use floats as keys for a hash table");
33+
hash_bytes(reinterpret_cast<unsigned char*>(&value), sizeof(T));
34+
return static_cast<std ::remove_reference_t<decltype(*this)>&&>(*this);
35+
}
36+
37+
template<typename CharT>
38+
DefaultHash&& hash_c_str(const CharT* str) {
39+
while (*str != '\0') {
40+
hash_scalar(*str);
41+
++str;
42+
}
43+
return static_cast<std ::remove_reference_t<decltype(*this)>&&>(*this);
44+
}
45+
46+
std::uint64_t finish() && {
47+
return _hash;
48+
}
49+
50+
private:
51+
std::uint64_t _hash;
52+
};
53+
}
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
#pragma once
2+
3+
#include <bit>
4+
#include <cassert>
5+
#include <climits>
6+
#include <cstddef>
7+
#include <type_traits>
8+
9+
#include <openvic-dataloader/detail/Utility.hpp>
10+
11+
namespace ovdl::detail {
12+
/// A simple hash table for trivial keys with linear probing.
13+
/// It is non-owning as it does not store the used memory resource.
14+
template<typename Traits, std::size_t MinTableSize>
15+
class HashTable {
16+
public:
17+
using value_type = typename Traits::value_type;
18+
static_assert(std::is_trivial_v<value_type>);
19+
20+
constexpr HashTable() = default;
21+
22+
template<typename ResourcePtr>
23+
void free(ResourcePtr resource) {
24+
if (_table_capacity == 0) {
25+
return;
26+
}
27+
28+
resource->deallocate(_table, _table_capacity * sizeof(value_type), alignof(value_type));
29+
_table = nullptr;
30+
_table_size = 0;
31+
_table_capacity = 0;
32+
}
33+
34+
struct entry_handle {
35+
HashTable* _self;
36+
value_type* _entry;
37+
bool _valid;
38+
39+
explicit operator bool() const {
40+
return _valid;
41+
}
42+
43+
std::size_t index() const {
44+
return std::size_t(_entry - _self->_table);
45+
}
46+
47+
value_type& get() const {
48+
assert(*this);
49+
return *_entry;
50+
}
51+
52+
void create(const value_type& value) {
53+
assert(!*this);
54+
*_entry = value;
55+
++_self->_table_size;
56+
_valid = true;
57+
}
58+
59+
void remove() {
60+
assert(*this);
61+
Traits::fill_removed(_entry, 1);
62+
--_self->_table_size;
63+
_valid = false;
64+
}
65+
};
66+
67+
// Looks for an entry in the table, creating one if necessary.
68+
//
69+
// If it is already in the table, returns a pointer to its valid entry.
70+
//
71+
// Otherwise, locates a new entry for that value and returns a pointer to it which is currently
72+
// invalid. Invariants of map are broken until the ptr has been written to.
73+
template<typename Key>
74+
entry_handle lookup_entry(const Key& key, Traits traits = {}) {
75+
assert(_table_size < _table_capacity);
76+
77+
auto hash = traits.hash(key);
78+
auto table_idx = hash & (_table_capacity - 1);
79+
80+
while (true) {
81+
auto entry = _table + table_idx;
82+
if (Traits::is_unoccupied(*entry)) {
83+
// We found an empty entry, return it.
84+
return { this, entry, false };
85+
}
86+
87+
// Check whether the entry is the same string.
88+
if (traits.is_equal(*entry, key)) {
89+
// It is already in the table, return it.
90+
return { this, entry, true };
91+
}
92+
93+
// Go to next entry.
94+
table_idx = (table_idx + 1) & (_table_capacity - 1);
95+
}
96+
}
97+
template<typename Key>
98+
value_type* lookup(const Key& key, Traits traits = {}) const {
99+
if (_table_size == 0) {
100+
return nullptr;
101+
}
102+
103+
auto entry = const_cast<HashTable*>(this)->lookup_entry(key, traits);
104+
return entry ? &entry.get() : nullptr;
105+
}
106+
107+
bool should_rehash() const {
108+
return _table_size >= _table_capacity / 2;
109+
}
110+
111+
static constexpr std::size_t to_table_capacity(unsigned long long cap) {
112+
if (cap < MinTableSize) {
113+
return MinTableSize;
114+
}
115+
116+
// Round up to next power of two.
117+
return std::size_t(1) << (int(sizeof(cap) * CHAR_BIT) - std::countl_zero<size_t>(cap - 1));
118+
}
119+
120+
template<typename ResourcePtr, typename Callback = void (*)(entry_handle, std::size_t)>
121+
void rehash(
122+
ResourcePtr resource, std::size_t new_capacity, Traits traits = {},
123+
Callback entry_cb = +[](entry_handle, std::size_t) {}) {
124+
assert(new_capacity == to_table_capacity(new_capacity));
125+
if (new_capacity <= _table_capacity) {
126+
return;
127+
}
128+
129+
auto old_table = _table;
130+
auto old_capacity = _table_capacity;
131+
132+
// Allocate a bigger, currently empty table.
133+
_table = static_cast<value_type*>(
134+
resource->allocate(new_capacity * sizeof(value_type), alignof(value_type)));
135+
_table_capacity = new_capacity;
136+
Traits::fill_unoccupied(_table, _table_capacity);
137+
138+
// Insert existing values into the new table.
139+
if (_table_size > 0) {
140+
_table_size = 0;
141+
142+
for (auto entry = old_table; entry != old_table + old_capacity; ++entry) {
143+
if (!Traits::is_unoccupied(*entry)) {
144+
auto new_entry = lookup_entry(*entry, traits);
145+
new_entry.create(*entry);
146+
entry_cb(new_entry, std::size_t(entry - old_table));
147+
}
148+
}
149+
}
150+
151+
if (old_capacity > 0) {
152+
resource->deallocate(old_table, old_capacity * sizeof(value_type), alignof(value_type));
153+
}
154+
}
155+
template<typename ResourcePtr, typename Callback = void (*)(entry_handle, std::size_t)>
156+
void rehash(
157+
ResourcePtr resource, Traits traits = {},
158+
Callback entry_cb = +[](entry_handle, std::size_t) {}) {
159+
rehash(resource, to_table_capacity(2 * _table_capacity), traits, entry_cb);
160+
}
161+
162+
//=== access ===//
163+
std::size_t size() const {
164+
return _table_size;
165+
}
166+
std::size_t capacity() const {
167+
return _table_capacity;
168+
}
169+
170+
struct entry_range {
171+
struct iterator {
172+
using value_type = std::remove_cv_t<entry_handle>;
173+
using reference = entry_handle;
174+
struct pointer {
175+
value_type value;
176+
177+
constexpr value_type* operator->() noexcept {
178+
return &value;
179+
}
180+
};
181+
using difference_type = std::ptrdiff_t;
182+
using iterator_category = std::forward_iterator_tag;
183+
184+
constexpr reference operator*() const noexcept {
185+
return static_cast<const iterator&>(*this).deref();
186+
}
187+
constexpr pointer operator->() const noexcept {
188+
return pointer { **this };
189+
}
190+
191+
constexpr iterator& operator++() noexcept {
192+
auto& derived = static_cast<iterator&>(*this);
193+
derived.increment();
194+
return derived;
195+
}
196+
constexpr iterator operator++(int) noexcept {
197+
auto& derived = static_cast<iterator&>(*this);
198+
auto copy = derived;
199+
derived.increment();
200+
return copy;
201+
}
202+
203+
friend constexpr bool operator==(const iterator& lhs, const iterator& rhs) {
204+
return lhs.equal(rhs);
205+
}
206+
friend constexpr bool operator!=(const iterator& lhs, const iterator& rhs) {
207+
return !lhs.equal(rhs);
208+
}
209+
210+
HashTable* _self;
211+
HashTable::value_type* _cur;
212+
213+
iterator() : _self(nullptr), _cur(nullptr) {}
214+
explicit iterator(HashTable& self, HashTable::value_type* cur)
215+
: _self(&self), _cur(cur) {}
216+
217+
entry_handle deref() const {
218+
return { _self, _cur, true };
219+
}
220+
void increment() {
221+
auto end = _self->_table + _self->_table_capacity;
222+
do {
223+
++_cur;
224+
} while (_cur != end && Traits::is_unoccupied(*_cur));
225+
}
226+
bool equal(iterator rhs) const {
227+
return _cur == rhs._cur;
228+
}
229+
};
230+
231+
iterator begin() const {
232+
if (_self->size() == 0) {
233+
return {};
234+
}
235+
236+
auto cur = _self->_table;
237+
while (Traits::is_unoccupied(*cur)) {
238+
cur++;
239+
}
240+
return iterator(*_self, cur);
241+
}
242+
iterator end() const {
243+
if (_self->size() == 0) {
244+
return {};
245+
}
246+
247+
return iterator(*_self, _self->_table + _self->_table_capacity);
248+
}
249+
250+
HashTable* _self;
251+
};
252+
253+
/// Iterates over all occupied entries.
254+
entry_range entries() {
255+
return { this };
256+
}
257+
258+
private:
259+
value_type* _table = nullptr;
260+
std::size_t _table_capacity = 0; // power of two
261+
std::size_t _table_size = 0;
262+
};
263+
}

0 commit comments

Comments
 (0)