Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored buffer and buffer_view #74

Merged
merged 10 commits into from
Apr 18, 2024
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ set(SPARROW_HEADERS
${SPARROW_INCLUDE_DIR}/sparrow/allocator.hpp
${SPARROW_INCLUDE_DIR}/sparrow/array_data.hpp
${SPARROW_INCLUDE_DIR}/sparrow/buffer.hpp
${SPARROW_INCLUDE_DIR}/sparrow/buffer_view.hpp
${SPARROW_INCLUDE_DIR}/sparrow/data_type.hpp
${SPARROW_INCLUDE_DIR}/sparrow/data_traits.hpp
${SPARROW_INCLUDE_DIR}/sparrow/dynamic_bitset.hpp
Expand Down
182 changes: 115 additions & 67 deletions include/sparrow/allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
#include <cstdint>
#include <memory>
#include <memory_resource>
#include <typeindex>
#include <type_traits>
#include <typeindex>
#include <variant>

namespace sparrow
Expand All @@ -29,25 +29,29 @@ namespace sparrow
*/
template <class T>
concept allocator = std::copy_constructible<std::remove_cvref_t<T>>
and std::move_constructible<std::remove_cvref_t<T>>
and std::equality_comparable<std::remove_cvref_t<T>>
and requires(std::remove_cvref_t<T>& alloc,
typename std::remove_cvref_t<T>::value_type* p,
std::size_t n)
{
typename std::remove_cvref_t<T>::value_type;
{ alloc.allocate(n) } -> std::same_as<typename std::remove_cvref_t<T>::value_type*>;
{ alloc.deallocate(p, n) } -> std::same_as<void>;
};
and std::move_constructible<std::remove_cvref_t<T>>
and std::equality_comparable<std::remove_cvref_t<T>>
and requires(
std::remove_cvref_t<T>& alloc,
typename std::remove_cvref_t<T>::value_type* p,
std::size_t n
) {
typename std::remove_cvref_t<T>::value_type;
{
alloc.allocate(n)
} -> std::same_as<typename std::remove_cvref_t<T>::value_type*>;
{ alloc.deallocate(p, n) } -> std::same_as<void>;
};

/*
* When the allocator A with value_type T satisfies this concept, any_allocator
* can store it as a value in a small buffer instead of having to type-erased it
* (Small Buffer Optimization).
*/
template <class A, class T>
concept can_any_allocator_sbo = allocator<A> &&
(std::same_as<A, std::allocator<T>> || std::same_as<A, std::pmr::polymorphic_allocator<T>>);
concept can_any_allocator_sbo = allocator<A>
&& (std::same_as<A, std::allocator<T>>
|| std::same_as<A, std::pmr::polymorphic_allocator<T>>);

/*
* Type erasure class for allocators. This allows to use any kind of allocator
Expand All @@ -71,18 +75,17 @@ namespace sparrow

template <class A>
any_allocator(A&& alloc)
requires (not std::same_as<std::remove_cvref_t<A>, any_allocator>
and sparrow::allocator<A>)
requires(not std::same_as<std::remove_cvref_t<A>, any_allocator> and sparrow::allocator<A>)
: m_storage(make_storage(std::forward<A>(alloc)))
{
}

[[nodiscard]] T* allocate(std::size_t n);
void deallocate(T* p, std::size_t n);

any_allocator select_on_container_copy_construction() const;

bool equal(const any_allocator& rhs) const;
bool equal(const any_allocator& rhs) const;

private:

Expand All @@ -100,7 +103,10 @@ namespace sparrow
{
A m_alloc;

explicit impl(A alloc) : m_alloc(std::move(alloc)) {}
explicit impl(A alloc)
: m_alloc(std::move(alloc))
{
}

[[nodiscard]] T* allocate(std::size_t n) override
{
Expand All @@ -127,12 +133,8 @@ namespace sparrow
}
};

using storage_type = std::variant
<
std::allocator<T>,
std::pmr::polymorphic_allocator<T>,
std::unique_ptr<interface>
>;
using storage_type = std::
variant<std::allocator<T>, std::pmr::polymorphic_allocator<T>, std::unique_ptr<interface>>;

template <class A>
std::unique_ptr<interface> make_storage(A&& alloc) const
Expand All @@ -141,35 +143,56 @@ namespace sparrow
}

template <class A>
requires can_any_allocator_sbo<A, T>
requires can_any_allocator_sbo<A, T>
A&& make_storage(A&& alloc) const
{
return std::forward<A>(alloc);
}

template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};
// Although not required in C++20, clang needs it to build the code below
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

storage_type copy_storage(const storage_type& rhs) const
{
return std::visit(overloaded {
[](const auto& arg) -> storage_type { return { std::decay_t<decltype(arg)>(arg) }; },
[](const std::unique_ptr<interface>& arg) -> storage_type { return { arg->clone() }; }
}, rhs);
return std::visit(
overloaded{
[](const auto& arg) -> storage_type
{
return {std::decay_t<decltype(arg)>(arg)};
},
[](const std::unique_ptr<interface>& arg) -> storage_type
{
return {arg->clone()};
}
},
rhs
);
}

template <class F>
decltype(auto) visit_storage(F&& f)
{
return std::visit([&f](auto&& arg)
{
using A = std::decay_t<decltype(arg)>;
if constexpr (can_any_allocator_sbo<A, T>)
return f(arg);
else
return f(*arg);
}, m_storage);
return std::visit(
[&f](auto&& arg)
{
using A = std::decay_t<decltype(arg)>;
if constexpr (can_any_allocator_sbo<A, T>)
{
return f(arg);
}
else
{
return f(*arg);
}
},
m_storage
);
}

storage_type m_storage;
Expand All @@ -194,13 +217,23 @@ namespace sparrow
template <class T>
[[nodiscard]] T* any_allocator<T>::allocate(std::size_t n)
{
return visit_storage([n](auto& allocator) { return allocator.allocate(n); });
return visit_storage(
[n](auto& allocator)
{
return allocator.allocate(n);
}
);
}

template <class T>
void any_allocator<T>::deallocate(T* p, std::size_t n)
{
return visit_storage([n, p](auto& allocator) { return allocator.deallocate(p, n); });
return visit_storage(
[n, p](auto& allocator)
{
return allocator.deallocate(p, n);
}
);
}

template <class T>
Expand All @@ -213,33 +246,49 @@ namespace sparrow
bool any_allocator<T>::equal(const any_allocator& rhs) const
{
// YOLO!!
return std::visit([&rhs](auto&& arg)
{
using A = std::decay_t<decltype(arg)>;
if constexpr (can_any_allocator_sbo<A, T>)
return std::visit(
[&rhs](auto&& arg)
{
return std::visit([&arg](auto&& arg2)
using A = std::decay_t<decltype(arg)>;
if constexpr (can_any_allocator_sbo<A, T>)
{
using A2 = std::decay_t<decltype(arg2)>;
if constexpr (can_any_allocator_sbo<A2, T> && std::same_as<A, A2>)
return arg == arg2;
else
return false;
}, rhs.m_storage);
}
else
{
return std::visit([&arg](auto&& arg2)
return std::visit(
[&arg](auto&& arg2)
{
using A2 = std::decay_t<decltype(arg2)>;
if constexpr (can_any_allocator_sbo<A2, T> && std::same_as<A, A2>)
{
return arg == arg2;
}
else
{
return false;
}
},
rhs.m_storage
);
}
else
{
using A2 = std::decay_t<decltype(arg2)>;
if constexpr (can_any_allocator_sbo<A2, T>)
return false;
else
return arg->equal(*arg2);
}, rhs.m_storage);
}

}, m_storage);
return std::visit(
[&arg](auto&& arg2)
{
using A2 = std::decay_t<decltype(arg2)>;
if constexpr (can_any_allocator_sbo<A2, T>)
{
return false;
}
else
{
return arg->equal(*arg2);
}
},
rhs.m_storage
);
}
},
m_storage
);
}

template <class T>
Expand All @@ -248,4 +297,3 @@ namespace sparrow
return lhs.equal(rhs);
}
}

Loading
Loading