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
63 changes: 58 additions & 5 deletions src/support/inplace_vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
#ifndef wasm_support_inplace_vector_h
#define wasm_support_inplace_vector_h

#include <algorithm>
#include <array>
#include <cassert>
#include <iterator>
#include <type_traits>
#include <vector>

#include "support/parent_index_iterator.h"
Expand Down Expand Up @@ -134,9 +137,9 @@ template<typename T, size_t N> class inplace_vector {
Iterator(inplace_vector<T, N>* parent, size_t index)
: wasm::ParentIndexIterator<inplace_vector<T, N>*, Iterator>{parent,
index} {}
Iterator(const Iterator& other) = default;

T& operator*() { return (*this->parent)[this->index]; }
T* operator->() { return &(*this->parent)[this->index]; }
};

struct ConstIterator
Expand All @@ -151,20 +154,70 @@ template<typename T, size_t N> class inplace_vector {
ConstIterator(const ConstIterator& other) = default;

const T& operator*() const { return (*this->parent)[this->index]; }
const T* operator->() const { return &(*this->parent)[this->index]; }
};

Iterator begin() { return Iterator(this, 0); }
Iterator end() { return Iterator(this, size()); }
ConstIterator begin() const { return ConstIterator(this, 0); }
ConstIterator end() const { return ConstIterator(this, size()); }

void erase(Iterator a, Iterator b) {
// Atm we only support erasing at the end, which is very efficient.
assert(b == end());
resize(a.index);
Iterator erase(ConstIterator first, ConstIterator last) {
assert(first.index <= last.index);
assert(last.index <= usedFixed);
size_t numToErase = last.index - first.index;
if (numToErase > 0) {
std::move(fixed.begin() + last.index,
fixed.begin() + usedFixed,
fixed.begin() + first.index);
usedFixed -= numToErase;
}
return Iterator(this, first.index);
}

Iterator erase(Iterator first, Iterator last) {
return erase(ConstIterator(this, first.index),
ConstIterator(this, last.index));
}

Iterator erase(ConstIterator pos) { return erase(pos, pos + 1); }

Iterator erase(Iterator pos) { return erase(pos, pos + 1); }
};

namespace detail {

template<typename T> struct is_inplace_vector_or_derived {
private:
template<typename U, size_t N>
static std::true_type test(const inplace_vector<U, N>*);
static std::false_type test(...);

public:
static constexpr bool value = decltype(test(std::declval<T*>()))::value;
};

} // namespace detail

template<typename Vector, typename Pred>
requires detail::is_inplace_vector_or_derived<Vector>::value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're still hiding use of requires behind ifdefs so jukka's old system compilers still work... but if we're not testing that on CI, maybe we shouldn't worry about that beyond letting him fix things up as necessary.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I see the lattice code has such ifdefs, but we have other requires without? E.g.

requires std::is_same_v<FallbackGenerator, DefaultTypeNameGenerator>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Landing, if there is an issue later I can fix it up.

size_t erase_if(Vector& c, Pred pred) {
auto it = std::remove_if(c.begin(), c.end(), pred);
auto r = std::distance(it, c.end());
c.erase(it, c.end());
return r;
}

} // namespace wasm

namespace std {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of weird that you have to provide a new overload of a std function to support custom containers, but it looks like this is the standard practice?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't know how else to do this. The compiler errors without these overloads.


template<typename Vector, typename Pred>
requires wasm::detail::is_inplace_vector_or_derived<Vector>::value
size_t erase_if(Vector& c, Pred pred) {
return wasm::erase_if(c, pred);
}

} // namespace std

#endif // wasm_support_inplace_vector_h
42 changes: 42 additions & 0 deletions test/gtest/inplace_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,45 @@ TEST_F(InplaceVectorTest, I) {

EXPECT_EQ(normal, std::vector<int>({10, 20, 30}));
}

TEST_F(InplaceVectorTest, Erase) {
inplace_vector<int, 5> vec{10, 20, 30, 40, 50};

// Erase single element in the middle (30 at index 2)
auto it = vec.erase(vec.begin() + 2);
EXPECT_EQ(*it, 40);
EXPECT_EQ(vec.size(), 4u);
EXPECT_EQ(vec[0], 10);
EXPECT_EQ(vec[1], 20);
EXPECT_EQ(vec[2], 40);
EXPECT_EQ(vec[3], 50);

// Erase range at beginning [10, 20]
it = vec.erase(vec.begin(), vec.begin() + 2);
EXPECT_EQ(*it, 40);
EXPECT_EQ(vec.size(), 2u);
EXPECT_EQ(vec[0], 40);
EXPECT_EQ(vec[1], 50);

// Erase at end
it = vec.erase(vec.begin() + 1, vec.end());
EXPECT_EQ(it, vec.end());
EXPECT_EQ(vec.size(), 1u);
EXPECT_EQ(vec[0], 40);
}

TEST_F(InplaceVectorTest, EraseIf) {
// Test std::erase_if on inplace_vector
inplace_vector<int, 5> vec{1, 2, 3, 4, 5};
size_t erased = std::erase_if(vec, [](int x) { return x % 2 == 0; });
EXPECT_EQ(erased, 2u);
EXPECT_EQ(vec.size(), 3u);
EXPECT_EQ(vec[0], 1);
EXPECT_EQ(vec[1], 3);
EXPECT_EQ(vec[2], 5);

// Erase remaining elements
erased = std::erase_if(vec, [](int x) { return x > 0; });
EXPECT_EQ(erased, 3u);
EXPECT_TRUE(vec.empty());
}
Loading