Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions include/tsl/sparse_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,9 @@ class sparse_array {
m_capacity(0),
m_last_array(false) {}

//needed for "is_constructible" with no parameters
sparse_array(std::allocator_arg_t, Allocator const&) noexcept : sparse_array() {}

explicit sparse_array(bool last_bucket) noexcept
: m_values(nullptr),
m_bitmap_vals(0),
Expand All @@ -445,21 +448,24 @@ class sparse_array {
m_capacity(0),
m_last_array(last_bucket) {}

sparse_array(size_type capacity, Allocator &alloc)
//const Allocator needed for MoveInsertable requirement
sparse_array(size_type capacity, Allocator const &const_alloc)
: m_values(nullptr),
m_bitmap_vals(0),
m_bitmap_deleted_vals(0),
m_nb_elements(0),
m_capacity(capacity),
m_last_array(false) {
if (m_capacity > 0) {
auto alloc = const_cast<Allocator&>(const_alloc);
m_values = alloc.allocate(m_capacity);
tsl_sh_assert(m_values !=
nullptr); // allocate should throw if there is a failure
}
}

sparse_array(const sparse_array &other, Allocator &alloc)
//const Allocator needed for MoveInsertable requirement
sparse_array(const sparse_array &other, Allocator const &const_alloc)
: m_values(nullptr),
m_bitmap_vals(other.m_bitmap_vals),
m_bitmap_deleted_vals(other.m_bitmap_deleted_vals),
Expand All @@ -471,6 +477,7 @@ class sparse_array {
return;
}

auto alloc = const_cast<Allocator&>(const_alloc);
m_values = alloc.allocate(m_capacity);
tsl_sh_assert(m_values !=
nullptr); // allocate should throw if there is a failure
Expand Down Expand Up @@ -499,7 +506,8 @@ class sparse_array {
other.m_capacity = 0;
}

sparse_array(sparse_array &&other, Allocator &alloc)
//const Allocator needed for MoveInsertable requirement
sparse_array(sparse_array &&other, Allocator const &const_alloc)
: m_values(nullptr),
m_bitmap_vals(other.m_bitmap_vals),
m_bitmap_deleted_vals(other.m_bitmap_deleted_vals),
Expand All @@ -511,6 +519,7 @@ class sparse_array {
return;
}

auto alloc = const_cast<Allocator&>(const_alloc);
m_values = alloc.allocate(m_capacity);
tsl_sh_assert(m_values !=
nullptr); // allocate should throw if there is a failure
Expand Down
4 changes: 3 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ add_executable(tsl_sparse_map_tests "main.cpp"
"fancy_pointer/sparse_hash_set_tests.cpp"
"fancy_pointer/CustomAllocator.h"
"fancy_pointer/sparse_hash_map_tests.cpp"
"../include/tsl/boost_offset_pointer.h")
"../include/tsl/boost_offset_pointer.h"
"scoped_allocator_adaptor/sparse_array_tests.cpp"
"scoped_allocator_adaptor/sparse_hash_set_tests.cpp")

target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11)

Expand Down
138 changes: 138 additions & 0 deletions tests/scoped_allocator_adaptor/sparse_array_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <memory>
#include <scoped_allocator>
#include <tsl/sparse_set.h>
#include <type_traits>
#include <unordered_set>

// Globals
constexpr auto MAX_INDEX = 32; // BITMAP_NB_BITS

template <typename T> void compilation() { typename T::Array test; }

template <typename T> void construction() {
typename T::Allocator a;
typename T::Array test(MAX_INDEX, a);
test.clear(a);
}

template <typename T>
void set(std::initializer_list<typename T::value_type> l) {
typename T::Allocator a;
typename T::Array array(MAX_INDEX, a);
std::vector<typename T::value_type> check;
check.reserve(l.size());
std::size_t counter = 0;
for (auto const &value : l) {
array.set(a, counter++, value);
check.emplace_back(value);
}
BOOST_TEST_REQUIRE(std::equal(array.begin(), array.end(), check.begin()),
"'set' did not create the correct order of items");
array.clear(a);
}

template <typename T> void uses_allocator() {
BOOST_TEST_REQUIRE(
(std::uses_allocator<typename T::Array, typename T::Allocator>::value),
"uses_allocator returns false");
}

template <typename T, typename... Args>
void trailing_allocator_convention(Args...) {
using Alloc = typename T::Allocator;
BOOST_TEST_REQUIRE(
(std::is_constructible<typename T::Array, Args..., const Alloc &>::value),
"trailing_allocator thinks construction is not possible");
}

template <typename T> void trailing_allocator_convention_without_parameters() {
using Alloc = typename std::allocator_traits<
typename T::Allocator>::template rebind_alloc<typename T::Array>;
BOOST_TEST_REQUIRE(
(std::is_constructible<typename T::Array, const Alloc &>::value),
"trailing_allocator thinks construction is not possible");
}

/** This test creates a memory leak.
* However it is only one single sparse_array and with this, the test is
* simpler.
*/
template <typename T>
void is_move_insertable(std::initializer_list<typename T::value_type> l) {
using A = typename std::allocator_traits<
typename T::Allocator>::template rebind_alloc<typename T::Array>;
A m;
auto p = m.allocate(1);
typename T::Allocator ArrayAlloc;
typename T::Array rv(MAX_INDEX, ArrayAlloc);
std::size_t counter = 0;
for (auto const &value : l) {
rv.set(ArrayAlloc, counter++, value);
}
std::cout << "Before\n";
std::allocator_traits<A>::construct(m, p, std::move(rv));
std::cout << "After\n";
rv.clear(ArrayAlloc);
}

/** This test creates a memory leak.
* However it is only one single sparse_array and with this, the test is
* simpler.
*/
template <typename T> void is_default_insertable() {
using A = typename std::allocator_traits<
typename T::Allocator>::template rebind_alloc<typename T::Array>;
A m;
typename T::Array *p = m.allocate(1);
std::allocator_traits<A>::construct(m, p);
}

template <typename T, tsl::sh::sparsity Sparsity = tsl::sh::sparsity::medium>
struct NORMAL {
using value_type = T;
using Allocator = std::allocator<T>;
using Array = tsl::detail_sparse_hash::sparse_array<T, Allocator, Sparsity>;
};

template <typename T, tsl::sh::sparsity Sparsity = tsl::sh::sparsity::medium>
struct SCOPED {
using value_type = T;
using Allocator = std::scoped_allocator_adaptor<std::allocator<T>>;
using Array = tsl::detail_sparse_hash::sparse_array<T, Allocator, Sparsity>;
};

BOOST_AUTO_TEST_SUITE(scoped_allocators)
BOOST_AUTO_TEST_SUITE(sparse_array_tests)

BOOST_AUTO_TEST_CASE(normal_compilation) { compilation<NORMAL<int>>(); }
BOOST_AUTO_TEST_CASE(normal_construction) { construction<NORMAL<int>>(); }
BOOST_AUTO_TEST_CASE(normal_set) { set<NORMAL<int>>({0, 1, 2, 3, 4}); }
BOOST_AUTO_TEST_CASE(normal_uses_allocator) { uses_allocator<NORMAL<int>>(); }
BOOST_AUTO_TEST_CASE(normal_trailing_allocator_convention) {
trailing_allocator_convention<NORMAL<int>>(0);
}
BOOST_AUTO_TEST_CASE(normal_is_move_insertable) {
is_move_insertable<NORMAL<int>>({0, 1, 2, 3, 4, 5});
}
BOOST_AUTO_TEST_CASE(normal_is_default_insertable) {
is_default_insertable<NORMAL<int>>();
}

BOOST_AUTO_TEST_CASE(scoped_compilation) { compilation<SCOPED<int>>(); }
BOOST_AUTO_TEST_CASE(scoped_construction) { construction<SCOPED<int>>(); }
BOOST_AUTO_TEST_CASE(scoped_set) { set<SCOPED<int>>({0, 1, 2, 3, 4}); }
BOOST_AUTO_TEST_CASE(scoped_uses_allocator) { uses_allocator<SCOPED<int>>(); }
BOOST_AUTO_TEST_CASE(scoped_trailing_allocator_convention) {
trailing_allocator_convention<SCOPED<int>>(0);
}
BOOST_AUTO_TEST_CASE(scoped_is_move_insertable) {
is_move_insertable<SCOPED<int>>({0, 1, 2, 3, 4, 5});
}
BOOST_AUTO_TEST_CASE(scoped_is_default_insertable) {
is_default_insertable<SCOPED<int>>();
}

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
51 changes: 51 additions & 0 deletions tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <boost/test/unit_test.hpp>
#include <tsl/sparse_hash.h>
#include <scoped_allocator>

namespace details {
template <typename Key> struct KeySelect {
using key_type = Key;
const key_type &operator()(Key const &key) const noexcept { return key; }
key_type &operator()(Key &key) noexcept { return key; }
};

template <typename T, typename Alloc>
using sparse_set = tsl::detail_sparse_hash::sparse_hash<
T, details::KeySelect<T>, void, std::hash<T>, std::equal_to<T>, Alloc,
tsl::sh::power_of_two_growth_policy<2>,
tsl::sh::exception_safety::basic,
tsl::sh::sparsity::medium,
tsl::sh::probing::quadratic>;
} // namespace details

template <typename T> void construction() {
using Type = typename T::value_type;
typename T::Set(T::Set::DEFAULT_INIT_BUCKET_COUNT, std::hash<Type>(),
std::equal_to<Type>(), typename T::Allocator(),
T::Set::DEFAULT_MAX_LOAD_FACTOR);
}


template <typename T>
struct NORMAL {
using value_type = T;
using Allocator = std::allocator<T>;
using Set = details::sparse_set<T, Allocator>;
};

template <typename T>
struct SCOPED {
using value_type = T;
using Allocator = std::scoped_allocator_adaptor<std::allocator<T>>;
using Set = details::sparse_set<T, Allocator>;
};

BOOST_AUTO_TEST_SUITE(scoped_allocators)
BOOST_AUTO_TEST_SUITE(sparse_hash_set_tests)

BOOST_AUTO_TEST_CASE(normal_construction){construction<NORMAL<int>>();}

BOOST_AUTO_TEST_CASE(scoped_construction){construction<SCOPED<int>>();}

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()