[libcxx-commits] [libcxx] [libc++] Add compact bounded specialization for capacity_aware_iterator (PR #208271)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 10 21:38:16 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: William Tran-Viet (smallp-o-p)

<details>
<summary>Changes</summary>

- 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.

---
Full diff: https://github.com/llvm/llvm-project/pull/208271.diff


6 Files Affected:

- (modified) libcxx/include/__iterator/capacity_aware_iterator.h (+224) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp (+15-7) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp (+55-3) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp (+5-2) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp (+15-6) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp (+12) 


``````````diff
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index a516feef60f36..36281bccdf2ec 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,11 @@
 #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
@@ -51,6 +56,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 +178,224 @@ 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/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index f4aa5c29790ff..d84648a75e8f7 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++()
   {
@@ -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;
 
@@ -162,5 +166,9 @@ 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 ceac20d549c34..889e67899d8a4 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 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");
+}
 
 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..149f37691eae0 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*, 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 42ab763896e2f..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,14 +25,19 @@
 #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; }
 };
 
-template <typename Iter>
+struct alignas(8) Foo2 {
+  int 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..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
@@ -29,3 +29,15 @@ 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>>);

``````````

</details>


https://github.com/llvm/llvm-project/pull/208271


More information about the libcxx-commits mailing list