mirror of
https://github.com/fmtlib/fmt.git
synced 2024-12-24 12:14:26 +00:00
Get rid of 'null_terminating_iterator' in printf.h (#980)
The iterator class itself and its helpers 'to_pointer' are removed after reformulating the formatting loop in terms of a 'basic_string_view<Char>::iterator'. Signed-off-by: Daniela Engert <dani@ngrt.de>
This commit is contained in:
parent
39623a7400
commit
f5cc77cea0
@ -16,130 +16,6 @@
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
// An iterator that produces a null terminator on *end. This simplifies parsing
|
||||
// and allows comparing the performance of processing a null-terminated string
|
||||
// vs string_view.
|
||||
template <typename Char>
|
||||
class null_terminating_iterator {
|
||||
public:
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef Char value_type;
|
||||
typedef const Char* pointer;
|
||||
typedef const Char& reference;
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
|
||||
null_terminating_iterator() : ptr_(0), end_(0) {}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end)
|
||||
: ptr_(ptr), end_(end) {}
|
||||
|
||||
template <typename Range>
|
||||
FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r)
|
||||
: ptr_(r.begin()), end_(r.end()) {}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator &operator=(const Char *ptr) {
|
||||
assert(ptr <= end_);
|
||||
ptr_ = ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR Char operator*() const {
|
||||
return ptr_ != end_ ? *ptr_ : Char();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator operator++() {
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator operator++(int) {
|
||||
null_terminating_iterator result(*this);
|
||||
++ptr_;
|
||||
return result;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator operator--() {
|
||||
--ptr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) {
|
||||
return null_terminating_iterator(ptr_ + n, end_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) {
|
||||
return null_terminating_iterator(ptr_ - n, end_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) {
|
||||
ptr_ += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR difference_type operator-(
|
||||
null_terminating_iterator other) const {
|
||||
return ptr_ - other.ptr_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const {
|
||||
return ptr_ != other.ptr_;
|
||||
}
|
||||
|
||||
bool operator>=(null_terminating_iterator other) const {
|
||||
return ptr_ >= other.ptr_;
|
||||
}
|
||||
|
||||
// This should be a friend specialization pointer_from<Char> but the latter
|
||||
// doesn't compile by gcc 5.1 due to a compiler bug.
|
||||
template <typename CharT>
|
||||
friend FMT_CONSTEXPR_DECL const CharT *pointer_from(
|
||||
null_terminating_iterator<CharT> it);
|
||||
|
||||
private:
|
||||
const Char *ptr_;
|
||||
const Char *end_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR const T *pointer_from(const T *p) { return p; }
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator<Char> it) {
|
||||
return it.ptr_;
|
||||
}
|
||||
|
||||
// DEPRECATED: Parses the input as an unsigned integer. This function assumes
|
||||
// that the first character is a digit and presence of a non-digit character at
|
||||
// the end.
|
||||
// it: an iterator pointing to the beginning of the input range.
|
||||
template <typename Iterator, typename ErrorHandler>
|
||||
FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
|
||||
assert('0' <= *it && *it <= '9');
|
||||
if (*it == '0') {
|
||||
++it;
|
||||
return 0;
|
||||
}
|
||||
unsigned value = 0;
|
||||
// Convert to unsigned to prevent a warning.
|
||||
unsigned max_int = (std::numeric_limits<int>::max)();
|
||||
unsigned big = max_int / 10;
|
||||
do {
|
||||
// Check for overflow.
|
||||
if (value > big) {
|
||||
value = max_int + 1;
|
||||
break;
|
||||
}
|
||||
value = value * 10 + unsigned(*it - '0');
|
||||
// Workaround for MSVC "setup_exception stack overflow" error:
|
||||
auto next = it;
|
||||
++next;
|
||||
it = next;
|
||||
} while ('0' <= *it && *it <= '9');
|
||||
if (value > max_int)
|
||||
eh.on_error("number is too big");
|
||||
return value;
|
||||
}
|
||||
|
||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||
// signed and unsigned integers.
|
||||
template <bool IsSigned>
|
||||
@ -485,18 +361,15 @@ class basic_printf_context :
|
||||
typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
|
||||
typedef typename base::format_arg format_arg;
|
||||
typedef basic_format_specs<char_type> format_specs;
|
||||
typedef internal::null_terminating_iterator<char_type> iterator;
|
||||
|
||||
void parse_flags(format_specs &spec, iterator &it);
|
||||
static void parse_flags(format_specs &spec, const Char *&it, const Char *end);
|
||||
|
||||
// Returns the argument with specified index or, if arg_index is equal
|
||||
// to the maximum unsigned value, the next argument.
|
||||
format_arg get_arg(
|
||||
iterator it,
|
||||
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
|
||||
format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
|
||||
|
||||
// Parses argument index, flags and width and returns the argument index.
|
||||
unsigned parse_header(iterator &it, format_specs &spec);
|
||||
unsigned parse_header(const Char *&it, const Char *end, format_specs &spec);
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -520,9 +393,9 @@ class basic_printf_context :
|
||||
|
||||
template <typename OutputIt, typename Char, typename AF>
|
||||
void basic_printf_context<OutputIt, Char, AF>::parse_flags(
|
||||
format_specs &spec, iterator &it) {
|
||||
for (;;) {
|
||||
switch (*it++) {
|
||||
format_specs &spec, const Char *&it, const Char *end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
spec.align_ = ALIGN_LEFT;
|
||||
break;
|
||||
@ -539,7 +412,6 @@ void basic_printf_context<OutputIt, Char, AF>::parse_flags(
|
||||
spec.flags |= HASH_FLAG;
|
||||
break;
|
||||
default:
|
||||
--it;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -547,9 +419,7 @@ void basic_printf_context<OutputIt, Char, AF>::parse_flags(
|
||||
|
||||
template <typename OutputIt, typename Char, typename AF>
|
||||
typename basic_printf_context<OutputIt, Char, AF>::format_arg
|
||||
basic_printf_context<OutputIt, Char, AF>::get_arg(
|
||||
iterator it, unsigned arg_index) {
|
||||
(void)it;
|
||||
basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) {
|
||||
if (arg_index == std::numeric_limits<unsigned>::max())
|
||||
return this->do_get_arg(this->parse_context().next_arg_id());
|
||||
return base::get_arg(arg_index - 1);
|
||||
@ -557,15 +427,15 @@ typename basic_printf_context<OutputIt, Char, AF>::format_arg
|
||||
|
||||
template <typename OutputIt, typename Char, typename AF>
|
||||
unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
||||
iterator &it, format_specs &spec) {
|
||||
const Char *&it, const Char *end, format_specs &spec) {
|
||||
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
||||
char_type c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
internal::error_handler eh;
|
||||
unsigned value = parse_nonnegative_int(it, eh);
|
||||
if (*it == '$') { // value is an argument index
|
||||
unsigned value = parse_nonnegative_int(it, end, eh);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value;
|
||||
} else {
|
||||
@ -579,15 +449,17 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_flags(spec, it);
|
||||
parse_flags(spec, it, end);
|
||||
// Parse width.
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
internal::error_handler eh;
|
||||
spec.width_ = parse_nonnegative_int(it, eh);
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
spec.width_ = visit_format_arg(
|
||||
internal::printf_width_handler<char_type>(spec), get_arg(it));
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
internal::error_handler eh;
|
||||
spec.width_ = parse_nonnegative_int(it, end, eh);
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
spec.width_ = visit_format_arg(
|
||||
internal::printf_width_handler<char_type>(spec), get_arg());
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
@ -595,41 +467,43 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
||||
template <typename OutputIt, typename Char, typename AF>
|
||||
void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||
auto &buffer = internal::get_container(this->out());
|
||||
auto start = iterator(this->parse_context());
|
||||
const auto range = this->parse_context();
|
||||
const Char * end = range.end();
|
||||
const Char * start = range.begin();
|
||||
auto it = start;
|
||||
using internal::pointer_from;
|
||||
while (*it) {
|
||||
while (it != end) {
|
||||
char_type c = *it++;
|
||||
if (c != '%') continue;
|
||||
if (*it == c) {
|
||||
buffer.append(pointer_from(start), pointer_from(it));
|
||||
if (it != end && *it == c) {
|
||||
buffer.append(start, it);
|
||||
start = ++it;
|
||||
continue;
|
||||
}
|
||||
buffer.append(pointer_from(start), pointer_from(it) - 1);
|
||||
buffer.append(start, it - 1);
|
||||
|
||||
format_specs spec;
|
||||
spec.align_ = ALIGN_RIGHT;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
unsigned arg_index = parse_header(it, spec);
|
||||
unsigned arg_index = parse_header(it, end, spec);
|
||||
|
||||
// Parse precision.
|
||||
if (*it == '.') {
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
if ('0' <= *it && *it <= '9') {
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
internal::error_handler eh;
|
||||
spec.precision = static_cast<int>(parse_nonnegative_int(it, eh));
|
||||
} else if (*it == '*') {
|
||||
spec.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
spec.precision =
|
||||
visit_format_arg(internal::printf_precision_handler(), get_arg(it));
|
||||
visit_format_arg(internal::printf_precision_handler(), get_arg());
|
||||
} else {
|
||||
spec.precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
format_arg arg = get_arg(it, arg_index);
|
||||
format_arg arg = get_arg(arg_index);
|
||||
if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
|
||||
spec.flags = static_cast<uint_least8_t>(spec.flags & (~internal::to_unsigned<int>(HASH_FLAG)));
|
||||
if (spec.fill_ == '0') {
|
||||
@ -640,28 +514,36 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||
}
|
||||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
char_type t = it != end ? *it : 0;
|
||||
using internal::convert_arg;
|
||||
switch (*it++) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
if (*it == 'h')
|
||||
convert_arg<signed char>(arg, *++it);
|
||||
else
|
||||
convert_arg<short>(arg, *it);
|
||||
if (t == 'h') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<signed char>(arg, t);
|
||||
} else {
|
||||
convert_arg<short>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (*it == 'l')
|
||||
convert_arg<long long>(arg, *++it);
|
||||
else
|
||||
convert_arg<long>(arg, *it);
|
||||
if (t == 'l') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<long long>(arg, t);
|
||||
} else {
|
||||
convert_arg<long>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
convert_arg<intmax_t>(arg, *it);
|
||||
convert_arg<intmax_t>(arg, t);
|
||||
break;
|
||||
case 'z':
|
||||
convert_arg<std::size_t>(arg, *it);
|
||||
convert_arg<std::size_t>(arg, t);
|
||||
break;
|
||||
case 't':
|
||||
convert_arg<std::ptrdiff_t>(arg, *it);
|
||||
convert_arg<std::ptrdiff_t>(arg, t);
|
||||
break;
|
||||
case 'L':
|
||||
// printf produces garbage when 'L' is omitted for long double, no
|
||||
@ -669,11 +551,11 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||
break;
|
||||
default:
|
||||
--it;
|
||||
convert_arg<void>(arg, *it);
|
||||
convert_arg<void>(arg, c);
|
||||
}
|
||||
|
||||
// Parse type.
|
||||
if (!*it)
|
||||
if (it == end)
|
||||
FMT_THROW(format_error("invalid format string"));
|
||||
spec.type = static_cast<char>(*it++);
|
||||
if (arg.is_integral()) {
|
||||
@ -695,7 +577,7 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||
// Format argument.
|
||||
visit_format_arg(AF(buffer, spec, *this), arg);
|
||||
}
|
||||
buffer.append(pointer_from(start), pointer_from(it));
|
||||
buffer.append(start, it);
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
|
Loading…
Reference in New Issue
Block a user