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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ if(WIN32)
# resolve to the same *W types minwin.h forward-declares (otherwise the two
# disagree and MSVC reports "redefinition; different basic types").
target_compile_definitions(eq_base PUBLIC OS_WIN NOMINMAX UNICODE _UNICODE)
target_link_libraries(eq_base PRIVATE psapi)
target_link_libraries(eq_base PRIVATE psapi synchronization)
if(MSVC)
target_compile_options(eq_base PUBLIC "/utf-8")
endif()
Expand Down
35 changes: 34 additions & 1 deletion base/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ inline mem_size CountIf(Iter first, Iter last, Pred pred) {
return n;
}

// ── RemoveIf (unstable - swaps to end) ──────────────────────────────
// ── RemoveIf (keeps the order of what it keeps, like std::remove_if) ──
template <typename Iter, typename Pred>
inline Iter RemoveIf(Iter first, Iter last, Pred pred) {
Iter result = first;
Expand Down Expand Up @@ -303,4 +303,37 @@ inline T* LowerBound(T* first, T* last, const T& value) {
return first;
}

// First element for which comp(element, value) is false: std::lower_bound
// with a comparator, which may compare an element against a different type
// (a record against its key).
template <typename T, typename V, typename Comp>
inline T* LowerBound(T* first, T* last, const V& value, Comp comp) {
while (first < last) {
T* mid = first + (last - first) / 2;
if (comp(*mid, value))
first = mid + 1;
else
last = mid;
}
return first;
}

// First element for which comp(value, element) is true: std::upper_bound.
template <typename T, typename V, typename Comp>
inline T* UpperBound(T* first, T* last, const V& value, Comp comp) {
while (first < last) {
T* mid = first + (last - first) / 2;
if (!comp(value, *mid))
first = mid + 1;
else
last = mid;
}
return first;
}

template <typename T>
inline T* UpperBound(T* first, T* last, const T& value) {
return UpperBound(first, last, value, [](const T& a, const T& b) { return a < b; });
}

} // namespace base
26 changes: 26 additions & 0 deletions base/algorithm_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,29 @@ TEST(AlgorithmTest, SortOrdersAscending) {
}

} // namespace

namespace {
struct Keyed {
i32 key;
i32 payload;
};

TEST(AlgorithmBounds, LowerAndUpperBoundWithComparator) {
Keyed items[] = {{1, 0}, {3, 1}, {3, 2}, {3, 3}, {7, 4}};
auto before = [](const Keyed& k, i32 v) { return k.key < v; };
auto after = [](i32 v, const Keyed& k) { return v < k.key; };
EXPECT_EQ(base::LowerBound(items, items + 5, 3, before), items + 1);
EXPECT_EQ(base::UpperBound(items, items + 5, 3, after), items + 4);
EXPECT_EQ(base::LowerBound(items, items + 5, 0, before), items);
EXPECT_EQ(base::LowerBound(items, items + 5, 9, before), items + 5);
EXPECT_EQ(base::UpperBound(items, items + 5, 7, after), items + 5);
EXPECT_EQ(base::LowerBound(items, items, 3, before), items);
}

TEST(AlgorithmBounds, UpperBoundPlain) {
i32 v[] = {1, 2, 2, 2, 5};
EXPECT_EQ(base::UpperBound(v, v + 5, 2), v + 4);
EXPECT_EQ(base::UpperBound(v, v + 5, 0), v);
EXPECT_EQ(base::UpperBound(v, v + 5, 5), v + 5);
}
} // namespace
223 changes: 111 additions & 112 deletions base/containers/builtins_bit.h
Original file line number Diff line number Diff line change
@@ -1,112 +1,111 @@
// Copyright (C) 2022 Vincent Hengel.
// For licensing information see LICENSE at the root of this distribution.
#pragma once

#include <base/meta/traits.h>
#include <base/numeric_limits.h>

#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#endif

namespace base {

#if defined(_MSC_VER)
template <typename T>
[[nodiscard]] mem_size PopCount(const T value) noexcept {
constexpr mem_size _Digits = base::MinMax<T>::digits();
if constexpr (_Digits <= 16) {
return static_cast<mem_size>(__popcnt16(value));
} else if constexpr (_Digits == 32) {
return static_cast<mem_size>(__popcnt(value));
} else {
#ifdef _M_IX86
return static_cast<mem_size>(__popcnt(value >> 32) +
__popcnt(static_cast<unsigned int>(value)));
#else // ^^^ _M_IX86 / !_M_IX86 vvv
return static_cast<mem_size>(__popcnt64(value));
#endif // _M_IX86
}
}
#endif

#if defined(OS_POSIX)
template <typename T>
[[nodiscard]] mem_size PopCount(const T value) noexcept {
constexpr mem_size _Digits = base::MinMax<T>::digits();
if constexpr (_Digits <= 16) {
return static_cast<mem_size>(__builtin_popcount(static_cast<u32>(value)));
} else if constexpr (_Digits == 32) {
return static_cast<mem_size>(__builtin_popcount(value));
} else {
#ifdef _M_IX86
return static_cast<mem_size>(__builtin_popcount(value >> 32) +
__builtin_popcount(static_cast<unsigned int>(value)));
#else // ^^^ _M_IX86 / !_M_IX86 vvv
return static_cast<mem_size>(__builtin_popcountll(value));
#endif // _M_IX86
}
}
#endif

// Count of leading zero bits in |value|, as std::countl_zero would give. The
// builtins are undefined for zero, so that case is answered from the width.
template <typename T>
[[nodiscard]] constexpr int CountLeftZero(const T value) noexcept {
// Width from sizeof, not MinMax<T>::digits(): digits() follows
// std::numeric_limits and drops the sign bit, which is one short of the
// bit count these builtins actually operate on.
static_assert(base::is_integral_v<T> && !base::is_signed_v<T>,
"bit counting is defined for unsigned integers only");
constexpr int kDigits = static_cast<int>(sizeof(T) * 8);
if (value == 0)
return kDigits;
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long index = 0;
if constexpr (kDigits <= 32) {
::_BitScanReverse(&index, static_cast<unsigned long>(value));
return kDigits - 1 - static_cast<int>(index);
} else {
::_BitScanReverse64(&index, static_cast<unsigned long long>(value));
return kDigits - 1 - static_cast<int>(index);
}
#else
if constexpr (kDigits <= 32) {
// __builtin_clz works on a 32-bit unsigned; narrower types are widened,
// so subtract the padding the widening introduced.
return __builtin_clz(static_cast<unsigned int>(value)) - (32 - kDigits);
} else {
return __builtin_clzll(static_cast<unsigned long long>(value));
}
#endif
}

// Count of trailing zero bits in |value|, as std::countr_zero would give.
template <typename T>
[[nodiscard]] constexpr int CountRightZero(const T value) noexcept {
// Width from sizeof, not MinMax<T>::digits(): digits() follows
// std::numeric_limits and drops the sign bit, which is one short of the
// bit count these builtins actually operate on.
static_assert(base::is_integral_v<T> && !base::is_signed_v<T>,
"bit counting is defined for unsigned integers only");
constexpr int kDigits = static_cast<int>(sizeof(T) * 8);
if (value == 0)
return kDigits;
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long index = 0;
if constexpr (kDigits <= 32) {
::_BitScanForward(&index, static_cast<unsigned long>(value));
} else {
::_BitScanForward64(&index, static_cast<unsigned long long>(value));
}
return static_cast<int>(index);
#else
if constexpr (kDigits <= 32) {
return __builtin_ctz(static_cast<unsigned int>(value));
} else {
return __builtin_ctzll(static_cast<unsigned long long>(value));
}
#endif
}

} // namespace base
// Copyright (C) 2022 Vincent Hengel.
// For licensing information see LICENSE at the root of this distribution.
#pragma once

#include <base/meta/traits.h>
#include <base/numeric_limits.h>

#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#endif

namespace base {

#if defined(_MSC_VER)
template <typename T>
[[nodiscard]] mem_size PopCount(const T value) noexcept {
constexpr mem_size _Digits = base::MinMax<T>::digits();
if constexpr (_Digits <= 16) {
return static_cast<mem_size>(__popcnt16(value));
} else if constexpr (_Digits == 32) {
return static_cast<mem_size>(__popcnt(value));
} else {
#ifdef _M_IX86
return static_cast<mem_size>(__popcnt(value >> 32) +
__popcnt(static_cast<unsigned int>(value)));
#else // ^^^ _M_IX86 / !_M_IX86 vvv
return static_cast<mem_size>(__popcnt64(value));
#endif // _M_IX86
}
}
#else
// Every non-MSVC compiler, mingw-w64 included, which is not OS_POSIX.
template <typename T>
[[nodiscard]] mem_size PopCount(const T value) noexcept {
constexpr mem_size _Digits = base::MinMax<T>::digits();
if constexpr (_Digits <= 16) {
return static_cast<mem_size>(__builtin_popcount(static_cast<u32>(value)));
} else if constexpr (_Digits == 32) {
return static_cast<mem_size>(__builtin_popcount(value));
} else {
#ifdef _M_IX86
return static_cast<mem_size>(__builtin_popcount(value >> 32) +
__builtin_popcount(static_cast<unsigned int>(value)));
#else // ^^^ _M_IX86 / !_M_IX86 vvv
return static_cast<mem_size>(__builtin_popcountll(value));
#endif // _M_IX86
}
}
#endif

// Count of leading zero bits in |value|, as std::countl_zero would give. The
// builtins are undefined for zero, so that case is answered from the width.
template <typename T>
[[nodiscard]] constexpr int CountLeftZero(const T value) noexcept {
// Width from sizeof, not MinMax<T>::digits(): digits() follows
// std::numeric_limits and drops the sign bit, which is one short of the
// bit count these builtins actually operate on.
static_assert(base::is_integral_v<T> && !base::is_signed_v<T>,
"bit counting is defined for unsigned integers only");
constexpr int kDigits = static_cast<int>(sizeof(T) * 8);
if (value == 0)
return kDigits;
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long index = 0;
if constexpr (kDigits <= 32) {
::_BitScanReverse(&index, static_cast<unsigned long>(value));
return kDigits - 1 - static_cast<int>(index);
} else {
::_BitScanReverse64(&index, static_cast<unsigned long long>(value));
return kDigits - 1 - static_cast<int>(index);
}
#else
if constexpr (kDigits <= 32) {
// __builtin_clz works on a 32-bit unsigned; narrower types are widened,
// so subtract the padding the widening introduced.
return __builtin_clz(static_cast<unsigned int>(value)) - (32 - kDigits);
} else {
return __builtin_clzll(static_cast<unsigned long long>(value));
}
#endif
}

// Count of trailing zero bits in |value|, as std::countr_zero would give.
template <typename T>
[[nodiscard]] constexpr int CountRightZero(const T value) noexcept {
// Width from sizeof, not MinMax<T>::digits(): digits() follows
// std::numeric_limits and drops the sign bit, which is one short of the
// bit count these builtins actually operate on.
static_assert(base::is_integral_v<T> && !base::is_signed_v<T>,
"bit counting is defined for unsigned integers only");
constexpr int kDigits = static_cast<int>(sizeof(T) * 8);
if (value == 0)
return kDigits;
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long index = 0;
if constexpr (kDigits <= 32) {
::_BitScanForward(&index, static_cast<unsigned long>(value));
} else {
::_BitScanForward64(&index, static_cast<unsigned long long>(value));
}
return static_cast<int>(index);
#else
if constexpr (kDigits <= 32) {
return __builtin_ctz(static_cast<unsigned int>(value));
} else {
return __builtin_ctzll(static_cast<unsigned long long>(value));
}
#endif
}

} // namespace base
2 changes: 2 additions & 0 deletions base/containers/queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

#include <base/arch.h>
#include <base/containers/deque.h>
#include <base/memory/move.h>

namespace base {

template <typename T, class TAllocator = DefaultAllocator>
class Queue {
public:
void push(const T& value) { deque_.push_back(value); }
void push(T&& value) { deque_.push_back(base::move(value)); }
void pop() { deque_.pop_front(); }

T& front() { return deque_.front(); }
Expand Down
13 changes: 13 additions & 0 deletions base/containers/queue_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <string>

#include "queue.h"
#include "base/memory/unique_pointer.h"

namespace base {
namespace {
Expand Down Expand Up @@ -53,5 +54,17 @@ TEST(QueueTest, GrowsPastInitialCapacity) {
EXPECT_TRUE(queue.empty());
}

TEST(QueueTest, PushMovesMoveOnlyElements) {
base::Queue<base::UniquePointer<int>> queue;
queue.push(base::MakeUnique<int>(7));
auto value = base::MakeUnique<int>(8);
queue.push(base::move(value));
EXPECT_FALSE(value);
ASSERT_EQ(queue.size(), 2u);
EXPECT_EQ(*queue.front(), 7);
queue.pop();
EXPECT_EQ(*queue.front(), 8);
}

} // namespace
} // namespace base
Loading