Commit 8668639a authored by Victor Zverovich's avatar Victor Zverovich

Get rid of null_terminating_iterator in format

parent 93fd473b
This diff is collapsed.
...@@ -16,6 +16,130 @@ ...@@ -16,6 +16,130 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { 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 // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template <bool IsSigned>
......
...@@ -116,17 +116,16 @@ template <typename Char> ...@@ -116,17 +116,16 @@ template <typename Char>
struct formatter<std::tm, Char> { struct formatter<std::tm, Char> {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
auto it = internal::null_terminating_iterator<Char>(ctx); auto it = ctx.begin();
if (*it == ':') if (it != ctx.end() && *it == ':')
++it; ++it;
auto end = it; auto end = it;
while (*end && *end != '}') while (end != ctx.end() && *end != '}')
++end; ++end;
tm_format.reserve(end - it + 1); tm_format.reserve(end - it + 1);
using internal::pointer_from; tm_format.append(it, end);
tm_format.append(pointer_from(it), pointer_from(end));
tm_format.push_back('\0'); tm_format.push_back('\0');
return pointer_from(end); return end;
} }
template <typename FormatContext> template <typename FormatContext>
......
...@@ -193,13 +193,16 @@ TEST(UtilTest, ParseNonnegativeInt) { ...@@ -193,13 +193,16 @@ TEST(UtilTest, ParseNonnegativeInt) {
fmt::print("Skipping parse_nonnegative_int test\n"); fmt::print("Skipping parse_nonnegative_int test\n");
return; return;
} }
const char *s = "10000000000"; fmt::string_view s = "10000000000";
auto begin = s.begin(), end = s.end();
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
parse_nonnegative_int(s, fmt::internal::error_handler()), parse_nonnegative_int(begin, end, fmt::internal::error_handler()),
fmt::format_error, "number is too big"); fmt::format_error, "number is too big");
s = "2147483649"; s = "2147483649";
begin = s.begin();
end = s.end();
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
parse_nonnegative_int(s, fmt::internal::error_handler()), parse_nonnegative_int(begin, end, fmt::internal::error_handler()),
fmt::format_error, "number is too big"); fmt::format_error, "number is too big");
} }
...@@ -941,6 +944,7 @@ TEST(FormatterTest, Fill) { ...@@ -941,6 +944,7 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("abc**", format("{0:*<5}", "abc")); EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("foo=", format("{:}=", "foo")); EXPECT_EQ("foo=", format("{:}=", "foo"));
EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*'));
} }
TEST(FormatterTest, PlusSign) { TEST(FormatterTest, PlusSign) {
...@@ -2087,9 +2091,10 @@ struct test_arg_id_handler { ...@@ -2087,9 +2091,10 @@ struct test_arg_id_handler {
FMT_CONSTEXPR void on_error(const char *) { res = ERROR; } FMT_CONSTEXPR void on_error(const char *) { res = ERROR; }
}; };
FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char* s) { template <size_t N>
FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char (&s)[N]) {
test_arg_id_handler h; test_arg_id_handler h;
fmt::internal::parse_arg_id(s, h); fmt::internal::parse_arg_id(s, s + N, h);
return h; return h;
} }
...@@ -2149,9 +2154,10 @@ struct test_format_specs_handler { ...@@ -2149,9 +2154,10 @@ struct test_format_specs_handler {
FMT_CONSTEXPR void on_error(const char *) { res = ERROR; } FMT_CONSTEXPR void on_error(const char *) { res = ERROR; }
}; };
FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char *s) { template <size_t N>
FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char (&s)[N]) {
test_format_specs_handler h; test_format_specs_handler h;
fmt::internal::parse_format_specs(s, h); fmt::internal::parse_format_specs(s, s + N, h);
return h; return h;
} }
...@@ -2195,11 +2201,12 @@ struct test_context { ...@@ -2195,11 +2201,12 @@ struct test_context {
FMT_CONSTEXPR test_context error_handler() { return *this; } FMT_CONSTEXPR test_context error_handler() { return *this; }
}; };
FMT_CONSTEXPR fmt::format_specs parse_specs(const char *s) { template <size_t N>
FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
fmt::format_specs specs; fmt::format_specs specs;
test_context ctx{}; test_context ctx{};
fmt::internal::specs_handler<test_context> h(specs, ctx); fmt::internal::specs_handler<test_context> h(specs, ctx);
parse_format_specs(s, h); parse_format_specs(s, s + N, h);
return specs; return specs;
} }
...@@ -2220,12 +2227,13 @@ TEST(FormatTest, ConstexprSpecsHandler) { ...@@ -2220,12 +2227,13 @@ TEST(FormatTest, ConstexprSpecsHandler) {
static_assert(parse_specs("d").type == 'd', ""); static_assert(parse_specs("d").type == 'd', "");
} }
template <size_t N>
FMT_CONSTEXPR fmt::internal::dynamic_format_specs<char> FMT_CONSTEXPR fmt::internal::dynamic_format_specs<char>
parse_dynamic_specs(const char *s) { parse_dynamic_specs(const char (&s)[N]) {
fmt::internal::dynamic_format_specs<char> specs; fmt::internal::dynamic_format_specs<char> specs;
test_context ctx{}; test_context ctx{};
fmt::internal::dynamic_specs_handler<test_context> h(specs, ctx); fmt::internal::dynamic_specs_handler<test_context> h(specs, ctx);
parse_format_specs(s, h); parse_format_specs(s, s + N, h);
return specs; return specs;
} }
...@@ -2246,10 +2254,11 @@ TEST(FormatTest, ConstexprDynamicSpecsHandler) { ...@@ -2246,10 +2254,11 @@ TEST(FormatTest, ConstexprDynamicSpecsHandler) {
static_assert(parse_dynamic_specs("d").type == 'd', ""); static_assert(parse_dynamic_specs("d").type == 'd', "");
} }
FMT_CONSTEXPR test_format_specs_handler check_specs(const char *s) { template <size_t N>
FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) {
fmt::internal::specs_checker<test_format_specs_handler> fmt::internal::specs_checker<test_format_specs_handler>
checker(test_format_specs_handler(), fmt::internal::double_type); checker(test_format_specs_handler(), fmt::internal::double_type);
parse_format_specs(s, checker); parse_format_specs(s, s + N, checker);
return checker; return checker;
} }
...@@ -2278,11 +2287,11 @@ struct test_format_string_handler { ...@@ -2278,11 +2287,11 @@ struct test_format_string_handler {
template <typename T> template <typename T>
FMT_CONSTEXPR void on_arg_id(T) {} FMT_CONSTEXPR void on_arg_id(T) {}
template <typename Iterator> FMT_CONSTEXPR void on_replacement_field(const char *) {}
FMT_CONSTEXPR void on_replacement_field(Iterator) {}
template <typename Iterator> FMT_CONSTEXPR const char *on_format_specs(const char *begin, const char*) {
FMT_CONSTEXPR Iterator on_format_specs(Iterator it) { return it; } return begin;
}
FMT_CONSTEXPR void on_error(const char *) { error = true; } FMT_CONSTEXPR void on_error(const char *) { error = true; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment