Skip to content

Commit

Permalink
Merge branch 'fmtlib:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
pablohoch authored Mar 12, 2024
2 parents edb385a + c816fa6 commit 3503709
Show file tree
Hide file tree
Showing 14 changed files with 182 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cifuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
dry-run: false
language: c++
- name: Upload Crash
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: SARIF file
path: results.sarif
retention-days: 5

# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
with:
sarif_file: results.sarif
38 changes: 30 additions & 8 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,20 @@ using appender = basic_appender<char>;

namespace detail {

template <typename T, typename Enable = void>
struct locking : std::true_type {};
template <typename T>
struct locking<T, void_t<typename formatter<remove_cvref_t<T>>::nonlocking>>
: std::false_type {};

template <typename T = int> FMT_CONSTEXPR inline auto is_locking() -> bool {
return locking<T>::value;
}
template <typename T1, typename T2, typename... Tail>
FMT_CONSTEXPR inline auto is_locking() -> bool {
return locking<T1>::value || is_locking<T2, Tail...>();
}

// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt>
auto copy(InputIt begin, InputIt end, appender out) -> appender {
Expand Down Expand Up @@ -2631,6 +2645,7 @@ template <typename T> struct strip_named_arg<T, true> {
};

template <typename T, typename ParseContext>
FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769).
FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
-> decltype(ctx.begin()) {
using char_type = typename ParseContext::char_type;
Expand Down Expand Up @@ -2795,6 +2810,8 @@ struct formatter<T, Char,
detail::dynamic_format_specs<Char> specs_;

public:
using nonlocking = void;

template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
Expand Down Expand Up @@ -2944,8 +2961,8 @@ template <typename OutputIt, typename Sentinel = OutputIt>
struct format_to_result {
/** Iterator pointing to just after the last successful write in the range. */
OutputIt out;
/** Sentinel indicating the end of the output range. */
Sentinel out_last;
/** Specifies if the output was truncated. */
bool truncated;

FMT_CONSTEXPR operator OutputIt&() & noexcept { return out; }
FMT_CONSTEXPR operator const OutputIt&() const& noexcept { return out; }
Expand All @@ -2957,13 +2974,15 @@ struct format_to_result {
template <size_t N>
auto vformat_to(char (&out)[N], string_view fmt, format_args args)
-> format_to_result<char*> {
return {vformat_to_n(out, N, fmt, args).out, out + N};
auto result = vformat_to_n(out, N, fmt, args);
return {result.out, result.size > N};
}

template <size_t N, typename... T>
FMT_INLINE auto format_to(char (&out)[N], format_string<T...> fmt, T&&... args)
-> format_to_result<char*> {
return vformat_to(out, fmt, fmt::make_format_args(args...));
auto result = fmt::format_to_n(out, N, fmt, static_cast<T&&>(args)...);
return {result.out, result.size > N};
}

/** Returns the number of chars in the output of ``format(fmt, args...)``. */
Expand All @@ -2977,6 +2996,7 @@ FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,

FMT_API void vprint(string_view fmt, format_args args);
FMT_API void vprint(FILE* f, string_view fmt, format_args args);
FMT_API void vprint_locked(FILE* f, string_view fmt, format_args args);
FMT_API void vprintln(FILE* f, string_view fmt, format_args args);

/**
Expand All @@ -2992,8 +3012,9 @@ FMT_API void vprintln(FILE* f, string_view fmt, format_args args);
template <typename... T>
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
return detail::is_utf8() ? vprint(fmt, vargs)
: detail::vprint_mojibake(stdout, fmt, vargs);
if (!detail::is_utf8()) return detail::vprint_mojibake(stdout, fmt, vargs);
return detail::is_locking<T...>() ? vprint(fmt, vargs)
: vprint_locked(stdout, fmt, vargs);
}

/**
Expand All @@ -3009,8 +3030,9 @@ FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
template <typename... T>
FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
return detail::is_utf8() ? vprint(f, fmt, vargs)
: detail::vprint_mojibake(f, fmt, vargs);
if (!detail::is_utf8()) return detail::vprint_mojibake(f, fmt, vargs);
return detail::is_locking<T...>() ? vprint(f, fmt, vargs)
: vprint_locked(f, fmt, vargs);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,9 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
if (detail::is_utf8() && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4.
#if FMT_MSC_VERSION != 0 || \
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
#if FMT_MSC_VERSION != 0 || \
(defined(__GLIBCXX__) && \
(!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0))
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
// and newer.
using code_unit = wchar_t;
Expand Down
16 changes: 9 additions & 7 deletions include/fmt/format-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# include <locale>
#endif

#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
# include <io.h> // _isatty
#endif

Expand Down Expand Up @@ -1639,7 +1639,7 @@ class file_print_buffer : public buffer<char> {
}
};

#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE)
FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
#else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
Expand All @@ -1665,7 +1665,7 @@ FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args,
#endif

FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
int fd = _fileno(f);
if (_isatty(fd)) {
std::fflush(f);
Expand All @@ -1677,15 +1677,17 @@ FMT_FUNC void print(std::FILE* f, string_view text) {
} // namespace detail

FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
if (detail::file_ref(f).is_buffered()) {
auto&& buffer = detail::file_print_buffer(f);
return detail::vformat_to(buffer, fmt, args);
}
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
detail::print(f, {buffer.data(), buffer.size()});
}

FMT_FUNC void vprint_locked(std::FILE* f, string_view fmt, format_args args) {
if (!detail::file_ref(f).is_buffered()) return vprint(f, fmt, args);
auto&& buffer = detail::file_print_buffer(f);
return detail::vformat_to(buffer, fmt, args);
}

FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
Expand Down
1 change: 0 additions & 1 deletion include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -2218,7 +2218,6 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
case presentation_type::chr:
return write_char<Char>(out, static_cast<Char>(abs_value), specs);
}
return out;
}
template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out,
Expand Down
4 changes: 2 additions & 2 deletions include/fmt/printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ namespace detail {
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>();
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
static auto fits_in_int(bool) -> bool { return true; }
Expand Down Expand Up @@ -205,7 +205,7 @@ class printf_width_handler {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
Expand Down
54 changes: 46 additions & 8 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <tuple>
#include <type_traits>

#include "base.h"
#include "format.h"

FMT_BEGIN_NAMESPACE

Expand Down Expand Up @@ -388,6 +388,7 @@ struct range_formatter<
detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{};
bool is_debug = false;

public:
FMT_CONSTEXPR range_formatter() {}
Expand All @@ -410,36 +411,73 @@ struct range_formatter<
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
detail::maybe_set_debug_format(underlying_, true);
if (it == end) return underlying_.parse(ctx);

if (it != end && *it == 'n') {
switch (detail::to_ascii(*it)) {
case 'n':
set_brackets({}, {});
++it;
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
if (it == end || *it != 's') report_error("invalid format specifier");
FMT_FALLTHROUGH;
case 's':
if (!std::is_same<T, Char>::value)
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
}

if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
detail::maybe_set_debug_format(underlying_, false);
++it;
} else {
detail::maybe_set_debug_format(underlying_, true);
}

ctx.advance_to(it);
return underlying_.parse(ctx);
}

template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.type = presentation_type::debug;
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}

template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
detail::range_mapper<buffered_context<Char>> mapper;
auto out = ctx.out();
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
if (is_debug) return write_debug_string(out, it, end);

out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) {
if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out);
auto&& item = *it;
out = underlying_.format(mapper.map(item), ctx);
out = underlying_.format(mapper.map(*it), ctx);
++i;
}
out = detail::copy<Char>(closing_bracket_, out);
Expand Down
12 changes: 6 additions & 6 deletions test/base-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -697,13 +697,13 @@ TEST(core_test, format_to_c_array) {
char buffer[4];
auto result = fmt::format_to(buffer, "{}", 12345);
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ(buffer + 4, result.out);
EXPECT_EQ("1234", fmt::string_view(buffer, 4));

result = fmt::format_to(buffer, "{:s}", "foobar");
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ(buffer + 4, result.out);
EXPECT_EQ("foob", fmt::string_view(buffer, 4));

Expand All @@ -713,24 +713,24 @@ TEST(core_test, format_to_c_array) {
buffer[3] = 'x';
result = fmt::format_to(buffer, "{}", 'A');
EXPECT_EQ(1, std::distance(&buffer[0], result.out));
EXPECT_EQ(3, std::distance(result.out, result.out_last));
EXPECT_FALSE(result.truncated);
EXPECT_EQ(buffer + 1, result.out);
EXPECT_EQ("Axxx", fmt::string_view(buffer, 4));

result = fmt::format_to(buffer, "{}{} ", 'B', 'C');
EXPECT_EQ(3, std::distance(&buffer[0], result.out));
EXPECT_EQ(1, std::distance(result.out, result.out_last));
EXPECT_FALSE(result.truncated);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ("BC x", fmt::string_view(buffer, 4));

result = fmt::format_to(buffer, "{}", "ABCDE");
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ("ABCD", fmt::string_view(buffer, 4));

result = fmt::format_to(buffer, "{}", std::string(1000, '*'));
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ("****", fmt::string_view(buffer, 4));
}

Expand Down
2 changes: 1 addition & 1 deletion test/format-impl-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ TEST(format_impl_test, write_dragon_even) {
if (!FMT_MSC_VERSION) EXPECT_EQ(s, "33554450");
}

#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
# include <windows.h>

TEST(format_impl_test, write_console_signature) {
Expand Down
Loading

0 comments on commit 3503709

Please sign in to comment.