Improvements and New Features
-----------------------------
+- ``std::string_view`` now provides iterators that check for out-of-bounds accesses when the safe
+ libc++ mode is enabled.
+
Deprecations and Removals
-------------------------
if (__year < 1000 || __year > 9999)
__formatter::__format_century(__year, __sstr);
else
- __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
} break;
case _CharT('j'):
// an intemediate step.
__sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count();
else
- __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
break;
case _CharT('q'):
case _CharT('S'):
case _CharT('T'):
- __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
if constexpr (__use_fraction<_Tp>())
__formatter::__format_sub_seconds(__value, __sstr);
break;
if (__year < 1000)
__formatter::__format_year(__year, __sstr);
else
- __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
} break;
case _CharT('F'): {
__formatter::__format_year(__year, __sstr);
__sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);
} else
- __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
} break;
case _CharT('O'):
// fractional part should be formatted.
if (*(__it + 1) == 'S') {
++__it;
- __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
__formatter::__format_sub_seconds(__value, __sstr);
break;
}
++__it;
[[fallthrough]];
default:
- __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
break;
}
} else {
template <class _CharT>
class _LIBCPP_TEMPLATE_VIS __parser_chrono {
+ using _ConstIterator = typename basic_format_parse_context<_CharT>::const_iterator;
+
public:
_LIBCPP_HIDE_FROM_ABI constexpr auto
__parse(basic_format_parse_context<_CharT>& __parse_ctx, __fields __fields, __flags __flags)
-> decltype(__parse_ctx.begin()) {
- const _CharT* __begin = __parser_.__parse(__parse_ctx, __fields);
- const _CharT* __end = __parse_ctx.end();
+ _ConstIterator __begin = __parser_.__parse(__parse_ctx, __fields);
+ _ConstIterator __end = __parse_ctx.end();
if (__begin == __end)
return __begin;
- const _CharT* __last = __parse_chrono_specs(__begin, __end, __flags);
- __chrono_specs_ = basic_string_view<_CharT>{__begin, __last};
+ _ConstIterator __last = __parse_chrono_specs(__begin, __end, __flags);
+ __chrono_specs_ = basic_string_view<_CharT>{__begin, __last};
return __last;
}
basic_string_view<_CharT> __chrono_specs_;
private:
- _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
- __parse_chrono_specs(const _CharT* __begin, const _CharT* __end, __flags __flags) {
+ _LIBCPP_HIDE_FROM_ABI constexpr _ConstIterator
+ __parse_chrono_specs(_ConstIterator __begin, _ConstIterator __end, __flags __flags) {
_LIBCPP_ASSERT(__begin != __end,
"When called with an empty input the function will cause "
"undefined behavior by evaluating data not in the input");
/// \pre *__begin == '%'
/// \post __begin points at the end parsed conversion-spec
_LIBCPP_HIDE_FROM_ABI constexpr void
- __parse_conversion_spec(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
+ __parse_conversion_spec(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier chrono conversion-spec");
/// \pre *__begin == 'E'
/// \post __begin is incremented by one.
_LIBCPP_HIDE_FROM_ABI constexpr void
- __parse_modifier_E(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
+ __parse_modifier_E(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier E");
/// \pre *__begin == 'O'
/// \post __begin is incremented by one.
_LIBCPP_HIDE_FROM_ABI constexpr void
- __parse_modifier_O(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
+ __parse_modifier_O(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier O");
__unicode::__code_point_view<_CharT> __view{__values.begin(), __values.end()};
while (!__view.__at_end()) {
- const _CharT* __first = __view.__position();
+ auto __first = __view.__position();
typename __unicode::__consume_p2286_result __result = __view.__consume_p2286();
if (__result.__ill_formed_size == 0) {
if (!__formatter::__is_escaped_sequence_written(__str, __result.__value, __mark))
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __parse_ctx) {
- const _CharT* __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_tuple);
+ auto __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_tuple);
// [format.tuple]/7
// ... For each element e in underlying_, if e.set_debug_format()
std::__set_debug_format(std::get<_Index>(__underlying_));
});
- const _CharT* __end = __parse_ctx.end();
+ auto __end = __parse_ctx.end();
if (__begin == __end)
return __begin;
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __parse_ctx) {
- const _CharT* __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_range);
- const _CharT* __end = __parse_ctx.end();
+ auto __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_range);
+ auto __end = __parse_ctx.end();
if (__begin == __end)
return __begin;
__format_spec::__parser<_CharT> __parser_{.__alignment_ = __format_spec::__alignment::__left};
private:
- _LIBCPP_HIDE_FROM_ABI constexpr void __parse_type(const _CharT*& __begin, const _CharT* __end) {
+ template <contiguous_iterator _Iterator>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __parse_type(_Iterator& __begin, _Iterator __end) {
switch (*__begin) {
case _CharT('m'):
if constexpr (__fmt_pair_like<_Tp>) {
#include <__functional/hash.h>
#include <__functional/unary_function.h>
#include <__fwd/string_view.h>
+#include <__iterator/bounded_iter.h>
#include <__iterator/concepts.h>
#include <__iterator/readable_traits.h>
#include <__iterator/reverse_iterator.h>
using const_pointer = const _CharT*;
using reference = _CharT&;
using const_reference = const _CharT&;
+#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING
+ using const_iterator = __bounded_iter<const_pointer>;
+#else
using const_iterator = const_pointer; // See [string.view.iterators]
+#endif
using iterator = const_iterator;
using const_reverse_iterator = _VSTD::reverse_iterator<const_iterator>;
using reverse_iterator = const_reverse_iterator;
const_iterator end() const _NOEXCEPT { return cend(); }
_LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY
- const_iterator cbegin() const _NOEXCEPT { return __data_; }
+ const_iterator cbegin() const _NOEXCEPT {
+#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING
+ return std::__make_bounded_iter(data(), data(), data() + size());
+#else
+ return __data_;
+#endif
+ }
_LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY
- const_iterator cend() const _NOEXCEPT { return __data_ + __size_; }
+ const_iterator cend() const _NOEXCEPT {
+#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING
+ return std::__make_bounded_iter(data() + size(), data(), data() + size());
+#else
+ return __data_ + __size_;
+#endif
+ }
_LIBCPP_CONSTEXPR_SINCE_CXX17 _LIBCPP_INLINE_VISIBILITY
const_reverse_iterator rbegin() const _NOEXCEPT { return const_reverse_iterator(cend()); }
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that std::string_view's iterators check for OOB accesses when the debug mode is enabled.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: !libcpp-has-debug-mode
+
+#include <string_view>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ // string_view::iterator
+ {
+ std::string_view const str("hello world");
+ {
+ auto it = str.end();
+ TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.end();
+ TEST_LIBCPP_ASSERT_FAILURE(it.operator->(), "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.begin();
+ TEST_LIBCPP_ASSERT_FAILURE(it[99], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range");
+ }
+ }
+
+ // string_view::const_iterator
+ {
+ std::string_view const str("hello world");
+ {
+ auto it = str.cend();
+ TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.cend();
+ TEST_LIBCPP_ASSERT_FAILURE(it.operator->(), "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.cbegin();
+ TEST_LIBCPP_ASSERT_FAILURE(it[99], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range");
+ }
+ }
+
+ // string_view::reverse_iterator
+ {
+ std::string_view const str("hello world");
+ {
+ auto it = str.rend();
+ TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.rend();
+ TEST_LIBCPP_ASSERT_FAILURE(it.operator->(), "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.rbegin();
+ TEST_LIBCPP_ASSERT_FAILURE(it[99], "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator");
+ }
+ }
+
+ // string_view::const_reverse_iterator
+ {
+ std::string_view const str("hello world");
+ {
+ auto it = str.crend();
+ TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.crend();
+ TEST_LIBCPP_ASSERT_FAILURE(it.operator->(), "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator");
+ }
+ {
+ auto it = str.crbegin();
+ TEST_LIBCPP_ASSERT_FAILURE(it[99], "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator");
+ }
+ }
+
+ return 0;
+}
for (const auto& d : data) {
assert(d.code_points.size() == d.breaks.size());
- std::__unicode::__extended_grapheme_cluster_view view{d.input.data(), d.input.data() + d.input.size()};
+ std::__unicode::__extended_grapheme_cluster_view view{d.input.begin(), d.input.end()};
for (size_t i = 0; i < d.breaks.size(); ++i) {
auto r = view.__consume();
assert(r.__code_point_ == d.code_points[i]);
- assert(r.__last_ == d.input.data() + d.breaks[i]);
+ assert(r.__last_ == d.input.begin() + d.breaks[i]);
}
}
}
constexpr MoveOnlyView(std::string_view v) : view_(v) {}
constexpr MoveOnlyView(MoveOnlyView&&) = default;
constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default;
- constexpr const char* begin() const { return view_.begin(); }
- constexpr const char* end() const { return view_.end(); }
+ constexpr std::string_view::const_iterator begin() const { return view_.begin(); }
+ constexpr std::string_view::const_iterator end() const { return view_.end(); }
constexpr bool operator==(MoveOnlyView rhs) const { return view_ == rhs.view_; }
};
static_assert( std::ranges::view<MoveOnlyView>);
// C++23 and later.
template <std::ranges::range R>
constexpr StrView(R&& r) : buffer_(r.begin(), r.end()) {}
- constexpr const char* begin() const { return buffer_.begin(); }
- constexpr const char* end() const { return buffer_.end(); }
+ constexpr std::string_view::const_iterator begin() const { return buffer_.begin(); }
+ constexpr std::string_view::const_iterator end() const { return buffer_.end(); }
constexpr bool operator==(const StrView& rhs) const { return buffer_ == rhs.buffer_; }
};
static_assert( std::ranges::random_access_range<StrView>);
constexpr ForwardViewCommonIfConst& operator=(const ForwardViewCommonIfConst&) = default;
constexpr forward_iterator<char*> begin() { return forward_iterator<char*>(nullptr); }
constexpr std::default_sentinel_t end() { return std::default_sentinel; }
- constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); }
- constexpr forward_iterator<const char*> end() const { return forward_iterator<const char*>(view_.end()); }
+ constexpr forward_iterator<std::string_view::const_iterator> begin() const { return forward_iterator<std::string_view::const_iterator>(view_.begin()); }
+ constexpr forward_iterator<std::string_view::const_iterator> end() const { return forward_iterator<std::string_view::const_iterator>(view_.end()); }
};
bool operator==(forward_iterator<char*>, std::default_sentinel_t) { return false; }
constexpr ForwardViewNonCommonRange& operator=(const ForwardViewNonCommonRange&) = default;
constexpr forward_iterator<char*> begin() { return forward_iterator<char*>(nullptr); }
constexpr std::default_sentinel_t end() { return std::default_sentinel; }
- constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); }
+ constexpr forward_iterator<std::string_view::const_iterator> begin() const { return forward_iterator<std::string_view::const_iterator>(view_.begin()); }
constexpr std::default_sentinel_t end() const { return std::default_sentinel; }
};
-bool operator==(forward_iterator<const char*>, std::default_sentinel_t) { return false; }
+bool operator==(forward_iterator<std::string_view::const_iterator>, std::default_sentinel_t) { return false; }
constexpr bool test() {
// non-const: forward_range<V> && simple_view<V> && simple_view<P> -> outer-iterator<Const = true>
constexpr explicit CopyableView() = default;
constexpr CopyableView(const char* ptr) : view_(ptr) {}
constexpr CopyableView(std::string_view v) : view_(v) {}
- constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); }
- constexpr forward_iterator<const char*> end() const { return forward_iterator<const char*>(view_.end()); }
+ constexpr forward_iterator<std::string_view::const_iterator> begin() const { return forward_iterator<std::string_view::const_iterator>(view_.begin()); }
+ constexpr forward_iterator<std::string_view::const_iterator> end() const { return forward_iterator<std::string_view::const_iterator>(view_.end()); }
constexpr bool operator==(const CopyableView& rhs) const { return view_ == rhs.view_; }
};
static_assert( std::ranges::forward_range<CopyableView>);
constexpr ForwardView(std::string_view v) : view_(v) {}
constexpr ForwardView(ForwardView&&) = default;
constexpr ForwardView& operator=(ForwardView&&) = default;
- constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); }
- constexpr forward_iterator<const char*> end() const { return forward_iterator<const char*>(view_.end()); }
+ constexpr forward_iterator<std::string_view::const_iterator> begin() const { return forward_iterator<std::string_view::const_iterator>(view_.begin()); }
+ constexpr forward_iterator<std::string_view::const_iterator> end() const { return forward_iterator<std::string_view::const_iterator>(view_.end()); }
};
static_assert( std::ranges::forward_range<ForwardView>);
static_assert( std::ranges::forward_range<const ForwardView>);
constexpr ForwardOnlyIfNonConstView(ForwardOnlyIfNonConstView&&) = default;
constexpr ForwardOnlyIfNonConstView& operator=(ForwardOnlyIfNonConstView&&) = default;
- constexpr forward_iterator<const char*> begin() { return forward_iterator<const char*>(view_.begin()); }
- constexpr forward_iterator<const char*> end() { return forward_iterator<const char*>(view_.end()); }
- constexpr almost_forward_iterator<const char*> begin() const {
- return almost_forward_iterator<const char*>(view_.begin());
+ constexpr forward_iterator<std::string_view::const_iterator> begin() { return forward_iterator<std::string_view::const_iterator>(view_.begin()); }
+ constexpr forward_iterator<std::string_view::const_iterator> end() { return forward_iterator<std::string_view::const_iterator>(view_.end()); }
+ constexpr almost_forward_iterator<std::string_view::const_iterator> begin() const {
+ return almost_forward_iterator<std::string_view::const_iterator>(view_.begin());
}
- constexpr almost_forward_iterator<const char*> end() const {
- return almost_forward_iterator<const char*>(view_.end());
+ constexpr almost_forward_iterator<std::string_view::const_iterator> end() const {
+ return almost_forward_iterator<std::string_view::const_iterator>(view_.end());
}
};
static_assert( std::ranges::forward_range<ForwardOnlyIfNonConstView>);