[libcxx-commits] [libcxx] [libc++] Add compact bounded specialization for capacity_aware_iterator (PR #208271)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 29 21:19:28 PDT 2026
https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/208271
>From 8496aed34b30e0c706852d0115b2f92dfbc97148 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 29 Jun 2026 23:18:11 -0400
Subject: [PATCH 1/7] [libc++] Add bounded specialization for
capacity_aware_iterator
- By taking advantage of unused alignment bits for a pointer of type T and the static capacity known at compile time,
we can make a compact bounded iterator who's size == sizeof(T*) by stuffing a counter inside those bits.
- This overload only applies in the following situation:
- Iter is a pointer
- The capacity is <= (1 << (bit_width(alignof(T)) - 1)) - 1. The extra -1 is required, because we need to represent
the end position past the last element.
---
.../__iterator/capacity_aware_iterator.h | 226 ++++++++++++++++++
.../capacity_aware_iter/arithmetic.pass.cpp | 10 +-
.../capacity_aware_iter/assert.pass.cpp | 58 ++++-
.../capacity_aware_iter/comparison.pass.cpp | 7 +-
.../capacity_aware_iter/dereference.pass.cpp | 19 +-
.../types.compile.pass.cpp | 11 +
6 files changed, 318 insertions(+), 13 deletions(-)
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index a516feef60f36..fa394e227e774 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -11,6 +11,8 @@
#define _LIBCPP___CAPACITY_AWARE_ITERATOR_H
#include <__assert>
+#include <__bit/bit_cast.h>
+#include <__bit/countr.h>
#include <__compare/ordering.h>
#include <__compare/three_way_comparable.h>
#include <__config>
@@ -21,8 +23,12 @@
#include <__memory/pointer_traits.h>
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_pointer.h>
+#include <__utility/declval.h>
#include <__utility/move.h>
+#include <cstdint>
+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
@@ -51,6 +57,7 @@ class __capacity_aware_iterator {
public:
static_assert(contiguous_iterator<_Iter>, "__capacity_aware_iterator can only be used with contiguous iterators");
+ constexpr static bool __can_track_count_ = false;
using iterator_category = iterator_traits<_Iter>::iterator_category;
using iterator_concept = contiguous_iterator_tag;
@@ -172,6 +179,225 @@ class __capacity_aware_iterator {
}
};
+template <class _Tp>
+consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
+ auto __bits = std::countr_zero(alignof(_Tp));
+
+ // Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
+ // reserve one for the end position, so __num_elems must be < 3.
+ auto __allowed_range = (1 << __bits) - 1;
+ return __allowed_range > __num_elems;
+}
+
+// A specialization of capacity_aware_iterator where we store a runtime count of the current position inside
+// the unused bottom bits of a pointer to T. Only applies if capacity can fit inside those bits.
+template <class _Iter, class _Tag, size_t _RangeMaxElements>
+ requires(std::is_pointer_v<_Iter> &&
+ std::__range_fits_in_alignment<decltype(* std::declval<_Iter>())>(_RangeMaxElements))
+class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
+ constexpr static std::size_t __bits_ = std::countr_zero(alignof(decltype(*std::declval<_Iter>())));
+
+ union {
+ _Iter __ptr_;
+ std::uintptr_t __current_ : __bits_;
+ };
+
+ template <class, class, size_t>
+ friend class __capacity_aware_iterator;
+
+public:
+ constexpr static bool __can_track_count_ = false;
+
+ using iterator_category = iterator_traits<_Iter>::iterator_category;
+ using iterator_concept = contiguous_iterator_tag;
+ using difference_type = iter_difference_t<_Iter>;
+ using pointer = iterator_traits<_Iter>::pointer;
+ using reference = iter_reference_t<_Iter>;
+ using value_type = iter_value_t<_Iter>;
+
+ constexpr __capacity_aware_iterator()
+ requires is_default_constructible_v<_Iter>
+ = default;
+
+ template <typename _Iter2>
+ requires is_convertible_v<_Iter2, _Iter>
+ constexpr __capacity_aware_iterator(const __capacity_aware_iterator<_Iter2, _Tag, _RangeMaxElements>& __y) noexcept
+ : __ptr_(__y.__ptr_) {
+ if !consteval {
+ __current_ = __y.__current_;
+ }
+ }
+
+ template <class _It, class _Tag2, size_t _RangeMaxElems2>
+ friend constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept;
+
+private:
+ constexpr explicit __capacity_aware_iterator(_Iter __iter) : __ptr_(std::move(__iter)) {
+ if !consteval {
+ __current_ = 0u;
+ }
+ }
+
+ constexpr _Iter __curr_iter() const {
+ if consteval {
+ return __ptr_;
+ }
+
+ return std::bit_cast<_Iter>((std::bit_cast<std::uintptr_t>(__ptr_) >> __bits_) << __bits_) + __current_;
+ }
+
+ constexpr void __update(difference_type __n) {
+ if consteval {
+ __ptr_ += __n;
+ } else {
+ __current_ += __n;
+ }
+ }
+
+public:
+ [[nodiscard]] constexpr decltype(auto) operator*() const noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __current_ < _RangeMaxElements,
+ "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
+ }
+
+ return *(__curr_iter());
+ }
+
+ constexpr decltype(auto) operator->() const noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __current_ < _RangeMaxElements,
+ "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
+ }
+
+ return __curr_iter();
+ }
+
+ constexpr __capacity_aware_iterator& operator++() noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __current_ != _RangeMaxElements,
+ "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
+ }
+
+ __update(1);
+
+ return *this;
+ }
+
+ constexpr __capacity_aware_iterator operator++(int) noexcept {
+ __capacity_aware_iterator __tmp(*this);
+ ++*this;
+ return __tmp;
+ }
+
+ constexpr __capacity_aware_iterator& operator--() noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __current_ != 0u, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
+ }
+
+ __update(-1);
+
+ return *this;
+ }
+
+ constexpr __capacity_aware_iterator operator--(int) noexcept {
+ __capacity_aware_iterator __tmp(*this);
+ --*this;
+ return __tmp;
+ }
+
+ constexpr __capacity_aware_iterator& operator+=(difference_type __n) noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (static_cast<difference_type>(__current_) + __n) >= 0,
+ "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
+ "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
+ }
+
+ __update(__n);
+
+ return *this;
+ }
+
+ constexpr __capacity_aware_iterator& operator-=(difference_type __n) noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (static_cast<difference_type>(__current_) - __n) >= 0,
+ "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ static_cast<std::size_t>(__current_ - __n) <= _RangeMaxElements,
+ "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
+ }
+
+ __update(-__n);
+
+ return *this;
+ }
+
+ [[nodiscard]] constexpr decltype(auto) operator[](difference_type __n) const noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (static_cast<difference_type>(__current_) + __n) >= 0,
+ "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
+ "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+ }
+ return *(*this + __n);
+ }
+
+ friend constexpr bool
+ operator==(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+ return __x.__curr_iter() == __y.__curr_iter();
+ }
+
+ friend constexpr auto
+ operator<=>(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+ if constexpr (three_way_comparable_with<_Iter, _Iter, strong_ordering>) {
+ return __x.__curr_iter() <=> __y.__curr_iter();
+ } else {
+ if (__x.__curr_iter() < __y.__curr_iter()) {
+ return strong_ordering::less;
+ } else if (__x.__curr_iter() == __y.__curr_iter()) {
+ return strong_ordering::equal;
+ }
+ return strong_ordering::greater;
+ }
+ }
+
+ [[nodiscard]] friend constexpr __capacity_aware_iterator
+ operator+(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ [[nodiscard]] friend constexpr __capacity_aware_iterator
+ operator+(difference_type __n, const __capacity_aware_iterator& __i) noexcept {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ [[nodiscard]] friend constexpr __capacity_aware_iterator
+ operator-(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
+ auto __tmp = __i;
+ __tmp -= __n;
+ return __tmp;
+ }
+
+ [[nodiscard]] friend constexpr difference_type
+ operator-(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+ return difference_type(__x.__curr_iter() - __y.__curr_iter());
+ }
+};
+
template <class _It, class _Tag2, size_t _RangeMaxElems2>
_LIBCPP_HIDE_FROM_ABI constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept {
return __capacity_aware_iterator<_It, _Tag2, _RangeMaxElems2>(__iter);
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index f4aa5c29790ff..59a1e080d4d37 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -33,14 +33,14 @@
#include "test_iterators.h"
#include "test_macros.h"
-template <typename Iter>
+template <typename Iter, typename Ty = int>
constexpr bool test() {
- int arr[] = {1, 2, 3, 4, 5, 6};
+ Ty arr[] = {1, 2, 3, 4, 5, 6};
constexpr size_t sz = std::size(arr);
using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
- int* i = arr + 0;
+ Ty* i = arr + 0;
// operator++()
{
@@ -162,5 +162,9 @@ int main(int, char**) {
test<contiguous_iterator<int*>>();
static_assert(test<contiguous_iterator<int*>>());
+ // bounded overload
+ test<long*, long>();
+ static_assert(test<long*, long>());
+
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
index ceac20d549c34..ed0d29f0cbcef 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
@@ -13,13 +13,64 @@
// struct __capacity_aware_iterator;
// Check assert failure if advancing, rewinding or indexing iterator past its maximum range size
+// Or if we're keeping track of the current position, if we're advancing, rewinding, indexing out of bounds.
#include <__iterator/capacity_aware_iterator.h>
#include <iterator>
#include "check_assertion.h"
-#include "test_iterators.h"
-#include "test_macros.h"
+
+// Specialization where we can fit a running count inside unused alignment bits.
+template <typename Iter>
+void test_bounded() {
+ int arr[] = {1, 2};
+ int* p = arr;
+
+ constexpr long sz = std::size(arr);
+
+ using CapIter = std::__capacity_aware_iterator<Iter, decltype(p), sz>;
+
+ CapIter it = std::__make_capacity_aware_iterator<Iter, decltype(p), sz>(Iter(arr));
+
+ TEST_LIBCPP_ASSERT_FAILURE(it--, "__capacity_aware_iterator::operator--: Attempt to rewind iterator past the start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind iterator past the start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it += (sz + 1), "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it -= (sz + 1), "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it -= -(sz + 1),
+ "__capacity_aware_iterator::operator-=: Attempting to move iterator past its container's possible range");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it[-sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
+
+ ++it;
+ ++it;
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ *it, "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it.operator->(), "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ++it, "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
+}
template <typename Iter>
void test() {
@@ -57,7 +108,8 @@ void test() {
}
int main(int, char**) {
- test<contiguous_iterator<int*>>();
+ test_bounded<int*>();
+ test<int*>();
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
index 34aa73bf4459c..a7f3ae44fa5ee 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
@@ -30,9 +30,9 @@
#include "test_iterators.h"
#include "test_macros.h"
-template <typename Iter>
+template <typename Iter, typename Ty = int>
constexpr bool test() {
- int arr[] = {1, 2, 3, 4};
+ Ty arr[] = {1, 2, 3, 4};
constexpr long sz = std::size(arr);
using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -113,5 +113,8 @@ int main(int, char**) {
test<contiguous_iterator<int*>>();
static_assert(test<contiguous_iterator<int*>>());
+ test<long*, long>();
+ static_assert(test<long*, long>());
+
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
index 42ab763896e2f..910078db4a7d4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
@@ -30,9 +30,14 @@ struct Foo {
constexpr bool operator==(Foo const& other) const { return x == other.x; }
};
-template <typename Iter>
+struct Foo2 {
+ long x;
+ constexpr bool operator==(Foo2 const& other) const { return x == other.x; }
+};
+
+template <typename Iter, typename Ty = Foo>
constexpr bool test() {
- Foo arr[] = {Foo{1}, Foo{2}, Foo{3}, Foo{4}};
+ Ty arr[] = {Ty{1}, Ty{2}, Ty{3}, Ty{4}};
constexpr long sz = std::size(arr);
using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -41,7 +46,7 @@ constexpr bool test() {
// operator[]
{
- std::same_as<Foo&> decltype(auto) res = it[0];
+ std::same_as<Ty&> decltype(auto) res = it[0];
ASSERT_NOEXCEPT(it[0]);
assert(res == arr[0]);
assert(&res == &arr[0]);
@@ -56,7 +61,7 @@ constexpr bool test() {
// operator*
{
- std::same_as<Foo&> decltype(auto) res = *it;
+ std::same_as<Ty&> decltype(auto) res = *it;
ASSERT_NOEXCEPT(*it);
assert(*it == arr[0]);
assert(&res == &arr[0]);
@@ -65,7 +70,7 @@ constexpr bool test() {
// operator->
{
- std::same_as<Foo*> decltype(auto) ptr = it.operator->();
+ std::same_as<Ty*> decltype(auto) ptr = it.operator->();
ASSERT_NOEXCEPT(it->x);
assert(ptr->x == 1);
assert(ptr == &arr[0]);
@@ -78,5 +83,9 @@ int main(int, char**) {
test<three_way_contiguous_iterator<Foo*>>();
static_assert(test<three_way_contiguous_iterator<Foo*>>());
+ // bounded overload
+ test<Foo2*, Foo2>();
+ static_assert(test<Foo2*, Foo2>());
+
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
index b3b72a0728ed7..37680d0e7c859 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
@@ -29,3 +29,14 @@ static_assert(std::is_same_v<CapIter::difference_type, std::iter_difference_t<It
static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
static_assert(std::is_same_v<CapIter::value_type, std::iter_value_t<It>>);
+
+// bounded overload
+using It2 = long*;
+using CapIter2 = std::__capacity_aware_iterator<It2, long[6], 126>;
+
+static_assert(std::is_same_v<CapIter2::iterator_category, std::iterator_traits<It2>::iterator_category>);
+static_assert(std::is_same_v<CapIter2::iterator_concept, std::contiguous_iterator_tag>);
+static_assert(std::is_same_v<CapIter2::difference_type, std::iter_difference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::value_type, std::iter_value_t<It2>>);
>From 4b70812bcf012fabbd792444e575dd70eca2ce04 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 8 Jul 2026 16:12:17 -0400
Subject: [PATCH 2/7] Fix
---
.../include/__iterator/capacity_aware_iterator.h | 6 +++---
.../capacity_aware_iter/arithmetic.pass.cpp | 12 ++++++++----
.../iterators/capacity_aware_iter/assert.pass.cpp | 14 +++++++-------
3 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index fa394e227e774..442d9c4d2a561 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -314,7 +314,7 @@ class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
if !consteval {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
(static_cast<difference_type>(__current_) + __n) >= 0,
- "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+ "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
"__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
@@ -346,8 +346,8 @@ class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
(static_cast<difference_type>(__current_) + __n) >= 0,
"__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
- "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+ static_cast<std::size_t>(__current_ + __n) < _RangeMaxElements,
+ "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
}
return *(*this + __n);
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index 59a1e080d4d37..d46bb4d3e8bec 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -66,7 +66,8 @@ constexpr bool test() {
// operator--()
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+ ++iter;
std::same_as<CapIter&> decltype(auto) res = --iter;
@@ -77,7 +78,8 @@ constexpr bool test() {
// operator--(int)
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+ ++iter;
std::same_as<CapIter> decltype(auto) res = iter--;
@@ -121,7 +123,8 @@ constexpr bool test() {
// operator-=(difference_type)
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+ iter += 2;
std::same_as<CapIter&> decltype(auto) res = iter -= 2;
@@ -132,7 +135,8 @@ constexpr bool test() {
// operator-(__capacity_aware_iterator, difference_type)
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+ iter += 2;
std::same_as<CapIter> decltype(auto) res = iter - 2;
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
index ed0d29f0cbcef..889e67899d8a4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
@@ -32,29 +32,29 @@ void test_bounded() {
CapIter it = std::__make_capacity_aware_iterator<Iter, decltype(p), sz>(Iter(arr));
- TEST_LIBCPP_ASSERT_FAILURE(it--, "__capacity_aware_iterator::operator--: Attempt to rewind iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it--, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
TEST_LIBCPP_ASSERT_FAILURE(
- it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind iterator past the start");
+ it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
TEST_LIBCPP_ASSERT_FAILURE(
- it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+ it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
TEST_LIBCPP_ASSERT_FAILURE(
it += (sz + 1), "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
TEST_LIBCPP_ASSERT_FAILURE(
- it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+ it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
TEST_LIBCPP_ASSERT_FAILURE(
it -= (sz + 1), "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
TEST_LIBCPP_ASSERT_FAILURE(
- it -= -(sz + 1),
- "__capacity_aware_iterator::operator-=: Attempting to move iterator past its container's possible range");
+ it -= -(sz + 1), "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
TEST_LIBCPP_ASSERT_FAILURE(
- it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+ it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
TEST_LIBCPP_ASSERT_FAILURE(
it[-sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
>From 29eebbf0d6af4c5da9002208a9996b66061b2173 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 9 Jul 2026 00:42:21 -0400
Subject: [PATCH 3/7] Fix incorrect pointer arithmetic
---
libcxx/include/__iterator/capacity_aware_iterator.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index 442d9c4d2a561..02db1d6b52d19 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -243,7 +243,7 @@ class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
return __ptr_;
}
- return std::bit_cast<_Iter>((std::bit_cast<std::uintptr_t>(__ptr_) >> __bits_) << __bits_) + __current_;
+ return std::bit_cast<_Iter>(std::bit_cast<std::uintptr_t>(__ptr_) - __current_) + __current_;
}
constexpr void __update(difference_type __n) {
>From bc6bf987bdc44e70a5290e0d2cf94600d358a06c Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 9 Jul 2026 00:44:43 -0400
Subject: [PATCH 4/7] Simplify
---
libcxx/include/__iterator/capacity_aware_iterator.h | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index 02db1d6b52d19..2572e1c0bd83c 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -24,7 +24,6 @@
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/is_pointer.h>
-#include <__utility/declval.h>
#include <__utility/move.h>
#include <cstdint>
@@ -192,10 +191,9 @@ consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
// A specialization of capacity_aware_iterator where we store a runtime count of the current position inside
// the unused bottom bits of a pointer to T. Only applies if capacity can fit inside those bits.
template <class _Iter, class _Tag, size_t _RangeMaxElements>
- requires(std::is_pointer_v<_Iter> &&
- std::__range_fits_in_alignment<decltype(* std::declval<_Iter>())>(_RangeMaxElements))
+ requires(std::is_pointer_v<_Iter> && std::__range_fits_in_alignment<std::iter_value_t<_Iter>>(_RangeMaxElements))
class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
- constexpr static std::size_t __bits_ = std::countr_zero(alignof(decltype(*std::declval<_Iter>())));
+ constexpr static std::size_t __bits_ = std::countr_zero(alignof(std::iter_value_t<_Iter>));
union {
_Iter __ptr_;
>From bc8d8ec7a90a804dcb492815f231de43c57e7501 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 9 Jul 2026 01:31:20 -0400
Subject: [PATCH 5/7] Try force alignment
---
.../iterators/capacity_aware_iter/arithmetic.pass.cpp | 4 ++--
.../iterators/capacity_aware_iter/comparison.pass.cpp | 4 ++--
.../iterators/capacity_aware_iter/dereference.pass.cpp | 6 +++---
.../iterators/capacity_aware_iter/types.compile.pass.cpp | 5 +++--
4 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index d46bb4d3e8bec..d84648a75e8f7 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -167,8 +167,8 @@ int main(int, char**) {
static_assert(test<contiguous_iterator<int*>>());
// bounded overload
- test<long*, long>();
- static_assert(test<long*, long>());
+ test<long long*, long long>();
+ static_assert(test<long long*, long long>());
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
index a7f3ae44fa5ee..149f37691eae0 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
@@ -113,8 +113,8 @@ int main(int, char**) {
test<contiguous_iterator<int*>>();
static_assert(test<contiguous_iterator<int*>>());
- test<long*, long>();
- static_assert(test<long*, long>());
+ test<long long*, long long>();
+ static_assert(test<long long*, long long>());
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
index 910078db4a7d4..9048207d92ad2 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
@@ -25,13 +25,13 @@
#include "test_iterators.h"
#include "test_macros.h"
-struct Foo {
+struct alignas(4) Foo {
int x;
constexpr bool operator==(Foo const& other) const { return x == other.x; }
};
-struct Foo2 {
- long x;
+struct alignas(8) Foo2 {
+ int x;
constexpr bool operator==(Foo2 const& other) const { return x == other.x; }
};
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
index 37680d0e7c859..e633dc67803f4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
@@ -30,9 +30,10 @@ static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
static_assert(std::is_same_v<CapIter::value_type, std::iter_value_t<It>>);
+struct alignas(8) A {};
// bounded overload
-using It2 = long*;
-using CapIter2 = std::__capacity_aware_iterator<It2, long[6], 126>;
+using It2 = A*;
+using CapIter2 = std::__capacity_aware_iterator<It2, A[], 126>;
static_assert(std::is_same_v<CapIter2::iterator_category, std::iterator_traits<It2>::iterator_category>);
static_assert(std::is_same_v<CapIter2::iterator_concept, std::contiguous_iterator_tag>);
>From 9cd041fd668fb40e045d9ef6375ceb971db04e64 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 10 Jul 2026 14:50:12 -0400
Subject: [PATCH 6/7] Fix GCC warning
---
libcxx/include/__iterator/capacity_aware_iterator.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index 2572e1c0bd83c..36281bccdf2ec 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -180,11 +180,11 @@ class __capacity_aware_iterator {
template <class _Tp>
consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
- auto __bits = std::countr_zero(alignof(_Tp));
+ std::size_t __bits = std::countr_zero(alignof(_Tp));
// Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
// reserve one for the end position, so __num_elems must be < 3.
- auto __allowed_range = (1 << __bits) - 1;
+ std::size_t __allowed_range = (1 << __bits) - 1;
return __allowed_range > __num_elems;
}
>From ef1b88520d4b4ca03a7024aea5d327282fa129a0 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 22 Jul 2026 19:17:57 -0400
Subject: [PATCH 7/7] Move packed packed iterator to its own type
---
libcxx/include/CMakeLists.txt | 1 +
.../__iterator/capacity_aware_iterator.h | 224 ---------------
.../__iterator/static_packed_bounded_iter.h | 269 ++++++++++++++++++
libcxx/include/module.modulemap.in | 1 +
.../capacity_aware_iter/arithmetic.pass.cpp | 22 +-
.../capacity_aware_iter/assert.pass.cpp | 58 +---
.../capacity_aware_iter/comparison.pass.cpp | 7 +-
.../capacity_aware_iter/dereference.pass.cpp | 21 +-
.../types.compile.pass.cpp | 12 -
.../arithmetic.pass.cpp | 116 ++++++++
.../assert.pass.cpp | 74 +++++
.../comparison.pass.cpp | 85 ++++++
.../dereference.pass.cpp | 58 ++++
.../types.compile.pass.cpp | 30 ++
14 files changed, 652 insertions(+), 326 deletions(-)
create mode 100644 libcxx/include/__iterator/static_packed_bounded_iter.h
create mode 100644 libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp
create mode 100644 libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp
create mode 100644 libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.pass.cpp
create mode 100644 libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp
create mode 100644 libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..02d83c13fb5fd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -527,6 +527,7 @@ set(files
__iterator/size.h
__iterator/sortable.h
__iterator/static_bounded_iter.h
+ __iterator/static_packed_bounded_iter.h
__iterator/unreachable_sentinel.h
__iterator/wrap_iter.h
__locale
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index 36281bccdf2ec..a516feef60f36 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -11,8 +11,6 @@
#define _LIBCPP___CAPACITY_AWARE_ITERATOR_H
#include <__assert>
-#include <__bit/bit_cast.h>
-#include <__bit/countr.h>
#include <__compare/ordering.h>
#include <__compare/three_way_comparable.h>
#include <__config>
@@ -23,11 +21,8 @@
#include <__memory/pointer_traits.h>
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_convertible.h>
-#include <__type_traits/is_pointer.h>
#include <__utility/move.h>
-#include <cstdint>
-
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
@@ -56,7 +51,6 @@ class __capacity_aware_iterator {
public:
static_assert(contiguous_iterator<_Iter>, "__capacity_aware_iterator can only be used with contiguous iterators");
- constexpr static bool __can_track_count_ = false;
using iterator_category = iterator_traits<_Iter>::iterator_category;
using iterator_concept = contiguous_iterator_tag;
@@ -178,224 +172,6 @@ class __capacity_aware_iterator {
}
};
-template <class _Tp>
-consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
- std::size_t __bits = std::countr_zero(alignof(_Tp));
-
- // Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
- // reserve one for the end position, so __num_elems must be < 3.
- std::size_t __allowed_range = (1 << __bits) - 1;
- return __allowed_range > __num_elems;
-}
-
-// A specialization of capacity_aware_iterator where we store a runtime count of the current position inside
-// the unused bottom bits of a pointer to T. Only applies if capacity can fit inside those bits.
-template <class _Iter, class _Tag, size_t _RangeMaxElements>
- requires(std::is_pointer_v<_Iter> && std::__range_fits_in_alignment<std::iter_value_t<_Iter>>(_RangeMaxElements))
-class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
- constexpr static std::size_t __bits_ = std::countr_zero(alignof(std::iter_value_t<_Iter>));
-
- union {
- _Iter __ptr_;
- std::uintptr_t __current_ : __bits_;
- };
-
- template <class, class, size_t>
- friend class __capacity_aware_iterator;
-
-public:
- constexpr static bool __can_track_count_ = false;
-
- using iterator_category = iterator_traits<_Iter>::iterator_category;
- using iterator_concept = contiguous_iterator_tag;
- using difference_type = iter_difference_t<_Iter>;
- using pointer = iterator_traits<_Iter>::pointer;
- using reference = iter_reference_t<_Iter>;
- using value_type = iter_value_t<_Iter>;
-
- constexpr __capacity_aware_iterator()
- requires is_default_constructible_v<_Iter>
- = default;
-
- template <typename _Iter2>
- requires is_convertible_v<_Iter2, _Iter>
- constexpr __capacity_aware_iterator(const __capacity_aware_iterator<_Iter2, _Tag, _RangeMaxElements>& __y) noexcept
- : __ptr_(__y.__ptr_) {
- if !consteval {
- __current_ = __y.__current_;
- }
- }
-
- template <class _It, class _Tag2, size_t _RangeMaxElems2>
- friend constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept;
-
-private:
- constexpr explicit __capacity_aware_iterator(_Iter __iter) : __ptr_(std::move(__iter)) {
- if !consteval {
- __current_ = 0u;
- }
- }
-
- constexpr _Iter __curr_iter() const {
- if consteval {
- return __ptr_;
- }
-
- return std::bit_cast<_Iter>(std::bit_cast<std::uintptr_t>(__ptr_) - __current_) + __current_;
- }
-
- constexpr void __update(difference_type __n) {
- if consteval {
- __ptr_ += __n;
- } else {
- __current_ += __n;
- }
- }
-
-public:
- [[nodiscard]] constexpr decltype(auto) operator*() const noexcept {
- if !consteval {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __current_ < _RangeMaxElements,
- "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
- }
-
- return *(__curr_iter());
- }
-
- constexpr decltype(auto) operator->() const noexcept {
- if !consteval {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __current_ < _RangeMaxElements,
- "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
- }
-
- return __curr_iter();
- }
-
- constexpr __capacity_aware_iterator& operator++() noexcept {
- if !consteval {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __current_ != _RangeMaxElements,
- "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
- }
-
- __update(1);
-
- return *this;
- }
-
- constexpr __capacity_aware_iterator operator++(int) noexcept {
- __capacity_aware_iterator __tmp(*this);
- ++*this;
- return __tmp;
- }
-
- constexpr __capacity_aware_iterator& operator--() noexcept {
- if !consteval {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __current_ != 0u, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
- }
-
- __update(-1);
-
- return *this;
- }
-
- constexpr __capacity_aware_iterator operator--(int) noexcept {
- __capacity_aware_iterator __tmp(*this);
- --*this;
- return __tmp;
- }
-
- constexpr __capacity_aware_iterator& operator+=(difference_type __n) noexcept {
- if !consteval {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- (static_cast<difference_type>(__current_) + __n) >= 0,
- "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
- "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
- }
-
- __update(__n);
-
- return *this;
- }
-
- constexpr __capacity_aware_iterator& operator-=(difference_type __n) noexcept {
- if !consteval {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- (static_cast<difference_type>(__current_) - __n) >= 0,
- "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- static_cast<std::size_t>(__current_ - __n) <= _RangeMaxElements,
- "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
- }
-
- __update(-__n);
-
- return *this;
- }
-
- [[nodiscard]] constexpr decltype(auto) operator[](difference_type __n) const noexcept {
- if !consteval {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- (static_cast<difference_type>(__current_) + __n) >= 0,
- "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- static_cast<std::size_t>(__current_ + __n) < _RangeMaxElements,
- "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
- }
- return *(*this + __n);
- }
-
- friend constexpr bool
- operator==(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
- return __x.__curr_iter() == __y.__curr_iter();
- }
-
- friend constexpr auto
- operator<=>(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
- if constexpr (three_way_comparable_with<_Iter, _Iter, strong_ordering>) {
- return __x.__curr_iter() <=> __y.__curr_iter();
- } else {
- if (__x.__curr_iter() < __y.__curr_iter()) {
- return strong_ordering::less;
- } else if (__x.__curr_iter() == __y.__curr_iter()) {
- return strong_ordering::equal;
- }
- return strong_ordering::greater;
- }
- }
-
- [[nodiscard]] friend constexpr __capacity_aware_iterator
- operator+(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
- auto __tmp = __i;
- __tmp += __n;
- return __tmp;
- }
-
- [[nodiscard]] friend constexpr __capacity_aware_iterator
- operator+(difference_type __n, const __capacity_aware_iterator& __i) noexcept {
- auto __tmp = __i;
- __tmp += __n;
- return __tmp;
- }
-
- [[nodiscard]] friend constexpr __capacity_aware_iterator
- operator-(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
- auto __tmp = __i;
- __tmp -= __n;
- return __tmp;
- }
-
- [[nodiscard]] friend constexpr difference_type
- operator-(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
- return difference_type(__x.__curr_iter() - __y.__curr_iter());
- }
-};
-
template <class _It, class _Tag2, size_t _RangeMaxElems2>
_LIBCPP_HIDE_FROM_ABI constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept {
return __capacity_aware_iterator<_It, _Tag2, _RangeMaxElems2>(__iter);
diff --git a/libcxx/include/__iterator/static_packed_bounded_iter.h b/libcxx/include/__iterator/static_packed_bounded_iter.h
new file mode 100644
index 0000000000000..b36c357218892
--- /dev/null
+++ b/libcxx/include/__iterator/static_packed_bounded_iter.h
@@ -0,0 +1,269 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP__STATIC_PACKED_BOUNDED_ITER_H
+#define _LIBCPP__STATIC_PACKED_BOUNDED_ITER_H
+
+#include <__assert>
+#include <__bit/bit_cast.h>
+#include <__bit/countr.h>
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iterator_traits.h>
+#include <__type_traits/is_pointer.h>
+
+#include <cstdint>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 26
+
+// static_packed_bounded_iter is a bounded, contiguous iterator that is aware of its container's (compile-time) maximum
+// capacity. It reuses the bottom unused bits of a pointer to keep track of its current position.
+// This only applies if the container's maximum range can fit inside (2 ^ available_bits) - 1
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+consteval bool __range_fits_in_alignment(size_t __alignment, size_t __num_elems) {
+ size_t __bits = std::countr_zero(__alignment);
+
+ // Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
+ // reserve one for the end position, so __num_elems must be < 3.
+ size_t __allowed_range = (1 << __bits) - 1;
+ return __allowed_range > __num_elems;
+}
+
+template <class _Ptr, class _Tag, size_t _RangeCapacity>
+ requires(is_pointer_v<_Ptr> && std::__range_fits_in_alignment(_LIBCPP_ALIGNOF(iter_value_t<_Ptr>), _RangeCapacity))
+class __static_packed_bounded_iterator {
+public:
+ using iterator_category = iterator_traits<_Ptr>::iterator_category;
+ using iterator_concept = contiguous_iterator_tag;
+ using difference_type = iter_difference_t<_Ptr>;
+ using pointer = iterator_traits<_Ptr>::pointer;
+ using reference = iter_reference_t<_Ptr>;
+ using value_type = iter_value_t<_Ptr>;
+
+private:
+ friend class __static_packed_bounded_iterator;
+
+ static constexpr size_t __bits_available_ = std::countr_zero(_LIBCPP_ALIGNOF(value_type));
+ static constexpr uintptr_t __count_mask_ = (1 << __bits_available_) - 1;
+ static constexpr uintptr_t __ptr_mask_ = ~__count_mask_;
+
+ struct __count_rep {
+ uintptr_t __count_ : __bits_available_;
+ };
+
+ union __rep {
+ uintptr_t __value_;
+ __count_rep __crep_;
+ };
+
+ union {
+ pointer __ptr_;
+ __rep __underlying_;
+ };
+
+ constexpr _Ptr __current() const {
+ if consteval {
+ return __ptr_;
+ } else {
+ return std::bit_cast<pointer>(__underlying_.__value_ & __ptr_mask_) + __count();
+ }
+ }
+
+ constexpr uintptr_t __count() const { return __underlying_.__crep_.__count_; }
+
+ constexpr void __update(difference_type __n) {
+ if consteval {
+ __ptr_ += __n;
+ } else {
+ __underlying_.__crep_.__count_ += __n;
+ }
+ }
+
+ constexpr explicit __static_packed_bounded_iterator(_Ptr __p) noexcept : __ptr_(__p) {
+ if !consteval {
+ __update(0);
+ }
+ }
+
+public:
+ template <class _Ptr2, class _Tag2, size_t _RangeCapacity2>
+ friend constexpr auto __make_static_packed_bounded_iter(_Ptr2) noexcept;
+
+ constexpr __static_packed_bounded_iterator()
+ requires is_default_constructible_v<_Ptr>
+ = default;
+
+ template <class _Ptr2>
+ requires is_convertible_v<_Ptr2, _Ptr>
+ constexpr __static_packed_bounded_iterator(const __static_packed_bounded_iterator<_Ptr2, _Tag, _RangeCapacity>& __y)
+ : __ptr_(__y.__ptr_) {
+ if !consteval {
+ __underlying_.__crep_.__count_ = __y.__count();
+ }
+ }
+
+ [[nodiscard]] constexpr decltype(auto) operator*() const noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __count() != _RangeCapacity,
+ "__static_packed_bounded_iterator::operator*: Attempt to dereference an iterator at the end");
+ }
+
+ return *(__current());
+ }
+
+ constexpr decltype(auto) operator->() const noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __count() != _RangeCapacity,
+ "__static_packed_bounded_iterator::operator->: Attempt to dereference an iterator at the end");
+ }
+
+ return __current();
+ }
+
+ constexpr __static_packed_bounded_iterator& operator++() noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __count() != _RangeCapacity,
+ "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+ }
+
+ __update(1);
+
+ return *this;
+ }
+
+ constexpr __static_packed_bounded_iterator operator++(int) noexcept {
+ __static_packed_bounded_iterator __tmp(*this);
+ ++*this;
+ return __tmp;
+ }
+
+ constexpr __static_packed_bounded_iterator& operator--() noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __count() != 0u,
+ "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+ }
+
+ __update(-1);
+
+ return *this;
+ }
+
+ constexpr __static_packed_bounded_iterator operator--(int) noexcept {
+ __static_packed_bounded_iterator __tmp(*this);
+ --*this;
+ return __tmp;
+ }
+
+ constexpr __static_packed_bounded_iterator& operator+=(difference_type __n) noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (static_cast<difference_type>(__count()) + __n) >= 0,
+ "__static_packed_bounded_iterator::operator+=: Attempt to rewind an iterator past the start");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ static_cast<size_t>(__count() + __n) <= _RangeCapacity,
+ "__static_packed_bounded_iterator::operator+=: Attempt to advance an iterator past the end");
+ }
+
+ __update(__n);
+
+ return *this;
+ }
+
+ constexpr __static_packed_bounded_iterator& operator-=(difference_type __n) noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (static_cast<difference_type>(__count()) - __n) >= 0,
+ "__static_packed_bounded_iterator::operator-=: Attempt to rewind an iterator past the start");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ static_cast<size_t>(__count() - __n) <= _RangeCapacity,
+ "__static_packed_bounded_iterator::operator-=: Attempt to advance an iterator past the end");
+ }
+
+ __update(-__n);
+
+ return *this;
+ }
+
+ [[nodiscard]] constexpr decltype(auto) operator[](difference_type __n) const noexcept {
+ if !consteval {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (static_cast<difference_type>(__count()) + __n) >= 0,
+ "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator past the start");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ static_cast<size_t>(__count() + __n) < _RangeCapacity,
+ "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator at or past the end");
+ }
+ return *(*this + __n);
+ }
+
+ friend constexpr bool
+ operator==(const __static_packed_bounded_iterator& __x, const __static_packed_bounded_iterator& __y) noexcept {
+ return __x.__current() == __y.__current();
+ }
+
+ friend constexpr auto
+ operator<=>(const __static_packed_bounded_iterator& __x, const __static_packed_bounded_iterator& __y) noexcept {
+ return __x.__current() <=> __y.__current();
+ }
+
+ [[nodiscard]] friend constexpr __static_packed_bounded_iterator
+ operator+(const __static_packed_bounded_iterator& __i, difference_type __n) noexcept {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ [[nodiscard]] friend constexpr __static_packed_bounded_iterator
+ operator+(difference_type __n, const __static_packed_bounded_iterator& __i) noexcept {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ [[nodiscard]] friend constexpr __static_packed_bounded_iterator
+ operator-(const __static_packed_bounded_iterator& __i, difference_type __n) noexcept {
+ auto __tmp = __i;
+ __tmp -= __n;
+ return __tmp;
+ }
+
+ [[nodiscard]] friend constexpr difference_type
+ operator-(const __static_packed_bounded_iterator& __x, const __static_packed_bounded_iterator& __y) noexcept {
+ return difference_type(__x.__current() - __y.__current());
+ }
+};
+
+template <class _Ptr, class _Tag, size_t _RangeCapacity>
+constexpr auto __make_static_packed_bounded_iter(_Ptr __p) noexcept {
+ return __static_packed_bounded_iterator<_Ptr, _Tag, _RangeCapacity>(__p);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+_LIBCPP_POP_MACROS
+#endif
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..f8cf79d81245c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1565,6 +1565,7 @@ module std {
module size { header "__iterator/size.h" }
module sortable { header "__iterator/sortable.h" }
module static_bounded_iter { header "__iterator/static_bounded_iter.h" }
+ module static_packed_bounded_iter { header "__iterator/static_packed_bounded_iter.h" }
module unreachable_sentinel { header "__iterator/unreachable_sentinel.h" }
module capacity_aware_iterator { header "__iterator/capacity_aware_iterator.h" }
module wrap_iter { header "__iterator/wrap_iter.h" }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index d84648a75e8f7..f4aa5c29790ff 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -33,14 +33,14 @@
#include "test_iterators.h"
#include "test_macros.h"
-template <typename Iter, typename Ty = int>
+template <typename Iter>
constexpr bool test() {
- Ty arr[] = {1, 2, 3, 4, 5, 6};
+ int arr[] = {1, 2, 3, 4, 5, 6};
constexpr size_t sz = std::size(arr);
using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
- Ty* i = arr + 0;
+ int* i = arr + 0;
// operator++()
{
@@ -66,8 +66,7 @@ constexpr bool test() {
// operator--()
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
- ++iter;
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
std::same_as<CapIter&> decltype(auto) res = --iter;
@@ -78,8 +77,7 @@ constexpr bool test() {
// operator--(int)
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
- ++iter;
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
std::same_as<CapIter> decltype(auto) res = iter--;
@@ -123,8 +121,7 @@ constexpr bool test() {
// operator-=(difference_type)
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
- iter += 2;
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
std::same_as<CapIter&> decltype(auto) res = iter -= 2;
@@ -135,8 +132,7 @@ constexpr bool test() {
// operator-(__capacity_aware_iterator, difference_type)
{
- CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
- iter += 2;
+ CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
std::same_as<CapIter> decltype(auto) res = iter - 2;
@@ -166,9 +162,5 @@ int main(int, char**) {
test<contiguous_iterator<int*>>();
static_assert(test<contiguous_iterator<int*>>());
- // bounded overload
- test<long long*, long long>();
- static_assert(test<long long*, long long>());
-
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
index 889e67899d8a4..ceac20d549c34 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
@@ -13,64 +13,13 @@
// struct __capacity_aware_iterator;
// Check assert failure if advancing, rewinding or indexing iterator past its maximum range size
-// Or if we're keeping track of the current position, if we're advancing, rewinding, indexing out of bounds.
#include <__iterator/capacity_aware_iterator.h>
#include <iterator>
#include "check_assertion.h"
-
-// Specialization where we can fit a running count inside unused alignment bits.
-template <typename Iter>
-void test_bounded() {
- int arr[] = {1, 2};
- int* p = arr;
-
- constexpr long sz = std::size(arr);
-
- using CapIter = std::__capacity_aware_iterator<Iter, decltype(p), sz>;
-
- CapIter it = std::__make_capacity_aware_iterator<Iter, decltype(p), sz>(Iter(arr));
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it--, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it += (sz + 1), "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it -= (sz + 1), "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it -= -(sz + 1), "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it[-sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
-
- ++it;
- ++it;
-
- TEST_LIBCPP_ASSERT_FAILURE(
- *it, "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- it.operator->(), "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
-
- TEST_LIBCPP_ASSERT_FAILURE(
- ++it, "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
-}
+#include "test_iterators.h"
+#include "test_macros.h"
template <typename Iter>
void test() {
@@ -108,8 +57,7 @@ void test() {
}
int main(int, char**) {
- test_bounded<int*>();
- test<int*>();
+ test<contiguous_iterator<int*>>();
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
index 149f37691eae0..34aa73bf4459c 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
@@ -30,9 +30,9 @@
#include "test_iterators.h"
#include "test_macros.h"
-template <typename Iter, typename Ty = int>
+template <typename Iter>
constexpr bool test() {
- Ty arr[] = {1, 2, 3, 4};
+ int arr[] = {1, 2, 3, 4};
constexpr long sz = std::size(arr);
using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -113,8 +113,5 @@ int main(int, char**) {
test<contiguous_iterator<int*>>();
static_assert(test<contiguous_iterator<int*>>());
- test<long long*, long long>();
- static_assert(test<long long*, long long>());
-
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
index 9048207d92ad2..42ab763896e2f 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
@@ -25,19 +25,14 @@
#include "test_iterators.h"
#include "test_macros.h"
-struct alignas(4) Foo {
+struct Foo {
int x;
constexpr bool operator==(Foo const& other) const { return x == other.x; }
};
-struct alignas(8) Foo2 {
- int x;
- constexpr bool operator==(Foo2 const& other) const { return x == other.x; }
-};
-
-template <typename Iter, typename Ty = Foo>
+template <typename Iter>
constexpr bool test() {
- Ty arr[] = {Ty{1}, Ty{2}, Ty{3}, Ty{4}};
+ Foo arr[] = {Foo{1}, Foo{2}, Foo{3}, Foo{4}};
constexpr long sz = std::size(arr);
using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -46,7 +41,7 @@ constexpr bool test() {
// operator[]
{
- std::same_as<Ty&> decltype(auto) res = it[0];
+ std::same_as<Foo&> decltype(auto) res = it[0];
ASSERT_NOEXCEPT(it[0]);
assert(res == arr[0]);
assert(&res == &arr[0]);
@@ -61,7 +56,7 @@ constexpr bool test() {
// operator*
{
- std::same_as<Ty&> decltype(auto) res = *it;
+ std::same_as<Foo&> decltype(auto) res = *it;
ASSERT_NOEXCEPT(*it);
assert(*it == arr[0]);
assert(&res == &arr[0]);
@@ -70,7 +65,7 @@ constexpr bool test() {
// operator->
{
- std::same_as<Ty*> decltype(auto) ptr = it.operator->();
+ std::same_as<Foo*> decltype(auto) ptr = it.operator->();
ASSERT_NOEXCEPT(it->x);
assert(ptr->x == 1);
assert(ptr == &arr[0]);
@@ -83,9 +78,5 @@ int main(int, char**) {
test<three_way_contiguous_iterator<Foo*>>();
static_assert(test<three_way_contiguous_iterator<Foo*>>());
- // bounded overload
- test<Foo2*, Foo2>();
- static_assert(test<Foo2*, Foo2>());
-
return 0;
}
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
index e633dc67803f4..b3b72a0728ed7 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
@@ -29,15 +29,3 @@ static_assert(std::is_same_v<CapIter::difference_type, std::iter_difference_t<It
static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
static_assert(std::is_same_v<CapIter::value_type, std::iter_value_t<It>>);
-
-struct alignas(8) A {};
-// bounded overload
-using It2 = A*;
-using CapIter2 = std::__capacity_aware_iterator<It2, A[], 126>;
-
-static_assert(std::is_same_v<CapIter2::iterator_category, std::iterator_traits<It2>::iterator_category>);
-static_assert(std::is_same_v<CapIter2::iterator_concept, std::contiguous_iterator_tag>);
-static_assert(std::is_same_v<CapIter2::difference_type, std::iter_difference_t<It2>>);
-static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
-static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
-static_assert(std::is_same_v<CapIter2::value_type, std::iter_value_t<It2>>);
diff --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..a34dee56f3274
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template <class _Iterator>
+// class __static_packed_bounded_iter;
+//
+// Arithmetic operators
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+
+#include "test_macros.h"
+
+struct alignas(8) Foo {
+ int x;
+
+ constexpr Foo(int y) : x(y) {}
+
+ constexpr bool operator==(const Foo& rhs) const { return x == rhs.x; }
+};
+
+template <class Iter>
+TEST_CONSTEXPR_CXX14 bool tests() {
+ Foo array[] = {40, 41, 42, 43, 44};
+ Foo* b = array + 0;
+ Foo* e = array + 5;
+ using BoundedIter = std::__static_packed_bounded_iterator<Iter, decltype(array), std::size(array)>;
+ // ++it
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter& result = ++iter;
+ assert(&result == &iter);
+ assert(*iter == 41);
+ }
+ // it++
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter result = iter++;
+ assert(*result == 40);
+ assert(*iter == 41);
+ }
+ // --it
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b)) + 3;
+ BoundedIter& result = --iter;
+ assert(&result == &iter);
+ assert(*iter == 42);
+ }
+ // it--
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b)) + 3;
+ BoundedIter result = iter--;
+ assert(*result == 43);
+ assert(*iter == 42);
+ }
+ // it += n
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter& result = (iter += 3);
+ assert(&result == &iter);
+ assert(*iter == 43);
+ }
+ // it + n
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter result = iter + 3;
+ assert(*iter == 40);
+ assert(*result == 43);
+ }
+ // n + it
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter result = 3 + iter;
+ assert(*iter == 40);
+ assert(*result == 43);
+ }
+ // it -= n
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b)) + 3;
+ BoundedIter& result = (iter -= 3);
+ assert(&result == &iter);
+ assert(*iter == 40);
+ }
+ // it - n
+ {
+ BoundedIter iter = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b)) + 3;
+ BoundedIter result = iter - 3;
+ assert(*iter == 43);
+ assert(*result == 40);
+ }
+ // it - it
+ {
+ BoundedIter iter1 = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter iter2 = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(e));
+ std::ptrdiff_t result = iter2 - iter1;
+ assert(result == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ tests<Foo*>();
+ static_assert(tests<Foo*>());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp
new file mode 100644
index 0000000000000..94d097132bd85
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none
+
+// template <class _Ptr, class _Tag, class _RangeCapacity>
+// class __static_packed_bounded_iterator;
+
+// Check assert failure if advancing, rewinding past start or past end, and dereferencing if at end
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <iterator>
+
+#include "check_assertion.h"
+
+struct alignas(4) Foo {
+ char x;
+ Foo(int y) : x(y) {}
+};
+
+template <typename Ptr, typename Ty = Foo>
+void test() {
+ Ty arr[] = {1, 2};
+
+ constexpr long sz = std::size(arr);
+
+ using BoundedIter = std::__static_packed_bounded_iterator<Ptr, decltype(arr), sz>;
+
+ BoundedIter it = std::__make_static_packed_bounded_iter<Ptr, decltype(arr), sz>(Ptr(arr));
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it--, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ --it, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it += -1, "__static_packed_bounded_iterator::operator+=: Attempt to rewind an iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it += (sz + 1), "__static_packed_bounded_iterator::operator+=: Attempt to advance an iterator past the end");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it -= 1, "__static_packed_bounded_iterator::operator-=: Attempt to rewind an iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it -= -(sz + 1), "__static_packed_bounded_iterator::operator-=: Attempt to advance an iterator past the end");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it[sz], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator at or past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it[-1], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator past the start");
+
+ it += sz;
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it++, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ++it, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ *it, "__static_packed_bounded_iterator::operator*: Attempt to dereference an iterator at the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ it.operator->(), "__static_packed_bounded_iterator::operator->: Attempt to dereference an iterator at the end");
+}
+
+int main(int, char**) {
+ test<Foo*>();
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.pass.cpp
new file mode 100644
index 0000000000000..7c7320a2589ae
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// template <class _Ptr, class _Tag, size_t _RangeCapacity>
+// class __static_packed_bounded_iter;
+//
+// Comparison operators
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cassert>
+#include <compare>
+#include <concepts>
+#include <iterator>
+
+#include "test_macros.h"
+
+struct alignas(4) Foo {
+ int x;
+ constexpr Foo(int y) : x(y) {}
+};
+
+template <class Iter>
+TEST_CONSTEXPR_CXX14 bool tests() {
+ Foo array[] = {0, 1};
+ Foo* b = array + 0;
+ using BoundedIter = std::__static_packed_bounded_iterator<Iter, decltype(array), std::size(array)>;
+ BoundedIter const iter1 = std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter const iter2 =
+ std::__make_static_packed_bounded_iter<Iter, decltype(array), std::size(array)>(Iter(b)) + 2;
+
+ // operator==
+ {
+ assert(iter1 == iter1);
+ assert(!(iter1 == iter2));
+ }
+ // operator!=
+ {
+ assert(iter1 != iter2);
+ assert(!(iter1 != iter1));
+ }
+ // operator<
+ {
+ assert(iter1 < iter2);
+ assert(!(iter2 < iter1));
+ assert(!(iter1 < iter1));
+ }
+ // operator>
+ {
+ assert(iter2 > iter1);
+ assert(!(iter1 > iter2));
+ assert(!(iter1 > iter1));
+ }
+ // operator<=
+ {
+ assert(iter1 <= iter2);
+ assert(!(iter2 <= iter1));
+ assert(iter1 <= iter1);
+ }
+ // operator>=
+ {
+ assert(iter2 >= iter1);
+ assert(!(iter1 >= iter2));
+ assert(iter1 >= iter1);
+ }
+
+#if TEST_STD_VER >= 20
+ // P1614
+ std::same_as<std::strong_ordering> decltype(auto) r1 = iter1 <=> iter2;
+ assert(r1 == std::strong_ordering::less);
+#endif
+
+ return true;
+}
+
+int main(int, char**) {
+ tests<Foo*>();
+ static_assert(tests<Foo*>(), "");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp
new file mode 100644
index 0000000000000..9b71afabcca39
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// template <class _Ptr, class _Tag, size_t _RangeCapacity>
+// class __static_packed_bounded_iter;
+//
+// Dereference and indexing operators
+
+// REQUIRES: has-unix-headers, std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cassert>
+#include <iterator>
+
+#include "test_macros.h"
+
+struct alignas(8) Foo {
+ char x;
+ constexpr bool operator==(Foo const& other) const { return x == other.x; }
+};
+
+template <class Iter>
+TEST_CONSTEXPR_CXX14 bool tests() {
+ Foo array[] = {Foo{40}, Foo{41}, Foo{42}, Foo{43}, Foo{44}};
+ Foo* b = array + 0;
+
+ using BoundedIter = std::__static_packed_bounded_iterator<Foo*, decltype(array), std::size(array)>;
+
+ BoundedIter const iter1 = std::__make_static_packed_bounded_iter<Foo*, decltype(array), std::size(array)>(Iter(b));
+ BoundedIter const iter2 =
+ std::__make_static_packed_bounded_iter<Foo*, decltype(array), std::size(array)>(Iter(b)) + 5;
+
+ // operator*
+ assert(*iter1 == Foo{40});
+ // operator->
+ assert(iter1->x == 40);
+ // operator[]
+ assert(iter1[0] == Foo{40});
+ assert(iter1[1] == Foo{41});
+ assert(iter1[2] == Foo{42});
+ assert(iter2[-1] == Foo{44});
+ assert(iter2[-2] == Foo{43});
+
+ return true;
+}
+
+int main(int, char**) {
+ tests<Foo*>();
+ static_assert(tests<Foo*>(), "");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..d3987ffbd0c6a
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template <class _Ptr, class _Tag, class _RangeCapacity>
+// class __packed_static_bounded_iterator;
+//
+// Nested types
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+using Iter = std::__static_packed_bounded_iterator<int*, int[], 2>;
+
+static_assert(std::is_same_v<Iter::value_type, int>, "");
+static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>, "");
+static_assert(std::is_same_v<Iter::pointer, int*>, "");
+static_assert(std::is_same_v<Iter::reference, int&>, "");
+static_assert(std::is_same_v<Iter::iterator_category, std::random_access_iterator_tag>, "");
+static_assert(std::is_same_v<Iter::iterator_concept, std::contiguous_iterator_tag>, "");
+
+static_assert(sizeof(Iter) == sizeof(void*));
More information about the libcxx-commits
mailing list