Merge commit 'f1087e81ecdca5a59ba5ffca684c955c5b38f7c2' as 'third_party/unordered_dense'
This commit is contained in:
+75
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#define ANKERL_NANOBENCH_IMPLEMENT
|
||||
#include <third-party/nanobench.h>
|
||||
+19
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user