flush ignored the number of bytes written by write which can be less
than requested, silently dropping the rest of the buffer. Write in a loop
until all data is written.
The destructor flushed the buffer and let write errors escape, calling
std::terminate on errors like a full disk. Report the error instead, like
file and buffered_file do on close errors. Also clear the buffer before
writing in flush so that data from a failed write, e.g. in close, is not
written again by the destructor.
init_buffer decremented the write pointer even when putc_unlocked failed,
turning a null pointer into (char*)-1 that print then wrote through, e.g.
when printing to a stream opened for reading. Report an error instead.
Return zero from detail::compare when n == 0. Sanitizer checking reported
null pointers being passed to memcmp when comparing empty string views,
even though the comparison length was zero.
* Fixup range formatting to match stdlib
fmt would format a range of characters with `{:s}`
spec with surrounding quotes, whereas stdlib omits the quotes.
This patch drops the quotes.
* update docs
* Fix precision formatting of long double values just below a power of 10
format_dragon included the upper boundary when checking whether the
estimated exponent is one too high. This is needed for the shortest
output, but with a given precision it left a leading zero digit, so
values such as nextafter(1e43L, 0) were printed with an extra digit in
fixed notation or a mantissa below 1 in exponent notation.
* Apply clang-format
output_file(path, buffer_size = 0) allocated a zero-size buffer, but
ostream::grow can only make room by flushing, so a zero capacity is never
increased. push_back then writes past the allocation and leaves size()
above capacity(), after which append computes capacity_ - size_ as
SIZE_MAX and copies the rest of the output out of bounds. If the first
write is an append instead, the count is clamped to 0 and print never
returns.
Allocate at least one byte so a zero buffer size behaves like 1.
* Avoid narrowing duration day counts to the original representation
* Keep duration field values in the promoted representation
---------
Co-authored-by: Aikhan Jumashukurov <117706338+aikhanj@users.noreply.github.com>
* Fix generic path format when formatting to wide strings
get_path_string used the native or generic string computed by the path
formatter only when converting wchar_t to UTF-8 and fell back to
p.string<Char>() otherwise, so the 'g' specifier was ignored when the
output character type matched path::value_type. On Windows,
fmt::format(L"{:g}", path(L"C:\\foo")) returned L"C:\\foo" instead of
L"C:/foo". Use the passed string in that case, as write_escaped_path
already does.
* Keep generic path format when converting to wide strings on POSIX
The formatters for std::variant, std::monostate, std::expected and
std::unexpected wrote narrow string literals with detail::write<Char>,
which is ambiguous for wchar_t, so formatting these types into a wide
string failed to compile even though is_formattable reported them as
formattable. Use detail::string_literal like the std::optional
formatter does.
Compute the upper bound as 2^digits with std::ldexp instead of
static_cast<FromRep>(max) + 1, which is only exact when rounding to
nearest: with FE_UPWARD in an unoptimized build the bound became
2^63 + 2048 and a count of 2^63 reached the undefined conversion.
Truncate the count once and use it for both the check and the cast.
Skip the checks that need safe duration casts when
FMT_SAFE_DURATION_CAST is 0, where the NaN time point check failed.
Drop the 1e300 time point and two bool assertions, which don't catch
anything the others don't, and test max + 1 and NaN directly.
* fix unchecked float to integral conversion in detail::duration_cast
* Check the truncated count in float to integral duration_cast
* Keep the duration_cast fallback for custom representations
* Use an explicit rep in the custom_rep duration_cast test
std::chrono::seconds is duration<long> on 64-bit Linux so common_type<long,
custom_rep> had no specialization and chrono-test failed to compile there.
* Exclude bool from the checked float to integral duration_cast
Move writing of the formatted hexfloat into write_hexfloat, which only
depends on the character and iterator types, so that it is not duplicated
for each floating-point type, and mark decimal_point_impl noinline to
avoid inlining the std::locale lookup into every caller.
Compute the ISO week-based year and week number from the day of the year
of the Thursday in the current week instead of the weekday of December 31.
This only needs a leap year check, which works for negative years without
floored division, and is about 3 times faster.
For std::tm values with tm_wday and tm_yday inconsistent with tm_year,
%G, %g and %V now give the same results as strftime.
Replace the negative year tests with a year_month_day test that also
covers the leap year check and days_from_civil for negative years.
* Fix formatting of nullptr with FMT_COMPILE and to_string
std::nullptr_t is implicitly convertible to string_view and const char*,
so compiled format strings and fmt::to_string treated nullptr as a null
C string: FMT_COMPILE("{}!") crashed and FMT_COMPILE("{}") and to_string
threw "string pointer is null". Format it as a pointer ("0x0") like the
runtime API does.
* Write nullptr as a pointer in detail::write
detail::write<Char>(out, nullptr) picked the const Char* overload, so
fmt::to_string(nullptr) and FMT_COMPILE("{}") threw "string pointer is
null". Add a nullptr_t overload that forwards to the const void* one.
With the L flag, floating-point values are formatted with the locale's
decimal point, but the hexfloat presentation ('a'/'A') always emitted
'.'. Replace it with the locale decimal point when the value is
localized, like the other floating-point presentations do.
allocator<T>::allocate(n) computed n * sizeof(T) with only an FMT_ASSERT
guarding against overflow, which is a no-op under NDEBUG. On 32-bit targets
a width of 2^30 or more with a 4-byte character type, e.g.
fmt::format(L"{:>{}}", s, 1100000000), wraps the byte count around, and
basic_memory_buffer records a capacity much larger than the block it got,
so padding is written past the end of the allocation.
Make the check unconditional and throw std::bad_alloc, as std::allocator
did before it was replaced in 11.1.0. Move <new> back to format.h since the
header now throws std::bad_alloc.
iso_year_weeks computed the weekday of December 31 with truncating
division and a remainder that is negative for negative years, so it
returned the wrong number of ISO weeks for years before year 0. As a
result %V and %G/%g were wrong around the start and end of such years,
e.g. 1 January of year -1 was formatted as week 52 of year -2 instead
of week 53, and 31 December of year -2 as week 1 of year -1.
Use floored division and a non-negative remainder.
Claude-Session: https://claude.ai/code/session_01Y1b55sEL1gA1c3FCMcQCbm
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The year_month_day formatter only filled in tm_year, tm_mon and tm_mday,
so specifiers that depend on the weekday or the day of the year (%a, %A,
%u, %w, %j, %U, %W, %V, %G, %g) were formatted from zero-initialized
fields, e.g. every date was printed as a Sunday. Compute tm_wday and
tm_yday from the date before passing it to the tm formatter.
nested_format_specs::write formatted the body of a padded value into a
temporary buffer using a context of the same type as the outer one. This
only works when the outer context writes to an appender, so FMT_COMPILE
and format_to with other output iterators failed to compile even when no
width was given.
Use a buffer context that takes arguments and locale from the outer
context when the outer context doesn't write to an appender.
Fixes#4949.
Floor the seconds before calling gmtime instead of adjusting tm_sec, and
keep the subsecond remainder in the common duration type, which fixes
overflow for narrow reps such as duration<int16_t, std::micro>.
The sign is cleared for unsigned conversions regardless of whether the
'+' or the space flag was given, so "% o" and "% X" only repeat what
"%+o" and "%+X" already cover.
C only applies the '+' and space flags to signed conversions, so
fmt::sprintf("%+u", 42) should give "42" like printf does, not "+42".
The same goes for %o, %x and %X.