Poorformance improvements for fmt lib compile time

This commit is contained in:
Wojtek Figat
2021-07-09 11:00:08 +02:00
parent 8da7a17a66
commit 0515e41d89
3 changed files with 6 additions and 349 deletions

View File

@@ -44,7 +44,6 @@ namespace fmt {
}
};
#else
#include <memory>
namespace fmt {
// std::back_insert_iterator impl to not include <iterator>
template<class _Category, class _Ty, class _Diff = intptr, class _Pointer = _Ty *, class _Reference = _Ty&>
@@ -403,19 +402,6 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
constexpr unsigned char micro[] = "\u00B5";
template <typename Char> constexpr bool is_unicode() {
return FMT_UNICODE || sizeof(Char) != 1 ||
(sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
}
#ifdef __cpp_char8_t
using char8_type = char8_t;
#else
enum char8_type : unsigned char {};
#endif
} // namespace internal
template <typename... Ts>
@@ -434,7 +420,6 @@ template <typename Char> class basic_string_view {
size_t size_;
public:
using char_type FMT_DEPRECATED_ALIAS = Char;
using value_type = Char;
using iterator = const Char*;
@@ -520,16 +505,10 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;
#ifndef __cpp_char8_t
// char8_t is deprecated; use char instead.
using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
#endif
/** Specifies if ``T`` is a character type. Can be specialized by users. */
template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {};
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<internal::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
@@ -696,12 +675,6 @@ class basic_format_parse_context : private ErrorHandler {
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
template <typename Char, typename ErrorHandler = internal::error_handler>
using basic_parse_context FMT_DEPRECATED_ALIAS =
basic_format_parse_context<Char, ErrorHandler>;
using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
@@ -712,11 +685,6 @@ struct formatter {
formatter() = delete;
};
template <typename T, typename Char, typename Enable = void>
struct FMT_DEPRECATED convert_to_int
: bool_constant<!std::is_arithmetic<T>::value &&
std::is_convertible<T, int>::value> {};
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>

View File

@@ -55,82 +55,6 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
// A portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
FMT_FUNC int safe_strerror(int error_code, char*& buffer,
std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
class dispatcher {
private:
int error_code_;
char*& buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const dispatcher&) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
FMT_MAYBE_UNUSED
int handle(char* message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED
int handle(internal::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
FMT_MAYBE_UNUSED
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
: result;
}
#if !FMT_MSC_VER
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
#endif
public:
dispatcher(int err_code, char*& buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
};
return dispatcher(error_code, buffer, buffer_size).run();
}
// A wrapper around fwrite that throws on error.
FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) FMT_THROW_FORMAT_ERROR("cannot write to file");
}
} // namespace internal
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)

View File

@@ -35,6 +35,7 @@
#include <math.h>
#include <stdint.h>
#include <limits>
#include "core.h"
@@ -221,11 +222,6 @@ FMT_END_NAMESPACE
# define FMT_NUMERIC_ALIGN 1
#endif
// Enable the deprecated percent specifier.
#ifndef FMT_DEPRECATED_PERCENT
# define FMT_DEPRECATED_PERCENT 0
#endif
FMT_BEGIN_NAMESPACE
namespace internal {
@@ -284,43 +280,6 @@ template <> constexpr int num_bits<fallback_uintptr>() {
std::numeric_limits<unsigned char>::digits);
}
// An approximation of iterator_t for pre-C++20 systems.
template <typename T>
using iterator_t = decltype(std::begin(std::declval<T&>()));
// Detect the iterator category of *any* given type in a SFINAE-friendly way.
// Unfortunately, older implementations of std::iterator_traits are not safe
// for use in a SFINAE-context.
template <typename It, typename Enable = void>
struct iterator_category : std::false_type {};
template <typename T> struct iterator_category<T*> {
using type = std::random_access_iterator_tag;
};
template <typename It>
struct iterator_category<It, void_t<typename It::iterator_category>> {
using type = typename It::iterator_category;
};
// Detect if *any* given type models the OutputIterator concept.
template <typename It> class is_output_iterator {
// Check for mutability because all iterator categories derived from
// std::input_iterator_tag *may* also meet the requirements of an
// OutputIterator, thereby falling into the category of 'mutable iterators'
// [iterator.requirements.general] clause 4. The compiler reveals this
// property only at the point of *actually dereferencing* the iterator!
template <typename U>
static decltype(*(std::declval<U>())) test(std::input_iterator_tag);
template <typename U> static char& test(std::output_iterator_tag);
template <typename U> static const char& test(...);
using type = decltype(test<It>(typename iterator_category<It>::type{}));
public:
enum { value = !std::is_const<remove_reference_t<type>>::value };
};
#if FMT_USE_STRING
// A workaround for std::string not having mutable data() until C++17.
template <typename Char> inline Char* get_data(std::basic_string<Char>& s) {
@@ -332,24 +291,13 @@ inline typename Container::value_type* get_data(Container& c) {
return c.data();
}
#if defined(_SECURE_SCL) && _SECURE_SCL
// Make a checked iterator to avoid MSVC warnings.
template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
template <typename T> checked_ptr<T> make_checked(T* p, std::size_t size) {
return {p, size};
}
#else
template <typename T> using checked_ptr = T*;
template <typename T> inline T* make_checked(T* p, std::size_t) { return p; }
#endif
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
inline checked_ptr<typename Container::value_type> reserve(
inline typename Container::value_type* reserve(
fmt::back_insert_iterator<Container>& it, std::size_t n) {
Container& c = get_container(it);
std::size_t size = c.size();
c.resize(size + n);
return make_checked(get_data(c) + size, n);
return get_data(c) + size;
}
template <typename Iterator>
@@ -364,7 +312,6 @@ class counting_iterator {
std::size_t count_;
public:
using iterator_category = std::output_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
@@ -392,79 +339,6 @@ class counting_iterator {
value_type operator*() const { return {}; }
};
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
std::size_t limit_;
std::size_t count_;
truncating_iterator_base(OutputIt out, std::size_t limit)
: out_(out), limit_(limit), count_(0) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = void;
using pointer = void;
using reference = void;
using _Unchecked_type =
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; }
std::size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator(OutputIt out, std::size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator(OutputIt out, std::size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A range with the specified output iterator and value type.
template <typename OutputIt, typename T = typename OutputIt::value_type>
class output_range {
@@ -496,29 +370,12 @@ inline size_t count_code_points(basic_string_view<char> s) {
return num_code_points;
}
inline size_t count_code_points(basic_string_view<char8_type> s) {
return count_code_points(basic_string_view<char>(
reinterpret_cast<const char*>(s.data()), s.size()));
}
template <typename Char>
inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
size_t size = s.size();
return n < size ? n : size;
}
// Calculates the index of the nth code point in a UTF-8 string.
inline size_t code_point_index(basic_string_view<char8_type> s, size_t n) {
const char8_type* data = s.data();
size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
return i;
}
}
return s.size();
}
template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last, OutputIt d_first) {
while (first != last)
@@ -543,26 +400,11 @@ OutputIt fill_n(OutputIt first, Size count, const T& value) {
return first;
}
template <typename InputIt, typename OutChar>
using needs_conversion = bool_constant<
std::is_same<typename std::iterator_traits<InputIt>::value_type,
char>::value &&
std::is_same<OutChar, char8_type>::value>;
template <typename OutChar, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
template <typename OutChar, typename InputIt, typename OutputIt>
OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
return internal::copy(begin, end, it);
}
template <typename OutChar, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
while (begin != end)
*it++ = static_cast<char8_type>(*begin++);
return it;
}
#ifndef FMT_USE_GRISU
# define FMT_USE_GRISU 1
#endif
@@ -577,7 +419,7 @@ template <typename U>
void buffer<T>::append(const U* begin, const U* end) {
std::size_t new_size = size_ + to_unsigned(end - begin);
reserve(new_size);
std::uninitialized_copy(begin, end, make_checked(ptr_, capacity_) + size_);
::memcpy(ptr_ + size_, begin, (end - begin) * sizeof(U));
size_ = new_size;
}
} // namespace internal
@@ -1059,7 +901,6 @@ struct float_specs {
sign_t sign : 8;
bool upper : 1;
bool locale : 1;
bool percent : 1;
bool binary32 : 1;
bool use_grisu : 1;
bool showpoint : 1;
@@ -1262,12 +1103,6 @@ FMT_CONSTEXPR float_specs parse_float_type_spec(
result.format = float_format::fixed;
result.showpoint |= specs.precision != 0;
break;
#if FMT_DEPRECATED_PERCENT
case '%':
result.format = float_format::fixed;
result.percent = true;
break;
#endif
case 'A':
result.upper = true;
FMT_FALLTHROUGH;
@@ -1583,8 +1418,7 @@ template <typename Range> class basic_writer {
++group;
}
buffer -= s.size();
std::uninitialized_copy(s.data(), s.data() + s.size(),
make_checked(buffer, s.size()));
::memcpy(buffer, s.data(), s.size() * sizeof(char_type));
});
}
};
@@ -1757,12 +1591,7 @@ template <typename Range> class basic_writer {
}
if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
fspecs.use_grisu = use_grisu<T>();
if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100;
int exp = format_float(promote_float(value), precision, fspecs, buffer);
if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) {
buffer.push_back('%');
--exp; // Adjust decimal place position.
}
fspecs.precision = precision;
char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
: static_cast<char_type>('.');
@@ -1861,7 +1690,6 @@ class arg_formatter_base {
protected:
writer_type& writer() { return writer_; }
FMT_DEPRECATED format_specs* spec() { return specs_; }
format_specs* specs() { return specs_; }
iterator out() { return writer_.out(); }
@@ -3118,17 +2946,6 @@ typename Context::iterator vformat_to(
return h.context.out();
}
// Casts ``p`` to ``const void*`` for pointer formatting.
// Example:
// auto s = format("{}", ptr(p));
template <typename T> inline const void* ptr(const T* p) { return p; }
template <typename T> inline const void* ptr(const std::unique_ptr<T>& p) {
return p.get();
}
template <typename T> inline const void* ptr(const std::shared_ptr<T>& p) {
return p.get();
}
class bytes {
private:
string_view data_;
@@ -3230,7 +3047,6 @@ using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
template <typename S, typename OutputIt, typename... Args,
FMT_ENABLE_IF(
internal::is_output_iterator<OutputIt>::value &&
!internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
inline OutputIt vformat_to(
OutputIt out, const S& format_str,
@@ -3253,7 +3069,6 @@ inline OutputIt vformat_to(
*/
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(
internal::is_output_iterator<OutputIt>::value &&
!internal::is_contiguous_back_insert_iterator<OutputIt>::value &&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
@@ -3263,56 +3078,6 @@ inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
make_format_args<context>(args...));
}
template <typename OutputIt> struct format_to_n_result {
/** Iterator past the end of the output range. */
OutputIt out;
/** Total (not truncated) output size. */
std::size_t size;
};
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_context =
format_context_t<internal::truncating_iterator<OutputIt>, Char>;
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
template <typename OutputIt, typename Char, typename... Args>
inline format_arg_store<format_to_n_context<OutputIt, Char>, Args...>
make_format_to_n_args(const Args&... args) {
return format_arg_store<format_to_n_context<OutputIt, Char>, Args...>(
args...);
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
inline format_to_n_result<OutputIt> vformat_to_n(
OutputIt out, std::size_t n, basic_string_view<Char> format_str,
format_to_n_args<type_identity_t<OutputIt>, type_identity_t<Char>> args) {
auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n),
format_str, args);
return {it.base(), it.count()};
}
/**
\rst
Formats arguments, writes up to ``n`` characters of the result to the output
iterator ``out`` and returns the total output size and the iterator past the
end of the output range.
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value&&
internal::is_output_iterator<OutputIt>::value)>
inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n,
const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = format_to_n_context<OutputIt, char_t<S>>;
return vformat_to_n(out, n, to_string_view(format_str),
make_format_args<context>(args...));
}
#if FMT_USE_STRING
template <typename Char>
inline std::basic_string<Char> internal::vformat(