diff --git a/common/include/partial_range.h b/common/include/partial_range.h index db263f27a..18121e795 100644 --- a/common/include/partial_range.h +++ b/common/include/partial_range.h @@ -196,8 +196,8 @@ namespace partial_range_detail namespace { -template -inline void check_range_bounds(const char *file, unsigned line, const char *estr, const uintptr_t t, const std::size_t o, const std::size_t l, const std::size_t d) +template +inline void check_range_bounds(const char *file, unsigned line, const char *estr, const uintptr_t t, const std::size_t index_begin, const std::size_t index_end, const std::size_t d) { #ifdef DXX_CONSTANT_TRUE /* @@ -230,21 +230,33 @@ inline void check_range_bounds(const char *file, unsigned line, const char *estr #endif #define PARTIAL_RANGE_CHECK_BOUND(EXPR,S) \ PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S), \ - ((EXPR > d) && (I::template report(file, line, estr, #S, EXPR, t, d), 0)) - PARTIAL_RANGE_CHECK_BOUND(o, begin); - PARTIAL_RANGE_CHECK_BOUND(l, end); + ((EXPR > d) && (range_exception::template report(file, line, estr, #S, EXPR, t, d), 0)) + PARTIAL_RANGE_CHECK_BOUND(index_begin, begin); + PARTIAL_RANGE_CHECK_BOUND(index_end, end); #undef PARTIAL_RANGE_CHECK_BOUND #undef PARTIAL_RANGE_COMPILE_CHECK_BOUND } #ifdef DXX_HAVE_BUILTIN_OBJECT_SIZE -template -inline void check_range_object_size(const char *file, unsigned line, const char *estr, P &ref, const std::size_t o, const std::size_t l) +template +inline void check_range_object_size(const char *file, unsigned line, const char *estr, P &ref, const std::size_t index_begin, const std::size_t index_end) { const auto ptr = std::addressof(ref); + /* Get the size of the object, according to the compiler's data + * analysis. If a size is known, check that the index values are in + * range. If no size is known, do nothing. + * + * The compiler may return a value of "unknown" here if the target + * object is dynamically allocated with unknown extents, or if the + * optimizer is not able to identify a specific object behind the + * pointer. The latter could happen if the object has a fixed size + * assigned in a different translation unit, and only the pointer to + * the object is visible in the translation unit evaluating this + * function. + */ const std::size_t bos = __builtin_object_size(ptr, 1); if (bos != static_cast(-1)) - check_range_bounds(file, line, estr, reinterpret_cast(ptr), o, l, bos / sizeof(P)); + check_range_bounds(file, line, estr, reinterpret_cast(ptr), index_begin, index_end, bos / sizeof(P)); } /* When P refers to a temporary, this overload is picked. Temporaries @@ -252,7 +264,7 @@ inline void check_range_object_size(const char *file, unsigned line, const char * be present if iterator.operator*() returns a proxy object, rather * than a reference to an element in the container. */ -template +template void check_range_object_size(const char *, unsigned, const char *, const P &&, std::size_t, std::size_t) {} #endif @@ -264,22 +276,22 @@ namespace { template [[nodiscard]] -inline partial_range_t (unchecked_partial_range)(const char *const file, const unsigned line, const char *const estr, I range_begin, const std::size_t o, const std::size_t l) +inline partial_range_t (unchecked_partial_range)(const char *const file, const unsigned line, const char *const estr, I range_begin, const std::size_t index_begin, const std::size_t index_end) { #ifdef DXX_CONSTANT_TRUE - /* Compile-time only check. Runtime handles (o > l) correctly, and - * it can happen in a correct program. If it is guaranteed to - * happen, then the range is always empty, which likely indicates a - * bug. + /* Compile-time only check. Runtime handles (index_begin > + * index_end) correctly, and it can happen in a correct program. If + * it is guaranteed to happen, then the range is always empty, which + * likely indicates a bug. */ - if (DXX_CONSTANT_TRUE(!(o < l))) + if (DXX_CONSTANT_TRUE(!(index_begin < index_end))) DXX_ALWAYS_ERROR_FUNCTION(partial_range_is_always_empty, "offset never less than length"); #endif #ifdef DXX_HAVE_BUILTIN_OBJECT_SIZE /* Avoid iterator dereference if range is empty */ - if (l) + if (index_end) { - partial_range_detail::check_range_object_size::partial_range_error, required_buffer_size>(file, line, estr, *range_begin, o, l); + partial_range_detail::check_range_object_size::partial_range_error, required_buffer_size>(file, line, estr, *range_begin, index_begin, index_end); } #else (void)file; @@ -287,64 +299,92 @@ inline partial_range_t (unchecked_partial_range)(const char *const file, cons (void)estr; #endif auto range_end = range_begin; - /* Use <= so that (o == 0) makes the expression always-true, so the - * compiler will optimize out the test. + /* Use <= so that (index_begin == 0) makes the expression + * always-true, so the compiler will optimize out the test. */ - if (o <= l) + if (index_begin <= index_end) { using std::advance; - advance(range_begin, o); - advance(range_end, l); + advance(range_begin, index_begin); + advance(range_end, index_end); } return {range_begin, range_end}; } -template +template [[nodiscard]] -inline partial_range_t (unchecked_partial_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], I range_begin, const UO &o, const UL &l) +inline partial_range_t (unchecked_partial_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], I range_begin, const index_begin_type &index_begin, const index_end_type &index_end) { /* Require unsigned length */ - static_assert(std::is_unsigned::value, "offset to partial_range must be unsigned"); - static_assert(std::is_unsigned
    ::value, "length to partial_range must be unsigned"); + static_assert(std::is_unsigned::value, "offset to partial_range must be unsigned"); + static_assert(std::is_unsigned::value, "length to partial_range must be unsigned"); return unchecked_partial_range>( - file, line, estr, range_begin, o, l + file, line, estr, range_begin, index_begin, index_end ); } -template +template [[nodiscard]] -inline partial_range_t (unchecked_partial_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], I range_begin, const UL &l) +inline partial_range_t (unchecked_partial_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], I range_begin, const index_end_type &index_end) { - return unchecked_partial_range(file, line, estr, range_begin, 0, l); + return unchecked_partial_range(file, line, estr, range_begin, 0, index_end); } -template ()))> +template ())) + > [[nodiscard]] -inline partial_range_t (partial_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, const UO &o, const UL &l) +inline partial_range_t (partial_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], T &t, const index_begin_type &index_begin, const index_end_type &index_end) { - partial_range_detail::check_range_bounds::partial_range_error, partial_range_detail::required_buffer_size>(file, line, estr, reinterpret_cast(std::addressof(t)), o, l, std::size(t)); - return unchecked_partial_range(file, line, estr, std::begin(t), o, l); + partial_range_detail::check_range_bounds::partial_range_error, partial_range_detail::required_buffer_size>(file, line, estr, reinterpret_cast(std::addressof(t)), index_begin, index_end, std::size(t)); + return unchecked_partial_range(file, line, estr, std::begin(t), index_begin, index_end); } -template +template [[nodiscard]] -inline auto (partial_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], T &t, const UL &l) +inline auto (partial_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], T &t, const index_end_type &index_end) { - return partial_range(file, line, estr, t, 0, l); + return partial_range(file, line, estr, t, 0, index_end); } -template +template [[nodiscard]] -inline auto (partial_const_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], const T &t, const UO &o, const UL &l) +inline auto (partial_const_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], const T &t, const index_begin_type &index_begin, const index_end_type &index_end) { - return partial_range(file, line, estr, t, o, l); + return partial_range(file, line, estr, t, index_begin, index_end); } -template +template [[nodiscard]] -inline auto (partial_const_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], const T &t, const UL &l) +inline auto (partial_const_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], const T &t, const index_end_type &index_end) { - return partial_range(file, line, estr, t, l); + return partial_range(file, line, estr, t, index_end); } template ()))> @@ -359,11 +399,22 @@ inline partial_range_t (make_range)(T &t) /* Explicitly block use on rvalue t because returned partial_range_t * will outlive the rvalue. */ -template ()))> -partial_range_t (partial_const_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], const T &&t, const UO &o, const UL &l) = delete; +template ())) + > +partial_range_t (partial_const_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], const T &&t, const index_begin_type &index_begin, const index_end_type &index_end) = delete; -template ()))> -partial_range_t (partial_const_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], const T &&t, const UL &l) = delete; +template ())) + > +partial_range_t (partial_const_range)(const char (&file)[NF], const unsigned line, const char (&estr)[NE], const T &&t, const index_end_type &index_end) = delete; #define unchecked_partial_range(T,...) unchecked_partial_range(__FILE__, __LINE__, #T, T, ##__VA_ARGS__) #define partial_range(T,...) partial_range(__FILE__, __LINE__, #T, T, ##__VA_ARGS__)