Skip to content

Commit

Permalink
Refactored buffer and buffer_view (#74)
Browse files Browse the repository at this point in the history
Refactored buffer and buffer_view
  • Loading branch information
JohanMabille authored Apr 18, 2024
1 parent 9999445 commit a1e1b95
Show file tree
Hide file tree
Showing 7 changed files with 1,142 additions and 331 deletions.
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

0 comments on commit a1e1b95

Please sign in to comment.