Skip to content

Commit

Permalink
Add inplace_vector as specialization of small_vector
Browse files Browse the repository at this point in the history
  • Loading branch information
isidorostsa committed Nov 5, 2024
1 parent fb989a0 commit 606ec90
Showing 1 changed file with 99 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ namespace hpx::detail {

// note: Allocator is currently unused
template <typename T, std::size_t MinInlineCapacity,
typename Allocator = std::allocator<T>>
typename Allocator = std::allocator<T>, bool emulate_inplace_vector = false>
class small_vector
{
static_assert(MinInlineCapacity <= 127,
Expand Down Expand Up @@ -249,7 +249,8 @@ namespace hpx::detail {

[[nodiscard]] constexpr auto is_direct() const noexcept -> bool
{
return (m_data[0] & 1U) != 0U;
if constexpr (emulate_inplace_vector) return true;
else return (m_data[0] & 1U) != 0U;
}

[[nodiscard]] auto indirect() noexcept -> storage<T>*
Expand Down Expand Up @@ -310,6 +311,7 @@ namespace hpx::detail {

void realloc(std::size_t new_capacity)
{
static_assert(!emulate_inplace_vector, "If called in an inplace_vector, it is a bug.");
if (new_capacity <= N)
{
// put everything into direct storage
Expand Down Expand Up @@ -539,9 +541,17 @@ namespace hpx::detail {
template <typename It>
void assign(It first, It last, std::forward_iterator_tag /*unused*/)
{
auto s = std::distance(first, last);
if constexpr (emulate_inplace_vector)
{
// Can not have an inplace_vector with a size larger than N
if (s <= capacity()) {
throw std::bad_alloc();
}
}

clear();

auto s = std::distance(first, last);
reserve(s);
std::uninitialized_copy(first, last, data());
set_size(s);
Expand Down Expand Up @@ -577,7 +587,7 @@ namespace hpx::detail {
// * source_begin <= target_begin
// * source_end onwards is uninitialized memory
//
// Destroys then empty elements in [source_begin, source_end)
// Destroys the empty elements in [source_begin, source_end)
auto shift_right(
T* source_begin, T* source_end, T* target_begin) noexcept
{
Expand All @@ -594,7 +604,7 @@ namespace hpx::detail {
std::destroy(source_begin, (std::min)(source_end, target_begin));
}

// makes space for uninitialized data of cout elements. Also updates
// makes space for uninitialized data of count elements. Also updates
// size.
template <direction D>
[[nodiscard]] auto make_uninitialized_space_new(
Expand Down Expand Up @@ -710,12 +720,26 @@ namespace hpx::detail {
std::size_t count, T const& value, Allocator const& = Allocator())
: small_vector()
{
if constexpr (emulate_inplace_vector)
{
// Can not have an inplace_vector with a size larger than N
if (count > N) {
throw std::bad_alloc();
}
}
resize(count, value);
}

explicit small_vector(std::size_t count, Allocator const& = Allocator())
: small_vector()
{
if constexpr (emulate_inplace_vector)
{
// Can not have an inplace_vector with a size larger than N
if (count > N) {
throw std::bad_alloc();
}
}
reserve(count);
if (is_direct())
{
Expand All @@ -741,7 +765,14 @@ namespace hpx::detail {
: small_vector()
{
auto s = other.size();
reserve(s);
// If both vectors are direct with the same capacity it will fit
// without further allocations needed.

if constexpr (!emulate_inplace_vector)
{
reserve(s);
}

std::uninitialized_copy(other.begin(), other.end(), begin());
set_size(s);
}
Expand Down Expand Up @@ -816,6 +847,13 @@ namespace hpx::detail {

void resize(std::size_t count)
{
if constexpr (emulate_inplace_vector) {
// Static vector cannot be resized beyond capacity
if (count > N) {
throw std::bad_alloc();
}
}

if (count > capacity())
{
reserve(count);
Expand All @@ -833,6 +871,13 @@ namespace hpx::detail {

void resize(std::size_t count, value_type const& value)
{
if constexpr (emulate_inplace_vector) {
// Static vector cannot resize beyond capacity
if (count > N) {
throw std::bad_alloc();
}
}

if (count > capacity())
{
reserve(count);
Expand All @@ -850,6 +895,12 @@ namespace hpx::detail {

auto reserve(std::size_t s)
{
if constexpr (emulate_inplace_vector) {
// Static vector cannot reserve beyond capacity
if (s > N) {
throw std::bad_alloc();
}
}
auto const old_capacity = capacity();
auto const new_capacity = calculate_new_capacity(s, old_capacity);
if (new_capacity > old_capacity)
Expand All @@ -860,6 +911,9 @@ namespace hpx::detail {

[[nodiscard]] constexpr auto capacity() const noexcept -> std::size_t
{
if constexpr (emulate_inplace_vector) {
return capacity<direction::direct>();
}
if (is_direct())
{
return capacity<direction::direct>();
Expand Down Expand Up @@ -898,7 +952,15 @@ namespace hpx::detail {
if (is_direct())
{
s = direct_size();
if (s < N)
if constexpr (emulate_inplace_vector) {
// Exceeded static_storage
if (s + 1 > N) {
throw std::bad_alloc();
}
}

// avoid double-checking for inplace_vector
if (emulate_inplace_vector || s < N)
{
set_direct_and_size(s + 1);
return *hpx::construct_at(
Expand Down Expand Up @@ -1097,6 +1159,7 @@ namespace hpx::detail {

[[nodiscard]] static constexpr auto max_size() -> std::size_t
{
if constexpr (emulate_inplace_vector) return N;
return (std::numeric_limits<std::size_t>::max)();
}

Expand All @@ -1110,6 +1173,12 @@ namespace hpx::detail {
{
// per the standard we wouldn't need to do anything here. But since
// we are so nice, let's do the shrink.

if constexpr (emulate_inplace_vector) {
// Can not change the capacity of a static vector so noop
return;
}

auto const c = capacity();
auto const s = size();
if (s >= c)
Expand All @@ -1130,6 +1199,12 @@ namespace hpx::detail {
template <typename... Args>
auto emplace(const_iterator pos, Args&&... args) -> iterator
{
if constexpr(emulate_inplace_vector) {
// it will be expanded by one element
if (direct_size() + 1 > N) {
throw std::bad_alloc();
}
}
auto* p = make_uninitialized_space(pos, 1);
return hpx::construct_at(
static_cast<T*>(p), HPX_FORWARD(Args, args)...);
Expand All @@ -1148,6 +1223,11 @@ namespace hpx::detail {
auto insert(const_iterator pos, std::size_t count, T const& value)
-> iterator
{
if constexpr(emulate_inplace_vector) {
if (direct_size() + count > N) {
throw std::bad_alloc();
}
}
auto* p = make_uninitialized_space(pos, count);
std::uninitialized_fill_n(p, count, value);
return p;
Expand All @@ -1170,6 +1250,8 @@ namespace hpx::detail {
auto s = size();
while (first != last)
{
// if we are emulating inplace_vector, the out-of-bounds
// emplacement is caught in emplace_back
emplace_back(*first);
++first;
}
Expand All @@ -1185,7 +1267,13 @@ namespace hpx::detail {
auto insert(const_iterator pos, It first, It last,
std::forward_iterator_tag /*unused*/)
{
auto* p = make_uninitialized_space(pos, std::distance(first, last));
auto d = std::distance(first, last);
if constexpr(emulate_inplace_vector) {
if (direct_size() + d > N) {
throw std::bad_alloc();
}
}
auto* p = make_uninitialized_space(pos, d);
std::uninitialized_copy(first, last, p);
return p;
}
Expand Down Expand Up @@ -1262,6 +1350,9 @@ namespace hpx::detail {
{
return !(a > b);
}

template <typename T, std::size_t MinInlineCapacity, typename Allocator = std::allocator<T>>
using inplace_vector = small_vector<T, MinInlineCapacity, Allocator, true>;
} // namespace hpx::detail

// NOLINTNEXTLINE(cert-dcl58-cpp)
Expand Down

0 comments on commit 606ec90

Please sign in to comment.