2014-06-01 17:55:23 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the DXX-Rebirth project <http://www.dxx-rebirth.com/>.
|
|
|
|
* It is copyright by its individual contributors, as recorded in the
|
|
|
|
* project's Git history. See COPYING.txt at the top level for license
|
|
|
|
* terms and a link to the Git history.
|
|
|
|
*/
|
2014-01-18 18:02:02 +00:00
|
|
|
#pragma once
|
2014-04-19 21:54:35 +00:00
|
|
|
#include <stdexcept>
|
2014-12-13 16:57:09 +00:00
|
|
|
#include <iterator>
|
2014-04-19 21:54:35 +00:00
|
|
|
#include <cstdio>
|
2014-11-30 17:00:03 +00:00
|
|
|
#include <string>
|
2015-02-14 22:48:30 +00:00
|
|
|
#include "fwd-partial_range.h"
|
2014-04-19 21:54:35 +00:00
|
|
|
#include "compiler-addressof.h"
|
2014-02-01 23:41:55 +00:00
|
|
|
#include "compiler-type_traits.h"
|
2014-01-18 18:02:02 +00:00
|
|
|
|
2015-02-14 22:48:30 +00:00
|
|
|
namespace partial_range_detail
|
|
|
|
{
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static inline auto adl_begin(T &t) -> decltype(begin(t))
|
|
|
|
{
|
|
|
|
return begin(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static inline auto adl_end(T &t) -> decltype(end(t))
|
|
|
|
{
|
|
|
|
return end(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-12-13 16:57:09 +00:00
|
|
|
template <typename I>
|
2015-02-14 22:48:30 +00:00
|
|
|
class partial_range_t
|
2014-01-18 18:02:02 +00:00
|
|
|
{
|
2015-02-14 22:48:30 +00:00
|
|
|
public:
|
2014-12-13 16:57:09 +00:00
|
|
|
typedef I iterator;
|
2014-01-18 18:02:02 +00:00
|
|
|
iterator m_begin, m_end;
|
|
|
|
partial_range_t(iterator b, iterator e) :
|
|
|
|
m_begin(b), m_end(e)
|
|
|
|
{
|
|
|
|
}
|
2015-02-14 22:48:30 +00:00
|
|
|
partial_range_t(const partial_range_t &) = default;
|
|
|
|
partial_range_t(partial_range_t &&) = default;
|
2016-01-29 04:05:47 +00:00
|
|
|
partial_range_t &operator=(const partial_range_t &) = default;
|
2015-02-14 22:48:30 +00:00
|
|
|
template <typename T>
|
|
|
|
partial_range_t(T &t) :
|
|
|
|
m_begin(partial_range_detail::adl_begin(t)), m_end(partial_range_detail::adl_end(t))
|
|
|
|
{
|
|
|
|
}
|
2015-02-01 23:07:18 +00:00
|
|
|
__attribute_warn_unused_result
|
2014-01-18 18:02:02 +00:00
|
|
|
iterator begin() const { return m_begin; }
|
2015-02-01 23:07:18 +00:00
|
|
|
__attribute_warn_unused_result
|
2014-01-18 18:02:02 +00:00
|
|
|
iterator end() const { return m_end; }
|
2014-12-13 16:57:09 +00:00
|
|
|
bool empty() const __attribute_warn_unused_result
|
|
|
|
{
|
|
|
|
return m_begin == m_end;
|
|
|
|
}
|
2015-02-14 22:48:30 +00:00
|
|
|
__attribute_warn_unused_result
|
|
|
|
std::size_t size() const { return std::distance(m_begin, m_end); }
|
2014-12-13 16:57:09 +00:00
|
|
|
std::reverse_iterator<iterator> rbegin() const __attribute_warn_unused_result { return std::reverse_iterator<iterator>{m_end}; }
|
|
|
|
std::reverse_iterator<iterator> rend() const __attribute_warn_unused_result { return std::reverse_iterator<iterator>{m_begin}; }
|
|
|
|
partial_range_t<std::reverse_iterator<iterator>> reversed() const __attribute_warn_unused_result
|
|
|
|
{
|
|
|
|
return {rbegin(), rend()};
|
|
|
|
}
|
2014-01-18 18:02:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef DXX_HAVE_BOOST_FOREACH
|
|
|
|
#include <boost/range/const_iterator.hpp>
|
|
|
|
#include <boost/range/mutable_iterator.hpp>
|
|
|
|
|
|
|
|
namespace boost
|
|
|
|
{
|
|
|
|
template <typename iterator>
|
|
|
|
struct range_mutable_iterator< partial_range_t<iterator> >
|
|
|
|
{
|
|
|
|
typedef iterator type;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename iterator>
|
|
|
|
struct range_const_iterator< partial_range_t<iterator> >
|
|
|
|
{
|
|
|
|
typedef iterator type;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-08-09 03:59:49 +00:00
|
|
|
struct base_partial_range_error_t : std::out_of_range
|
2014-04-19 21:54:35 +00:00
|
|
|
{
|
2014-08-09 03:59:49 +00:00
|
|
|
DXX_INHERIT_CONSTRUCTORS(base_partial_range_error_t, std::out_of_range);
|
2015-05-09 17:39:02 +00:00
|
|
|
#define REPORT_FORMAT_STRING "%s:%u: %s %lu past %p end %lu \"%s\""
|
2016-02-06 22:12:55 +00:00
|
|
|
template <std::size_t NF, std::size_t NE>
|
|
|
|
/* Round reporting into large buckets. Code size is more
|
|
|
|
* important than stack space.
|
|
|
|
*/
|
|
|
|
using required_buffer_size = tt::integral_constant<std::size_t, ((sizeof(REPORT_FORMAT_STRING) + sizeof("65535") + (sizeof("18446744073709551615") * 2) + sizeof("0x0000000000000000") + (NF + NE + sizeof("begin"))) | 0xff) + 1>;
|
2015-01-11 05:08:30 +00:00
|
|
|
template <std::size_t N>
|
|
|
|
__attribute_cold
|
2015-08-03 03:11:24 +00:00
|
|
|
static void prepare(char (&buf)[N], unsigned long d, const char *estr, const char *file, unsigned line, const char *desc, unsigned long expr, const void *t)
|
2014-04-19 21:54:35 +00:00
|
|
|
{
|
2015-05-09 17:39:02 +00:00
|
|
|
snprintf(buf, sizeof(buf), REPORT_FORMAT_STRING, file, line, desc, expr, t, d, estr);
|
2014-04-19 21:54:35 +00:00
|
|
|
}
|
2015-05-09 17:39:02 +00:00
|
|
|
#undef REPORT_FORMAT_STRING
|
2014-08-09 03:59:49 +00:00
|
|
|
};
|
|
|
|
|
2015-07-25 23:10:47 +00:00
|
|
|
template <typename I>
|
2014-08-09 03:59:49 +00:00
|
|
|
struct partial_range_error_t : base_partial_range_error_t
|
|
|
|
{
|
|
|
|
DXX_INHERIT_CONSTRUCTORS(partial_range_error_t, base_partial_range_error_t);
|
2015-05-09 17:39:02 +00:00
|
|
|
template <std::size_t N>
|
|
|
|
__attribute_cold
|
|
|
|
__attribute_noreturn
|
2016-02-06 22:12:55 +00:00
|
|
|
static void report(const char *file, unsigned line, const char *estr, const char *desc, unsigned long expr, const void *t, unsigned long d);
|
2014-04-19 21:54:35 +00:00
|
|
|
};
|
2014-02-01 23:41:55 +00:00
|
|
|
|
2015-05-09 17:39:02 +00:00
|
|
|
template <typename T>
|
|
|
|
template <std::size_t N>
|
2016-02-06 22:12:55 +00:00
|
|
|
void partial_range_error_t<T>::report(const char *file, unsigned line, const char *estr, const char *desc, unsigned long expr, const void *t, unsigned long d)
|
2015-05-09 17:39:02 +00:00
|
|
|
{
|
|
|
|
char buf[N];
|
2015-08-03 03:11:24 +00:00
|
|
|
base_partial_range_error_t::prepare(buf, d, estr, file, line, desc, expr, t);
|
2015-05-09 17:39:02 +00:00
|
|
|
throw partial_range_error_t<T>(buf);
|
|
|
|
}
|
|
|
|
|
2015-02-01 23:07:18 +00:00
|
|
|
namespace partial_range_detail
|
|
|
|
{
|
|
|
|
|
2016-02-06 22:12:55 +00:00
|
|
|
template <typename I, std::size_t required_buffer_size>
|
|
|
|
static inline void check_range_bounds(const char *file, unsigned line, const char *estr, const void *t, const std::size_t o, const std::size_t l, const std::size_t d)
|
2014-01-18 18:02:02 +00:00
|
|
|
{
|
2014-08-09 04:14:59 +00:00
|
|
|
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
|
2015-02-01 23:07:18 +00:00
|
|
|
/*
|
|
|
|
* If EXPR and d are compile-time constant, and the (EXPR > d)
|
|
|
|
* branch is optimized out, then the expansion of
|
|
|
|
* PARTIAL_RANGE_COMPILE_CHECK_BOUND is optimized out, preventing
|
|
|
|
* the compile error.
|
|
|
|
*
|
|
|
|
* If EXPR and d are compile-time constant, and the (EXPR > d)
|
|
|
|
* branch is not optimized out, then this function is guaranteed to
|
|
|
|
* throw if it is ever called. In that case, the compile fails,
|
|
|
|
* since the program is guaranteed not to work as the programmer
|
|
|
|
* intends.
|
|
|
|
*
|
|
|
|
* If they are not compile-time constant, but the compiler can
|
|
|
|
* optimize based on constants, then it will optimize out the
|
|
|
|
* expansion of PARTIAL_RANGE_COMPILE_CHECK_BOUND, preventing the
|
|
|
|
* compile error. The function might throw on invalid inputs,
|
|
|
|
* including constant inputs that the compiler failed to recognize
|
|
|
|
* as compile-time constant.
|
|
|
|
*
|
|
|
|
* If the compiler cannot optimize based on the result of
|
|
|
|
* __builtin_constant_p (such as at -O0), then configure tests set
|
|
|
|
* !DXX_HAVE_BUILTIN_CONSTANT_P and the macro expands to nothing.
|
|
|
|
*/
|
2014-08-09 04:14:59 +00:00
|
|
|
#define PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S) \
|
2015-07-29 03:05:28 +00:00
|
|
|
(DXX_CONSTANT_TRUE(EXPR > d) && (DXX_ALWAYS_ERROR_FUNCTION(partial_range_will_always_throw_##S, #S " will always throw"), 0))
|
2014-08-09 04:14:59 +00:00
|
|
|
#else
|
2015-07-25 23:10:47 +00:00
|
|
|
#define PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S) static_cast<void>(0)
|
2014-08-09 04:14:59 +00:00
|
|
|
#endif
|
2014-04-19 21:54:35 +00:00
|
|
|
#define PARTIAL_RANGE_CHECK_BOUND(EXPR,S) \
|
2015-07-25 23:10:47 +00:00
|
|
|
PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S), \
|
2016-02-06 22:12:55 +00:00
|
|
|
((EXPR > d) && (partial_range_error_t<I>::template report<required_buffer_size>(file, line, estr, #S, EXPR, t, d), 0))
|
2015-07-25 23:10:47 +00:00
|
|
|
PARTIAL_RANGE_CHECK_BOUND(o, begin);
|
|
|
|
PARTIAL_RANGE_CHECK_BOUND(l, end);
|
2014-04-19 21:54:35 +00:00
|
|
|
#undef PARTIAL_RANGE_CHECK_BOUND
|
2014-12-13 16:57:09 +00:00
|
|
|
#undef PARTIAL_RANGE_COMPILE_CHECK_BOUND
|
2015-01-11 05:08:30 +00:00
|
|
|
}
|
|
|
|
|
2015-02-01 23:07:18 +00:00
|
|
|
/* C arrays lack a size method, but have a constant size */
|
2015-02-28 19:36:00 +00:00
|
|
|
template <typename T, std::size_t d>
|
2015-04-26 20:15:56 +00:00
|
|
|
static constexpr tt::integral_constant<std::size_t, d> get_range_size(T (&)[d])
|
2015-02-01 23:07:18 +00:00
|
|
|
{
|
2015-02-28 19:36:00 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2015-04-26 20:15:56 +00:00
|
|
|
static constexpr std::size_t get_range_size(T &t)
|
2015-02-28 19:36:00 +00:00
|
|
|
{
|
|
|
|
return t.size();
|
2015-02-01 23:07:18 +00:00
|
|
|
}
|
|
|
|
|
2016-02-06 22:12:55 +00:00
|
|
|
template <typename I, std::size_t N, typename T>
|
|
|
|
static inline void check_partial_range(const char *file, unsigned line, const char *estr, const T &t, const std::size_t o, const std::size_t l)
|
2015-02-01 23:07:18 +00:00
|
|
|
{
|
2016-02-06 22:12:55 +00:00
|
|
|
check_range_bounds<I, N>(file, line, estr, addressof(t), o, l, get_range_size(t));
|
2015-02-01 23:07:18 +00:00
|
|
|
}
|
|
|
|
|
2016-02-06 22:12:55 +00:00
|
|
|
#ifdef DXX_HAVE_BUILTIN_OBJECT_SIZE
|
|
|
|
template <typename I, std::size_t required_buffer_size, typename P>
|
|
|
|
static 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)
|
|
|
|
{
|
|
|
|
const auto ptr = addressof(ref);
|
|
|
|
const std::size_t bos = __builtin_object_size(ptr, 1);
|
|
|
|
if (bos != static_cast<std::size_t>(-1))
|
|
|
|
check_range_bounds<I, required_buffer_size>(file, line, estr, ptr, o, l, bos / sizeof(P));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* When P refers to a temporary, this overload is picked. Temporaries
|
|
|
|
* have no useful address, so they cannot be checked. A temporary would
|
|
|
|
* be present if iterator.operator*() returns a proxy object, rather
|
|
|
|
* than a reference to an element in the container.
|
|
|
|
*/
|
|
|
|
template <typename I, std::size_t required_buffer_size, typename P>
|
|
|
|
static inline void check_range_object_size(const char *, unsigned, const char *, const P &&, const std::size_t, const std::size_t) {}
|
|
|
|
#endif
|
|
|
|
|
2015-02-01 23:07:18 +00:00
|
|
|
}
|
|
|
|
|
2016-02-06 22:12:55 +00:00
|
|
|
template <typename I, std::size_t required_buffer_size>
|
2015-01-11 05:08:30 +00:00
|
|
|
__attribute_warn_unused_result
|
2016-02-06 22:12:55 +00:00
|
|
|
static inline partial_range_t<I> (unchecked_partial_range)(const char *file, unsigned line, const char *estr, I range_begin, const std::size_t o, const std::size_t l, tt::true_type)
|
2015-01-11 05:08:30 +00:00
|
|
|
{
|
|
|
|
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
|
2015-02-01 23:07:18 +00:00
|
|
|
/* 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.
|
|
|
|
*/
|
2015-07-29 03:05:28 +00:00
|
|
|
if (DXX_CONSTANT_TRUE(!(o < l)))
|
2015-01-11 05:08:30 +00:00
|
|
|
DXX_ALWAYS_ERROR_FUNCTION(partial_range_is_always_empty, "offset never less than length");
|
2015-07-25 23:10:47 +00:00
|
|
|
#endif
|
|
|
|
#ifdef DXX_HAVE_BUILTIN_OBJECT_SIZE
|
|
|
|
/* Avoid iterator dereference if range is empty */
|
|
|
|
if (l)
|
|
|
|
{
|
2016-02-06 22:12:55 +00:00
|
|
|
partial_range_detail::check_range_object_size<I, required_buffer_size>(file, line, estr, *range_begin, o, l);
|
2015-07-25 23:10:47 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
(void)file;
|
|
|
|
(void)line;
|
|
|
|
(void)estr;
|
2015-01-11 05:08:30 +00:00
|
|
|
#endif
|
|
|
|
auto range_end = range_begin;
|
2015-02-01 23:07:18 +00:00
|
|
|
/* Use <= so that (o == 0) makes the expression always-true, so the
|
|
|
|
* compiler will optimize out the test.
|
|
|
|
*/
|
2014-04-19 21:54:35 +00:00
|
|
|
if (o <= l)
|
|
|
|
{
|
2015-01-11 05:08:30 +00:00
|
|
|
using std::advance;
|
2014-04-19 21:54:35 +00:00
|
|
|
advance(range_begin, o);
|
|
|
|
advance(range_end, l);
|
|
|
|
}
|
2014-07-06 03:30:04 +00:00
|
|
|
return {range_begin, range_end};
|
2014-01-18 18:02:02 +00:00
|
|
|
}
|
2014-04-27 23:12:34 +00:00
|
|
|
|
2016-02-06 22:12:55 +00:00
|
|
|
template <typename I, std::size_t N>
|
|
|
|
static inline partial_range_t<I> (unchecked_partial_range)(const char *, unsigned, const char *, I, std::size_t, std::size_t, tt::false_type) = delete;
|
2015-01-11 05:08:30 +00:00
|
|
|
|
2015-07-25 23:10:47 +00:00
|
|
|
template <typename I, typename UO, typename UL, std::size_t NF, std::size_t NE>
|
2015-01-11 05:08:30 +00:00
|
|
|
__attribute_warn_unused_result
|
2015-07-25 23:10:47 +00:00
|
|
|
static inline partial_range_t<I> (unchecked_partial_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], I range_begin, const UO &o, const UL &l)
|
2015-01-11 05:08:30 +00:00
|
|
|
{
|
|
|
|
/* Require unsigned length */
|
|
|
|
typedef typename tt::conditional<tt::is_unsigned<UO>::value, tt::is_unsigned<UL>, tt::false_type>::type enable_type;
|
2016-02-06 22:12:55 +00:00
|
|
|
return unchecked_partial_range<I, base_partial_range_error_t::required_buffer_size<NF, NE>::value>(file, line, estr, range_begin, o, l, enable_type());
|
2015-01-11 05:08:30 +00:00
|
|
|
}
|
|
|
|
|
2015-07-25 23:10:47 +00:00
|
|
|
template <typename I, typename UL, std::size_t NF, std::size_t NE>
|
2015-01-11 05:08:30 +00:00
|
|
|
__attribute_warn_unused_result
|
2015-07-25 23:10:47 +00:00
|
|
|
static inline partial_range_t<I> (unchecked_partial_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], I range_begin, const UL &l)
|
2015-01-11 05:08:30 +00:00
|
|
|
{
|
2015-07-25 23:10:47 +00:00
|
|
|
return unchecked_partial_range<I, UL, UL>(file, line, estr, range_begin, 0, l);
|
2015-01-11 05:08:30 +00:00
|
|
|
}
|
|
|
|
|
2015-02-14 22:48:30 +00:00
|
|
|
template <typename T, typename UO, typename UL, std::size_t NF, std::size_t NE, typename I>
|
2015-01-11 05:08:30 +00:00
|
|
|
__attribute_warn_unused_result
|
2015-02-14 22:48:30 +00:00
|
|
|
static inline partial_range_t<I> (partial_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, const UO &o, const UL &l)
|
2015-01-11 05:08:30 +00:00
|
|
|
{
|
2016-02-06 22:12:55 +00:00
|
|
|
partial_range_detail::check_partial_range<I, base_partial_range_error_t::required_buffer_size<NF, NE>::value, T>(file, line, estr, t, o, l);
|
|
|
|
return unchecked_partial_range<I, UO, UL>(file, line, estr, begin(t), o, l);
|
2015-01-11 05:08:30 +00:00
|
|
|
}
|
2014-04-27 23:12:34 +00:00
|
|
|
|
2015-02-14 22:48:30 +00:00
|
|
|
template <typename T, typename UL, std::size_t NF, std::size_t NE, typename I>
|
2015-01-11 05:08:30 +00:00
|
|
|
__attribute_warn_unused_result
|
2015-02-14 22:48:30 +00:00
|
|
|
static inline partial_range_t<I> (partial_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, const UL &l)
|
2014-04-27 23:12:34 +00:00
|
|
|
{
|
2015-01-11 05:08:30 +00:00
|
|
|
return partial_range<T, UL, UL, NF, NE, I>(file, line, estr, t, 0, l);
|
2014-04-27 23:12:34 +00:00
|
|
|
}
|
2014-08-09 03:59:49 +00:00
|
|
|
|
2015-02-14 22:48:30 +00:00
|
|
|
template <typename T, typename UO, typename UL, std::size_t NF, std::size_t NE, typename I>
|
|
|
|
__attribute_warn_unused_result
|
|
|
|
static inline partial_range_t<I> (partial_const_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], const T &t, const UO &o, const UL &l)
|
|
|
|
{
|
|
|
|
return partial_range<const T, UO, UL, NF, NE, I>(file, line, estr, t, o, l);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename UL, std::size_t NF, std::size_t NE, typename I>
|
|
|
|
__attribute_warn_unused_result
|
|
|
|
static inline partial_range_t<I> (partial_const_range)(const char (&file)[NF], unsigned line, const char (&estr)[NE], const T &t, const UL &l)
|
|
|
|
{
|
|
|
|
return partial_range<const T, UL, NF, NE, I>(file, line, estr, t, l);
|
|
|
|
}
|
2015-09-13 21:02:19 +00:00
|
|
|
|
2016-01-29 04:05:47 +00:00
|
|
|
template <typename T, typename I>
|
2015-09-13 21:02:19 +00:00
|
|
|
__attribute_warn_unused_result
|
2016-01-29 04:05:47 +00:00
|
|
|
static inline partial_range_t<I> (make_range)(T &t)
|
2015-09-13 21:02:19 +00:00
|
|
|
{
|
2016-01-29 04:05:47 +00:00
|
|
|
return t;
|
2015-09-13 21:02:19 +00:00
|
|
|
}
|