Path: blob/master/thirdparty/manifold/src/hashtable.h
9906 views
// Copyright 2022 The Manifold Authors.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.13#pragma once14#include <stdint.h>1516#include <atomic>1718#include "utils.h"19#include "vec.h"2021namespace {22using hash_fun_t = uint64_t(uint64_t);23inline constexpr uint64_t kOpen = std::numeric_limits<uint64_t>::max();2425template <typename T>26T AtomicCAS(T& target, T compare, T val) {27std::atomic<T>& tar = reinterpret_cast<std::atomic<T>&>(target);28tar.compare_exchange_strong(compare, val, std::memory_order_acq_rel);29return compare;30}3132template <typename T>33void AtomicStore(T& target, T val) {34std::atomic<T>& tar = reinterpret_cast<std::atomic<T>&>(target);35// release is good enough, although not really something general36tar.store(val, std::memory_order_release);37}3839template <typename T>40T AtomicLoad(const T& target) {41const std::atomic<T>& tar = reinterpret_cast<const std::atomic<T>&>(target);42// acquire is good enough, although not general43return tar.load(std::memory_order_acquire);44}4546} // namespace4748namespace manifold {4950template <typename V, hash_fun_t H = hash64bit>51class HashTableD {52public:53HashTableD(Vec<uint64_t>& keys, Vec<V>& values, std::atomic<size_t>& used,54uint32_t step = 1)55: step_{step}, keys_{keys}, values_{values}, used_{used} {}5657int Size() const { return keys_.size(); }5859bool Full() const {60return used_.load(std::memory_order_relaxed) * 2 >61static_cast<size_t>(Size());62}6364void Insert(uint64_t key, const V& val) {65uint32_t idx = H(key) & (Size() - 1);66while (1) {67if (Full()) return;68uint64_t& k = keys_[idx];69const uint64_t found = AtomicCAS(k, kOpen, key);70if (found == kOpen) {71used_.fetch_add(1, std::memory_order_relaxed);72values_[idx] = val;73return;74}75if (found == key) return;76idx = (idx + step_) & (Size() - 1);77}78}7980V& operator[](uint64_t key) {81uint32_t idx = H(key) & (Size() - 1);82while (1) {83const uint64_t k = AtomicLoad(keys_[idx]);84if (k == key || k == kOpen) {85return values_[idx];86}87idx = (idx + step_) & (Size() - 1);88}89}9091const V& operator[](uint64_t key) const {92uint32_t idx = H(key) & (Size() - 1);93while (1) {94const uint64_t k = AtomicLoad(keys_[idx]);95if (k == key || k == kOpen) {96return values_[idx];97}98idx = (idx + step_) & (Size() - 1);99}100}101102uint64_t KeyAt(int idx) const { return AtomicLoad(keys_[idx]); }103V& At(int idx) { return values_[idx]; }104const V& At(int idx) const { return values_[idx]; }105106private:107uint32_t step_;108VecView<uint64_t> keys_;109VecView<V> values_;110std::atomic<size_t>& used_;111};112113template <typename V, hash_fun_t H = hash64bit>114class HashTable {115public:116HashTable(size_t size, uint32_t step = 1)117: keys_{size == 0 ? 0 : 1_uz << (int)ceil(log2(size)), kOpen},118values_{size == 0 ? 0 : 1_uz << (int)ceil(log2(size)), {}},119step_(step) {}120121HashTable(const HashTable& other)122: keys_(other.keys_), values_(other.values_), step_(other.step_) {123used_.store(other.used_.load());124}125126HashTable& operator=(const HashTable& other) {127if (this == &other) return *this;128keys_ = other.keys_;129values_ = other.values_;130used_.store(other.used_.load());131step_ = other.step_;132return *this;133}134135HashTableD<V, H> D() { return {keys_, values_, used_, step_}; }136137int Entries() const { return used_.load(std::memory_order_relaxed); }138139size_t Size() const { return keys_.size(); }140141bool Full() const {142return used_.load(std::memory_order_relaxed) * 2 > Size();143}144145double FilledFraction() const {146return static_cast<double>(used_.load(std::memory_order_relaxed)) / Size();147}148149Vec<V>& GetValueStore() { return values_; }150151static uint64_t Open() { return kOpen; }152153private:154Vec<uint64_t> keys_;155Vec<V> values_;156std::atomic<size_t> used_ = 0;157uint32_t step_;158};159} // namespace manifold160161162