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.
operator= deallocates the destination's storage but leaves it pointing at
the freed block with a stale capacity and size. When the allocator doesn't
propagate and the two allocators are unequal, move_alloc copies into that
pointer rather than taking over the source's storage, so it writes to freed
memory and the destructor then frees the same block again.
Restore the inline buffer after deallocating and before calling move().
Drop the assertions that return from write_escaped_alternative before
reaching the advance_to call they were added to cover: the std::string
and char alternatives take the has_to_string_view and is_same_v<T, Char>
early returns, and expected<void, int> never calls the function at all.
All four are already checked in std-test.cc.
Merge format_unexpected into format_expected since both are guarded by
__cpp_lib_expected and the latter was a single assertion, and note that
std::unexpected has a formatter of its own so the adjacent unexpected(42)
checks are not duplicates.
* Fix null allocator reference in non-propagating move test
* Test ostream chunking with a real buffer
* Initialize the file pointer when constructing a buffer writer
* Enable ASan and UBSan in supported Linux CI builds
* Limit sanitizer CI to recent Clang and GCC configurations
Unify the duplicated trailing-zero computation in write_fixed and remove
the redundant guards around fill_n. Reduce the constexpr branch of
basic_memory_buffer::grow to a single construct loop that uses the
buffer's own allocator.
In tests, drop the MSVC version checks that FMT_USE_CONSTEVAL already
implies, replace a loop computing 2^-900 with the 0x1p-900 literal, and
report high_precision_long_double as skipped rather than passed when
long double is not an extended-precision type.
The U+1FAE0 and U+31350 cases added in #4962 duplicate the is_printable
checks for the same code points. For a code point >= 0x80 needs_escape is
exactly !is_printable(cp), and the existing U+1F984 case already covers a
printable 4-byte sequence passing through unmodified.
Fill available appender storage in bulk, continuing across buffer growth and flushes
Preserve the constexpr-compatible scalar fallback
Add testing of large padded sizes
Build format-test.cc with FMT_USE_LOCALE=0 in nolocale-test instead of a
separate test file. This replaces two ad-hoc checks with the whole format
test suite. The locale-dependent tests were guarded by the removed
FMT_STATIC_THOUSANDS_SEPARATOR macro, so the guard never took effect.
Format an enum as its underlying value if the enum is annotated with
fmt::as_underlying, the equivalent of std::format_as_underlying from P4267:
enum class [[=fmt::as_underlying]] color { red = 1, green = 2, blue = 4 };
fmt::format("{:04x}", color::blue); // "0004"
The value is mapped before type erasure via the existing
formatter<T>::format_as hook, as in the std::byte formatter, so it is stored
as a builtin argument and can be used as dynamic width or precision.
Combining the annotation with fmt::as_identifiers is rejected with a
static_assert.
%.0i and %.0u take the same path as %.0d because 'i' and 'u' are normalized
to 'd', the uppercase variants only set the upper flag which is unobservable
when there are no digits, and %-5.0d cannot be distinguished from %5.0d
because the output is empty.
tm_writer takes a locale_ref and the localized flag instead of a std::locale
reference, and resolves the locale the same way numeric {:L} does: classic
unless localized, otherwise the one passed to the formatting function or the
global one. The facet calls are confined to three shims, so with locale
support disabled nothing pulls in std::locale. get_locale, which existed only
to materialize and own a std::locale, is no longer needed.
The std::tm formatter treated a missing locale argument as the classic locale
rather than the global one, and since #4935 routes {:L} on weekday and month
through it, {:L} without a locale argument ignored the global locale for all
calendar types. It now uses the global locale, as numeric {:L} does. Output
with an explicit locale is unchanged. With FMT_USE_LOCALE=0, {:L} no longer
consults the global locale, matching numeric formatting under that option. An
-Os test program loses all 16 of its std::locale and time_put symbols.
nolocale-test defines FMT_STATIC_THOUSANDS_SEPARATOR, which stopped doing
anything in b90b4bc9 ("Remove FMT_STATIC_THOUSANDS_SEPARATOR in favor of
FMT_USE_LOCALE"). That macro no longer appears anywhere under include/, so
since then the target has compiled src/format.cc with locale support enabled.
It is not a dead target: the pedantic CI jobs build it (linux.yml, macos.yml
both pass -DFMT_PEDANTIC=ON), and it passes. So the configuration looks
covered while nothing actually tests it. #4627 - a locale-off build break -
landed during that window.
Define FMT_USE_LOCALE=0 instead.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nolocale-test compiles src/format.cc directly, so it does not pick up the
/utf-8 that CMakeLists.txt adds to the fmt target. On MSVC the static_assert
in base.h then fires: "Unicode support requires compiling with /utf-8".
The target only exists under FMT_PEDANTIC, which the Windows workflow does not
set, so this has not shown up in CI. unicode-test already guards the same flag
the same way.
* Widen display_width_of() to cover Emoji_Presentation code points
fmt::detail::display_width_of() only treated East Asian Wide/Fullwidth
code points and two hand-picked emoji ranges as two columns wide, so
emoji outside those ranges (e.g. the Dingbats block: cross mark U+274C,
white heavy check mark U+2705) were measured as one column even though
most terminals render them double-width. This produced visibly
inconsistent padding under {:^N} compared to CJK text (fixes#4851).
Replaced the ad hoc boolean expression with a sorted table of ranges
(East Asian Wide/Fullwidth plus the full Emoji_Presentation set from
Unicode's emoji-data.txt) looked up via binary search, and added
regression tests covering the original report plus edge cases:
multiple emoji, mixed emoji/CJK/ASCII content, precision truncation,
alignment/fill variants, newly covered emoji ranges, and regional
indicator (flag) pairs.
* Make wide_cp_ranges C++11-compatible; apply clang-format
* Move wide_cp_range struct into display_width_of()
* Derive wide_cp_ranges from East_Asian_Width data, restore constexpr
* Move wide_cp_ranges to display_width_of function body
* Update format.h
* Update wide_cp_ranges array to include comments
* Add inline to display_width_of and add 1F300-1F5FF and 1F900-1F9FF ranges to match [format.string.std]
* Update display_width_of lo hi varible declaration
---------
Co-authored-by: Eduardo Gomez Saldias <50159560+edugomez102@users.noreply.github.com>
Don't inject -freflection when building the module, so it isn't forced onto
the module or its importers. Build enum-test unconditionally (it is a no-op
without reflection) and drop the now-unneeded reflection detection. Enable
reflection explicitly in the g++-16 C++26 CI job.
Use std::string_view directly for enumerator identifiers, dropping the
identifier wrapper and inlining count_enumerators. Update the fmt/enum.h
documentation to point at cppreference and drop an inaccurate note. Also
remove the no-longer-needed -Wno-sfinae-incomplete option for format-test.
Enums that are too sparse for the index table were formatted by scanning
all enumerators. Replace the scan with an open-addressed hash table with
linear probing, sized to the smallest power of two that keeps the load
factor at or below 0.5. This guarantees a free slot, which terminates the
probe sequence and doubles as the not-found result because identifiers
are never empty.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
fmt::format("{:6}", nan) gave "nan " where std::format and printf("%6f")
give " nan". write_nonfinite was the only numeric write path taking
write_padded's align::left default, so a width with no explicit align
left-aligned inf and nan while every finite value right-aligned. That
default only reaches align::none, leaving explicit <, > and ^ and the 0
flag unchanged.
* Format enums annotated with fmt::as_identifiers (requires C++26 reflection)
Format an enum as the identifier of the matching enumerator if the enum is
annotated with fmt::as_identifiers:
enum class [[=fmt::as_identifiers]] color { red, green, blue };
fmt::format("{}", color::green); // "green"
Values that don't match any enumerator are represented as their underlying
value in decimal before applying string formatting.
Identifiers are retrieved via C++26 reflection (P2996) and the annotation
via P3394. FMT_USE_REFLECTION is autodetected and can be overridden by the
user; without reflection the header is empty.
The header is also part of the fmt module, but, unlike with headers, whether
it provides anything is decided when the module is compiled, so the module
build detects reflection and enables it if the configured standard allows.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Look up enum identifiers by index when the values are dense
The formatter for enums annotated with fmt::as_identifiers did a linear
search over all enumerators. Build a table indexed by the distance from
the smallest enumerator value instead, with empty string views in the
holes, which reduces the lookup to a bounds check and one load.
The table is only used if at least 70% of its elements are identifiers,
limiting its size to 10/7 of the number of enumerators. Sparser enums
keep using the linear search. Distances are computed in uint64_t so that
enums with negative values and values spanning the whole range of the
underlying type are handled without overflow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Suppress -Wsfinae-incomplete in format-test on GCC 16
GCC 16 warns when a type is completed after it failed to be complete in a
SFINAE context. format-test does this deliberately to check that formatting
of incomplete types works, so the warning is a false positive there and
breaks the build with -Werror.
* Test GCC 16 on CI
GCC 16 is the first compiler with C++26 reflection support, which is needed
by fmt/enum.h, so add a job that builds with it in C++26 mode. It comes from
the ubuntu-toolchain-r/test PPA since Ubuntu 24.04 only ships GCC 14.
Also report when reflection is not detected to make it visible that
enum-test was skipped.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Treat a leading '0' as the zero flag rather than the start of the width so
it is rejected (as in the core formatter) instead of being silently consumed.
Also trim the exception_align test to the cases this actually introduces.
* Support width and alignment in std::exception formatter
The formatter for std::exception (and std::exception_ptr) previously
accepted only the optional 't' type-name specifier, so standard fill,
align and width specifiers were rejected with "unknown format
specifier". This made it impossible to pad or align exception messages,
e.g. when laying them out in a log column.
Parse the standard fill/align/width specifiers (as the std::filesystem::
path formatter already does) and apply them when writing the message.
Dynamic width ({:{}}) and the 'none'/'unknown exception' cases for
exception_ptr are handled too. Existing behavior ({}, {:t}, nested
exception unwinding) is unchanged.
* Update std.h
fmt::format("{:a}", 0.0) produced 0x0p-1022 where printf's %a gives
0x0p+0, and likewise for -0.0, {:A}, {:#a} and any explicit precision.
basic_fp::assign maps a zero biased exponent to 1 ("subnormals use
biased exponent 1"), which is right for subnormals but not for zero, so
format_hexfloat inherited the minimum subnormal exponent for a value
whose significand carries no information. Reset the exponent there
rather than in assign, which the decimal path shares.
A zero significand can only come from +-0: normals get the implicit bit
added, and a subnormal has a nonzero significand by definition. So the
guard cannot affect anything else, and denorm_min keeps its existing
denormalized form (0x0.0000000000001p-1022, asserted in format_double).
The five new expectations were taken from printf rather than derived,
and all fail without this change.
* Set matching CC alongside CXX in Linux CI matrix
The Configure step only set CXX, so CMake's C compiler detection
fell back to whatever the default happened to be on the runner,
independent of which C++ compiler the matrix entry was actually
testing (e.g. CXX=clang++-3.6 but CC left to detect GCC 11).
Derive CC from the same matrix.cxx value the job already installs
a matching compiler for.
* Skip c-test when the C compiler is Clang < 3.8
Setting CC from matrix.cxx means the clang++-3.6 job now configures with real
clang 3.6 instead of falling back to the runner's default GCC, which exposes a
compile failure in test/c-test.c: clang predates the LLVM PR16340 fix (landed in
3.8) that applies array-to-pointer decay to the controlling expression of
_Generic, so fmt-c.h's FMT_MAKE_ARG dispatch never matches a string literal
(char[4]) against its char*/const char* associations. It falls through to the
zero-argument default association, which is then called with one argument.
The guard is at configure time rather than in the CI Test step. c-test is part
of the default target, so `cmake --build` compiles it before ctest ever runs --
excluding it with `ctest -E` cannot help, because the job has already failed in
the Build step. Guarding the add_executable also means anyone building fmt's
tests with an old clang benefits, not just this one CI job.
enable_language(C) stays first and unconditional, since CMAKE_C_COMPILER_VERSION
is not set before it. fmt-c itself (src/fmt-c.cc, C++) still builds on that job;
only the C-consumer smoke test is skipped.
A named argument that arrives const-qualified was not recognized by
is_named_arg, silently dropping its name. This happens both when a
named argument is passed through an intermediate function returning
const T& (https://github.com/fmtlib/fmt/issues/4866) and in fmt's own
compiled format path, which passes arguments as const T&.
Make is_named_arg and is_static_named_arg see through top-level const so
the name is preserved instead of dropped.