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 @@
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>
......
......@@ -116,17 +116,16 @@ template <typename Char>
struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
auto it = internal::null_terminating_iterator<Char>(ctx);
if (*it == ':')
auto it = ctx.begin();
if (it != ctx.end() && *it == ':')
++it;
auto end = it;
while (*end && *end != '}')
while (end != ctx.end() && *end != '}')
++end;
tm_format.reserve(end - it + 1);
using internal::pointer_from;
tm_format.append(pointer_from(it), pointer_from(end));
tm_format.append(it, end);
tm_format.push_back('\0');
return pointer_from(end);
return end;
}
template <typename FormatContext>
......
......@@ -193,13 +193,16 @@ TEST(UtilTest, ParseNonnegativeInt) {
fmt::print("Skipping parse_nonnegative_int test\n");
return;
}
const char *s = "10000000000";
fmt::string_view s = "10000000000";
auto begin = s.begin(), end = s.end();
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");
s = "2147483649";
begin = s.begin();
end = s.end();
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");
}
......@@ -941,6 +944,7 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("foo=", format("{:}=", "foo"));
EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*'));
}
TEST(FormatterTest, PlusSign) {
......@@ -2087,9 +2091,10 @@ struct test_arg_id_handler {
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;
fmt::internal::parse_arg_id(s, h);
fmt::internal::parse_arg_id(s, s + N, h);
return h;
}
......@@ -2149,9 +2154,10 @@ struct test_format_specs_handler {
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;
fmt::internal::parse_format_specs(s, h);
fmt::internal::parse_format_specs(s, s + N, h);
return h;
}
......@@ -2195,11 +2201,12 @@ struct test_context {
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;
test_context ctx{};
fmt::internal::specs_handler<test_context> h(specs, ctx);
parse_format_specs(s, h);
parse_format_specs(s, s + N, h);
return specs;
}
......@@ -2220,12 +2227,13 @@ TEST(FormatTest, ConstexprSpecsHandler) {
static_assert(parse_specs("d").type == 'd', "");
}
template <size_t N>
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;
test_context 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;
}
......@@ -2246,10 +2254,11 @@ TEST(FormatTest, ConstexprDynamicSpecsHandler) {
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>
checker(test_format_specs_handler(), fmt::internal::double_type);
parse_format_specs(s, checker);
parse_format_specs(s, s + N, checker);
return checker;
}
......@@ -2278,11 +2287,11 @@ struct test_format_string_handler {
template <typename T>
FMT_CONSTEXPR void on_arg_id(T) {}
template <typename Iterator>
FMT_CONSTEXPR void on_replacement_field(Iterator) {}
FMT_CONSTEXPR void on_replacement_field(const char *) {}
template <typename Iterator>
FMT_CONSTEXPR Iterator on_format_specs(Iterator it) { return it; }
FMT_CONSTEXPR const char *on_format_specs(const char *begin, const char*) {
return begin;
}
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