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
55 changes: 49 additions & 6 deletions base/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@ inline OutputIt Copy(InputIt first, InputIt last, OutputIt dest) {
return dest;
}

// ── Prev / Next ─────────────────────────────────────────────────────
// The iterator before / after `it`, which is left alone.
template <typename Iter>
inline Iter Prev(Iter it) {
--it;
return it;
}
template <typename Iter>
inline Iter Next(Iter it) {
++it;
return it;
}

// ── CopyN ───────────────────────────────────────────────────────────
template <typename InputIt, typename OutputIt>
inline OutputIt CopyN(InputIt first, mem_size count, OutputIt dest) {
for (; count; --count, ++first, ++dest) {
*dest = *first;
}
return dest;
}

// ── Find ────────────────────────────────────────────────────────────
template <typename Iter, typename T>
inline Iter Find(Iter first, Iter last, const T& value) {
Expand Down Expand Up @@ -68,6 +90,12 @@ inline bool AnyOf(Iter first, Iter last, Pred pred) {
return false;
}

// ── NoneOf ──────────────────────────────────────────────────────────
template <typename Iter, typename Pred>
inline bool NoneOf(Iter first, Iter last, Pred pred) {
return !AnyOf(first, last, pred);
}

// ── AllOf ───────────────────────────────────────────────────────────
template <typename Iter, typename Pred>
inline bool AllOf(Iter first, Iter last, Pred pred) {
Expand Down Expand Up @@ -280,14 +308,29 @@ inline T* Unique(T* first, T* last) {
// ── EraseIf (container-level erase-remove) ──────────────────────────
// Drops every element matching `pred` and returns how many went. Takes the
// container, not a range, so the caller does not hand-write erase(remove_if()).
// Contiguous containers compact in one pass; node containers (maps, sets)
// erase element by element, handing `pred` each element as iteration does.
template <typename TContainer, typename Pred>
inline mem_size EraseIf(TContainer& container, Pred pred) {
auto* first = container.begin();
auto* last = container.end();
auto* kept = base::RemoveIf(first, last, pred);
const mem_size removed = static_cast<mem_size>(last - kept);
if (removed > 0) container.erase(kept, last);
return removed;
if constexpr (base::is_pointer_v<decltype(container.begin())>) {
auto* first = container.begin();
auto* last = container.end();
auto* kept = base::RemoveIf(first, last, pred);
const mem_size removed = static_cast<mem_size>(last - kept);
if (removed > 0) container.erase(kept, last);
return removed;
} else {
mem_size removed = 0;
for (auto it = container.begin(); it != container.end();) {
if (pred(*it)) {
it = container.erase(it);
removed++;
} else {
++it;
}
}
return removed;
}
}

// ── LowerBound (binary search) ──────────────────────────────────────
Expand Down
58 changes: 58 additions & 0 deletions base/algorithm_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

#include <gtest/gtest.h>

#include <base/containers/array.h>
#include <base/containers/hash_map.h>
#include <base/containers/map.h>
#include <base/containers/vector.h>
#include <base/math/value_bounds.h>

#include <base/algorithm.h>
#include <base/containers/vector.h>

Expand Down Expand Up @@ -82,4 +88,56 @@ TEST(AlgorithmBounds, UpperBoundPlain) {
EXPECT_EQ(base::UpperBound(v, v + 5, 0), v);
EXPECT_EQ(base::UpperBound(v, v + 5, 5), v + 5);
}
TEST(AlgorithmAdditions, CopyNAndNoneOf) {
int src[4] = {1, 2, 3, 4};
int dst[4] = {};
EXPECT_EQ(base::CopyN(src, 3, dst), dst + 3);
EXPECT_EQ(dst[2], 3);
EXPECT_EQ(dst[3], 0);
EXPECT_TRUE(base::NoneOf(src, src + 4, [](int v) { return v > 4; }));
EXPECT_FALSE(base::NoneOf(src, src + 4, [](int v) { return v == 2; }));
}

TEST(AlgorithmAdditions, EraseIfOnANodeContainer) {
base::HashMap<int, int> map;
for (int i = 0; i < 10; i++)
map[i] = i;
EXPECT_EQ(base::EraseIf(map, [](const auto& kv) { return kv.second % 3 == 0; }),
4u);
EXPECT_EQ(map.size(), 6u);
EXPECT_FALSE(map.contains(3));
base::Vector<int> vec{1, 2, 3, 4};
EXPECT_EQ(base::EraseIf(vec, [](int v) { return v % 2 == 0; }), 2u);
EXPECT_EQ(vec.size(), 2u);
}

TEST(AlgorithmAdditions, MinMaxOfAList) {
EXPECT_EQ(base::Min({5, 2, 9}), 2);
EXPECT_EQ(base::Max({5, 2, 9}), 9);
EXPECT_EQ(base::Max<u64>({1, 7}), 7u);
}

TEST(AlgorithmAdditions, ArrayAndVectorOrderLexicographically) {
base::Array<int, 3> a{1, 2, 3}, b{1, 3, 0};
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
EXPECT_FALSE(a < a);
base::Vector<int> x{1, 2}, y{1, 2, 0};
EXPECT_TRUE(x < y);
EXPECT_FALSE(y < x);
EXPECT_TRUE((base::Vector<int>{0, 9} < base::Vector<int>{1}));
}

TEST(AlgorithmAdditions, PrevAndNextLeaveTheIteratorAlone) {
base::Map<int, int> map;
map[1] = 10;
map[2] = 20;
auto it = map.find(2);
EXPECT_EQ(base::Prev(it)->first, 1);
EXPECT_EQ(it->first, 2);
EXPECT_TRUE(base::Next(it) == map.end());
int v[3] = {1, 2, 3};
EXPECT_EQ(*base::Next(v), 2);
}

} // namespace
11 changes: 10 additions & 1 deletion base/containers/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,21 @@ class Array {

constexpr bool operator!=(const Array& other) const { return !(*this == other); }

// Lexicographic, so an Array can key an ordered container.
constexpr bool operator<(const Array& other) const {
for (mem_size i = 0; i < N; ++i) {
if (storage_[i] < other.storage_[i]) return true;
if (other.storage_[i] < storage_[i]) return false;
}
return false;
}

private:
Storage storage_{};
};

template <typename T, mem_size N>
mem_size ArraySize(T (&)[N]) {
constexpr mem_size ArraySize(const T (&)[N]) noexcept {
return N;
}
} // namespace base
11 changes: 10 additions & 1 deletion base/containers/array_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,13 @@ TEST(ArrayTest, DataAccessors) {
EXPECT_EQ(arr.back(), 30);
}

} // namespace base
TEST(ArrayTest, ArraySizeIsConstant) {
static constexpr int values[7] = {};
const char names[3][4] = {"a", "b", "c"};
static_assert(ArraySize(values) == 7);
Array<int, ArraySize(values) * 2> doubled;
EXPECT_EQ(doubled.size(), 14u);
EXPECT_EQ(ArraySize(names), 3u);
}

} // namespace base
Loading
Loading