Merge commit 'f1087e81ecdca5a59ba5ffca684c955c5b38f7c2' as 'third_party/unordered_dense'

This commit is contained in:
Siarhei Fedartsou
2024-05-30 19:06:16 +02:00
2383 changed files with 16243 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
#pragma once
#include <app/counter.h>
#include <cstddef>
#include <cstdint>
#include <string_view>
namespace checksum {
// final step from MurmurHash3
[[nodiscard]] static inline auto mix(uint64_t k) -> uint64_t {
k ^= k >> 33U;
k *= 0xff51afd7ed558ccdULL;
k ^= k >> 33U;
k *= 0xc4ceb9fe1a85ec53ULL;
k ^= k >> 33U;
return k;
}
[[maybe_unused]] [[nodiscard]] static inline auto mix(std::string_view data) -> uint64_t {
static constexpr uint64_t fnv_offset_basis = UINT64_C(14695981039346656037);
static constexpr uint64_t fnv_prime = UINT64_C(1099511628211);
uint64_t val = fnv_offset_basis;
for (auto c : data) {
val ^= static_cast<uint64_t>(c);
val *= fnv_prime;
}
return val;
}
[[maybe_unused]] [[nodiscard]] static inline auto mix(counter::obj const& cdv) -> uint64_t {
return mix(cdv.get());
}
// from boost::hash_combine, with additional fmix64 of value
[[maybe_unused]] [[nodiscard]] static inline auto combine(uint64_t seed, uint64_t value) -> uint64_t {
return seed ^ (value + 0x9e3779b9 + (seed << 6U) + (seed >> 2U));
}
// calculates a hash of any iterable map. Order is irrelevant for the hash's result, as it simply
// xors the elements together.
template <typename M>
[[nodiscard]] auto map(const M& map) -> uint64_t {
uint64_t combined_hash = 1;
uint64_t num_elements = 0;
for (auto const& entry : map) {
auto entry_hash = combine(mix(entry.first), mix(entry.second));
combined_hash ^= entry_hash;
++num_elements;
}
return combine(combined_hash, num_elements);
}
// map of maps
template <typename MM>
[[nodiscard]] auto mapmap(const MM& mapmap) -> uint64_t {
uint64_t combined_hash = 1;
uint64_t num_elements = 0;
for (auto const& entry : mapmap) {
auto entry_hash = combine(mix(entry.first), map(entry.second));
combined_hash ^= entry_hash;
++num_elements;
}
return combine(combined_hash, num_elements);
}
} // namespace checksum
+254
View File
@@ -0,0 +1,254 @@
#include <app/counter.h>
#include <app/print.h> // for print
#include <cstdlib> // for abort
#include <ostream> // for ostream
#include <stdexcept> // for runtime_error
#include <unordered_set> // for unordered_set
#include <utility> // for swap, pair
static inline constexpr bool counter_enable_unordered_set = true;
auto singleton_constructed_objects() -> std::unordered_set<counter::obj const*>& {
static std::unordered_set<counter::obj const*> static_data{};
return static_data;
}
counter::obj::obj()
: m_data(0)
, m_counts(nullptr) {
if constexpr (counter_enable_unordered_set) {
if (!singleton_constructed_objects().emplace(this).second) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
++static_default_ctor;
}
counter::obj::obj(const size_t& data, counter& counts)
: m_data(data)
, m_counts(&counts) {
if constexpr (counter_enable_unordered_set) {
if (!singleton_constructed_objects().emplace(this).second) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
++m_counts->m_data.m_ctor;
}
counter::obj::obj(const counter::obj& o)
: m_data(o.m_data)
, m_counts(o.m_counts) {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().count(&o)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
if (!singleton_constructed_objects().emplace(this).second) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
if (nullptr != m_counts) {
++m_counts->m_data.m_copy_ctor;
}
}
counter::obj::obj(counter::obj&& o) noexcept
: m_data(o.m_data)
, m_counts(o.m_counts) {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().count(&o)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
if (!singleton_constructed_objects().emplace(this).second) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
if (nullptr != m_counts) {
++m_counts->m_data.m_move_ctor;
}
}
counter::obj::~obj() {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().erase(this)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
if (nullptr != m_counts) {
++m_counts->m_data.m_dtor;
} else {
++static_dtor;
}
}
auto counter::obj::operator==(obj const& o) const -> bool {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
if (nullptr != m_counts) {
++m_counts->m_data.m_equals;
}
return m_data == o.m_data;
}
auto counter::obj::operator<(obj const& o) const -> bool {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
if (nullptr != m_counts) {
++m_counts->m_data.m_less;
}
return m_data < o.m_data;
}
// NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp)
auto counter::obj::operator=(obj const& o) -> counter::obj& {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
m_counts = o.m_counts;
if (nullptr != m_counts) {
++m_counts->m_data.m_assign;
}
m_data = o.m_data;
return *this;
}
auto counter::obj::operator=(obj&& o) noexcept -> counter::obj& {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
if (nullptr != o.m_counts) {
m_counts = o.m_counts;
}
m_data = o.m_data;
if (nullptr != m_counts) {
++m_counts->m_data.m_move_assign;
}
return *this;
}
auto counter::obj::get() const -> size_t const& {
if (nullptr != m_counts) {
++m_counts->m_data.m_const_get;
}
return m_data;
}
auto counter::obj::get() -> size_t& {
if (nullptr != m_counts) {
++m_counts->m_data.m_get;
}
return m_data;
}
void counter::obj::swap(obj& other) {
if constexpr (counter_enable_unordered_set) {
if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&other)) {
test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__);
std::abort();
}
}
using std::swap;
swap(m_data, other.m_data);
swap(m_counts, other.m_counts);
if (nullptr != m_counts) {
++m_counts->m_data.m_swaps;
}
}
auto counter::obj::get_for_hash() const -> size_t {
if (nullptr != m_counts) {
++m_counts->m_data.m_hash;
}
return m_data;
}
counter::counter() {
counter::static_default_ctor = 0;
counter::static_dtor = 0;
}
void counter::check_all_done() const {
if constexpr (counter_enable_unordered_set) {
// check that all are destructed
if (!singleton_constructed_objects().empty()) {
test::print("ERROR at ~counter(): got {} objects still alive!", singleton_constructed_objects().size());
std::abort();
}
if (m_data.m_dtor + static_dtor !=
m_data.m_ctor + static_default_ctor + m_data.m_copy_ctor + m_data.m_default_ctor + m_data.m_move_ctor) {
test::print("ERROR at ~counter(): number of counts does not match!\n");
test::print(
"{} dtor + {} staticDtor != {} ctor + {} staticDefaultCtor + {} copyCtor + {} defaultCtor + {} moveCtor\n",
m_data.m_dtor,
static_dtor,
m_data.m_ctor,
static_default_ctor,
m_data.m_copy_ctor,
m_data.m_default_ctor,
m_data.m_move_ctor);
std::abort();
}
}
}
counter::~counter() {
check_all_done();
}
auto counter::total() const -> size_t {
return m_data.m_ctor + static_default_ctor + m_data.m_copy_ctor + (m_data.m_dtor + static_dtor) + m_data.m_equals +
m_data.m_less + m_data.m_assign + m_data.m_swaps + m_data.m_get + m_data.m_const_get + m_data.m_hash +
m_data.m_move_ctor + m_data.m_move_assign;
}
void counter::operator()(std::string_view title) {
m_records += fmt::format("{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}|{:9}| {}\n",
m_data.m_ctor,
static_default_ctor,
m_data.m_copy_ctor,
m_data.m_dtor + static_dtor,
m_data.m_assign,
m_data.m_swaps,
m_data.m_get,
m_data.m_const_get,
m_data.m_hash,
m_data.m_equals,
m_data.m_less,
m_data.m_move_ctor,
m_data.m_move_assign,
total(),
title);
}
auto operator<<(std::ostream& os, counter const& c) -> std::ostream& {
return os << c.m_records;
}
auto operator new(size_t /*unused*/, counter::obj* /*unused*/) -> void* {
throw std::runtime_error("operator new overload is taken! Cast to void* to ensure the void pointer overload is taken.");
}
size_t counter::static_default_ctor = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
size_t counter::static_dtor = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
+170
View File
@@ -0,0 +1,170 @@
#pragma once
#include <fmt/core.h> // for format_context, format_parse_context, format_to
#include <cstddef> // for size_t
#include <functional> // for hash
#include <iosfwd> // for ostream
#include <string> // for allocator, string
#include <string_view> // for hash, string_view
#include <type_traits>
class counter {
public:
struct data_t {
size_t m_ctor{};
size_t m_default_ctor{};
size_t m_copy_ctor{};
size_t m_dtor{};
size_t m_assign{};
size_t m_swaps{};
size_t m_get{};
size_t m_const_get{};
size_t m_hash{};
size_t m_equals{};
size_t m_less{};
size_t m_move_ctor{};
size_t m_move_assign{};
friend auto operator==(data_t const& a, data_t const& b) -> bool {
static_assert(std::has_unique_object_representations_v<data_t>);
return 0 == std::memcmp(&a, &b, sizeof(data_t));
}
friend auto operator!=(data_t const& a, data_t const& b) -> bool {
return !(a == b);
}
};
counter(counter const&) = delete;
counter(counter&&) = delete;
auto operator=(counter const&) -> counter& = delete;
auto operator=(counter&&) -> counter&& = delete;
// Obj for only swaps & equals. Used for optimizing.
// Can't use static counters here because I want to do it in parallel.
class obj {
public:
// required for operator[]
obj();
obj(const size_t& data, counter& counts);
obj(const obj& o);
obj(obj&& o) noexcept;
~obj();
auto operator==(const obj& o) const -> bool;
auto operator<(const obj& o) const -> bool;
auto operator=(const obj& o) -> obj&;
auto operator=(obj&& o) noexcept -> obj&;
[[nodiscard]] auto get() const -> size_t const&;
auto get() -> size_t&;
void swap(obj& other);
[[nodiscard]] auto get_for_hash() const -> size_t;
private:
size_t m_data;
counter* m_counts;
};
counter();
~counter();
void check_all_done() const;
[[nodiscard]] auto ctor() const -> size_t {
return m_data.m_ctor;
}
[[nodiscard]] auto default_ctor() const -> size_t {
return m_data.m_default_ctor;
}
[[nodiscard]] auto copy_ctor() const -> size_t {
return m_data.m_copy_ctor;
}
[[nodiscard]] auto dtor() const -> size_t {
return m_data.m_dtor;
}
[[nodiscard]] auto equals() const -> size_t {
return m_data.m_equals;
}
[[nodiscard]] auto less() const -> size_t {
return m_data.m_less;
}
[[nodiscard]] auto assign() const -> size_t {
return m_data.m_assign;
}
[[nodiscard]] auto swaps() const -> size_t {
return m_data.m_swaps;
}
[[nodiscard]] auto get() const -> size_t {
return m_data.m_get;
}
[[nodiscard]] auto const_get() const -> size_t {
return m_data.m_const_get;
}
[[nodiscard]] auto hash() const -> size_t {
return m_data.m_hash;
}
[[nodiscard]] auto move_ctor() const -> size_t {
return m_data.m_move_ctor;
}
[[nodiscard]] auto move_assign() const -> size_t {
return m_data.m_move_assign;
}
[[nodiscard]] auto data() const -> data_t const& {
return m_data;
}
friend auto operator<<(std::ostream& os, counter const& c) -> std::ostream&;
void operator()(std::string_view title);
[[nodiscard]] auto total() const -> size_t;
static size_t static_default_ctor; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
static size_t static_dtor; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
private:
data_t m_data{};
std::string m_records =
"\n ctor defctor cpyctor dtor assign swaps get cnstget hash equals less ctormv assignmv| total |\n";
};
// Throws an exception, this overload should never be taken!
inline auto operator new(size_t s, counter::obj* ptr) -> void*;
namespace std {
template <>
struct hash<counter::obj> {
[[nodiscard]] auto operator()(const counter::obj& c) const noexcept -> size_t {
return hash<size_t>{}(c.get_for_hash());
}
};
} // namespace std
template <>
struct fmt::formatter<counter::obj> {
static constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.end();
}
static auto format(counter::obj const& o, fmt::format_context& ctx) -> decltype(ctx.out()) {
return fmt::format_to(ctx.out(), "{}", o.get());
}
};
@@ -0,0 +1,138 @@
#pragma once
#include <fmt/ostream.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <limits>
#include <vector>
// Source: https://github.com/bitcoin/bitcoin/blob/master/src/memusage.h#L41-L61
static inline auto malloc_usage(size_t alloc) -> size_t {
static_assert(sizeof(void*) == 8 || sizeof(void*) == 4);
// Measured on libc6 2.19 on Linux.
if constexpr (sizeof(void*) == 8U) {
return ((alloc + 31U) >> 4U) << 4U;
} else {
return ((alloc + 15U) >> 3U) << 3U;
}
}
class counts_for_allocator {
struct measurement_internal {
std::chrono::steady_clock::time_point m_tp{};
size_t m_diff{};
};
struct measurement {
std::chrono::steady_clock::duration m_duration{};
size_t m_num_bytes_allocated{};
};
std::vector<measurement_internal> m_measurements{};
std::chrono::steady_clock::time_point m_start = std::chrono::steady_clock::now();
template <typename Op>
void each_measurement(Op op) const {
auto total_bytes = size_t();
auto const start_time = m_start;
for (auto const& m : m_measurements) {
bool is_add = true;
size_t bytes = m.m_diff;
if (bytes > (0U - bytes)) {
// negative number
is_add = false;
bytes = 0U - bytes;
}
if (is_add) {
total_bytes += malloc_usage(bytes);
} else {
total_bytes -= malloc_usage(bytes);
}
op(measurement{m.m_tp - start_time, total_bytes});
}
}
public:
void add(size_t count) {
m_measurements.emplace_back(measurement_internal{std::chrono::steady_clock::now(), count});
}
void sub(size_t count) {
// overflow, but it's ok
m_measurements.emplace_back(measurement_internal{std::chrono::steady_clock::now(), 0U - count});
}
void save(std::filesystem::path const& filename) const {
auto fout = std::ofstream(filename);
each_measurement([&](measurement m) {
fmt::print(fout, "{}; {}\n", std::chrono::duration<double>(m.m_duration).count(), m.m_num_bytes_allocated);
});
}
[[nodiscard]] auto size() const -> size_t {
return m_measurements.size();
}
void reset() {
m_measurements.clear();
m_start = std::chrono::steady_clock::now();
}
};
/**
* Forwards all allocations/deallocations to the counts
*/
template <class T>
class counting_allocator {
counts_for_allocator* m_counts;
template <typename U>
friend class counting_allocator;
public:
using value_type = T;
/**
* Not explicit so we can easily construct it with the correct resource
*/
counting_allocator(counts_for_allocator* counts) noexcept // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
: m_counts(counts) {}
/**
* Not explicit so we can easily construct it with the correct resource
*/
template <class U>
// NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
counting_allocator(counting_allocator<U> const& other) noexcept
: m_counts(other.m_counts) {}
counting_allocator(counting_allocator const& other) noexcept = default;
counting_allocator(counting_allocator&& other) noexcept = default;
auto operator=(counting_allocator const& other) noexcept -> counting_allocator& = default;
auto operator=(counting_allocator&& other) noexcept -> counting_allocator& = default;
~counting_allocator() = default;
auto allocate(size_t n) -> T* {
m_counts->add(sizeof(T) * n);
return std::allocator<T>{}.allocate(n);
}
void deallocate(T* p, size_t n) noexcept {
m_counts->sub(sizeof(T) * n);
std::allocator<T>{}.deallocate(p, n);
}
template <class U>
friend auto operator==(counting_allocator const& a, counting_allocator<U> const& b) noexcept -> bool {
return a.m_counts == b.m_counts;
}
template <class U>
friend auto operator!=(counting_allocator const& a, counting_allocator<U> const& b) noexcept -> bool {
return a.m_counts != b.m_counts;
}
};
+10
View File
@@ -0,0 +1,10 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest.h>
namespace doctest {
[[nodiscard]] auto current_test_name() -> char const* {
return doctest::detail::g_cs->currentTest->m_name;
}
} // namespace doctest
+120
View File
@@ -0,0 +1,120 @@
#pragma once
#include <ankerl/unordered_dense.h>
#include <app/counter.h>
#include <doctest.h>
#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
# undef DOCTEST_REQUIRE
# define DOCTEST_REQUIRE(...) \
do { \
if (!(__VA_ARGS__)) { \
std::abort(); \
} \
} while (0)
#endif
namespace doctest {
[[nodiscard]] auto current_test_name() -> char const*;
} // namespace doctest
#include <deque>
#include <sstream>
template <class Key,
class T,
class Hash = ankerl::unordered_dense::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class AllocatorOrContainer = std::deque<std::pair<Key, T>>,
class Bucket = ankerl::unordered_dense::bucket_type::standard>
class deque_map : public ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, false> {
using base_t = ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, false>;
using base_t::base_t;
};
template <class Key,
class Hash = ankerl::unordered_dense::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class AllocatorOrContainer = std::deque<Key>,
class Bucket = ankerl::unordered_dense::bucket_type::standard>
class deque_set
: public ankerl::unordered_dense::detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket, false> {
using base_t = ankerl::unordered_dense::detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket, false>;
using base_t::base_t;
};
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define TEST_CASE_MAP(name, ...) \
TEST_CASE_TEMPLATE(name, \
map_t, \
ankerl::unordered_dense::map<__VA_ARGS__>, \
ankerl::unordered_dense::segmented_map<__VA_ARGS__>, \
deque_map<__VA_ARGS__>)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define TEST_CASE_SET(name, ...) \
TEST_CASE_TEMPLATE(name, \
set_t, \
ankerl::unordered_dense::set<__VA_ARGS__>, \
ankerl::unordered_dense::segmented_set<__VA_ARGS__>, \
deque_set<__VA_ARGS__>)
#define TYPE_TO_STRING_MAP(...) /*NOLINT*/ \
TYPE_TO_STRING(ankerl::unordered_dense::map<__VA_ARGS__>); /*NOLINT*/ \
TYPE_TO_STRING(ankerl::unordered_dense::segmented_map<__VA_ARGS__>); /*NOLINT*/ \
TYPE_TO_STRING(deque_map<__VA_ARGS__>) /*NOLINT*/
#define TYPE_TO_STRING_SET(...) /*NOLINT*/ \
TYPE_TO_STRING(ankerl::unordered_dense::set<__VA_ARGS__>); /*NOLINT*/ \
TYPE_TO_STRING(ankerl::unordered_dense::segmented_set<__VA_ARGS__>); /*NOLINT*/ \
TYPE_TO_STRING(deque_set<__VA_ARGS__>) /*NOLINT*/
#if defined(ANKERL_UNORDERED_DENSE_PMR)
// unfortunately there's no std::experimental::pmr::deque on macos, so just skip this here
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
# define TEST_CASE_PMR_MAP(name, ...) \
TEST_CASE_TEMPLATE(name, \
map_t, \
ankerl::unordered_dense::pmr::map<__VA_ARGS__>, \
ankerl::unordered_dense::pmr::segmented_map<__VA_ARGS__>)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
# define TEST_CASE_PMR_SET(name, ...) \
TEST_CASE_TEMPLATE(name, \
set_t, \
ankerl::unordered_dense::pmr::set<__VA_ARGS__>, \
ankerl::unordered_dense::pmr::segmented_set<__VA_ARGS__>)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
# define TYPE_TO_STRING_PMR_MAP(...) \
TYPE_TO_STRING(ankerl::unordered_dense::pmr::map<__VA_ARGS__>); /*NOLINT*/ \
TYPE_TO_STRING(ankerl::unordered_dense::pmr::segmented_map<__VA_ARGS__>) /*NOLINT*/
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
# define TYPE_TO_STRING_PMR_SET(...) \
TYPE_TO_STRING(ankerl::unordered_dense::pmr::set<__VA_ARGS__>); /*NOLINT*/ \
TYPE_TO_STRING(ankerl::unordered_dense::pmr::segmented_set<__VA_ARGS__>) /*NOLINT*/
#endif
// adds the most important type to strings here
TYPE_TO_STRING_MAP(counter::obj, counter::obj);
TYPE_TO_STRING_MAP(int, char const*);
TYPE_TO_STRING_MAP(int, int);
TYPE_TO_STRING_MAP(int, std::string);
TYPE_TO_STRING_MAP(std::string, size_t);
TYPE_TO_STRING_MAP(std::string, std::string);
TYPE_TO_STRING_MAP(uint64_t, uint64_t);
TYPE_TO_STRING_MAP(uint32_t, int);
TYPE_TO_STRING_MAP(uint64_t, int);
TYPE_TO_STRING_SET(counter::obj);
TYPE_TO_STRING_SET(int);
TYPE_TO_STRING_SET(std::string);
TYPE_TO_STRING_SET(uint32_t);
TYPE_TO_STRING_SET(uint64_t);
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <cmath>
#include <iterator>
#include <utility>
template <typename It, typename Op>
[[nodiscard]] auto geomean(It begin, It end, Op op) -> double {
double sum = 0.0;
size_t count = 0;
while (begin != end) {
sum += std::log(op(*begin));
++begin;
++count;
}
sum /= static_cast<double>(count);
return std::exp(sum);
}
template <typename Container, typename Op>
[[nodiscard]] auto geomean(Container&& c, Op op) -> double {
return geomean(std::begin(c), std::end(c), std::move(op));
}
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <string_view>
namespace detail {
template <typename T>
[[nodiscard]] constexpr auto name_of_type_raw() -> std::string_view {
#if defined(_MSC_VER)
return __FUNCSIG__; // NOLINT
#else
return __PRETTY_FUNCTION__; // NOLINT
#endif
}
} // namespace detail
template <typename T>
[[nodiscard]] constexpr auto name_of_type() -> std::string_view {
using namespace std::literals;
// idea from https://github.com/TheLartians/StaticTypeInfo/blob/master/include/static_type_info/type_name.h
auto for_double = detail::name_of_type_raw<double>();
auto n_before = for_double.find("double"sv);
auto n_after = for_double.size() - (n_before + "double"sv.size());
auto str = detail::name_of_type_raw<T>();
str.remove_prefix(n_before);
str.remove_suffix(n_after);
return str;
}
+2
View File
@@ -0,0 +1,2 @@
#define ANKERL_NANOBENCH_IMPLEMENT
#include <third-party/nanobench.h>
+19
View File
@@ -0,0 +1,19 @@
#include <fmt/format.h>
#include <cstdio>
namespace test {
template <typename... Args>
constexpr void print(fmt::format_string<Args...> f, Args&&... args) {
fmt::print(f, std::forward<Args>(args)...);
(void)std::fflush(stdout);
}
#ifndef ENABLE_LOG_LINE
# define LOG_LINE(what)
#else
# define LOG_LINE(what) ::test::print("{}({:3}) {}\n", __FILE__, __LINE__, what)
#endif
} // namespace test
+41
View File
@@ -0,0 +1,41 @@
#if __GNUC__
# include <fmt/format.h>
# include <array>
# include <csignal>
# include <cstdio>
# include <cstdlib>
# include <execinfo.h>
# include <unistd.h>
namespace {
void handle(int sig) {
fmt::print(stderr, "Error: signal {}:\n", sig);
auto ary = std::array<void*, 50>();
// get void*'s for all entries on the stack
auto size = backtrace(ary.data(), static_cast<int>(ary.size()));
// print out all the frames to stderr
fmt::print(stderr, "Error: signal {}. See stacktrace with\n", sig);
fmt::print(stderr, "addr2line -Cafpie ./test/udm-test");
for (size_t i = 0; i < static_cast<size_t>(size); ++i) {
fmt::print(stderr, " {}", ary[i]);
}
exit(1); // NOLINT(concurrency-mt-unsafe)
}
class handler {
public:
handler() {
(void)signal(SIGTERM, handle);
}
};
auto const global_h = handler();
} // namespace
#endif
+17
View File
@@ -0,0 +1,17 @@
#include "periodic.h"
namespace ui {
periodic::periodic(std::chrono::steady_clock::duration interval)
: m_interval(interval) {}
periodic::operator bool() {
auto now = std::chrono::steady_clock::now();
if (now < m_next) {
return false;
}
m_next = now + m_interval;
return true;
}
} // namespace ui
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <chrono>
namespace ui {
class periodic {
std::chrono::steady_clock::time_point m_next{};
std::chrono::steady_clock::duration m_interval{};
public:
explicit periodic(std::chrono::steady_clock::duration interval);
explicit operator bool();
};
} // namespace ui
@@ -0,0 +1,53 @@
#include "progress_bar.h"
#include <algorithm> // for min
namespace {
auto split(std::string_view symbols, char sep) -> std::vector<std::string> {
auto s = std::vector<std::string>();
while (true) {
auto idx = symbols.find(sep);
if (idx == std::string_view::npos) {
break;
}
s.emplace_back(symbols.substr(0, idx));
symbols.remove_prefix(idx + 1);
}
s.emplace_back(symbols);
return s;
}
} // namespace
namespace ui {
progress_bar::progress_bar(size_t width, size_t total, std::string_view symbols)
: m_width(width)
, m_total(total)
, m_symbols(split(symbols, ' ')) {}
auto progress_bar::operator()(size_t current) const -> std::string {
auto const total_states = m_width * m_symbols.size() + 1;
auto const current_state = total_states * current / m_total;
std::string str;
auto num_full = std::min(m_width, current_state / m_symbols.size());
for (size_t i = 0; i < num_full; ++i) {
str += m_symbols.back();
}
if (num_full < m_width) {
auto remaining = current_state - num_full * m_symbols.size();
if (0U != remaining) {
str += m_symbols[remaining - 1];
}
auto num_fillers = m_width - num_full - (0U == remaining ? 0 : 1);
for (size_t i = 0; i < num_fillers; ++i) {
str.push_back(' ');
}
}
return str;
}
} // namespace ui
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <cstddef> // for size_t
#include <string> // for string, basic_string
#include <string_view> // for string_view
#include <vector> // for vector
namespace ui {
class progress_bar {
size_t m_width;
size_t m_total;
std::vector<std::string> m_symbols;
public:
progress_bar(size_t width, size_t total, std::string_view symbols = "⡀ ⡄ ⡆ ⡇ ⡏ ⡟ ⡿ ⣿");
auto operator()(size_t current) const -> std::string;
};
} // namespace ui
@@ -0,0 +1,3 @@
#include <ankerl/unordered_dense.h>
// this cpp file is only for include-what-you-use