From 1371371fbcff4ae6c9c391d67b83d3b2f02f9000 Mon Sep 17 00:00:00 2001 From: Martin Leitner-Ankerl Date: Thu, 8 Sep 2022 11:49:32 +0200 Subject: [PATCH] Fixes deallocation of nullptr bucket --- CMakeLists.txt | 2 +- include/ankerl/unordered_dense.h | 8 +++++--- meson.build | 2 +- test/unit/namespace.cpp | 6 +++--- test/unit/pmr.cpp | 31 +++++++++++++++++++++++++++++++ 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 633719bd..1aeb87f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12) project("unordered_dense" - VERSION 1.3.0 + VERSION 1.3.1 DESCRIPTION "A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion" HOMEPAGE_URL "https://github.com/martinus/unordered_dense") diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h index 30ccc6a3..220c65b8 100644 --- a/include/ankerl/unordered_dense.h +++ b/include/ankerl/unordered_dense.h @@ -1,7 +1,7 @@ ///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// // A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. -// Version 1.3.0 +// Version 1.3.1 // https://github.com/martinus/unordered_dense // // Licensed under the MIT License . @@ -32,7 +32,7 @@ // see https://semver.org/spec/v2.0.0.html #define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 1 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes #define ANKERL_UNORDERED_DENSE_VERSION_MINOR 3 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality -#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes +#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 1 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes // API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ #define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch @@ -530,7 +530,9 @@ class table { void deallocate_buckets() { auto ba = bucket_alloc(m_values.get_allocator()); - bucket_alloc_traits::deallocate(ba, m_buckets, bucket_count()); + if (nullptr != m_buckets) { + bucket_alloc_traits::deallocate(ba, m_buckets, bucket_count()); + } m_buckets = nullptr; m_num_buckets = 0; m_max_bucket_capacity = 0; diff --git a/meson.build b/meson.build index 2f9c1ce0..65ca1daa 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ # project('unordered_dense', 'cpp', - version: '1.3.0', + version: '1.3.1', license: 'MIT', default_options : ['cpp_std=c++17', 'warning_level=3', 'werror=true']) diff --git a/test/unit/namespace.cpp b/test/unit/namespace.cpp index f78920e8..e9f1a158 100644 --- a/test/unit/namespace.cpp +++ b/test/unit/namespace.cpp @@ -2,10 +2,10 @@ #include -static_assert(std::is_same_v, ankerl::unordered_dense::map>); -static_assert(std::is_same_v, ankerl::unordered_dense::hash>); +static_assert(std::is_same_v, ankerl::unordered_dense::map>); +static_assert(std::is_same_v, ankerl::unordered_dense::hash>); TEST_CASE("version_namespace") { - auto map = ankerl::unordered_dense::v1_3_0::map{}; + auto map = ankerl::unordered_dense::v1_3_1::map{}; REQUIRE(map.empty()); } diff --git a/test/unit/pmr.cpp b/test/unit/pmr.cpp index 31be4091..24608dee 100644 --- a/test/unit/pmr.cpp +++ b/test/unit/pmr.cpp @@ -5,6 +5,7 @@ #include // for size_t #include // for uint64_t +#include // thrown in no_null_memory_resource #include // for string_view #include // for move #include // for vector @@ -29,6 +30,26 @@ class logging_memory_resource : public std::pmr::memory_resource { } }; +class no_null_memory_resource : public std::pmr::memory_resource { + auto do_allocate(std::size_t bytes, std::size_t alignment) -> void* override { + if (bytes == 0) { + throw std::runtime_error("no_null_memory_resource::do_allocate should not do_allocate 0"); + } + return std::pmr::new_delete_resource()->allocate(bytes, alignment); + } + + void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override { + if (nullptr == p || 0U == bytes || 0U == alignment) { + throw std::runtime_error("no_null_memory_resource::do_deallocate should not deallocate with any 0 value"); + } + return std::pmr::new_delete_resource()->deallocate(p, bytes, alignment); + } + + [[nodiscard]] auto do_is_equal(const std::pmr::memory_resource& other) const noexcept -> bool override { + return this == &other; + } +}; + class track_peak_memory_resource : public std::pmr::memory_resource { uint64_t m_peak = 0; uint64_t m_current = 0; @@ -99,6 +120,16 @@ TEST_CASE("pmr") { REQUIRE(mr.current() == 0); } +TEST_CASE("pmr_no_null") { + auto mr = no_null_memory_resource(); + { + auto map = ankerl::unordered_dense::pmr::map(&mr); + for (size_t i = 0; i < 1; ++i) { + map[i] = i; + } + } +} + void show([[maybe_unused]] track_peak_memory_resource const& mr, [[maybe_unused]] std::string_view name) { // fmt::print("{}: {} allocs, {} deallocs, {} is_equals\n", name, mr.num_allocs(), mr.num_deallocs(), mr.num_is_equals()); }