diff options
Diffstat (limited to 'src/util')
| -rw-r--r-- | src/util/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | src/util/include/bloom_filter.hpp | 40 | ||||
| -rw-r--r-- | src/util/include/lru_cache.hpp | 69 | ||||
| -rw-r--r-- | src/util/include/random.hpp | 39 | ||||
| -rw-r--r-- | src/util/random.cpp | 37 |
5 files changed, 192 insertions, 0 deletions
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt new file mode 100644 index 00000000..0903a912 --- /dev/null +++ b/src/util/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright 2023 jacqueline <me@jacqueline.id.au> +# +# SPDX-License-Identifier: GPL-3.0-only + +idf_component_register( + INCLUDE_DIRS "include" + REQUIRES "database") diff --git a/src/util/include/bloom_filter.hpp b/src/util/include/bloom_filter.hpp new file mode 100644 index 00000000..04b72e20 --- /dev/null +++ b/src/util/include/bloom_filter.hpp @@ -0,0 +1,40 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <bitset> +#include <cstdint> +#include <functional> + +namespace util { + +template <typename T> +class BloomFilter { + public: + explicit BloomFilter(std::function<uint64_t(T)> hasher) + : hasher_(hasher), bits_() {} + + auto Insert(T val) -> void { + uint64_t hash = std::invoke(hasher_, val); + bits_[hash & 0xFFFF] = 1; + bits_[(hash >> 16) & 0xFFFF] = 1; + bits_[(hash >> 32) & 0xFFFF] = 1; + bits_[(hash >> 48) & 0xFFFF] = 1; + } + + auto Contains(T val) -> bool { + uint64_t hash = std::invoke(hasher_, val); + return bits_[hash & 0xFFFF] && bits_[(hash >> 16) & 0xFFFF] && + bits_[(hash >> 32) & 0xFFFF] && bits_[(hash >> 48) & 0xFFFF]; + } + + private: + std::function<uint64_t(T)> hasher_; + std::bitset<(1 << 16)> bits_; +}; + +} // namespace util diff --git a/src/util/include/lru_cache.hpp b/src/util/include/lru_cache.hpp new file mode 100644 index 00000000..8f955a07 --- /dev/null +++ b/src/util/include/lru_cache.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <algorithm> +#include <bitset> +#include <cstdint> +#include <list> +#include <optional> +#include <unordered_map> +#include <utility> + +namespace util { + +/* + * Basic least recently used cache. Stores the `Size` most recently accessed + * entries in memory. + * + * Not safe for use from multiple tasks, but all operations are constant time. + */ +template <int Size, typename K, typename V> +class LruCache { + public: + LruCache() : entries_(), key_to_it_() {} + + auto Put(K key, V val) -> void { + if (key_to_it_.contains(key)) { + // This key was already present. Overwrite by removing the previous + // value. + entries_.erase(key_to_it_[key]); + key_to_it_.erase(key); + } else if (entries_.size() >= Size) { + // Cache is full. Evict the last entry. + key_to_it_.erase(entries_.back().first); + entries_.pop_back(); + } + + // Add the new value. + entries_.push_front({key, val}); + key_to_it_[key] = entries_.begin(); + } + + auto Get(K key) -> std::optional<V> { + if (!key_to_it_.contains(key)) { + return {}; + } + // Use splice() to move the entry to the front of the list. This approach + // doesn't invalidate any of the iterators in key_to_it_, and is constant + // time. + auto it = key_to_it_[key]; + entries_.splice(entries_.begin(), entries_, it); + return it->second; + } + + auto Clear() -> void { + entries_.clear(); + key_to_it_.clear(); + } + + private: + std::list<std::pair<K, V>> entries_; + std::unordered_map<K, decltype(entries_.begin())> key_to_it_; +}; + +} // namespace util diff --git a/src/util/include/random.hpp b/src/util/include/random.hpp new file mode 100644 index 00000000..cd8ab1c6 --- /dev/null +++ b/src/util/include/random.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <cstdint> + +#include "komihash.h" + +namespace util { + +class IRandom { + public: + virtual ~IRandom() {} + + virtual auto Next() -> std::uint64_t = 0; + virtual auto RangeInclusive(std::uint64_t lower, std::uint64_t upper) + -> std::uint64_t = 0; +}; + +extern IRandom* sRandom; + +class Random : public IRandom { + public: + Random(); + + auto Next() -> std::uint64_t override; + auto RangeInclusive(std::uint64_t lower, std::uint64_t upper) + -> std::uint64_t override; + + private: + std::uint64_t seed1_; + std::uint64_t seed2_; +}; + +} // namespace util diff --git a/src/util/random.cpp b/src/util/random.cpp new file mode 100644 index 00000000..ae543765 --- /dev/null +++ b/src/util/random.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "random.hpp" + +#include <cstdint> + +#include "esp_random.h" +#include "komihash.h" + +namespace util { + +IRandom* sRandom = new Random(); + +Random::Random() { + esp_fill_random(&seed1_, sizeof(seed1_)); + seed2_ = seed1_; + + // komirand needs four iterations to properly self-start. + for (int i = 0; i < 4; i++) { + Next(); + } +} + +auto Random::Next() -> std::uint64_t { + return komirand(&seed1_, &seed2_); +} + +auto Random::RangeInclusive(std::uint64_t lower, std::uint64_t upper) + -> std::uint64_t { + return (Next() % (upper - lower + 1)) + lower; +} + +} // namespace util |
