Skip to content

Commit

Permalink
Fixes deallocation of nullptr bucket
Browse files Browse the repository at this point in the history
  • Loading branch information
martinus committed Sep 8, 2022
1 parent e1088b3 commit 1371371
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 8 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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")

Expand Down
8 changes: 5 additions & 3 deletions include/ankerl/unordered_dense.h
Original file line number Diff line number Diff line change
@@ -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 <http://opensource.org/licenses/MIT>.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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'])

Expand Down
6 changes: 3 additions & 3 deletions test/unit/namespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

#include <doctest.h>

static_assert(std::is_same_v<ankerl::unordered_dense::v1_3_0::map<int, int>, ankerl::unordered_dense::map<int, int>>);
static_assert(std::is_same_v<ankerl::unordered_dense::v1_3_0::hash<int>, ankerl::unordered_dense::hash<int>>);
static_assert(std::is_same_v<ankerl::unordered_dense::v1_3_1::map<int, int>, ankerl::unordered_dense::map<int, int>>);
static_assert(std::is_same_v<ankerl::unordered_dense::v1_3_1::hash<int>, ankerl::unordered_dense::hash<int>>);

TEST_CASE("version_namespace") {
auto map = ankerl::unordered_dense::v1_3_0::map<int, int>{};
auto map = ankerl::unordered_dense::v1_3_1::map<int, int>{};
REQUIRE(map.empty());
}
31 changes: 31 additions & 0 deletions test/unit/pmr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <cstddef> // for size_t
#include <cstdint> // for uint64_t
#include <stdexcept> // thrown in no_null_memory_resource
#include <string_view> // for string_view
#include <utility> // for move
#include <vector> // for vector
Expand All @@ -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;
Expand Down Expand Up @@ -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<uint64_t, uint64_t>(&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());
}
Expand Down

0 comments on commit 1371371

Please sign in to comment.