1 #ifndef BUCKET_CONTAINER_H
2 #define BUCKET_CONTAINER_H
10 #include <type_traits>
29 template <
class Key,
class T,
class Allocator,
class Partial,
30 std::size_t SLOT_PER_BUCKET>
34 using mapped_type = T;
35 using value_type = std::pair<const Key, T>;
38 using traits_ =
typename std::allocator_traits<
39 Allocator>::template rebind_traits<value_type>;
42 using allocator_type =
typename traits_::allocator_type;
43 using partial_t = Partial;
44 using size_type =
typename traits_::size_type;
45 using reference = value_type &;
46 using const_reference =
const value_type &;
47 using pointer =
typename traits_::pointer;
48 using const_pointer =
typename traits_::const_pointer;
60 bucket() noexcept : occupied_() {}
62 const value_type &kvpair(size_type ind)
const {
63 return *
static_cast<const value_type *
>(
64 static_cast<const void *
>(&values_[ind]));
66 value_type &kvpair(size_type ind) {
67 return *
static_cast<value_type *
>(
static_cast<void *
>(&values_[ind]));
70 const key_type &key(size_type ind)
const {
71 return storage_kvpair(ind).first;
73 key_type &&movable_key(size_type ind) {
74 return std::move(storage_kvpair(ind).first);
77 const mapped_type &mapped(size_type ind)
const {
78 return storage_kvpair(ind).second;
80 mapped_type &mapped(size_type ind) {
return storage_kvpair(ind).second; }
82 partial_t partial(size_type ind)
const {
return partials_[ind]; }
83 partial_t &partial(size_type ind) {
return partials_[ind]; }
85 bool occupied(size_type ind)
const {
return occupied_[ind]; }
86 bool &occupied(size_type ind) {
return occupied_[ind]; }
91 using storage_value_type = std::pair<Key, T>;
93 const storage_value_type &storage_kvpair(size_type ind)
const {
94 return *
static_cast<const storage_value_type *
>(
95 static_cast<const void *
>(&values_[ind]));
97 storage_value_type &storage_kvpair(size_type ind) {
98 return *
static_cast<storage_value_type *
>(
99 static_cast<void *
>(&values_[ind]));
102 std::array<
typename std::aligned_storage<
sizeof(storage_value_type),
103 alignof(storage_value_type)>::type,
106 std::array<partial_t, SLOT_PER_BUCKET> partials_;
107 std::array<bool, SLOT_PER_BUCKET> occupied_;
111 : allocator_(allocator), bucket_allocator_(allocator), hashpower_(hp),
112 buckets_(bucket_allocator_.allocate(size())) {
116 static_assert(std::is_nothrow_constructible<bucket>::value,
117 "bucket_container requires bucket to be nothrow "
119 for (size_type i = 0; i < size(); ++i) {
120 traits_::construct(allocator_, &buckets_[i]);
124 ~bucket_container() noexcept { destroy_buckets(); }
126 bucket_container(
const bucket_container &bc)
128 traits_::select_on_container_copy_construction(bc.allocator_)),
129 bucket_allocator_(allocator_), hashpower_(bc.hashpower()),
130 buckets_(transfer(bc.hashpower(), bc, std::false_type())) {}
132 bucket_container(
const bucket_container &bc,
133 const allocator_type &a)
134 : allocator_(a), bucket_allocator_(allocator_),
135 hashpower_(bc.hashpower()),
136 buckets_(transfer(bc.hashpower(), bc, std::false_type())) {}
138 bucket_container(bucket_container &&bc)
139 : allocator_(std::move(bc.allocator_)), bucket_allocator_(allocator_),
140 hashpower_(bc.hashpower()), buckets_(std::move(bc.buckets_)) {
142 bc.buckets_ =
nullptr;
145 bucket_container(bucket_container &&bc,
146 const allocator_type &a)
147 : allocator_(a), bucket_allocator_(allocator_) {
148 move_assign(bc, std::false_type());
151 bucket_container &operator=(
const bucket_container &bc) {
153 copy_allocator(allocator_, bc.allocator_,
154 typename traits_::propagate_on_container_copy_assignment());
155 bucket_allocator_ = allocator_;
156 hashpower(bc.hashpower());
157 buckets_ = transfer(bc.hashpower(), bc, std::false_type());
161 bucket_container &operator=(bucket_container &&bc) {
163 move_assign(bc,
typename traits_::propagate_on_container_move_assignment());
167 void swap(bucket_container &bc) noexcept {
168 swap_allocator(allocator_, bc.allocator_,
169 typename traits_::propagate_on_container_swap());
170 swap_allocator(bucket_allocator_, bc.bucket_allocator_,
171 typename traits_::propagate_on_container_swap());
178 size_t bc_hashpower = bc.hashpower();
179 bc.hashpower(hashpower());
180 hashpower(bc_hashpower);
181 std::swap(buckets_, bc.buckets_);
184 size_type hashpower()
const {
185 return hashpower_.load(std::memory_order_acquire);
188 void hashpower(size_type val) {
189 hashpower_.store(val, std::memory_order_release);
192 size_type size()
const {
return size_type(1) << hashpower(); }
194 allocator_type get_allocator()
const {
return allocator_; }
196 bucket &operator[](size_type i) {
return buckets_[i]; }
197 const bucket &operator[](size_type i)
const {
return buckets_[i]; }
200 template <
typename K,
typename... Args>
201 void setKV(size_type ind, size_type slot, partial_t p, K &&k,
203 bucket &b = buckets_[ind];
204 assert(!b.occupied(slot));
206 traits_::construct(allocator_, std::addressof(b.storage_kvpair(slot)),
207 std::piecewise_construct,
208 std::forward_as_tuple(std::forward<K>(k)),
209 std::forward_as_tuple(std::forward<Args>(args)...));
211 b.occupied(slot) =
true;
215 void eraseKV(size_type ind, size_type slot) {
216 bucket &b = buckets_[ind];
217 assert(b.occupied(slot));
218 b.occupied(slot) =
false;
219 traits_::destroy(allocator_, std::addressof(b.storage_kvpair(slot)));
224 void clear() noexcept {
226 std::is_nothrow_destructible<key_type>::value &&
227 std::is_nothrow_destructible<mapped_type>::value,
228 "bucket_container requires key and value to be nothrow "
230 for (size_type i = 0; i < size(); ++i) {
231 bucket &b = buckets_[i];
232 for (size_type j = 0; j < SLOT_PER_BUCKET; ++j) {
243 void clear_and_deallocate() noexcept {
251 bool is_deallocated() const noexcept {
252 return buckets_ ==
nullptr;
256 using bucket_traits_ =
typename traits_::template rebind_traits<bucket>;
257 using bucket_pointer =
typename bucket_traits_::pointer;
260 template <
typename A>
261 void copy_allocator(A &dst,
const A &src, std::true_type) {
265 template <
typename A>
266 void copy_allocator(A &dst,
const A &src, std::false_type) {}
269 template <
typename A>
void swap_allocator(A &dst, A &src, std::true_type) {
273 template <
typename A>
void swap_allocator(A &, A &, std::false_type) {}
276 void move_assign(bucket_container &src, std::true_type) {
277 allocator_ = std::move(src.allocator_);
278 bucket_allocator_ = allocator_;
279 hashpower(src.hashpower());
280 buckets_ = src.buckets_;
281 src.buckets_ =
nullptr;
284 void move_assign(bucket_container &src, std::false_type) {
285 hashpower(src.hashpower());
286 if (allocator_ == src.allocator_) {
287 buckets_ = src.buckets_;
288 src.buckets_ =
nullptr;
290 buckets_ = transfer(src.hashpower(), src, std::true_type());
294 void destroy_buckets() noexcept {
295 if (is_deallocated()) {
301 static_assert(std::is_nothrow_destructible<bucket>::value,
302 "bucket_container requires bucket to be nothrow "
305 for (size_type i = 0; i < size(); ++i) {
306 traits_::destroy(allocator_, &buckets_[i]);
308 bucket_allocator_.deallocate(buckets_, size());
313 void move_or_copy(size_type dst_ind, size_type dst_slot, bucket &src,
314 size_type src_slot, std::true_type) {
315 setKV(dst_ind, dst_slot, src.partial(src_slot), src.movable_key(src_slot),
316 std::move(src.mapped(src_slot)));
319 void move_or_copy(size_type dst_ind, size_type dst_slot, bucket &src,
320 size_type src_slot, std::false_type) {
321 setKV(dst_ind, dst_slot, src.partial(src_slot), src.key(src_slot),
322 src.mapped(src_slot));
326 bucket_pointer transfer(
328 typename std::conditional<B, bucket_container &,
329 const bucket_container &>::type src,
330 std::integral_constant<bool, B> move) {
331 assert(dst_hp >= src.hashpower());
332 if (src.is_deallocated()) {
335 bucket_container dst(dst_hp, get_allocator());
337 for (
size_t i = 0; i < src.size(); ++i) {
338 for (
size_t j = 0; j < SLOT_PER_BUCKET; ++j) {
339 if (src.buckets_[i].occupied(j)) {
340 dst.move_or_copy(i, j, src.buckets_[i], j, move);
345 bucket_pointer dst_pointer = dst.buckets_;
346 dst.buckets_ =
nullptr;
352 allocator_type allocator_;
356 typename traits_::template rebind_alloc<bucket> bucket_allocator_;
359 std::atomic<size_type> hashpower_;
362 bucket_pointer buckets_;
370 template <
typename ThisKey,
typename ThisT>
371 friend typename std::enable_if<std::is_trivial<ThisKey>::value &&
372 std::is_trivial<ThisT>::value,
373 std::ostream &>::type
374 operator<<(std::ostream &os,
375 const bucket_container<ThisKey, ThisT, Allocator,
376 Partial, SLOT_PER_BUCKET> &bc) {
377 size_type hp = bc.hashpower();
378 os.write(
reinterpret_cast<const char *
>(&hp),
sizeof(size_type));
379 os.write(
reinterpret_cast<const char *
>(bc.buckets_),
380 sizeof(bucket) * bc.size());
384 template <
typename ThisKey,
typename ThisT>
385 friend typename std::enable_if<std::is_trivial<ThisKey>::value &&
386 std::is_trivial<ThisT>::value,
387 std::istream &>::type
388 operator>>(std::istream &is,
389 bucket_container<ThisKey, ThisT, Allocator,
390 Partial, SLOT_PER_BUCKET> &bc) {
392 is.read(
reinterpret_cast<char *
>(&hp),
sizeof(size_type));
393 bucket_container new_bc(hp, bc.get_allocator());
394 is.read(
reinterpret_cast<char *
>(new_bc.buckets_),
395 new_bc.size() *
sizeof(bucket));
403 #endif // BUCKET_CONTAINER_H