c9dec5da8c
9bdd1596c Update version d151562bd Fix punctuation in changelog 346500e70 Fix gcc version check a434a8f77 Update changelog 9eb47d951 Fix markup 51c58a56b Bump version 3fc33f627 Update changelog 2e32db5b9 Update changelog c1ce6e01f Update changelog 1c3c80dc1 Update changelog d1d653d89 Implement the L specifier 73c843748 Follow naming conventions e588b02b1 Fix posix-mock-test 1a62711d0 Reduce binary size 5b0288158 Merge branch 'master' of github.com:fmtlib/fmt a133187a8 Update changelog 80ce222ca Fix wide print overload (#1609) 770a94ede Use FMT_THROW macro where applicable 2864e8432 Update readme and add compatibility option 21a295c27 Undo comment change 96c68afe6 Fix -Wsign-conversion warnings 664dd88e3 Enable FMT_STRING() use with types other than string literals 69779b4ed Fix handling of small precision in general format 01a172c96 Add .vs to .gitignore 08ca40ea9 Detect /utf-8 in MSVC dd97f4920 Improve exception safety in dynamic_format_arg_store 295116948 Move FMT_USE_FLOAT and friends to fmt/format.h d3e668418 Allow disabling floating point support (#1590) 52d0e1bbe Don't use properties when setting FMT_LIB_NAME 5d32ccfc3 Add back missing OUTPUT_NAME in target properties. (#1598) 3cf619de5 Simplify dynamic_format_arg_store 2559983e7 Color formatting fixed for wide strings (fixes issue #1594) (#1596) 026f99178 Simplify dynamic store 9f70fc3e7 Minor tweaks for dynamic_format_arg_store 6012dc9ab Dynamic arguments storage. Implementation of enhancement from issue #1170. (#1584) 85050aa2e Ability to join elements of std::initializer_list was added ff486a72a Allow leading zeros in precision (#1579) 678341275 Deprecate fmt::char8_t 6f01b6ebb Fix a typo in CMake config: STRINGS -> STRING 61c5a5160 Fix handling of empty tuples (#1588) 02bfd8a9a Add FMT_HAS_CPP14_ATTRIBUTE / FMT_HAS_CPP17_ATTRIBUTE to test for language-specific attributes. 3c24052cf Workaround 'cannot call member function without object' error on gcc 4.9 f72a905eb Fix handling of volatile enums 941d5e147 Workaround broken fallthrough attribute in the PGI compiler (#1583) ee2b828b9 Tweak a comment 5bb885665 Workaround for broken [[deprecated]] in PGI compiler (#1581) 1c0c59d4a Fix empty debug postfix b1adaa988 Remove gcc 4.4 workaround 48e8d0ebe set_doc -> set_verbose ce0097915 Cleanup CMake config db4a6cfbf is_static_compiled_format -> is_compiled_format 29a1ea795 Fix clang -Wdisabled-macro-expansion warning from FMT_STRING_IMPL. 8a06ca84c Fix ambiguous overloads of format & format_to 153f753bd Deprecate undocumented _u suffix eafd07986 Improve width computation 0c6919ec7 Make FMT_DEBUG_POSTFIX a cache variable (#1566) 197a5c372 Apply clang-format 68742e1d8 Fix clang -Wsign-conversion warning in grisu_count_digits. (#1573) 1e8493196 Make compile-time checks in format_to handle references 58e6c84f5 Fix simple -Wsign-conversion cases. (#1571) 75a4525e5 Move FMT_CLANG_VERSION definition to core.h (#1568) 6ccb2e241 Add FMT_NORETURN to assert_fail prototype. (#1569) bed134a4a Tentative fix for default template param in friend error b2d3a86ec Make FMT_ASSERT work in constexpr on clang 4.0.1 13d82e32b Don't use internal GTest API 2161a73f2 Fix FMT_FORMAT_AS const specifier position (#1554) e00997b00 improved use of find (#1560) 0415cf235 add const begin and end overload to buffer (#1553) 3bafd0749 Fix to_string docs f733882b5 Remove misleading FMT_USE_WINDOWS_H dc22360c3 Workaround broken UDL templates in GCC < 6.4 1f1b50707 Make formatter override implicit conversion to a C string 24924128e Fix a link error in gcc8 (#1548) c54cd7180 only modify CMAKE_RUNTIME_OUTPUT_DIRECTORY if it is not already set 43e9b29e5 Only use compiler features if available b55ea5870 string_view::char_type -> value_type (#1539) 4098970db Update README.rst 314e15001 Fix symbol visibility on Linux when compiling with -fvisibility=hidden (#1535) f499b393d Apply coding conventions 6c30f4144 Configure fmt.pc library name correctly. 1acb73f97 Fix formatting std::chrono::duration types to wide strings (#1533) 09a13244c Disallow passing non-string-literals to FMT_STRING 419db8baa Fix length computation of constexpr C strings 9fc4161f5 fix interal compiler error when building with mingw 25d6916b3 Fix so can work without locale defined 0b2eb6501 Add locale example fd1cabe46 Workaround a bogus MSVC warning a844d7ab8 Add namespaces 47d396809 Add more examples 7800173eb Update fill docs b4218aa0f Test invalid fill 8a3a8177d Bump version e5f2f8ce7 Add variable-width fill support (#1109) 75765bfad Avoid unnecessary unsigned overflows (#1515) 9bd9738da Remove static and simplify names bd5f903f2 Add a locale example 06e437fd9 Move docs to the proper place 1bd4f54fa update format 11cc2903e re-fix url link b124e3e8e fix url link ffd5f3469 Correct display format 0f0e5ddf5 Add vcpkg installation instructions 1f110702a Remove redundant braces 4ccbe4b5f Avoid namespace clash for fmt 40638a75b Use C++11 compatible std::is_same operations c8dd9cc99 Use type_identity to block unnecessary template argument deduction (thanks Tim Song) 4bbe57ceb Work-around for nvcc 55b613005 Use C++11-compatible operations ae3ea156e Fix for older versions of intel compiler 77165fdf8 Use FMT_NOEXCEPT instead of noexcept directly 65ac626c5 Improve join docs cd0b3f969 check if _SECURE_SCL is defined not equal to 0 cef1e4354 Optimize grisu_gen_digits 0201c8db2 Restructure float_format 9e3f3e8cf Fix handling of output iterators in format_to_n (#1506) aa07c5765 Move vprint_mojibake to the internal namespace a73d89e9c Catch invalid uses of fmt::arg cb8e7caf7 Convert 'char8_t' character sequences to 'char' sequences b3fd0005d Suppress a bogus -Wdouble-promotion warning 7b478f9de Simplify example c85efef31 More showpoint fixes and tests (#1498) 455a7c078 Clarify lifetime of basic_format_args 674c326d7 Update syntax.rst 061a9897f Update syntax.rst d2d1c9c56 warning C4267: 'argument': conversion from 'size_t' to 'DWORD', possible loss of data b6e19e595 Update apidoc f219dcd59 Add fmt::bytes dea7fde8b Deprecate u8string_view 5390e29d4 Enable mojibake 9f6434dcd Improve UTF-8 handling on Windows dac9a7f99 Improve UTF-8 handling on Windows 3ca9533f3 Flatten forward 7eec036d9 Improve UTF-8 support e6b37b4af Handle block boundaries in utf8_to_utf16 8cf4c5206 Apply clang-format 74532c23a Make type a scoped enum b308159be Make round_direction a scoped enum 162995fed Add os.h to docs 8b41362a0 Add trailing decimal point if # is specified (#1476) 1b1c70108 trailing_zeros -> showpoint d7e72a09e Simplify FMT_STRING_IMPL 2201890d7 Apply clang-format and update inclusion guards 6100ed4bb Eliminate NVCC NVidia compiler emits unreachable code warnings 1afe201ae Handle block boundaries in utf8_to_utf16 cd2b99032 Chore(readme): use https (#1481) 9acf89fef Mitigate MSVC issue with min/max macros (#1480) 9ea42fb26 Rename posix-test to os-test da2569827 posix.cc -> os.cc 35959a31d Move OS-specific APIs to a separate header ec2463c90 Implement utf8_to_utf16 using utf8_decode 0012917f6 Add a UTF-8 decoder 9e450911f Give an error on precision overflow 068d20bc3 Avoid shadowing warnings in FMT_STRING a99fbe67b Apply a typo fix retroactively adbed11ed Fix a typo 8ab1c5c6e Squelch MSVC warning exporting subclasses of runtime_error (fix for PR #1433) (#1470) a770009fc Improve error reporting 598e6042d warning C4468: 'fallthrough': attribute must be followed by a case label or a default label e09814dc9 Merge branch 'master' of github.com:fmtlib/fmt b272fb360 Extend FMT_FALLTHROUGH compatibily to gcc and clang pre-C++17 (#1469) f94b7364b Update version 7abec071b Update changelog b7eb8c892 Prepare for the next release ae7c50185 Reintroduce sprintf_format for ABI compatibility 9f2e7edae Fix handling of types convertible to std::string_view fd52de0c6 Add FMT_CUDA_TEST CMake option to enable cuda-test f675cb887 Remove redundant cast 73a16b827 Fix handling of int128_t in format-impl-test (#1461) 72879db40 Clean-up sign-conversion warnings in public headers d3aa0c3a2 Clean-up sign-conversion warnings in test code 31de9a1b8 Revert "Clean-up sign-conversion warnings in test code" 227bfe62d Clean-up sign-conversion warnings in test code 95dfdc6cc Update README.rst 5916ff63c Update README.rst 1ab80aa92 Fix handling of types with custom formatters that are convertible to std::string_view 4f4d87661 Remove '%' from the docs f443bd3ba Ditch decimal_formatter (#1363) 1219b65f2 Relax fallthrough attribute detection 071794ec6 Update version d22e4ad85 Remove trailing comma 983806b0c Update changelog 02af5beb8 Bump version and update changelog 123e7f7fc Revert #1433 because of build failures (#1450) 168460f02 Remove TYPES a64f60c84 Remove unneeded FMT_API. 1a599117d Export assert_fail with FMT_API. This fixes dll build. b160123e3 Update ChangeLog.rst 598158856 Fix compilation with MinGW 8bbe76af3 Add a missing decimal point in exponent notation with trailing zeros 4ca6821e8 Update version 7111a1eb9 Bump version ae00bbdc9 Update changelog e71e07d9f Update changlog 0184df702 Update docs 1cbae6e9b Put vprint declarations in one place 159f89e2b Fixing installation directory of '*.dll' files on Windows 4b120b68a Clean up includes 186b225d9 Update changlog 4cbf4888e Update changelog e31f2b3d0 Update changelog 62da1db62 Avoid wchar_t instantiations 3bc28fcc6 Squelch MSVC warning exporting subclasses of runtime_error 3c05fa46c Update changelog ba6e330fd digits -> num_bits 6037b3cae Fix dangling else problem in FMT_ASSERT fafb03fa6 Fix handling of fallback_uintptr 2f9acd183 Remove dependency on <cassert> aaf829bfb Fix fallback pointer formatting on big endian, take 2 b994a0ab1 Fix handling of missing fraction in snprintf_float bb205d940 Fix fallback pointer formatting on big endian ef7369ce9 Update docs 40e4c227d Update changelog ea54b21e7 Remove invalid noexcept annotation 9cbf4b087 Fix -Wconversion warnings 1200a34e1 Update changelog 9c7e2a6c6 Add missing newline 34e921f6f Update docs c3be0f593 Refactor floating-point formatting c68703c9f float_spec -> float_specs 9a21728b0 Remove gen_digits_params 3de36e934 Enable -Wswitch-enum in CI 4afb39bc2 Update README.rst 7ffa62db1 Fix precision handling in snprintf_float 0d07db123 Fix handling of streamable and convertible to string types d19ed6716 Fix hexfloat buffer reallocation 99b6e928d Fix handling of types with deleted rvalue conversion to string (#1421) 57cd3f72e Update comment 111fc127f Remove fp::operator- 6003ec3f2 Simplify Grisu implementation 8877a6772 Instantiate snprintf_float 75fff1db6 Minor cleanup 28d7191c2 Don't print trailing zero with fixed, precision=0, and showpoint (#1417) 43271ba8e Handle null terminator at the end of the buffer 63a9f3fcd fix bad oss fuzz link in the oss-fuzz badge 4cf59ce73 Integrate Grisu and sprintf digit generators 7395472dd Refactor floating-point formatting 9108b25da Merge branch 'float' 4d366c68b Merge branch 'master' of github.com:fmtlib/fmt ded1e7679 Refactor floating point formatting c7edd8e57 Cleanup FP formatting 75108a56f Don't print % for nan and inf 3e1f70fe0 Merge write_fp into write 125fc5e52 Update comment 6793ffc1d Update README.rst f4fcc5fd2 Update README.rst 4de41aa65 Move basic_writer::write_fp to where it belongs 404a880bd Make parse_arg_id more readable 092d2dc7b Merge safe-duration-cast.h into chrono.h 093e55421 Remove redundant qualification d0696b0aa warning C4456: declaration of 'num_digits' hides previous local declaration 66d7746bb Use grisu for exponent notation e9bff7881 Don't parse % unless FMT_DEPRECATED_PERCENT is set 57b6f2966 Deprecate the fmt macro d79493e5e Remove Grisu2 78842ce0d test: add default constructor for a const value 5420bcce2 Make % an opt-in to improve compatibility with std::format 56a2e2075 Refactor float spec parsing ed117baa4 Replace bool with float_format and add exponential f26446290 Move float_spec_handler to internal namespace and update asserts 7e1cb3237 Fix indentation f67783d7e Clarify that numeric alignment is deprecated 1c6d85f7b Apply coding conventions to examples 4a1da44f9 Apply coding conventions to examples 080b6899d Tweak the docs c01ec54fd Document and clean basic_format_parse_context b0c2ab93f Bump version 9b7fe2a4a Don't use POSIX API on UWP c58b7d9c2 Use overridden locale in ostream ceff9b0b2 Tweak the docs 3dc8639f8 [docs] Added conda dcde089b4 Improve POSIX API detection 2145a7bdc Move has_formatter into the public fmt namespace. (#1407) 52ae134f8 Remove broken CI config 0d6dd0cc6 Correct basic_string_view from string ctor 1f918159e [clang-tidy] Replace deprecated C headers 6868f888b [clang-tidy] Add missing override 87cd545a1 [clang-tidy] Replace {} with = default 12f9437e2 [clang-tidy] Use auto bb0c8bfea [clang-tidy] Add noexcept where move is used e6e829890 [clang-tidy] Add parentheses to macro arguments 0f0848e4f [clang-tidy] Use braced init list a1fb5c733 [clang-tidy] Changes suffixes to uppercase 8a411c2bc [clang-tidy] Turn deleted function to public 0047dc10a Mark apidoc as rst 263cdef8a Merge branch 'master' of github.com:fmtlib/fmt d4ca54253 Update docs 5bb7b28e1 Document members 1409dfe76 Try fix CI f1559e1d5 Use grouping() from locale for specifier 'n' ffd05e65e basic_parse_context -> basic_format_parse_context per standard and document 0889856d6 Fix UTF-8 truncation d6eede9e0 Remove redundant ctor 213e09644 Workaround X11 madness (#1388) 6bfc9af8c Add double support to compile 3487f1b9c Always inline grisu_gen_digits and disable grisu2 by default 791294d17 Apply get_cached_power optimization by jk-jeon 8e700619b Simplify format_handler 58c6f8c7f Make unsigned-integer-overflow sanitizer happy (#1377) 40414b344 Don't emit trailing zeros in exponential notation (#1376) b7a157401 Simplify grisu_writer 7aa58c30b Simplify NVCC checks 8e9bffa98 clang-format ce4d87acd Remove obsolete comment and clang-format 21acc2af4 Fix more Visual Studio 2019 pedantic warnings (#1371) 00669427d Patch compiler error when building using nvcc d39ebf3ff Optimize counting 6498bc6d3 Simplify grisu_writer a967dcbe2 Improve handling of signs 8498bc97d Initialize all the things e2ea94067 Handle assymetric boundaries 2bc5585ff Fix computing lower boundaries for smallest normalized double bb728a572 packed_arg_bitsize -> packed_arg_bits and remove packed_arg_mask 36d1390e6 Implement round half to even 599e0aef4 Support single precision floats in grisu formatting 91f7619cc Fix Visual Studio 2019 pedantic warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) c4dc6bef2 Apply clang-format 646966e97 Reduce bigint capacity a5abe5d95 Handle negative exponent and nonnegative power 1cbc5fa6c Handle negative exponent and rename value/pow10 to numerator/denominator f7a5748fd Partially implement (FPP)^2 0e94b931a Fix a linkage error introduced by #1360 (#1362) 5e58eb97b Implement add_compare 3a15ea3ea Rename write_double to write_fp b87ac4d84 Distinguish float from double a927dda9b Use words for packed constants dd11d4584 Encode types using 5 bits b55551f90 Implement more comparison operators 96f91428c Add defaulted copy and move operations to format_error and system_error (#1347) b732f28c0 Deduplicate color vformat and vprint a82c1dc6d use memory_buffer to make color print behave atomic #1348 (#1351) 2730e9018 Fix compile error in printf with gcc9 (#1354) e4d6d9d7c Implement divmod a1079e9fd Fix undefined in format-test (#1349) b66bb6b71 Fix undefined in core-test and printf-test (#1345) b60114533 Implement more bigint operations c41cea8b1 Initial implementation of square 0c7650373 Fix handling of types convertible to std::string_view 057101370 Repoint one more Python 2 link to Python 3 d2c9276fc let README point to python 3 instead of 2 0fc7bd157 Fix ambiguity for types with dodgy conversions b4f1988c4 Provide overload for `fmt::join` that handles `std::tuple`s 4b8f8fac9 Update README.rst 3b2fc033d Making CUDA test work with CMAKE_MSVC_RUNTIME_LIBRARY ac59d9f3a workaround VS2019 Update 3 compiler bug. (#1328) 8f27ce4d8 add test for multiple compilation types 89b0c71fa fix name clash in header-only mode f6a783ad2 fix `get` ccc8f5db0 Disable integral operator<< (#1316) 20fdb88a1 Remove redundant and nonportable test (#1313) f29901097 Don't use const char* overload of operator<< (#1309) 758446c80 Fix a warning (#1319) and simplify code f7aedc5fc Fix shared build on Solaris 840a817ed add oss fuzz badge 79c923ba2 fmt.pc.in: Fix for cross compilation 5dc577c06 Update ChangeLog.rst c6d1a94a9 Fix fallback_format (#1306) 0656045d0 Fix format overload that takes text_style (#1305) c85ae23c7 Add max_value b3bf66576 Implement multiplication and part of assignment from pow of 10 0887887e2 Implement left shift ac71d853b Refactor normalize and clean up 6649b8e0c value -> bigit 56b5c192a Add a bigint stub and reenable grisu b2f0b6e44 Tweak comment 3d9f3c163 Integrate new format string compilation 19547d514 Update index.rst 972ffd315 Update README.rst 2ed412fa3 Update README.rst df4dcf2ec Fixed vs 2015 warning about unused variable when grisu is off f7a4b4ab9 Make numeric alignment optional 611cf0b3c Format octal 0 as 0 58a8f2f53 Add ccache to the list of projects 1882b9687 Reduce the numer of ifdefs with an empty (u)int128_t fallback 6de0454b4 Add support for built-in __int128 when available 16e3c48bb Move definition of FMT_USE_INT128 to core.h 8ce5f680f Rename internal::is_integral and internal::is_arithmetic 2fd8f9ec8 Initial implementation of optimal compile-time formatter generation fe642d764 Clarify use of the core API in header-only mode (#1296) a128b5b2c Simplify format string compilation 466128de0 Remove unused code and refactor 22e98a5b6 Make compile work with user-defined types f18a3f36a Remove string_view_metadata 7cad33563 Refactor format string compilation e1ab6bc00 Simplify format string compilation 24a88545d Add -Werror to tests 422e7b9d7 Fix compile-time checks for user-defined types (#1292) d1dd9d532 Document floating-point n specifier (#1291) 9a56a608e Fix warnings (#1288) c76957565 FixedEnum -> StrongEnum and make it a regression test 200ee6f10 Fix minor clang-tidy warnings bcd9b9331 Map not int enum to correct underlying_type (#1286) 345ba07f1 Add a CUDA test 9e2490be4 Rename precision parameter 3f75e2b69 Make buffer_range public and update custom formatting docs (#1281) 744302add Workaround broken [[deprecated]] in Intel compiler (#1273) f5556225a Eliminate shadow variable warning ad3c7855e Fix typos. 7512a55aa Update version 9bd2f1f9a Temporarily disable Grisu b9815cf04 Simplify part_counter fe00cddde Move part counter to the namespace scope 1a7d172dc which_value -> kind 006c2546f Get rid of basic_prepared_format 4ce006fb6 Simplify format string compilation e2e557e27 Remove preparator a5f470eb1 Test and fix compiled format_to_n and formatted_size 4070c1d80 PrepareTest -> CompileTest f8b59251c Remove unused preparator 643aa4c8c Simplify format string compilation ffe29a92f Get rid of internal::compile e94d72366 Fix compiled format string version of format_to_n a9337395a Remove unused variable 562a9f499 Remove parts_container_concept_check b257c56e5 Fix size types 1488df339 Bump version b918e3ff8 Fix docopt usage 23b04ca6d Update usage instructions 5d5918a74 Update usage instructions 413d97b33 Fix formatting in changelog 790fd90f4 Update changelog c0890be77 Update changelog 5a4b24613 Update the default floating-point format docs (#1270) 45dc3486f Update license 5a353fa8c Draft license c84d227da Update changelog b2d4ca154 Workaround an issue with std::filesystem::path being an infinitely deep range (#1268) 2aae6b120 Fix doc for importing header only target with CMake 9f09b8eed Fix a warning 2bb8120d9 Fix a warning c1e97392b Fix warnings 4e99e09bb Fix warnings 1607a0187 Suppress a warning e8219952c Restrict fmt::compile to literal strings 544b53733 Update changelog 83c13a1fe Update changelog 2546dafec Update changelog 02c04b173 Update changelog 5360ab0b5 Fix iteration over named arguments (#1168) b615eca96 Update changelog 22a42c0b0 Cleanup compile implementation c63624ed4 Update changelog 211d31240 Don't terminate on I/O errors in report_error ef55e74e0 Update changelog 612669d34 Update changelog ba63ac8c1 Make format_to a non-member 3fe49163b Revert "Make format_to a non-member" a5bd3ddb2 Make format_to a non-member 3df0ea34e Remove unnecessary overloads 436acf348 Make formatted_size & format_to_n non-members 823128049 Make format a non-member 1dfa2591b Make operator _a constexpr 6f2dd30e3 Simplify udl_formatter return type 431d0f85c Use basic_string_view in non-template UDL ad71f5a70 Use forwarding references in UDL template d0f2f3b81 Update usage.rst eac2796ce Fix locale detection ee3625746 Internalize format_part bbf0bada3 Simplify compile 3730b4f03 Cleanup compile implementation 25ff2efc0 Update changelog 9393fe26f prepare -> compile b48ffc14a Update changelog 3268531bc Workaround a broken C locale on Windows 8bd59ec93 Use fputws for outputting wide strings 1235f0a24 Fix typo in usage.rst 4fb73d114 Update README.rst f9ceefb0f Fix a warning and is_negative usage 6bcc3fd21 Fix warnings 6a497e1d0 Fix warnings e9b9b0aef clang-format ec24342b2 Fix more warnings 230b24944 Fix sign conversion warnings cadd92d69 Fix #1232. 41076d1de Use `FMT_THROW` in fmt/color.h. d561cb47a Opt-in macro to enforce use of compile-time format strings 699fe8e71 Remove const qualification in compile-time checks da2d33f1f Update changelog af4734fd1 Fix warnings a3a74672a Update signatures c11e68305 Update signatures dd8cc8b0b Disallow passing views as lvalues f6f0415b8 typedef -> using c92dc3746 typedef -> using e3f20d3e1 Remove gcc 4.4 workaround and use proper alias templates (#940) b43881232 Clean up is_output_iterator bc15e3700 typedef -> using cf5ebf205 Cleanup 9c20e72de Cleanup 79209598f core_format_specs -> sprintf_specs 5488d0b53 spec -> specs e4f84ee1c Refactor format_specs for #1109 and #940 8e0dcd20b Remove old deprecated functions 1d3e3d8c0 Make the 'n' format specifier work with grisu disabled bc628f8d4 Fix EXPECT_DEBUG_DEATH_IF_SUPPORTED (#1214) edd13fcc1 Fix small number rounding with fixed precision in grisu 6a031347e Remove unnecessary qualification bc14c6ee2 Use the decimal point from locale 476f25cd8 Remove ancient gcc workaround c9d5a08ed Add the 'n' float format specifier f487ddbdf thousands_sep -> add_thousands_sep d8fd1699b Make data names follow naming conventions c286ffc88 int_traits -> uint32_or_64_t 1289782f0 Get rid of add_thousands_sep 2249f5571 Simplify thousands separator handling and cleanup e76446958 Cleanup and remove deprecated visit de37de912 Remove deprecated color API df1a3a141 Remove null_terminating_iterator 78dec87a4 typedef -> using ded0a3bb3 Internalize undocumented basic_writer 83174f2a1 Refactor ranges c2e84ee9c Fix FormatTest.StrError on Solaris 2711cb167 Apply clang format and other minor formatting tweaks ab0ba8a9d Don't emit decimal point if there are no trailing digits (#1210) bd3fd3bfd clang-format 260c11590 Fix formatting of 0.0 with (#1210) 9d97201ed add oss-fuzz support 037b84f21 Fix warnings e37ee419c Export exceptions destructors e3488fcae Fix Warning d5d586561 Move strtod_l check to CMake since it's very system-specific 29ef7d31e Fix a warning af83192d7 Fix warnings 6952732b6 Remove misplaced checks b97e5d8c2 Remove deprecated fmt/time.h 572b077db Fix warnings 72e519a4b Add formatter<std::string_view> 635e01fe7 Update <format> to the current wording 0e72c9804 Make undocumented output_range internal f13906f40 back_insert_range<internal::buffer> -> buffer_range 22ddd4b98 Simplify feature checks bb827341e Add FMT_FUNC where necessary 64c54703d Cleanup 1c3197b8d Fix warnings fd2292f13 Cleanup checked iterators 4912cff65 Fix handling of mapped types in compile checks (#1200) 463984383 Cleanup e29708ee5 uintptr -> fallback_uintptr to avoid confusion with uintptr_t f03a6c532 Cleanup 6c3d584e6 Simplify warning suppression 12f468388 Make iterator_t an alias template 874d6727e Remove workarounds for pre-C++11 compilers a9940192f Fix warnings 34b541835 Fix warnings cbbee1b38 Fix handling of hexfloat 92a44db11 Fix warnings d05d42751 Remove old msvc workaround from arg_formatter_base and fix warning d32fe0f3f Fix hadling of nullptr e5422db4b Fix handling of uintptr_t 9d7b64a25 More cleanup 5e293bd97 Remove unnecessary qualification 4a502d980 Add remove_reference_t d384cdd39 Make is_contiguous_back_insert_iterator internal 32544b610 More cleanup 39f522a13 get_types -> encode_types d7d2bebf9 Remove redundant typename 9427f15be Fixed issue with formatting to an array of chars 87fbc6f75 get_type -> mapped_type_constant a48daa60e Remove remove_volatile afdbbac75 Cleanup e33fe14f5 Simplify visit e895da2ec Clean up value e1a67b528 Remove TODO a291f07e1 Clean up argument mapping 5d9100fa2 Move char8_t to fmt/core.h 4faadff0a Add preliminary user-defined type support 5d4873359 Clean up value construction 0f0b42861 Cleanup 209db68b2 Get rid of FMT_CONSTEXPR11 cb4c59495 Deprecate convert_to_int 40779749a is_formattable -> has_formatter b3cf8613b Make formatter specializations override implicit conversions 3fdba0492 Reduce the number of nontrivial formatter instantiations f5f3ffac5 Merge string make_value overloads a38b99a18 Fix a regression in named argument handling introduced by prepare aa31028b2 char_t_impl -> char_t 0787d6974 Simplify SFINAE bae00aa8d Simplify is_string 388bb389e integral_constant -> bool_constant 7e39c7e6f Put stringy stuff together 3eff8f94d Update docs 469a4bbd3 Use enable_if_t d2ee5f240 Merge tests c264e641e Add conditional_t for pre-C++14 4aa0dc578 Fix docs 064ce6b6c Specialize is_char for character types 7893d8539 Clarify why we cannot have nice things 5bafcb437 Add comments about things broken in MSVC 153024255 Fix signature in the docs eddb84cfc Fix formatting of exotic characters 7e42c65bb Document a more useful to_string_view overload 1e6e87cb7 Update docs 0c6a6e025 Get rid of the FILE* hack and reword apidocs 1653244c6 Fix compilation issue on VS2019 (#1186) (#1191) d54e64b3c Make buffer_context an alias template ec6651087 Remove old is_constructible workarounds and replace typedefs with using 4d4b8c238 FMT_CHAR -> char_t 56d2b9110 Install git 89d6c959b Fix cmake link 76ef39fc5 Update vagrant config af2c73772 Implement parsing of string_views 9df0e2d1f Implement string parsing 5b7bbf885 Revert "FMT_CHAR -> char_t" 4c650057a FMT_CHAR -> char_t 2833c76f2 Move char_t to template param to reduce symbol size 67feef558 Make enable_if_t more std-like and move to fmt namespace 78daa50ff Fix handling of chrono durations with minimal signed rep 87e4ea290 Fix a warning c56b17029 Add msvc2019 to CI c929684e3 inlines count_code_points(basic_string_view<char8_t) f57227a14 FMT_ENABLE_IF -> enable_if_t 634f707f2 Simplify char_t and remove msvc2013 from CI 406e632bd result_of -> invoke_result_t 49f78a427 Demacrify 637bf3c6d Workaround a bug in clang-format 8302c2f33 fmt::internal::declval -> std::declval d07cc2026 FMT_EXPLICIT -> explicit, FMT_NULL -> nullptr 4a7966c77 Drop gcc 4.4 from CI 9b3c24b99 One weird trick to simplify docs; doxygen hates it 30bce6c14 Fix a few chrono formatting corner cases (#1178) e5512c5d5 Use static_assert instead of SFINAE in arg(...) afc571aed Document join and relax its compiler requirements ad360a62b add gcc 8 c++17 release build 3cf12d7b1 add gcc 8 c++17 build bb254d146 Disable std-format-test by default 291ba837f Remove wrong compile-time checks from printf (#1173) 01c631af9 Implement unsigned and long long parsing 4fcd4a4bd Reuse parse_format_string in scan 2346779d6 More documentation fixes afc1a74a6 Minor documentation fixes ad0eade47 Remove MSVC nonsense 28c187bcd Capitalize titles 91bb3aaf0 fmtlib.net -> fmt.dev 5e7bdf1b9 Clean up vagrant config 570453f27 Add a vagrant config for testing gcc 4.4 ef6282fc4 Fix gcc 4.4 build e3e470bb6 Remove deprecated format_decimal 67179dbc2 Remove deprecated format_decimal a5ffa735d Fix gcc 4.4 build 5ee080463 Experiment with scan API 25b72fc4c Move <format> to tests not to confuse users d179ec5f8 Simplify Grisu 2a9e8b52d Fix advance_to() and begin() using iterator (#1159) 2c77562b1 Fix ambiguous formatter specialization in fmt/ranges.h (#1123) 98b377529 Add support for exotic string_view iterators (#1156) b488df6cf Fix Grisu3 stopping conditions f4dfd6e30 Suppress all clang-target-msvc test warning in CMake and other misc fixes (#1151) a6e8ed15c Disable UDL templates on GCC 9 by default (#1148) de5da5091 Fix formatting of extreme durations (#1154) ea2976e6d Move internal::uintptr_t test to format-impl-test (#1152) 77d6036cd Fix unexpected trailing decimal point (#1153) ccc318e80 Update README.rst 4c8efd694 Update README.rst d22d11b5f Update README.rst e9bab6d02 Improve handling of large durations f52c09f92 Fix format_to_n docs in 5.1.0 118d8bccc Fix compilation error under MSVC 19.21 (#1140) 6828d549e Add FMT_ENABLE_IF_T 3fd134be0 Move test_count_digits to format-impl-test and disable gtest warnings (#1147) ca7c1f89d Fixed a compile error under MSVC. ca978b3d2 Fix handling of nan durations c1d430e61 Improve handling of negative durations 38a85502e Use the same rep type for seconds to prevent overflow 241414028 Eliminate shadowed variable warnings from gcc-7.2 29c10fbf6 Fix DLL visibility of explicit instantiation "declaration" of internal::basic_data<void> in header format.h and the explicit instantiation "definition" in format.cc (#1134) 4a4d72f91 Fix handling of invalid string in chrono formatting b3cc9c056 Merge remote-tracking branch 'upstream/master' into invalidcolons 2e3352fd0 provoke assertion 4c721e3a2 Fix chrono formatting with invalid argument id (#1132) 8d8ea21c6 Partially implement Grisu3 40a797564 Remove trailing zeros cb46397df Fix typo 134904c88 Re-enabled constexpr tests bd516e342 Convert negative precision to zero in printf (#1127) 5efb24dd2 Add specialization test 946498cfb Fix handling of zero precision 6b2086391 fmt::ptr: support unique_ptr and shared_ptr. d306585a3 Don't inject internal names into std (#1120) 544b92793 Don't detect C compiler dc94010fa Remove char_traits (#1117) 397e8dd9d clang-format 2b415b7af Restructure printf_arg_formatter to make it customizable 5d755d0a4 Fix handling of volatile char (#1115) bade46aae Optimize grisu using uint128_t 41fbaeb3b Add <format> test 8bc0adb9b Get rid of obsolete cmake stuff 1763d0e7a Add MongoDB to "Projects using this library" f569c1ba2 doc fix: time -> chrono ccd70f59e Workaround bogus unreachable warnings in MSVC a4969ebe0 Link to fmt(...) docs from index a6ad29aa3 Update <format> 52eb3fe27 Update <format> 09e2ac5e4 Update <format> df4ea0c76 Update <format> 718f60acc Fix shadowing warning (#1105) and clang format aeb5ad3ce Enable [[noreturn]] some. 280839548 basic_buffer -> buffer 6e37c2003 Use compile features cxx_auto_type cxx_variadic_templates instead of cxx_std_11 3de3d76a3 Add compile features for cmake 3.8+ 07d5a86a7 Fix warnings ab1474ef6 Workaround segfault in doxygen and apply clang-format 918ab77ba Try fixing doc build 735b1fadc Only update key on Linux 3c531b735 Fix expired key error when installing with apt f10a7e2e4 Test exotic pointer formatting bd8177177 Add support for platforms without uintptr_t 0302927f5 Optimize pointer formatting bb6842ba3 Simplify to_string b23c8633f Detect presence of uintptr_t b588d7f35 Fix a couple of deprecated things. 018d8b57f Remove broken snprintf 1987db663 clang-format a6d1ad741 handle fwrite results #1098 e979c782d Extend basic_writer with write() method for doubles with optional format_specs argument 9e1531c1e install pkg-config file into libdir 0a66e4cbb Update README.rst 91acfe685 Fix UBSAN warning b7e6bf967 clang format da0ea4161 Make compile-time checks work with fallback formatter (#1088) 7ad3015f5 Added missing typename to FMT_STRING. (#1089) f0b572da0 Update wording test 6d416cf67 Forward declare is_string<FILE*> specializations b742f622a Create PR template to help agree to the licensing terms (#1083) 02a6f16b5 Update LICENSE.rst 294fd7df9 Remove isinf workaround 17c6900f8 Update docs 0faa968cc Make floating-point formatting locale-independent bc784d362 Remove isnan workaround 53379dfd0 Don't set CMAKE_BUILD_TYPE if fmt is a subproject (#1081) 76d326a2a Enable grisu for general format c21c6b8c4 Move enable_if to template params ec645ca26 Update readme and doxygen config ae0b0dab9 Remove obsolete TODO 5466a5b41 Document inherited members 9b392a683 Update readme 0fa65cf32 Add example fdd0149e7 Update readme e19a95b27 Update readme ef3927497 Update readme a7f68dcc0 Update readme 1428b3429 Update readme 97619e27a More fixed precision tests dd6cc0e6a Merge time-test into chrono-test a939c7595 Merge fmt/time.h into fmt/chrono.h 17e4b5392 Make chrono formatting work without exceptions (#1062) a82b3680d More tests and fixed precision fixes 287342dab cmake: default FMT_PKGCONFIG_DIR to a relative path e28429ee7 Prevent overflow with zero precision 49bbf3c87 Simplify shadowing warning fix 5e5506f83 Update readme e06523361 Visual Studio 2017: warning C4456: declaration of 'result' hides previous local declaration ebec00138 Reword licensing part 8daa3c683 Clarify contribution licensing 0d418a8d5 Update and rename CONTRIBUTING.rst to CONTRIBUTING.md 4c66dad8c Refactor digit generation b1f7cca89 stop -> handler and swap args f90d33ca1 error_ulp -> error 78c755dc4 Update readme f23017015 Update readme ced8aa8c9 Update readme 77d54251f Update benchmark results 1632f72cb Test get_round_direction 8129b9bc4 Test that the library can be compiled witout locales 835087dd0 Add color-test 4523053e6 Fix typos. 8407f4cb2 Round close to zero fixed precision 49d244c06 Don't emit more than precision digits (#1072) 3466d9c84 Don't override fixed formatting depending on exponent (#1072) 93d22dec3 Implement rounding up and clean up FP formatting d560ddac2 Temporarily disable Grisu for fixed formatting 2d981bb13 Add documentation for '%' format type. (#1071) d8434baa0 Declare the size of RESET_COLOR. 8f7780a4f Correct comment 2e526a664 Fix handling of output iterator in ranges 79b79f329 Add support for '%' type to output floating point values as a percentage. 287eaab3b Increment output iterator in basic_writer::write for character types (#1056) a97757736 Use grisu for fixed precision 327d4b6e9 Fixes for some pedantic warnings (#1054) 8af651be3 Implement fixed precision 187bd1b8b Clarify lifetimes of named_arg parameters (#1051) 4e5694fd0 Update arg signature 82c24edcf Workaround a bogus Qt Creator warning ed138d794 test: assert-test: fix typo in else-branch of EXPECT_DEBUG_DEATH_IF_SUPPORTED 0476a51cb Add Sublime syntax (#1037) c5aad69f2 Restore deprecated begin() 5b0006476 Make stopping condition configurable in grisu a44238f2e Improve grisu 83808076e Minor cleanup 9660ea1bf Simplify format string checks 4a9d67636 Gradle 4.10 >> Gradle 5.2 f041f128f Minor cleanup e4572e5de Update std implementation 442fa1bd4 Decouple format and parse contexts 744e66bb0 Deprecate format_context::parse_context() d231d68a8 Fix handling of custom context 01f34d0b0 Fix library deprecation warning 9a0a24f90 Test is_streamable with overloaded comma operators 430e6ac9b Protect against overloaded comma operators in decltype 467520e7a Remove unused macro 5a314a528 Eliminate extra copy on floating-point formatting 9989e7f4e Update benchmark results 31510cb43 Fix warnings from Visual Studio 61c9b563c Replace 'std::result_of' by 'std::invoke_result' where possible (#1025) 864b9a220 Correct the comment 153833683 Remove unnecessary checks 22de5a755 Fix warnings from Travis 355eb6d29 Enable grisu for shortest roundtrip (default) formatting b8d34e0db Fix rounding e61cac687 Minor grisu improvements 7fbbfed8c Fix warnings caused by usage of deprecated functionality c3268f4e5 Remove use cases of deprecated functionality 34951f199 Replace comments regarding deprecation with attributes 3f52336e6 Simplify formatter selection 7ca8fc3b1 Fix a bogus warning 5289dd600 Test formatting of special numbers bf6529f2c Update docs 070061224 Implement 'chrono' formatting specifiers '%Q' and '%q' 06c005b7b Clarify that compile-time checks don't support named arguments 4f6fda558 Add a grisu test stub 9f70b034e Implement precision for floating-point durations. 7cdb1e5e4 Workaround broken is_default_constructible in MSVC 5f1ceebc7 Make formatter<T> override ostream<< for templates (#952) 1b11b000c Update readme 83f052930 Add code from p0645 fdd8e333c Fix compilation with locales disabled (#1011) dad1eec84 Workaround unimplemented T... on gcc 4.6.x (#1008) b0cde860a Implement 'snprintf(OutputIt it, size_t n, const S &format, const Args & ... args)' (#917) e05dfb088 Fix compile errors due to name-hiding and an unused function argument. dde095fab Revert "Reorder defines" 4a059914a Reorder defines 58b6f8db4 Format the code using clang-format 9a777b9e1 Implemented fmt::prepare() da55e96f5 Install ninja to fix android build dc8f8ce4c Fix handling of dynamic width in chrono formatter f5cc77cea Get rid of 'null_terminating_iterator' in printf.h (#980) 39623a740 Replaced usage of gtest's internal scoped_ptr with unique_ptr. ae1de3a8d Add support for using text_style in format and vformat directly (#993) * Closes #993 1b8a216dd Improve docs b3ad759a0 Bump version and correct changelog git-subtree-dir: externals/fmt git-subtree-split: 9bdd1596cef1b57b9556f8bef32dc4a32322ef3e
3648 lines
117 KiB
C++
3648 lines
117 KiB
C++
/*
|
|
Formatting library for C++
|
|
|
|
Copyright (c) 2012 - present, Victor Zverovich
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
--- Optional exception to the license ---
|
|
|
|
As an exception, if, as a result of your compiling your source code, portions
|
|
of this Software are embedded into a machine-executable object form of such
|
|
source code, you may redistribute such embedded portions in such object form
|
|
without including the above copyright and permission notices.
|
|
*/
|
|
|
|
#ifndef FMT_FORMAT_H_
|
|
#define FMT_FORMAT_H_
|
|
|
|
#include <algorithm>
|
|
#include <cerrno>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
|
|
#include "core.h"
|
|
|
|
#ifdef FMT_DEPRECATED_INCLUDE_OS
|
|
# include "os.h"
|
|
#endif
|
|
|
|
#ifdef __INTEL_COMPILER
|
|
# define FMT_ICC_VERSION __INTEL_COMPILER
|
|
#elif defined(__ICL)
|
|
# define FMT_ICC_VERSION __ICL
|
|
#else
|
|
# define FMT_ICC_VERSION 0
|
|
#endif
|
|
|
|
#ifdef __NVCC__
|
|
# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
|
|
#else
|
|
# define FMT_CUDA_VERSION 0
|
|
#endif
|
|
|
|
#ifdef __has_builtin
|
|
# define FMT_HAS_BUILTIN(x) __has_builtin(x)
|
|
#else
|
|
# define FMT_HAS_BUILTIN(x) 0
|
|
#endif
|
|
|
|
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
|
# define FMT_NOINLINE __attribute__((noinline))
|
|
#else
|
|
# define FMT_NOINLINE
|
|
#endif
|
|
|
|
#if __cplusplus == 201103L || __cplusplus == 201402L
|
|
# if defined(__clang__)
|
|
# define FMT_FALLTHROUGH [[clang::fallthrough]]
|
|
# elif FMT_GCC_VERSION >= 700 && !defined(__PGI)
|
|
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
|
|
# else
|
|
# define FMT_FALLTHROUGH
|
|
# endif
|
|
#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
|
|
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
|
# define FMT_FALLTHROUGH [[fallthrough]]
|
|
#else
|
|
# define FMT_FALLTHROUGH
|
|
#endif
|
|
|
|
#ifndef FMT_THROW
|
|
# if FMT_EXCEPTIONS
|
|
# if FMT_MSC_VER || FMT_NVCC
|
|
FMT_BEGIN_NAMESPACE
|
|
namespace internal {
|
|
template <typename Exception> inline void do_throw(const Exception& x) {
|
|
// Silence unreachable code warnings in MSVC and NVCC because these
|
|
// are nearly impossible to fix in a generic code.
|
|
volatile bool b = true;
|
|
if (b) throw x;
|
|
}
|
|
} // namespace internal
|
|
FMT_END_NAMESPACE
|
|
# define FMT_THROW(x) internal::do_throw(x)
|
|
# else
|
|
# define FMT_THROW(x) throw x
|
|
# endif
|
|
# else
|
|
# define FMT_THROW(x) \
|
|
do { \
|
|
static_cast<void>(sizeof(x)); \
|
|
FMT_ASSERT(false, ""); \
|
|
} while (false)
|
|
# endif
|
|
#endif
|
|
|
|
#if FMT_EXCEPTIONS
|
|
# define FMT_TRY try
|
|
# define FMT_CATCH(x) catch (x)
|
|
#else
|
|
# define FMT_TRY if (true)
|
|
# define FMT_CATCH(x) if (false)
|
|
#endif
|
|
|
|
#ifndef FMT_USE_USER_DEFINED_LITERALS
|
|
// For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs.
|
|
# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
|
|
FMT_MSC_VER >= 1900) && \
|
|
(!(FMT_ICC_VERSION || FMT_CUDA_VERSION) || FMT_ICC_VERSION >= 1500 || \
|
|
FMT_CUDA_VERSION >= 700)
|
|
# define FMT_USE_USER_DEFINED_LITERALS 1
|
|
# else
|
|
# define FMT_USE_USER_DEFINED_LITERALS 0
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef FMT_USE_UDL_TEMPLATE
|
|
// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly
|
|
// support UDL templates and GCC >= 9 warns about them.
|
|
# if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \
|
|
FMT_CUDA_VERSION == 0 && \
|
|
((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 && \
|
|
__cplusplus >= 201402L) || \
|
|
FMT_CLANG_VERSION >= 304)
|
|
# define FMT_USE_UDL_TEMPLATE 1
|
|
# else
|
|
# define FMT_USE_UDL_TEMPLATE 0
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef FMT_USE_FLOAT
|
|
# define FMT_USE_FLOAT 1
|
|
#endif
|
|
|
|
#ifndef FMT_USE_DOUBLE
|
|
# define FMT_USE_DOUBLE 1
|
|
#endif
|
|
|
|
#ifndef FMT_USE_LONG_DOUBLE
|
|
# define FMT_USE_LONG_DOUBLE 1
|
|
#endif
|
|
|
|
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
|
// https://github.com/fmtlib/fmt/issues/519
|
|
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
|
|
# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
|
|
#endif
|
|
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER
|
|
# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
|
|
#endif
|
|
|
|
// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
|
|
// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
|
|
// MSVC intrinsics if the clz and clzll builtins are not available.
|
|
#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED)
|
|
# include <intrin.h> // _BitScanReverse, _BitScanReverse64
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
namespace internal {
|
|
// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
|
|
# ifndef __clang__
|
|
# pragma intrinsic(_BitScanReverse)
|
|
# endif
|
|
inline uint32_t clz(uint32_t x) {
|
|
unsigned long r = 0;
|
|
_BitScanReverse(&r, x);
|
|
|
|
FMT_ASSERT(x != 0, "");
|
|
// Static analysis complains about using uninitialized data
|
|
// "r", but the only way that can happen is if "x" is 0,
|
|
// which the callers guarantee to not happen.
|
|
# pragma warning(suppress : 6102)
|
|
return 31 - r;
|
|
}
|
|
# define FMT_BUILTIN_CLZ(n) internal::clz(n)
|
|
|
|
# if defined(_WIN64) && !defined(__clang__)
|
|
# pragma intrinsic(_BitScanReverse64)
|
|
# endif
|
|
|
|
inline uint32_t clzll(uint64_t x) {
|
|
unsigned long r = 0;
|
|
# ifdef _WIN64
|
|
_BitScanReverse64(&r, x);
|
|
# else
|
|
// Scan the high 32 bits.
|
|
if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 - (r + 32);
|
|
|
|
// Scan the low 32 bits.
|
|
_BitScanReverse(&r, static_cast<uint32_t>(x));
|
|
# endif
|
|
|
|
FMT_ASSERT(x != 0, "");
|
|
// Static analysis complains about using uninitialized data
|
|
// "r", but the only way that can happen is if "x" is 0,
|
|
// which the callers guarantee to not happen.
|
|
# pragma warning(suppress : 6102)
|
|
return 63 - r;
|
|
}
|
|
# define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
|
|
} // namespace internal
|
|
FMT_END_NAMESPACE
|
|
#endif
|
|
|
|
// Enable the deprecated numeric alignment.
|
|
#ifndef FMT_NUMERIC_ALIGN
|
|
# 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 {
|
|
|
|
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
|
|
// undefined behavior (e.g. due to type aliasing).
|
|
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
|
|
template <typename Dest, typename Source>
|
|
inline Dest bit_cast(const Source& source) {
|
|
static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
|
|
Dest dest;
|
|
std::memcpy(&dest, &source, sizeof(dest));
|
|
return dest;
|
|
}
|
|
|
|
inline bool is_big_endian() {
|
|
const auto u = 1u;
|
|
struct bytes {
|
|
char data[sizeof(u)];
|
|
};
|
|
return bit_cast<bytes>(u).data[0] == 0;
|
|
}
|
|
|
|
// A fallback implementation of uintptr_t for systems that lack it.
|
|
struct fallback_uintptr {
|
|
unsigned char value[sizeof(void*)];
|
|
|
|
fallback_uintptr() = default;
|
|
explicit fallback_uintptr(const void* p) {
|
|
*this = bit_cast<fallback_uintptr>(p);
|
|
if (is_big_endian()) {
|
|
for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
|
|
std::swap(value[i], value[j]);
|
|
}
|
|
}
|
|
};
|
|
#ifdef UINTPTR_MAX
|
|
using uintptr_t = ::uintptr_t;
|
|
inline uintptr_t to_uintptr(const void* p) { return bit_cast<uintptr_t>(p); }
|
|
#else
|
|
using uintptr_t = fallback_uintptr;
|
|
inline fallback_uintptr to_uintptr(const void* p) {
|
|
return fallback_uintptr(p);
|
|
}
|
|
#endif
|
|
|
|
// Returns the largest possible value for type T. Same as
|
|
// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
|
|
template <typename T> constexpr T max_value() {
|
|
return (std::numeric_limits<T>::max)();
|
|
}
|
|
template <typename T> constexpr int num_bits() {
|
|
return std::numeric_limits<T>::digits;
|
|
}
|
|
template <> constexpr int num_bits<fallback_uintptr>() {
|
|
return static_cast<int>(sizeof(void*) *
|
|
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 };
|
|
};
|
|
|
|
// A workaround for std::string not having mutable data() until C++17.
|
|
template <typename Char> inline Char* get_data(std::basic_string<Char>& s) {
|
|
return &s[0];
|
|
}
|
|
template <typename Container>
|
|
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(
|
|
std::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);
|
|
}
|
|
|
|
template <typename Iterator>
|
|
inline Iterator& reserve(Iterator& it, std::size_t) {
|
|
return it;
|
|
}
|
|
|
|
// An output iterator that counts the number of objects written to it and
|
|
// discards them.
|
|
class counting_iterator {
|
|
private:
|
|
std::size_t count_;
|
|
|
|
public:
|
|
using iterator_category = std::output_iterator_tag;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = void;
|
|
using reference = void;
|
|
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
|
|
|
|
struct value_type {
|
|
template <typename T> void operator=(const T&) {}
|
|
};
|
|
|
|
counting_iterator() : count_(0) {}
|
|
|
|
std::size_t count() const { return count_; }
|
|
|
|
counting_iterator& operator++() {
|
|
++count_;
|
|
return *this;
|
|
}
|
|
|
|
counting_iterator operator++(int) {
|
|
auto it = *this;
|
|
++*this;
|
|
return it;
|
|
}
|
|
|
|
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 {
|
|
private:
|
|
OutputIt it_;
|
|
|
|
public:
|
|
using value_type = T;
|
|
using iterator = OutputIt;
|
|
struct sentinel {};
|
|
|
|
explicit output_range(OutputIt it) : it_(it) {}
|
|
OutputIt begin() const { return it_; }
|
|
sentinel end() const { return {}; } // Sentinel is not used yet.
|
|
};
|
|
|
|
template <typename Char>
|
|
inline size_t count_code_points(basic_string_view<Char> s) {
|
|
return s.size();
|
|
}
|
|
|
|
// Counts the number of code points in a UTF-8 string.
|
|
inline size_t count_code_points(basic_string_view<char> s) {
|
|
const char* 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;
|
|
}
|
|
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();
|
|
}
|
|
|
|
inline char8_type to_char8_t(char c) { return static_cast<char8_type>(c); }
|
|
|
|
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)>
|
|
OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
|
|
return std::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) {
|
|
return std::transform(begin, end, it, to_char8_t);
|
|
}
|
|
|
|
#ifndef FMT_USE_GRISU
|
|
# define FMT_USE_GRISU 1
|
|
#endif
|
|
|
|
template <typename T> constexpr bool use_grisu() {
|
|
return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559 &&
|
|
sizeof(T) <= sizeof(double);
|
|
}
|
|
|
|
template <typename T>
|
|
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_);
|
|
size_ = new_size;
|
|
}
|
|
} // namespace internal
|
|
|
|
// A range with an iterator appending to a buffer.
|
|
template <typename T>
|
|
class buffer_range : public internal::output_range<
|
|
std::back_insert_iterator<internal::buffer<T>>, T> {
|
|
public:
|
|
using iterator = std::back_insert_iterator<internal::buffer<T>>;
|
|
using internal::output_range<iterator, T>::output_range;
|
|
buffer_range(internal::buffer<T>& buf)
|
|
: internal::output_range<iterator, T>(std::back_inserter(buf)) {}
|
|
};
|
|
|
|
class FMT_DEPRECATED u8string_view
|
|
: public basic_string_view<internal::char8_type> {
|
|
public:
|
|
u8string_view(const char* s)
|
|
: basic_string_view<internal::char8_type>(
|
|
reinterpret_cast<const internal::char8_type*>(s)) {}
|
|
u8string_view(const char* s, size_t count) FMT_NOEXCEPT
|
|
: basic_string_view<internal::char8_type>(
|
|
reinterpret_cast<const internal::char8_type*>(s), count) {}
|
|
};
|
|
|
|
#if FMT_USE_USER_DEFINED_LITERALS
|
|
inline namespace literals {
|
|
FMT_DEPRECATED inline basic_string_view<internal::char8_type> operator"" _u(
|
|
const char* s, std::size_t n) {
|
|
return {reinterpret_cast<const internal::char8_type*>(s), n};
|
|
}
|
|
} // namespace literals
|
|
#endif
|
|
|
|
// The number of characters to store in the basic_memory_buffer object itself
|
|
// to avoid dynamic memory allocation.
|
|
enum { inline_buffer_size = 500 };
|
|
|
|
/**
|
|
\rst
|
|
A dynamically growing memory buffer for trivially copyable/constructible types
|
|
with the first ``SIZE`` elements stored in the object itself.
|
|
|
|
You can use one of the following type aliases for common character types:
|
|
|
|
+----------------+------------------------------+
|
|
| Type | Definition |
|
|
+================+==============================+
|
|
| memory_buffer | basic_memory_buffer<char> |
|
|
+----------------+------------------------------+
|
|
| wmemory_buffer | basic_memory_buffer<wchar_t> |
|
|
+----------------+------------------------------+
|
|
|
|
**Example**::
|
|
|
|
fmt::memory_buffer out;
|
|
format_to(out, "The answer is {}.", 42);
|
|
|
|
This will append the following output to the ``out`` object:
|
|
|
|
.. code-block:: none
|
|
|
|
The answer is 42.
|
|
|
|
The output can be converted to an ``std::string`` with ``to_string(out)``.
|
|
\endrst
|
|
*/
|
|
template <typename T, std::size_t SIZE = inline_buffer_size,
|
|
typename Allocator = std::allocator<T>>
|
|
class basic_memory_buffer : private Allocator, public internal::buffer<T> {
|
|
private:
|
|
T store_[SIZE];
|
|
|
|
// Deallocate memory allocated by the buffer.
|
|
void deallocate() {
|
|
T* data = this->data();
|
|
if (data != store_) Allocator::deallocate(data, this->capacity());
|
|
}
|
|
|
|
protected:
|
|
void grow(std::size_t size) FMT_OVERRIDE;
|
|
|
|
public:
|
|
using value_type = T;
|
|
using const_reference = const T&;
|
|
|
|
explicit basic_memory_buffer(const Allocator& alloc = Allocator())
|
|
: Allocator(alloc) {
|
|
this->set(store_, SIZE);
|
|
}
|
|
~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
|
|
|
|
private:
|
|
// Move data from other to this buffer.
|
|
void move(basic_memory_buffer& other) {
|
|
Allocator &this_alloc = *this, &other_alloc = other;
|
|
this_alloc = std::move(other_alloc);
|
|
T* data = other.data();
|
|
std::size_t size = other.size(), capacity = other.capacity();
|
|
if (data == other.store_) {
|
|
this->set(store_, capacity);
|
|
std::uninitialized_copy(other.store_, other.store_ + size,
|
|
internal::make_checked(store_, capacity));
|
|
} else {
|
|
this->set(data, capacity);
|
|
// Set pointer to the inline array so that delete is not called
|
|
// when deallocating.
|
|
other.set(other.store_, 0);
|
|
}
|
|
this->resize(size);
|
|
}
|
|
|
|
public:
|
|
/**
|
|
\rst
|
|
Constructs a :class:`fmt::basic_memory_buffer` object moving the content
|
|
of the other object to it.
|
|
\endrst
|
|
*/
|
|
basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); }
|
|
|
|
/**
|
|
\rst
|
|
Moves the content of the other ``basic_memory_buffer`` object to this one.
|
|
\endrst
|
|
*/
|
|
basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
|
|
FMT_ASSERT(this != &other, "");
|
|
deallocate();
|
|
move(other);
|
|
return *this;
|
|
}
|
|
|
|
// Returns a copy of the allocator associated with this buffer.
|
|
Allocator get_allocator() const { return *this; }
|
|
};
|
|
|
|
template <typename T, std::size_t SIZE, typename Allocator>
|
|
void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
|
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much");
|
|
#endif
|
|
std::size_t old_capacity = this->capacity();
|
|
std::size_t new_capacity = old_capacity + old_capacity / 2;
|
|
if (size > new_capacity) new_capacity = size;
|
|
T* old_data = this->data();
|
|
T* new_data = std::allocator_traits<Allocator>::allocate(*this, new_capacity);
|
|
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
|
std::uninitialized_copy(old_data, old_data + this->size(),
|
|
internal::make_checked(new_data, new_capacity));
|
|
this->set(new_data, new_capacity);
|
|
// deallocate must not throw according to the standard, but even if it does,
|
|
// the buffer already uses the new storage and will deallocate it in
|
|
// destructor.
|
|
if (old_data != store_) Allocator::deallocate(old_data, old_capacity);
|
|
}
|
|
|
|
using memory_buffer = basic_memory_buffer<char>;
|
|
using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
|
|
|
/** A formatting error such as invalid format string. */
|
|
FMT_CLASS_API
|
|
class FMT_API format_error : public std::runtime_error {
|
|
public:
|
|
explicit format_error(const char* message) : std::runtime_error(message) {}
|
|
explicit format_error(const std::string& message)
|
|
: std::runtime_error(message) {}
|
|
format_error(const format_error&) = default;
|
|
format_error& operator=(const format_error&) = default;
|
|
format_error(format_error&&) = default;
|
|
format_error& operator=(format_error&&) = default;
|
|
~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
|
|
};
|
|
|
|
namespace internal {
|
|
|
|
// Returns true if value is negative, false otherwise.
|
|
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
|
|
template <typename T, FMT_ENABLE_IF(std::numeric_limits<T>::is_signed)>
|
|
FMT_CONSTEXPR bool is_negative(T value) {
|
|
return value < 0;
|
|
}
|
|
template <typename T, FMT_ENABLE_IF(!std::numeric_limits<T>::is_signed)>
|
|
FMT_CONSTEXPR bool is_negative(T) {
|
|
return false;
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
|
FMT_CONSTEXPR bool is_supported_floating_point(T) {
|
|
return (std::is_same<T, float>::value && FMT_USE_FLOAT) ||
|
|
(std::is_same<T, double>::value && FMT_USE_DOUBLE) ||
|
|
(std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
|
|
}
|
|
|
|
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
|
|
// represent all values of T.
|
|
template <typename T>
|
|
using uint32_or_64_or_128_t = conditional_t<
|
|
std::numeric_limits<T>::digits <= 32, uint32_t,
|
|
conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
|
|
|
|
// Static data is placed in this class template for the header-only config.
|
|
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
|
|
static const uint64_t powers_of_10_64[];
|
|
static const uint32_t zero_or_powers_of_10_32[];
|
|
static const uint64_t zero_or_powers_of_10_64[];
|
|
static const uint64_t pow10_significands[];
|
|
static const int16_t pow10_exponents[];
|
|
static const char digits[];
|
|
static const char hex_digits[];
|
|
static const char foreground_color[];
|
|
static const char background_color[];
|
|
static const char reset_color[5];
|
|
static const wchar_t wreset_color[5];
|
|
static const char signs[];
|
|
};
|
|
|
|
FMT_EXTERN template struct basic_data<void>;
|
|
|
|
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
|
|
struct data : basic_data<> {};
|
|
|
|
#ifdef FMT_BUILTIN_CLZLL
|
|
// Returns the number of decimal digits in n. Leading zeros are not counted
|
|
// except for n == 0 in which case count_digits returns 1.
|
|
inline int count_digits(uint64_t n) {
|
|
// Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
|
|
// and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
|
|
int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12;
|
|
return t - (n < data::zero_or_powers_of_10_64[t]) + 1;
|
|
}
|
|
#else
|
|
// Fallback version of count_digits used when __builtin_clz is not available.
|
|
inline int count_digits(uint64_t n) {
|
|
int count = 1;
|
|
for (;;) {
|
|
// Integer division is slow so do it for a group of four digits instead
|
|
// of for every digit. The idea comes from the talk by Alexandrescu
|
|
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
|
if (n < 10) return count;
|
|
if (n < 100) return count + 1;
|
|
if (n < 1000) return count + 2;
|
|
if (n < 10000) return count + 3;
|
|
n /= 10000u;
|
|
count += 4;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if FMT_USE_INT128
|
|
inline int count_digits(uint128_t n) {
|
|
int count = 1;
|
|
for (;;) {
|
|
// Integer division is slow so do it for a group of four digits instead
|
|
// of for every digit. The idea comes from the talk by Alexandrescu
|
|
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
|
if (n < 10) return count;
|
|
if (n < 100) return count + 1;
|
|
if (n < 1000) return count + 2;
|
|
if (n < 10000) return count + 3;
|
|
n /= 10000U;
|
|
count += 4;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Counts the number of digits in n. BITS = log2(radix).
|
|
template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
|
|
int num_digits = 0;
|
|
do {
|
|
++num_digits;
|
|
} while ((n >>= BITS) != 0);
|
|
return num_digits;
|
|
}
|
|
|
|
template <> int count_digits<4>(internal::fallback_uintptr n);
|
|
|
|
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
|
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
|
|
#else
|
|
# define FMT_ALWAYS_INLINE
|
|
#endif
|
|
|
|
#ifdef FMT_BUILTIN_CLZ
|
|
// Optional version of count_digits for better performance on 32-bit platforms.
|
|
inline int count_digits(uint32_t n) {
|
|
int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12;
|
|
return t - (n < data::zero_or_powers_of_10_32[t]) + 1;
|
|
}
|
|
#endif
|
|
|
|
template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
|
|
template <typename Char> inline std::string grouping(locale_ref loc) {
|
|
return grouping_impl<char>(loc);
|
|
}
|
|
template <> inline std::string grouping<wchar_t>(locale_ref loc) {
|
|
return grouping_impl<wchar_t>(loc);
|
|
}
|
|
|
|
template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
|
|
template <typename Char> inline Char thousands_sep(locale_ref loc) {
|
|
return Char(thousands_sep_impl<char>(loc));
|
|
}
|
|
template <> inline wchar_t thousands_sep(locale_ref loc) {
|
|
return thousands_sep_impl<wchar_t>(loc);
|
|
}
|
|
|
|
template <typename Char> FMT_API Char decimal_point_impl(locale_ref loc);
|
|
template <typename Char> inline Char decimal_point(locale_ref loc) {
|
|
return Char(decimal_point_impl<char>(loc));
|
|
}
|
|
template <> inline wchar_t decimal_point(locale_ref loc) {
|
|
return decimal_point_impl<wchar_t>(loc);
|
|
}
|
|
|
|
// Formats a decimal unsigned integer value writing into buffer.
|
|
// add_thousands_sep is called after writing each char to add a thousands
|
|
// separator if necessary.
|
|
template <typename UInt, typename Char, typename F>
|
|
inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
|
|
F add_thousands_sep) {
|
|
FMT_ASSERT(num_digits >= 0, "invalid digit count");
|
|
buffer += num_digits;
|
|
Char* end = buffer;
|
|
while (value >= 100) {
|
|
// Integer division is slow so do it for a group of two digits instead
|
|
// of for every digit. The idea comes from the talk by Alexandrescu
|
|
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
|
auto index = static_cast<unsigned>((value % 100) * 2);
|
|
value /= 100;
|
|
*--buffer = static_cast<Char>(data::digits[index + 1]);
|
|
add_thousands_sep(buffer);
|
|
*--buffer = static_cast<Char>(data::digits[index]);
|
|
add_thousands_sep(buffer);
|
|
}
|
|
if (value < 10) {
|
|
*--buffer = static_cast<Char>('0' + value);
|
|
return end;
|
|
}
|
|
auto index = static_cast<unsigned>(value * 2);
|
|
*--buffer = static_cast<Char>(data::digits[index + 1]);
|
|
add_thousands_sep(buffer);
|
|
*--buffer = static_cast<Char>(data::digits[index]);
|
|
return end;
|
|
}
|
|
|
|
template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
|
|
return std::numeric_limits<Int>::digits10;
|
|
}
|
|
template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
|
|
template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
|
|
|
|
template <typename Char, typename UInt, typename Iterator, typename F>
|
|
inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
|
|
F add_thousands_sep) {
|
|
FMT_ASSERT(num_digits >= 0, "invalid digit count");
|
|
// Buffer should be large enough to hold all digits (<= digits10 + 1).
|
|
enum { max_size = digits10<UInt>() + 1 };
|
|
Char buffer[2 * max_size];
|
|
auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
|
|
return internal::copy_str<Char>(buffer, end, out);
|
|
}
|
|
|
|
template <typename Char, typename It, typename UInt>
|
|
inline It format_decimal(It out, UInt value, int num_digits) {
|
|
return format_decimal<Char>(out, value, num_digits, [](Char*) {});
|
|
}
|
|
|
|
template <unsigned BASE_BITS, typename Char, typename UInt>
|
|
inline Char* format_uint(Char* buffer, UInt value, int num_digits,
|
|
bool upper = false) {
|
|
buffer += num_digits;
|
|
Char* end = buffer;
|
|
do {
|
|
const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits;
|
|
unsigned digit = (value & ((1 << BASE_BITS) - 1));
|
|
*--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
|
|
: digits[digit]);
|
|
} while ((value >>= BASE_BITS) != 0);
|
|
return end;
|
|
}
|
|
|
|
template <unsigned BASE_BITS, typename Char>
|
|
Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
|
|
bool = false) {
|
|
auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
|
|
int start = (num_digits + char_digits - 1) / char_digits - 1;
|
|
if (int start_digits = num_digits % char_digits) {
|
|
unsigned value = n.value[start--];
|
|
buffer = format_uint<BASE_BITS>(buffer, value, start_digits);
|
|
}
|
|
for (; start >= 0; --start) {
|
|
unsigned value = n.value[start];
|
|
buffer += char_digits;
|
|
auto p = buffer;
|
|
for (int i = 0; i < char_digits; ++i) {
|
|
unsigned digit = (value & ((1 << BASE_BITS) - 1));
|
|
*--p = static_cast<Char>(data::hex_digits[digit]);
|
|
value >>= BASE_BITS;
|
|
}
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
|
|
inline It format_uint(It out, UInt value, int num_digits, bool upper = false) {
|
|
// Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
|
|
char buffer[num_bits<UInt>() / BASE_BITS + 1];
|
|
format_uint<BASE_BITS>(buffer, value, num_digits, upper);
|
|
return internal::copy_str<Char>(buffer, buffer + num_digits, out);
|
|
}
|
|
|
|
// A converter from UTF-8 to UTF-16.
|
|
class utf8_to_utf16 {
|
|
private:
|
|
wmemory_buffer buffer_;
|
|
|
|
public:
|
|
FMT_API explicit utf8_to_utf16(string_view s);
|
|
operator wstring_view() const { return {&buffer_[0], size()}; }
|
|
size_t size() const { return buffer_.size() - 1; }
|
|
const wchar_t* c_str() const { return &buffer_[0]; }
|
|
std::wstring str() const { return {&buffer_[0], size()}; }
|
|
};
|
|
|
|
template <typename T = void> struct null {};
|
|
|
|
// Workaround an array initialization issue in gcc 4.8.
|
|
template <typename Char> struct fill_t {
|
|
private:
|
|
enum { max_size = 4 };
|
|
Char data_[max_size];
|
|
unsigned char size_;
|
|
|
|
public:
|
|
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
|
|
auto size = s.size();
|
|
if (size > max_size) {
|
|
FMT_THROW(format_error("invalid fill"));
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < size; ++i) data_[i] = s[i];
|
|
size_ = static_cast<unsigned char>(size);
|
|
}
|
|
|
|
size_t size() const { return size_; }
|
|
const Char* data() const { return data_; }
|
|
|
|
FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; }
|
|
FMT_CONSTEXPR const Char& operator[](size_t index) const {
|
|
return data_[index];
|
|
}
|
|
|
|
static FMT_CONSTEXPR fill_t<Char> make() {
|
|
auto fill = fill_t<Char>();
|
|
fill[0] = Char(' ');
|
|
fill.size_ = 1;
|
|
return fill;
|
|
}
|
|
};
|
|
} // namespace internal
|
|
|
|
// We cannot use enum classes as bit fields because of a gcc bug
|
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
|
|
namespace align {
|
|
enum type { none, left, right, center, numeric };
|
|
}
|
|
using align_t = align::type;
|
|
|
|
namespace sign {
|
|
enum type { none, minus, plus, space };
|
|
}
|
|
using sign_t = sign::type;
|
|
|
|
// Format specifiers for built-in and string types.
|
|
template <typename Char> struct basic_format_specs {
|
|
int width;
|
|
int precision;
|
|
char type;
|
|
align_t align : 4;
|
|
sign_t sign : 3;
|
|
bool alt : 1; // Alternate form ('#').
|
|
internal::fill_t<Char> fill;
|
|
|
|
constexpr basic_format_specs()
|
|
: width(0),
|
|
precision(-1),
|
|
type(0),
|
|
align(align::none),
|
|
sign(sign::none),
|
|
alt(false),
|
|
fill(internal::fill_t<Char>::make()) {}
|
|
};
|
|
|
|
using format_specs = basic_format_specs<char>;
|
|
|
|
namespace internal {
|
|
|
|
// A floating-point presentation format.
|
|
enum class float_format : unsigned char {
|
|
general, // General: exponent notation or fixed point based on magnitude.
|
|
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
|
|
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
|
|
hex
|
|
};
|
|
|
|
struct float_specs {
|
|
int precision;
|
|
float_format format : 8;
|
|
sign_t sign : 8;
|
|
bool upper : 1;
|
|
bool locale : 1;
|
|
bool percent : 1;
|
|
bool binary32 : 1;
|
|
bool use_grisu : 1;
|
|
bool showpoint : 1;
|
|
};
|
|
|
|
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
|
template <typename Char, typename It> It write_exponent(int exp, It it) {
|
|
FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
|
|
if (exp < 0) {
|
|
*it++ = static_cast<Char>('-');
|
|
exp = -exp;
|
|
} else {
|
|
*it++ = static_cast<Char>('+');
|
|
}
|
|
if (exp >= 100) {
|
|
const char* top = data::digits + (exp / 100) * 2;
|
|
if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
|
|
*it++ = static_cast<Char>(top[1]);
|
|
exp %= 100;
|
|
}
|
|
const char* d = data::digits + exp * 2;
|
|
*it++ = static_cast<Char>(d[0]);
|
|
*it++ = static_cast<Char>(d[1]);
|
|
return it;
|
|
}
|
|
|
|
template <typename Char> class float_writer {
|
|
private:
|
|
// The number is given as v = digits_ * pow(10, exp_).
|
|
const char* digits_;
|
|
int num_digits_;
|
|
int exp_;
|
|
size_t size_;
|
|
float_specs specs_;
|
|
Char decimal_point_;
|
|
|
|
template <typename It> It prettify(It it) const {
|
|
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
|
int full_exp = num_digits_ + exp_;
|
|
if (specs_.format == float_format::exp) {
|
|
// Insert a decimal point after the first digit and add an exponent.
|
|
*it++ = static_cast<Char>(*digits_);
|
|
int num_zeros = specs_.precision - num_digits_;
|
|
if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_;
|
|
it = copy_str<Char>(digits_ + 1, digits_ + num_digits_, it);
|
|
if (num_zeros > 0 && specs_.showpoint)
|
|
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
|
|
*it++ = static_cast<Char>(specs_.upper ? 'E' : 'e');
|
|
return write_exponent<Char>(full_exp - 1, it);
|
|
}
|
|
if (num_digits_ <= full_exp) {
|
|
// 1234e7 -> 12340000000[.0+]
|
|
it = copy_str<Char>(digits_, digits_ + num_digits_, it);
|
|
it = std::fill_n(it, full_exp - num_digits_, static_cast<Char>('0'));
|
|
if (specs_.showpoint || specs_.precision < 0) {
|
|
*it++ = decimal_point_;
|
|
int num_zeros = specs_.precision - full_exp;
|
|
if (num_zeros <= 0) {
|
|
if (specs_.format != float_format::fixed)
|
|
*it++ = static_cast<Char>('0');
|
|
return it;
|
|
}
|
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
if (num_zeros > 1000)
|
|
throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
|
|
#endif
|
|
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
|
|
}
|
|
} else if (full_exp > 0) {
|
|
// 1234e-2 -> 12.34[0+]
|
|
it = copy_str<Char>(digits_, digits_ + full_exp, it);
|
|
if (!specs_.showpoint) {
|
|
// Remove trailing zeros.
|
|
int num_digits = num_digits_;
|
|
while (num_digits > full_exp && digits_[num_digits - 1] == '0')
|
|
--num_digits;
|
|
if (num_digits != full_exp) *it++ = decimal_point_;
|
|
return copy_str<Char>(digits_ + full_exp, digits_ + num_digits, it);
|
|
}
|
|
*it++ = decimal_point_;
|
|
it = copy_str<Char>(digits_ + full_exp, digits_ + num_digits_, it);
|
|
if (specs_.precision > num_digits_) {
|
|
// Add trailing zeros.
|
|
int num_zeros = specs_.precision - num_digits_;
|
|
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
|
|
}
|
|
} else {
|
|
// 1234e-6 -> 0.001234
|
|
*it++ = static_cast<Char>('0');
|
|
int num_zeros = -full_exp;
|
|
int num_digits = num_digits_;
|
|
if (num_digits == 0 && specs_.precision >= 0 &&
|
|
specs_.precision < num_zeros) {
|
|
num_zeros = specs_.precision;
|
|
}
|
|
// Remove trailing zeros.
|
|
if (!specs_.showpoint)
|
|
while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits;
|
|
if (num_zeros != 0 || num_digits != 0 || specs_.showpoint) {
|
|
*it++ = decimal_point_;
|
|
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
|
|
it = copy_str<Char>(digits_, digits_ + num_digits, it);
|
|
}
|
|
}
|
|
return it;
|
|
}
|
|
|
|
public:
|
|
float_writer(const char* digits, int num_digits, int exp, float_specs specs,
|
|
Char decimal_point)
|
|
: digits_(digits),
|
|
num_digits_(num_digits),
|
|
exp_(exp),
|
|
specs_(specs),
|
|
decimal_point_(decimal_point) {
|
|
int full_exp = num_digits + exp - 1;
|
|
int precision = specs.precision > 0 ? specs.precision : 16;
|
|
if (specs_.format == float_format::general &&
|
|
!(full_exp >= -4 && full_exp < precision)) {
|
|
specs_.format = float_format::exp;
|
|
}
|
|
size_ = prettify(counting_iterator()).count();
|
|
size_ += specs.sign ? 1 : 0;
|
|
}
|
|
|
|
size_t size() const { return size_; }
|
|
size_t width() const { return size(); }
|
|
|
|
template <typename It> void operator()(It&& it) {
|
|
if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
|
|
it = prettify(it);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
|
|
|
|
// Formats a floating-point number with snprintf.
|
|
template <typename T>
|
|
int snprintf_float(T value, int precision, float_specs specs,
|
|
buffer<char>& buf);
|
|
|
|
template <typename T> T promote_float(T value) { return value; }
|
|
inline double promote_float(float value) { return static_cast<double>(value); }
|
|
|
|
template <typename Handler>
|
|
FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
|
|
switch (spec) {
|
|
case 0:
|
|
case 'd':
|
|
handler.on_dec();
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
handler.on_hex();
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
handler.on_bin();
|
|
break;
|
|
case 'o':
|
|
handler.on_oct();
|
|
break;
|
|
case 'n':
|
|
case 'L':
|
|
handler.on_num();
|
|
break;
|
|
default:
|
|
handler.on_error();
|
|
}
|
|
}
|
|
|
|
template <typename ErrorHandler = error_handler, typename Char>
|
|
FMT_CONSTEXPR float_specs parse_float_type_spec(
|
|
const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
|
|
auto result = float_specs();
|
|
result.showpoint = specs.alt;
|
|
switch (specs.type) {
|
|
case 0:
|
|
result.format = float_format::general;
|
|
result.showpoint |= specs.precision > 0;
|
|
break;
|
|
case 'G':
|
|
result.upper = true;
|
|
FMT_FALLTHROUGH;
|
|
case 'g':
|
|
result.format = float_format::general;
|
|
break;
|
|
case 'E':
|
|
result.upper = true;
|
|
FMT_FALLTHROUGH;
|
|
case 'e':
|
|
result.format = float_format::exp;
|
|
result.showpoint |= specs.precision != 0;
|
|
break;
|
|
case 'F':
|
|
result.upper = true;
|
|
FMT_FALLTHROUGH;
|
|
case 'f':
|
|
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;
|
|
case 'a':
|
|
result.format = float_format::hex;
|
|
break;
|
|
case 'n':
|
|
result.locale = true;
|
|
break;
|
|
default:
|
|
eh.on_error("invalid type specifier");
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename Char, typename Handler>
|
|
FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>* specs,
|
|
Handler&& handler) {
|
|
if (!specs) return handler.on_char();
|
|
if (specs->type && specs->type != 'c') return handler.on_int();
|
|
if (specs->align == align::numeric || specs->sign != sign::none || specs->alt)
|
|
handler.on_error("invalid format specifier for char");
|
|
handler.on_char();
|
|
}
|
|
|
|
template <typename Char, typename Handler>
|
|
FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) {
|
|
if (spec == 0 || spec == 's')
|
|
handler.on_string();
|
|
else if (spec == 'p')
|
|
handler.on_pointer();
|
|
else
|
|
handler.on_error("invalid type specifier");
|
|
}
|
|
|
|
template <typename Char, typename ErrorHandler>
|
|
FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) {
|
|
if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
|
|
}
|
|
|
|
template <typename Char, typename ErrorHandler>
|
|
FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
|
|
if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier");
|
|
}
|
|
|
|
template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
|
|
public:
|
|
FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {}
|
|
|
|
FMT_CONSTEXPR void on_dec() {}
|
|
FMT_CONSTEXPR void on_hex() {}
|
|
FMT_CONSTEXPR void on_bin() {}
|
|
FMT_CONSTEXPR void on_oct() {}
|
|
FMT_CONSTEXPR void on_num() {}
|
|
|
|
FMT_CONSTEXPR void on_error() {
|
|
ErrorHandler::on_error("invalid type specifier");
|
|
}
|
|
};
|
|
|
|
template <typename ErrorHandler>
|
|
class char_specs_checker : public ErrorHandler {
|
|
private:
|
|
char type_;
|
|
|
|
public:
|
|
FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh)
|
|
: ErrorHandler(eh), type_(type) {}
|
|
|
|
FMT_CONSTEXPR void on_int() {
|
|
handle_int_type_spec(type_, int_type_checker<ErrorHandler>(*this));
|
|
}
|
|
FMT_CONSTEXPR void on_char() {}
|
|
};
|
|
|
|
template <typename ErrorHandler>
|
|
class cstring_type_checker : public ErrorHandler {
|
|
public:
|
|
FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh)
|
|
: ErrorHandler(eh) {}
|
|
|
|
FMT_CONSTEXPR void on_string() {}
|
|
FMT_CONSTEXPR void on_pointer() {}
|
|
};
|
|
|
|
template <typename Context>
|
|
void arg_map<Context>::init(const basic_format_args<Context>& args) {
|
|
if (map_) return;
|
|
map_ = new entry[internal::to_unsigned(args.max_size())];
|
|
if (args.is_packed()) {
|
|
for (int i = 0;; ++i) {
|
|
internal::type arg_type = args.type(i);
|
|
if (arg_type == internal::type::none_type) return;
|
|
if (arg_type == internal::type::named_arg_type)
|
|
push_back(args.values_[i]);
|
|
}
|
|
}
|
|
for (int i = 0, n = args.max_size(); i < n; ++i) {
|
|
auto type = args.args_[i].type_;
|
|
if (type == internal::type::named_arg_type) push_back(args.args_[i].value_);
|
|
}
|
|
}
|
|
|
|
template <typename Char> struct nonfinite_writer {
|
|
sign_t sign;
|
|
const char* str;
|
|
static constexpr size_t str_size = 3;
|
|
|
|
size_t size() const { return str_size + (sign ? 1 : 0); }
|
|
size_t width() const { return size(); }
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
if (sign) *it++ = static_cast<Char>(data::signs[sign]);
|
|
it = copy_str<Char>(str, str + str_size, it);
|
|
}
|
|
};
|
|
|
|
template <typename OutputIt, typename Char>
|
|
FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
|
|
auto fill_size = fill.size();
|
|
if (fill_size == 1) return std::fill_n(it, n, fill[0]);
|
|
for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it);
|
|
return it;
|
|
}
|
|
|
|
// This template provides operations for formatting and writing data into a
|
|
// character range.
|
|
template <typename Range> class basic_writer {
|
|
public:
|
|
using char_type = typename Range::value_type;
|
|
using iterator = typename Range::iterator;
|
|
using format_specs = basic_format_specs<char_type>;
|
|
|
|
private:
|
|
iterator out_; // Output iterator.
|
|
locale_ref locale_;
|
|
|
|
// Attempts to reserve space for n extra characters in the output range.
|
|
// Returns a pointer to the reserved range or a reference to out_.
|
|
auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
|
|
return internal::reserve(out_, n);
|
|
}
|
|
|
|
template <typename F> struct padded_int_writer {
|
|
size_t size_;
|
|
string_view prefix;
|
|
char_type fill;
|
|
std::size_t padding;
|
|
F f;
|
|
|
|
size_t size() const { return size_; }
|
|
size_t width() const { return size_; }
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
if (prefix.size() != 0)
|
|
it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
|
|
it = std::fill_n(it, padding, fill);
|
|
f(it);
|
|
}
|
|
};
|
|
|
|
// Writes an integer in the format
|
|
// <left-padding><prefix><numeric-padding><digits><right-padding>
|
|
// where <digits> are written by f(it).
|
|
template <typename F>
|
|
void write_int(int num_digits, string_view prefix, format_specs specs, F f) {
|
|
std::size_t size = prefix.size() + to_unsigned(num_digits);
|
|
char_type fill = specs.fill[0];
|
|
std::size_t padding = 0;
|
|
if (specs.align == align::numeric) {
|
|
auto unsiged_width = to_unsigned(specs.width);
|
|
if (unsiged_width > size) {
|
|
padding = unsiged_width - size;
|
|
size = unsiged_width;
|
|
}
|
|
} else if (specs.precision > num_digits) {
|
|
size = prefix.size() + to_unsigned(specs.precision);
|
|
padding = to_unsigned(specs.precision - num_digits);
|
|
fill = static_cast<char_type>('0');
|
|
}
|
|
if (specs.align == align::none) specs.align = align::right;
|
|
write_padded(specs, padded_int_writer<F>{size, prefix, fill, padding, f});
|
|
}
|
|
|
|
// Writes a decimal integer.
|
|
template <typename Int> void write_decimal(Int value) {
|
|
auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
|
|
bool negative = is_negative(value);
|
|
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
|
|
if (negative) abs_value = ~abs_value + 1;
|
|
int num_digits = count_digits(abs_value);
|
|
auto&& it = reserve((negative ? 1 : 0) + static_cast<size_t>(num_digits));
|
|
if (negative) *it++ = static_cast<char_type>('-');
|
|
it = format_decimal<char_type>(it, abs_value, num_digits);
|
|
}
|
|
|
|
// The handle_int_type_spec handler that writes an integer.
|
|
template <typename Int, typename Specs> struct int_writer {
|
|
using unsigned_type = uint32_or_64_or_128_t<Int>;
|
|
|
|
basic_writer<Range>& writer;
|
|
const Specs& specs;
|
|
unsigned_type abs_value;
|
|
char prefix[4];
|
|
unsigned prefix_size;
|
|
|
|
string_view get_prefix() const { return string_view(prefix, prefix_size); }
|
|
|
|
int_writer(basic_writer<Range>& w, Int value, const Specs& s)
|
|
: writer(w),
|
|
specs(s),
|
|
abs_value(static_cast<unsigned_type>(value)),
|
|
prefix_size(0) {
|
|
if (is_negative(value)) {
|
|
prefix[0] = '-';
|
|
++prefix_size;
|
|
abs_value = 0 - abs_value;
|
|
} else if (specs.sign != sign::none && specs.sign != sign::minus) {
|
|
prefix[0] = specs.sign == sign::plus ? '+' : ' ';
|
|
++prefix_size;
|
|
}
|
|
}
|
|
|
|
struct dec_writer {
|
|
unsigned_type abs_value;
|
|
int num_digits;
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
|
|
}
|
|
};
|
|
|
|
void on_dec() {
|
|
int num_digits = count_digits(abs_value);
|
|
writer.write_int(num_digits, get_prefix(), specs,
|
|
dec_writer{abs_value, num_digits});
|
|
}
|
|
|
|
struct hex_writer {
|
|
int_writer& self;
|
|
int num_digits;
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
it = format_uint<4, char_type>(it, self.abs_value, num_digits,
|
|
self.specs.type != 'x');
|
|
}
|
|
};
|
|
|
|
void on_hex() {
|
|
if (specs.alt) {
|
|
prefix[prefix_size++] = '0';
|
|
prefix[prefix_size++] = specs.type;
|
|
}
|
|
int num_digits = count_digits<4>(abs_value);
|
|
writer.write_int(num_digits, get_prefix(), specs,
|
|
hex_writer{*this, num_digits});
|
|
}
|
|
|
|
template <int BITS> struct bin_writer {
|
|
unsigned_type abs_value;
|
|
int num_digits;
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
it = format_uint<BITS, char_type>(it, abs_value, num_digits);
|
|
}
|
|
};
|
|
|
|
void on_bin() {
|
|
if (specs.alt) {
|
|
prefix[prefix_size++] = '0';
|
|
prefix[prefix_size++] = static_cast<char>(specs.type);
|
|
}
|
|
int num_digits = count_digits<1>(abs_value);
|
|
writer.write_int(num_digits, get_prefix(), specs,
|
|
bin_writer<1>{abs_value, num_digits});
|
|
}
|
|
|
|
void on_oct() {
|
|
int num_digits = count_digits<3>(abs_value);
|
|
if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
|
|
// Octal prefix '0' is counted as a digit, so only add it if precision
|
|
// is not greater than the number of digits.
|
|
prefix[prefix_size++] = '0';
|
|
}
|
|
writer.write_int(num_digits, get_prefix(), specs,
|
|
bin_writer<3>{abs_value, num_digits});
|
|
}
|
|
|
|
enum { sep_size = 1 };
|
|
|
|
struct num_writer {
|
|
unsigned_type abs_value;
|
|
int size;
|
|
const std::string& groups;
|
|
char_type sep;
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
basic_string_view<char_type> s(&sep, sep_size);
|
|
// Index of a decimal digit with the least significant digit having
|
|
// index 0.
|
|
int digit_index = 0;
|
|
std::string::const_iterator group = groups.cbegin();
|
|
it = format_decimal<char_type>(
|
|
it, abs_value, size,
|
|
[this, s, &group, &digit_index](char_type*& buffer) {
|
|
if (*group <= 0 || ++digit_index % *group != 0 ||
|
|
*group == max_value<char>())
|
|
return;
|
|
if (group + 1 != groups.cend()) {
|
|
digit_index = 0;
|
|
++group;
|
|
}
|
|
buffer -= s.size();
|
|
std::uninitialized_copy(s.data(), s.data() + s.size(),
|
|
make_checked(buffer, s.size()));
|
|
});
|
|
}
|
|
};
|
|
|
|
void on_num() {
|
|
std::string groups = grouping<char_type>(writer.locale_);
|
|
if (groups.empty()) return on_dec();
|
|
auto sep = thousands_sep<char_type>(writer.locale_);
|
|
if (!sep) return on_dec();
|
|
int num_digits = count_digits(abs_value);
|
|
int size = num_digits;
|
|
std::string::const_iterator group = groups.cbegin();
|
|
while (group != groups.cend() && num_digits > *group && *group > 0 &&
|
|
*group != max_value<char>()) {
|
|
size += sep_size;
|
|
num_digits -= *group;
|
|
++group;
|
|
}
|
|
if (group == groups.cend())
|
|
size += sep_size * ((num_digits - 1) / groups.back());
|
|
writer.write_int(size, get_prefix(), specs,
|
|
num_writer{abs_value, size, groups, sep});
|
|
}
|
|
|
|
FMT_NORETURN void on_error() {
|
|
FMT_THROW(format_error("invalid type specifier"));
|
|
}
|
|
};
|
|
|
|
template <typename Char> struct str_writer {
|
|
const Char* s;
|
|
size_t size_;
|
|
|
|
size_t size() const { return size_; }
|
|
size_t width() const {
|
|
return count_code_points(basic_string_view<Char>(s, size_));
|
|
}
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
it = copy_str<char_type>(s, s + size_, it);
|
|
}
|
|
};
|
|
|
|
struct bytes_writer {
|
|
string_view bytes;
|
|
|
|
size_t size() const { return bytes.size(); }
|
|
size_t width() const { return bytes.size(); }
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
const char* data = bytes.data();
|
|
it = copy_str<char>(data, data + size(), it);
|
|
}
|
|
};
|
|
|
|
template <typename UIntPtr> struct pointer_writer {
|
|
UIntPtr value;
|
|
int num_digits;
|
|
|
|
size_t size() const { return to_unsigned(num_digits) + 2; }
|
|
size_t width() const { return size(); }
|
|
|
|
template <typename It> void operator()(It&& it) const {
|
|
*it++ = static_cast<char_type>('0');
|
|
*it++ = static_cast<char_type>('x');
|
|
it = format_uint<4, char_type>(it, value, num_digits);
|
|
}
|
|
};
|
|
|
|
public:
|
|
explicit basic_writer(Range out, locale_ref loc = locale_ref())
|
|
: out_(out.begin()), locale_(loc) {}
|
|
|
|
iterator out() const { return out_; }
|
|
|
|
// Writes a value in the format
|
|
// <left-padding><value><right-padding>
|
|
// where <value> is written by f(it).
|
|
template <typename F> void write_padded(const format_specs& specs, F&& f) {
|
|
// User-perceived width (in code points).
|
|
unsigned width = to_unsigned(specs.width);
|
|
size_t size = f.size(); // The number of code units.
|
|
size_t num_code_points = width != 0 ? f.width() : size;
|
|
if (width <= num_code_points) return f(reserve(size));
|
|
size_t padding = width - num_code_points;
|
|
size_t fill_size = specs.fill.size();
|
|
auto&& it = reserve(size + padding * fill_size);
|
|
if (specs.align == align::right) {
|
|
it = fill(it, padding, specs.fill);
|
|
f(it);
|
|
} else if (specs.align == align::center) {
|
|
std::size_t left_padding = padding / 2;
|
|
it = fill(it, left_padding, specs.fill);
|
|
f(it);
|
|
it = fill(it, padding - left_padding, specs.fill);
|
|
} else {
|
|
f(it);
|
|
it = fill(it, padding, specs.fill);
|
|
}
|
|
}
|
|
|
|
void write(int value) { write_decimal(value); }
|
|
void write(long value) { write_decimal(value); }
|
|
void write(long long value) { write_decimal(value); }
|
|
|
|
void write(unsigned value) { write_decimal(value); }
|
|
void write(unsigned long value) { write_decimal(value); }
|
|
void write(unsigned long long value) { write_decimal(value); }
|
|
|
|
#if FMT_USE_INT128
|
|
void write(int128_t value) { write_decimal(value); }
|
|
void write(uint128_t value) { write_decimal(value); }
|
|
#endif
|
|
|
|
template <typename T, typename Spec>
|
|
void write_int(T value, const Spec& spec) {
|
|
handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
|
void write(T value, format_specs specs = {}) {
|
|
if (const_check(!is_supported_floating_point(value))) {
|
|
return;
|
|
}
|
|
float_specs fspecs = parse_float_type_spec(specs);
|
|
fspecs.sign = specs.sign;
|
|
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
|
|
fspecs.sign = sign::minus;
|
|
value = -value;
|
|
} else if (fspecs.sign == sign::minus) {
|
|
fspecs.sign = sign::none;
|
|
}
|
|
|
|
if (!std::isfinite(value)) {
|
|
auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf")
|
|
: (fspecs.upper ? "NAN" : "nan");
|
|
return write_padded(specs, nonfinite_writer<char_type>{fspecs.sign, str});
|
|
}
|
|
|
|
if (specs.align == align::none) {
|
|
specs.align = align::right;
|
|
} else if (specs.align == align::numeric) {
|
|
if (fspecs.sign) {
|
|
auto&& it = reserve(1);
|
|
*it++ = static_cast<char_type>(data::signs[fspecs.sign]);
|
|
fspecs.sign = sign::none;
|
|
if (specs.width != 0) --specs.width;
|
|
}
|
|
specs.align = align::right;
|
|
}
|
|
|
|
memory_buffer buffer;
|
|
if (fspecs.format == float_format::hex) {
|
|
if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
|
|
snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
|
|
write_padded(specs, str_writer<char>{buffer.data(), buffer.size()});
|
|
return;
|
|
}
|
|
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
|
|
if (fspecs.format == float_format::exp) {
|
|
if (precision == max_value<int>())
|
|
FMT_THROW(format_error("number is too big"));
|
|
else
|
|
++precision;
|
|
}
|
|
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>('.');
|
|
write_padded(specs, float_writer<char_type>(buffer.data(),
|
|
static_cast<int>(buffer.size()),
|
|
exp, fspecs, point));
|
|
}
|
|
|
|
void write(char value) {
|
|
auto&& it = reserve(1);
|
|
*it++ = value;
|
|
}
|
|
|
|
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
|
|
void write(Char value) {
|
|
auto&& it = reserve(1);
|
|
*it++ = value;
|
|
}
|
|
|
|
void write(string_view value) {
|
|
auto&& it = reserve(value.size());
|
|
it = copy_str<char_type>(value.begin(), value.end(), it);
|
|
}
|
|
void write(wstring_view value) {
|
|
static_assert(std::is_same<char_type, wchar_t>::value, "");
|
|
auto&& it = reserve(value.size());
|
|
it = std::copy(value.begin(), value.end(), it);
|
|
}
|
|
|
|
template <typename Char>
|
|
void write(const Char* s, std::size_t size, const format_specs& specs) {
|
|
write_padded(specs, str_writer<Char>{s, size});
|
|
}
|
|
|
|
template <typename Char>
|
|
void write(basic_string_view<Char> s, const format_specs& specs = {}) {
|
|
const Char* data = s.data();
|
|
std::size_t size = s.size();
|
|
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
|
|
size = code_point_index(s, to_unsigned(specs.precision));
|
|
write(data, size, specs);
|
|
}
|
|
|
|
void write_bytes(string_view bytes, const format_specs& specs) {
|
|
write_padded(specs, bytes_writer{bytes});
|
|
}
|
|
|
|
template <typename UIntPtr>
|
|
void write_pointer(UIntPtr value, const format_specs* specs) {
|
|
int num_digits = count_digits<4>(value);
|
|
auto pw = pointer_writer<UIntPtr>{value, num_digits};
|
|
if (!specs) return pw(reserve(to_unsigned(num_digits) + 2));
|
|
format_specs specs_copy = *specs;
|
|
if (specs_copy.align == align::none) specs_copy.align = align::right;
|
|
write_padded(specs_copy, pw);
|
|
}
|
|
};
|
|
|
|
using writer = basic_writer<buffer_range<char>>;
|
|
|
|
template <typename T> struct is_integral : std::is_integral<T> {};
|
|
template <> struct is_integral<int128_t> : std::true_type {};
|
|
template <> struct is_integral<uint128_t> : std::true_type {};
|
|
|
|
template <typename Range, typename ErrorHandler = internal::error_handler>
|
|
class arg_formatter_base {
|
|
public:
|
|
using char_type = typename Range::value_type;
|
|
using iterator = typename Range::iterator;
|
|
using format_specs = basic_format_specs<char_type>;
|
|
|
|
private:
|
|
using writer_type = basic_writer<Range>;
|
|
writer_type writer_;
|
|
format_specs* specs_;
|
|
|
|
struct char_writer {
|
|
char_type value;
|
|
|
|
size_t size() const { return 1; }
|
|
size_t width() const { return 1; }
|
|
|
|
template <typename It> void operator()(It&& it) const { *it++ = value; }
|
|
};
|
|
|
|
void write_char(char_type value) {
|
|
if (specs_)
|
|
writer_.write_padded(*specs_, char_writer{value});
|
|
else
|
|
writer_.write(value);
|
|
}
|
|
|
|
void write_pointer(const void* p) {
|
|
writer_.write_pointer(internal::to_uintptr(p), specs_);
|
|
}
|
|
|
|
protected:
|
|
writer_type& writer() { return writer_; }
|
|
FMT_DEPRECATED format_specs* spec() { return specs_; }
|
|
format_specs* specs() { return specs_; }
|
|
iterator out() { return writer_.out(); }
|
|
|
|
void write(bool value) {
|
|
string_view sv(value ? "true" : "false");
|
|
specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
|
|
}
|
|
|
|
void write(const char_type* value) {
|
|
if (!value) {
|
|
FMT_THROW(format_error("string pointer is null"));
|
|
} else {
|
|
auto length = std::char_traits<char_type>::length(value);
|
|
basic_string_view<char_type> sv(value, length);
|
|
specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
|
|
}
|
|
}
|
|
|
|
public:
|
|
arg_formatter_base(Range r, format_specs* s, locale_ref loc)
|
|
: writer_(r, loc), specs_(s) {}
|
|
|
|
iterator operator()(monostate) {
|
|
FMT_ASSERT(false, "invalid argument type");
|
|
return out();
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
|
|
iterator operator()(T value) {
|
|
if (specs_)
|
|
writer_.write_int(value, *specs_);
|
|
else
|
|
writer_.write(value);
|
|
return out();
|
|
}
|
|
|
|
iterator operator()(char_type value) {
|
|
internal::handle_char_specs(
|
|
specs_, char_spec_handler(*this, static_cast<char_type>(value)));
|
|
return out();
|
|
}
|
|
|
|
iterator operator()(bool value) {
|
|
if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
|
|
write(value != 0);
|
|
return out();
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
|
iterator operator()(T value) {
|
|
if (const_check(is_supported_floating_point(value)))
|
|
writer_.write(value, specs_ ? *specs_ : format_specs());
|
|
else
|
|
FMT_ASSERT(false, "unsupported float argument type");
|
|
return out();
|
|
}
|
|
|
|
struct char_spec_handler : ErrorHandler {
|
|
arg_formatter_base& formatter;
|
|
char_type value;
|
|
|
|
char_spec_handler(arg_formatter_base& f, char_type val)
|
|
: formatter(f), value(val) {}
|
|
|
|
void on_int() {
|
|
if (formatter.specs_)
|
|
formatter.writer_.write_int(value, *formatter.specs_);
|
|
else
|
|
formatter.writer_.write(value);
|
|
}
|
|
void on_char() { formatter.write_char(value); }
|
|
};
|
|
|
|
struct cstring_spec_handler : internal::error_handler {
|
|
arg_formatter_base& formatter;
|
|
const char_type* value;
|
|
|
|
cstring_spec_handler(arg_formatter_base& f, const char_type* val)
|
|
: formatter(f), value(val) {}
|
|
|
|
void on_string() { formatter.write(value); }
|
|
void on_pointer() { formatter.write_pointer(value); }
|
|
};
|
|
|
|
iterator operator()(const char_type* value) {
|
|
if (!specs_) return write(value), out();
|
|
internal::handle_cstring_type_spec(specs_->type,
|
|
cstring_spec_handler(*this, value));
|
|
return out();
|
|
}
|
|
|
|
iterator operator()(basic_string_view<char_type> value) {
|
|
if (specs_) {
|
|
internal::check_string_type_spec(specs_->type, internal::error_handler());
|
|
writer_.write(value, *specs_);
|
|
} else {
|
|
writer_.write(value);
|
|
}
|
|
return out();
|
|
}
|
|
|
|
iterator operator()(const void* value) {
|
|
if (specs_)
|
|
check_pointer_type_spec(specs_->type, internal::error_handler());
|
|
write_pointer(value);
|
|
return out();
|
|
}
|
|
};
|
|
|
|
template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
|
|
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
|
|
}
|
|
|
|
// Parses the range [begin, end) as an unsigned integer. This function assumes
|
|
// that the range is non-empty and the first character is a digit.
|
|
template <typename Char, typename ErrorHandler>
|
|
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
|
|
ErrorHandler&& eh) {
|
|
FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
|
|
unsigned value = 0;
|
|
// Convert to unsigned to prevent a warning.
|
|
constexpr unsigned max_int = max_value<int>();
|
|
unsigned big = max_int / 10;
|
|
do {
|
|
// Check for overflow.
|
|
if (value > big) {
|
|
value = max_int + 1;
|
|
break;
|
|
}
|
|
value = value * 10 + unsigned(*begin - '0');
|
|
++begin;
|
|
} while (begin != end && '0' <= *begin && *begin <= '9');
|
|
if (value > max_int) eh.on_error("number is too big");
|
|
return static_cast<int>(value);
|
|
}
|
|
|
|
template <typename Context> class custom_formatter {
|
|
private:
|
|
using char_type = typename Context::char_type;
|
|
|
|
basic_format_parse_context<char_type>& parse_ctx_;
|
|
Context& ctx_;
|
|
|
|
public:
|
|
explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
|
|
Context& ctx)
|
|
: parse_ctx_(parse_ctx), ctx_(ctx) {}
|
|
|
|
bool operator()(typename basic_format_arg<Context>::handle h) const {
|
|
h.format(parse_ctx_, ctx_);
|
|
return true;
|
|
}
|
|
|
|
template <typename T> bool operator()(T) const { return false; }
|
|
};
|
|
|
|
template <typename T>
|
|
using is_integer =
|
|
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
|
|
!std::is_same<T, char>::value &&
|
|
!std::is_same<T, wchar_t>::value>;
|
|
|
|
template <typename ErrorHandler> class width_checker {
|
|
public:
|
|
explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
|
|
|
|
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
|
|
FMT_CONSTEXPR unsigned long long operator()(T value) {
|
|
if (is_negative(value)) handler_.on_error("negative width");
|
|
return static_cast<unsigned long long>(value);
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
|
|
FMT_CONSTEXPR unsigned long long operator()(T) {
|
|
handler_.on_error("width is not integer");
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
ErrorHandler& handler_;
|
|
};
|
|
|
|
template <typename ErrorHandler> class precision_checker {
|
|
public:
|
|
explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
|
|
|
|
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
|
|
FMT_CONSTEXPR unsigned long long operator()(T value) {
|
|
if (is_negative(value)) handler_.on_error("negative precision");
|
|
return static_cast<unsigned long long>(value);
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
|
|
FMT_CONSTEXPR unsigned long long operator()(T) {
|
|
handler_.on_error("precision is not integer");
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
ErrorHandler& handler_;
|
|
};
|
|
|
|
// A format specifier handler that sets fields in basic_format_specs.
|
|
template <typename Char> class specs_setter {
|
|
public:
|
|
explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
|
|
: specs_(specs) {}
|
|
|
|
FMT_CONSTEXPR specs_setter(const specs_setter& other)
|
|
: specs_(other.specs_) {}
|
|
|
|
FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
|
|
FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
|
|
specs_.fill = fill;
|
|
}
|
|
FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; }
|
|
FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; }
|
|
FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; }
|
|
FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
|
|
|
|
FMT_CONSTEXPR void on_zero() {
|
|
specs_.align = align::numeric;
|
|
specs_.fill[0] = Char('0');
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
|
|
FMT_CONSTEXPR void on_precision(int precision) {
|
|
specs_.precision = precision;
|
|
}
|
|
FMT_CONSTEXPR void end_precision() {}
|
|
|
|
FMT_CONSTEXPR void on_type(Char type) {
|
|
specs_.type = static_cast<char>(type);
|
|
}
|
|
|
|
protected:
|
|
basic_format_specs<Char>& specs_;
|
|
};
|
|
|
|
template <typename ErrorHandler> class numeric_specs_checker {
|
|
public:
|
|
FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type)
|
|
: error_handler_(eh), arg_type_(arg_type) {}
|
|
|
|
FMT_CONSTEXPR void require_numeric_argument() {
|
|
if (!is_arithmetic_type(arg_type_))
|
|
error_handler_.on_error("format specifier requires numeric argument");
|
|
}
|
|
|
|
FMT_CONSTEXPR void check_sign() {
|
|
require_numeric_argument();
|
|
if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
|
|
arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
|
|
error_handler_.on_error("format specifier requires signed argument");
|
|
}
|
|
}
|
|
|
|
FMT_CONSTEXPR void check_precision() {
|
|
if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
|
|
error_handler_.on_error("precision not allowed for this argument type");
|
|
}
|
|
|
|
private:
|
|
ErrorHandler& error_handler_;
|
|
internal::type arg_type_;
|
|
};
|
|
|
|
// A format specifier handler that checks if specifiers are consistent with the
|
|
// argument type.
|
|
template <typename Handler> class specs_checker : public Handler {
|
|
public:
|
|
FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type)
|
|
: Handler(handler), checker_(*this, arg_type) {}
|
|
|
|
FMT_CONSTEXPR specs_checker(const specs_checker& other)
|
|
: Handler(other), checker_(*this, other.arg_type_) {}
|
|
|
|
FMT_CONSTEXPR void on_align(align_t align) {
|
|
if (align == align::numeric) checker_.require_numeric_argument();
|
|
Handler::on_align(align);
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_plus() {
|
|
checker_.check_sign();
|
|
Handler::on_plus();
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_minus() {
|
|
checker_.check_sign();
|
|
Handler::on_minus();
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_space() {
|
|
checker_.check_sign();
|
|
Handler::on_space();
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_hash() {
|
|
checker_.require_numeric_argument();
|
|
Handler::on_hash();
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_zero() {
|
|
checker_.require_numeric_argument();
|
|
Handler::on_zero();
|
|
}
|
|
|
|
FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
|
|
|
|
private:
|
|
numeric_specs_checker<Handler> checker_;
|
|
};
|
|
|
|
template <template <typename> class Handler, typename FormatArg,
|
|
typename ErrorHandler>
|
|
FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
|
|
unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
|
|
if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
|
|
return static_cast<int>(value);
|
|
}
|
|
|
|
struct auto_id {};
|
|
|
|
template <typename Context>
|
|
FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, int id) {
|
|
auto arg = ctx.arg(id);
|
|
if (!arg) ctx.on_error("argument index out of range");
|
|
return arg;
|
|
}
|
|
|
|
// The standard format specifier handler with checking.
|
|
template <typename ParseContext, typename Context>
|
|
class specs_handler : public specs_setter<typename Context::char_type> {
|
|
public:
|
|
using char_type = typename Context::char_type;
|
|
|
|
FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
|
|
ParseContext& parse_ctx, Context& ctx)
|
|
: specs_setter<char_type>(specs),
|
|
parse_context_(parse_ctx),
|
|
context_(ctx) {}
|
|
|
|
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
|
|
this->specs_.width = get_dynamic_spec<width_checker>(
|
|
get_arg(arg_id), context_.error_handler());
|
|
}
|
|
|
|
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
|
|
this->specs_.precision = get_dynamic_spec<precision_checker>(
|
|
get_arg(arg_id), context_.error_handler());
|
|
}
|
|
|
|
void on_error(const char* message) { context_.on_error(message); }
|
|
|
|
private:
|
|
// This is only needed for compatibility with gcc 4.4.
|
|
using format_arg = typename Context::format_arg;
|
|
|
|
FMT_CONSTEXPR format_arg get_arg(auto_id) {
|
|
return internal::get_arg(context_, parse_context_.next_arg_id());
|
|
}
|
|
|
|
FMT_CONSTEXPR format_arg get_arg(int arg_id) {
|
|
parse_context_.check_arg_id(arg_id);
|
|
return internal::get_arg(context_, arg_id);
|
|
}
|
|
|
|
FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
|
|
parse_context_.check_arg_id(arg_id);
|
|
return context_.arg(arg_id);
|
|
}
|
|
|
|
ParseContext& parse_context_;
|
|
Context& context_;
|
|
};
|
|
|
|
enum class arg_id_kind { none, index, name };
|
|
|
|
// An argument reference.
|
|
template <typename Char> struct arg_ref {
|
|
FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
|
|
|
|
FMT_CONSTEXPR explicit arg_ref(int index)
|
|
: kind(arg_id_kind::index), val(index) {}
|
|
FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
|
|
: kind(arg_id_kind::name), val(name) {}
|
|
|
|
FMT_CONSTEXPR arg_ref& operator=(int idx) {
|
|
kind = arg_id_kind::index;
|
|
val.index = idx;
|
|
return *this;
|
|
}
|
|
|
|
arg_id_kind kind;
|
|
union value {
|
|
FMT_CONSTEXPR value(int id = 0) : index{id} {}
|
|
FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
|
|
|
|
int index;
|
|
basic_string_view<Char> name;
|
|
} val;
|
|
};
|
|
|
|
// Format specifiers with width and precision resolved at formatting rather
|
|
// than parsing time to allow re-using the same parsed specifiers with
|
|
// different sets of arguments (precompilation of format strings).
|
|
template <typename Char>
|
|
struct dynamic_format_specs : basic_format_specs<Char> {
|
|
arg_ref<Char> width_ref;
|
|
arg_ref<Char> precision_ref;
|
|
};
|
|
|
|
// Format spec handler that saves references to arguments representing dynamic
|
|
// width and precision to be resolved at formatting time.
|
|
template <typename ParseContext>
|
|
class dynamic_specs_handler
|
|
: public specs_setter<typename ParseContext::char_type> {
|
|
public:
|
|
using char_type = typename ParseContext::char_type;
|
|
|
|
FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
|
|
ParseContext& ctx)
|
|
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
|
|
|
|
FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
|
|
: specs_setter<char_type>(other),
|
|
specs_(other.specs_),
|
|
context_(other.context_) {}
|
|
|
|
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
|
|
specs_.width_ref = make_arg_ref(arg_id);
|
|
}
|
|
|
|
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
|
|
specs_.precision_ref = make_arg_ref(arg_id);
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_error(const char* message) {
|
|
context_.on_error(message);
|
|
}
|
|
|
|
private:
|
|
using arg_ref_type = arg_ref<char_type>;
|
|
|
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(int arg_id) {
|
|
context_.check_arg_id(arg_id);
|
|
return arg_ref_type(arg_id);
|
|
}
|
|
|
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) {
|
|
return arg_ref_type(context_.next_arg_id());
|
|
}
|
|
|
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
|
|
context_.check_arg_id(arg_id);
|
|
basic_string_view<char_type> format_str(
|
|
context_.begin(), to_unsigned(context_.end() - context_.begin()));
|
|
return arg_ref_type(arg_id);
|
|
}
|
|
|
|
dynamic_format_specs<char_type>& specs_;
|
|
ParseContext& context_;
|
|
};
|
|
|
|
template <typename Char, typename IDHandler>
|
|
FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
|
|
IDHandler&& handler) {
|
|
FMT_ASSERT(begin != end, "");
|
|
Char c = *begin;
|
|
if (c == '}' || c == ':') {
|
|
handler();
|
|
return begin;
|
|
}
|
|
if (c >= '0' && c <= '9') {
|
|
int index = 0;
|
|
if (c != '0')
|
|
index = parse_nonnegative_int(begin, end, handler);
|
|
else
|
|
++begin;
|
|
if (begin == end || (*begin != '}' && *begin != ':'))
|
|
handler.on_error("invalid format string");
|
|
else
|
|
handler(index);
|
|
return begin;
|
|
}
|
|
if (!is_name_start(c)) {
|
|
handler.on_error("invalid format string");
|
|
return begin;
|
|
}
|
|
auto it = begin;
|
|
do {
|
|
++it;
|
|
} while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
|
|
handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
|
|
return it;
|
|
}
|
|
|
|
// Adapts SpecHandler to IDHandler API for dynamic width.
|
|
template <typename SpecHandler, typename Char> struct width_adapter {
|
|
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
|
|
|
|
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
|
|
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
|
|
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
|
handler.on_dynamic_width(id);
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_error(const char* message) {
|
|
handler.on_error(message);
|
|
}
|
|
|
|
SpecHandler& handler;
|
|
};
|
|
|
|
// Adapts SpecHandler to IDHandler API for dynamic precision.
|
|
template <typename SpecHandler, typename Char> struct precision_adapter {
|
|
explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
|
|
|
|
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
|
|
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
|
|
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
|
handler.on_dynamic_precision(id);
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_error(const char* message) {
|
|
handler.on_error(message);
|
|
}
|
|
|
|
SpecHandler& handler;
|
|
};
|
|
|
|
template <typename Char>
|
|
FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) {
|
|
if (const_check(sizeof(Char) != 1) || (*begin & 0x80) == 0) return begin + 1;
|
|
do {
|
|
++begin;
|
|
} while (begin != end && (*begin & 0xc0) == 0x80);
|
|
return begin;
|
|
}
|
|
|
|
// Parses fill and alignment.
|
|
template <typename Char, typename Handler>
|
|
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
|
|
Handler&& handler) {
|
|
FMT_ASSERT(begin != end, "");
|
|
auto align = align::none;
|
|
auto p = next_code_point(begin, end);
|
|
if (p == end) p = begin;
|
|
for (;;) {
|
|
switch (static_cast<char>(*p)) {
|
|
case '<':
|
|
align = align::left;
|
|
break;
|
|
case '>':
|
|
align = align::right;
|
|
break;
|
|
#if FMT_NUMERIC_ALIGN
|
|
case '=':
|
|
align = align::numeric;
|
|
break;
|
|
#endif
|
|
case '^':
|
|
align = align::center;
|
|
break;
|
|
}
|
|
if (align != align::none) {
|
|
if (p != begin) {
|
|
auto c = *begin;
|
|
if (c == '{')
|
|
return handler.on_error("invalid fill character '{'"), begin;
|
|
handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
|
|
begin = p + 1;
|
|
} else
|
|
++begin;
|
|
handler.on_align(align);
|
|
break;
|
|
} else if (p == begin) {
|
|
break;
|
|
}
|
|
p = begin;
|
|
}
|
|
return begin;
|
|
}
|
|
|
|
template <typename Char, typename Handler>
|
|
FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
|
|
Handler&& handler) {
|
|
FMT_ASSERT(begin != end, "");
|
|
if ('0' <= *begin && *begin <= '9') {
|
|
handler.on_width(parse_nonnegative_int(begin, end, handler));
|
|
} else if (*begin == '{') {
|
|
++begin;
|
|
if (begin != end)
|
|
begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
|
|
if (begin == end || *begin != '}')
|
|
return handler.on_error("invalid format string"), begin;
|
|
++begin;
|
|
}
|
|
return begin;
|
|
}
|
|
|
|
template <typename Char, typename Handler>
|
|
FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
|
|
Handler&& handler) {
|
|
++begin;
|
|
auto c = begin != end ? *begin : Char();
|
|
if ('0' <= c && c <= '9') {
|
|
handler.on_precision(parse_nonnegative_int(begin, end, handler));
|
|
} else if (c == '{') {
|
|
++begin;
|
|
if (begin != end) {
|
|
begin =
|
|
parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
|
|
}
|
|
if (begin == end || *begin++ != '}')
|
|
return handler.on_error("invalid format string"), begin;
|
|
} else {
|
|
return handler.on_error("missing precision specifier"), begin;
|
|
}
|
|
handler.end_precision();
|
|
return begin;
|
|
}
|
|
|
|
// Parses standard format specifiers and sends notifications about parsed
|
|
// components to handler.
|
|
template <typename Char, typename SpecHandler>
|
|
FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
|
|
SpecHandler&& handler) {
|
|
if (begin == end || *begin == '}') return begin;
|
|
|
|
begin = parse_align(begin, end, handler);
|
|
if (begin == end) return begin;
|
|
|
|
// Parse sign.
|
|
switch (static_cast<char>(*begin)) {
|
|
case '+':
|
|
handler.on_plus();
|
|
++begin;
|
|
break;
|
|
case '-':
|
|
handler.on_minus();
|
|
++begin;
|
|
break;
|
|
case ' ':
|
|
handler.on_space();
|
|
++begin;
|
|
break;
|
|
}
|
|
if (begin == end) return begin;
|
|
|
|
if (*begin == '#') {
|
|
handler.on_hash();
|
|
if (++begin == end) return begin;
|
|
}
|
|
|
|
// Parse zero flag.
|
|
if (*begin == '0') {
|
|
handler.on_zero();
|
|
if (++begin == end) return begin;
|
|
}
|
|
|
|
begin = parse_width(begin, end, handler);
|
|
if (begin == end) return begin;
|
|
|
|
// Parse precision.
|
|
if (*begin == '.') {
|
|
begin = parse_precision(begin, end, handler);
|
|
}
|
|
|
|
// Parse type.
|
|
if (begin != end && *begin != '}') handler.on_type(*begin++);
|
|
return begin;
|
|
}
|
|
|
|
// Return the result via the out param to workaround gcc bug 77539.
|
|
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
|
|
FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
|
|
for (out = first; out != last; ++out) {
|
|
if (*out == value) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <>
|
|
inline bool find<false, char>(const char* first, const char* last, char value,
|
|
const char*& out) {
|
|
out = static_cast<const char*>(
|
|
std::memchr(first, value, internal::to_unsigned(last - first)));
|
|
return out != nullptr;
|
|
}
|
|
|
|
template <typename Handler, typename Char> struct id_adapter {
|
|
FMT_CONSTEXPR void operator()() { handler.on_arg_id(); }
|
|
FMT_CONSTEXPR void operator()(int id) { handler.on_arg_id(id); }
|
|
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
|
handler.on_arg_id(id);
|
|
}
|
|
FMT_CONSTEXPR void on_error(const char* message) {
|
|
handler.on_error(message);
|
|
}
|
|
Handler& handler;
|
|
};
|
|
|
|
template <bool IS_CONSTEXPR, typename Char, typename Handler>
|
|
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
|
|
Handler&& handler) {
|
|
struct pfs_writer {
|
|
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
|
|
if (begin == end) return;
|
|
for (;;) {
|
|
const Char* p = nullptr;
|
|
if (!find<IS_CONSTEXPR>(begin, end, '}', p))
|
|
return handler_.on_text(begin, end);
|
|
++p;
|
|
if (p == end || *p != '}')
|
|
return handler_.on_error("unmatched '}' in format string");
|
|
handler_.on_text(begin, p);
|
|
begin = p + 1;
|
|
}
|
|
}
|
|
Handler& handler_;
|
|
} write{handler};
|
|
auto begin = format_str.data();
|
|
auto end = begin + format_str.size();
|
|
while (begin != end) {
|
|
// Doing two passes with memchr (one for '{' and another for '}') is up to
|
|
// 2.5x faster than the naive one-pass implementation on big format strings.
|
|
const Char* p = begin;
|
|
if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
|
|
return write(begin, end);
|
|
write(begin, p);
|
|
++p;
|
|
if (p == end) return handler.on_error("invalid format string");
|
|
if (static_cast<char>(*p) == '}') {
|
|
handler.on_arg_id();
|
|
handler.on_replacement_field(p);
|
|
} else if (*p == '{') {
|
|
handler.on_text(p, p + 1);
|
|
} else {
|
|
p = parse_arg_id(p, end, id_adapter<Handler, Char>{handler});
|
|
Char c = p != end ? *p : Char();
|
|
if (c == '}') {
|
|
handler.on_replacement_field(p);
|
|
} else if (c == ':') {
|
|
p = handler.on_format_specs(p + 1, end);
|
|
if (p == end || *p != '}')
|
|
return handler.on_error("unknown format specifier");
|
|
} else {
|
|
return handler.on_error("missing '}' in format string");
|
|
}
|
|
}
|
|
begin = p + 1;
|
|
}
|
|
}
|
|
|
|
template <typename T, typename ParseContext>
|
|
FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
|
|
ParseContext& ctx) {
|
|
using char_type = typename ParseContext::char_type;
|
|
using context = buffer_context<char_type>;
|
|
using mapped_type =
|
|
conditional_t<internal::mapped_type_constant<T, context>::value !=
|
|
type::custom_type,
|
|
decltype(arg_mapper<context>().map(std::declval<T>())), T>;
|
|
auto f = conditional_t<has_formatter<mapped_type, context>::value,
|
|
formatter<mapped_type, char_type>,
|
|
internal::fallback_formatter<T, char_type>>();
|
|
return f.parse(ctx);
|
|
}
|
|
|
|
template <typename Char, typename ErrorHandler, typename... Args>
|
|
class format_string_checker {
|
|
public:
|
|
explicit FMT_CONSTEXPR format_string_checker(
|
|
basic_string_view<Char> format_str, ErrorHandler eh)
|
|
: arg_id_(-1),
|
|
context_(format_str, eh),
|
|
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
|
|
|
|
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
|
|
|
FMT_CONSTEXPR void on_arg_id() {
|
|
arg_id_ = context_.next_arg_id();
|
|
check_arg_id();
|
|
}
|
|
FMT_CONSTEXPR void on_arg_id(int id) {
|
|
arg_id_ = id;
|
|
context_.check_arg_id(id);
|
|
check_arg_id();
|
|
}
|
|
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) {
|
|
on_error("compile-time checks don't support named arguments");
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
|
|
|
|
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, const Char*) {
|
|
advance_to(context_, begin);
|
|
return arg_id_ < num_args ? parse_funcs_[arg_id_](context_) : begin;
|
|
}
|
|
|
|
FMT_CONSTEXPR void on_error(const char* message) {
|
|
context_.on_error(message);
|
|
}
|
|
|
|
private:
|
|
using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
|
|
enum { num_args = sizeof...(Args) };
|
|
|
|
FMT_CONSTEXPR void check_arg_id() {
|
|
if (arg_id_ >= num_args) context_.on_error("argument index out of range");
|
|
}
|
|
|
|
// Format specifier parsing function.
|
|
using parse_func = const Char* (*)(parse_context_type&);
|
|
|
|
int arg_id_;
|
|
parse_context_type context_;
|
|
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
|
|
};
|
|
|
|
template <typename Char, typename ErrorHandler, typename... Args>
|
|
FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
|
|
ErrorHandler eh = ErrorHandler()) {
|
|
format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
|
|
parse_format_string<true>(s, checker);
|
|
return true;
|
|
}
|
|
|
|
template <typename... Args, typename S,
|
|
enable_if_t<(is_compile_string<S>::value), int>>
|
|
void check_format_string(S format_str) {
|
|
FMT_CONSTEXPR_DECL bool invalid_format = internal::do_check_format_string<
|
|
typename S::char_type, internal::error_handler,
|
|
remove_const_t<remove_reference_t<Args>>...>(to_string_view(format_str));
|
|
(void)invalid_format;
|
|
}
|
|
|
|
template <template <typename> class Handler, typename Context>
|
|
void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
|
|
Context& ctx) {
|
|
switch (ref.kind) {
|
|
case arg_id_kind::none:
|
|
break;
|
|
case arg_id_kind::index:
|
|
value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
|
|
ctx.error_handler());
|
|
break;
|
|
case arg_id_kind::name:
|
|
value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
|
|
ctx.error_handler());
|
|
break;
|
|
}
|
|
}
|
|
|
|
using format_func = void (*)(internal::buffer<char>&, int, string_view);
|
|
|
|
FMT_API void format_error_code(buffer<char>& out, int error_code,
|
|
string_view message) FMT_NOEXCEPT;
|
|
|
|
FMT_API void report_error(format_func func, int error_code,
|
|
string_view message) FMT_NOEXCEPT;
|
|
} // namespace internal
|
|
|
|
template <typename Range>
|
|
using basic_writer FMT_DEPRECATED_ALIAS = internal::basic_writer<Range>;
|
|
using writer FMT_DEPRECATED_ALIAS = internal::writer;
|
|
using wwriter FMT_DEPRECATED_ALIAS =
|
|
internal::basic_writer<buffer_range<wchar_t>>;
|
|
|
|
/** The default argument formatter. */
|
|
template <typename Range>
|
|
class arg_formatter : public internal::arg_formatter_base<Range> {
|
|
private:
|
|
using char_type = typename Range::value_type;
|
|
using base = internal::arg_formatter_base<Range>;
|
|
using context_type = basic_format_context<typename base::iterator, char_type>;
|
|
|
|
context_type& ctx_;
|
|
basic_format_parse_context<char_type>* parse_ctx_;
|
|
|
|
public:
|
|
using range = Range;
|
|
using iterator = typename base::iterator;
|
|
using format_specs = typename base::format_specs;
|
|
|
|
/**
|
|
\rst
|
|
Constructs an argument formatter object.
|
|
*ctx* is a reference to the formatting context,
|
|
*specs* contains format specifier information for standard argument types.
|
|
\endrst
|
|
*/
|
|
explicit arg_formatter(
|
|
context_type& ctx,
|
|
basic_format_parse_context<char_type>* parse_ctx = nullptr,
|
|
format_specs* specs = nullptr)
|
|
: base(Range(ctx.out()), specs, ctx.locale()),
|
|
ctx_(ctx),
|
|
parse_ctx_(parse_ctx) {}
|
|
|
|
using base::operator();
|
|
|
|
/** Formats an argument of a user-defined type. */
|
|
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
|
handle.format(*parse_ctx_, ctx_);
|
|
return ctx_.out();
|
|
}
|
|
};
|
|
|
|
/**
|
|
An error returned by an operating system or a language runtime,
|
|
for example a file opening error.
|
|
*/
|
|
FMT_CLASS_API
|
|
class FMT_API system_error : public std::runtime_error {
|
|
private:
|
|
void init(int err_code, string_view format_str, format_args args);
|
|
|
|
protected:
|
|
int error_code_;
|
|
|
|
system_error() : std::runtime_error(""), error_code_(0) {}
|
|
|
|
public:
|
|
/**
|
|
\rst
|
|
Constructs a :class:`fmt::system_error` object with a description
|
|
formatted with `fmt::format_system_error`. *message* and additional
|
|
arguments passed into the constructor are formatted similarly to
|
|
`fmt::format`.
|
|
|
|
**Example**::
|
|
|
|
// This throws a system_error with the description
|
|
// cannot open file 'madeup': No such file or directory
|
|
// or similar (system message may vary).
|
|
const char *filename = "madeup";
|
|
std::FILE *file = std::fopen(filename, "r");
|
|
if (!file)
|
|
throw fmt::system_error(errno, "cannot open file '{}'", filename);
|
|
\endrst
|
|
*/
|
|
template <typename... Args>
|
|
system_error(int error_code, string_view message, const Args&... args)
|
|
: std::runtime_error("") {
|
|
init(error_code, message, make_format_args(args...));
|
|
}
|
|
system_error(const system_error&) = default;
|
|
system_error& operator=(const system_error&) = default;
|
|
system_error(system_error&&) = default;
|
|
system_error& operator=(system_error&&) = default;
|
|
~system_error() FMT_NOEXCEPT FMT_OVERRIDE;
|
|
|
|
int error_code() const { return error_code_; }
|
|
};
|
|
|
|
/**
|
|
\rst
|
|
Formats an error returned by an operating system or a language runtime,
|
|
for example a file opening error, and writes it to *out* in the following
|
|
form:
|
|
|
|
.. parsed-literal::
|
|
*<message>*: *<system-message>*
|
|
|
|
where *<message>* is the passed message and *<system-message>* is
|
|
the system message corresponding to the error code.
|
|
*error_code* is a system error code as given by ``errno``.
|
|
If *error_code* is not a valid error code such as -1, the system message
|
|
may look like "Unknown error -1" and is platform-dependent.
|
|
\endrst
|
|
*/
|
|
FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
|
|
string_view message) FMT_NOEXCEPT;
|
|
|
|
// Reports a system error without throwing an exception.
|
|
// Can be used to report errors from destructors.
|
|
FMT_API void report_system_error(int error_code,
|
|
string_view message) FMT_NOEXCEPT;
|
|
|
|
/** Fast integer formatter. */
|
|
class format_int {
|
|
private:
|
|
// Buffer should be large enough to hold all digits (digits10 + 1),
|
|
// a sign and a null character.
|
|
enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
|
|
mutable char buffer_[buffer_size];
|
|
char* str_;
|
|
|
|
// Formats value in reverse and returns a pointer to the beginning.
|
|
char* format_decimal(unsigned long long value) {
|
|
char* ptr = buffer_ + (buffer_size - 1); // Parens to workaround MSVC bug.
|
|
while (value >= 100) {
|
|
// Integer division is slow so do it for a group of two digits instead
|
|
// of for every digit. The idea comes from the talk by Alexandrescu
|
|
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
|
auto index = static_cast<unsigned>((value % 100) * 2);
|
|
value /= 100;
|
|
*--ptr = internal::data::digits[index + 1];
|
|
*--ptr = internal::data::digits[index];
|
|
}
|
|
if (value < 10) {
|
|
*--ptr = static_cast<char>('0' + value);
|
|
return ptr;
|
|
}
|
|
auto index = static_cast<unsigned>(value * 2);
|
|
*--ptr = internal::data::digits[index + 1];
|
|
*--ptr = internal::data::digits[index];
|
|
return ptr;
|
|
}
|
|
|
|
void format_signed(long long value) {
|
|
auto abs_value = static_cast<unsigned long long>(value);
|
|
bool negative = value < 0;
|
|
if (negative) abs_value = 0 - abs_value;
|
|
str_ = format_decimal(abs_value);
|
|
if (negative) *--str_ = '-';
|
|
}
|
|
|
|
public:
|
|
explicit format_int(int value) { format_signed(value); }
|
|
explicit format_int(long value) { format_signed(value); }
|
|
explicit format_int(long long value) { format_signed(value); }
|
|
explicit format_int(unsigned value) : str_(format_decimal(value)) {}
|
|
explicit format_int(unsigned long value) : str_(format_decimal(value)) {}
|
|
explicit format_int(unsigned long long value) : str_(format_decimal(value)) {}
|
|
|
|
/** Returns the number of characters written to the output buffer. */
|
|
std::size_t size() const {
|
|
return internal::to_unsigned(buffer_ - str_ + buffer_size - 1);
|
|
}
|
|
|
|
/**
|
|
Returns a pointer to the output buffer content. No terminating null
|
|
character is appended.
|
|
*/
|
|
const char* data() const { return str_; }
|
|
|
|
/**
|
|
Returns a pointer to the output buffer content with terminating null
|
|
character appended.
|
|
*/
|
|
const char* c_str() const {
|
|
buffer_[buffer_size - 1] = '\0';
|
|
return str_;
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Returns the content of the output buffer as an ``std::string``.
|
|
\endrst
|
|
*/
|
|
std::string str() const { return std::string(str_, size()); }
|
|
};
|
|
|
|
// A formatter specialization for the core types corresponding to internal::type
|
|
// constants.
|
|
template <typename T, typename Char>
|
|
struct formatter<T, Char,
|
|
enable_if_t<internal::type_constant<T, Char>::value !=
|
|
internal::type::custom_type>> {
|
|
FMT_CONSTEXPR formatter() = default;
|
|
|
|
// Parses format specifiers stopping either at the end of the range or at the
|
|
// terminating '}'.
|
|
template <typename ParseContext>
|
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
using handler_type = internal::dynamic_specs_handler<ParseContext>;
|
|
auto type = internal::type_constant<T, Char>::value;
|
|
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
|
type);
|
|
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
|
auto eh = ctx.error_handler();
|
|
switch (type) {
|
|
case internal::type::none_type:
|
|
case internal::type::named_arg_type:
|
|
FMT_ASSERT(false, "invalid argument type");
|
|
break;
|
|
case internal::type::int_type:
|
|
case internal::type::uint_type:
|
|
case internal::type::long_long_type:
|
|
case internal::type::ulong_long_type:
|
|
case internal::type::int128_type:
|
|
case internal::type::uint128_type:
|
|
case internal::type::bool_type:
|
|
handle_int_type_spec(specs_.type,
|
|
internal::int_type_checker<decltype(eh)>(eh));
|
|
break;
|
|
case internal::type::char_type:
|
|
handle_char_specs(
|
|
&specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
|
|
break;
|
|
case internal::type::float_type:
|
|
if (internal::const_check(FMT_USE_FLOAT)) {
|
|
internal::parse_float_type_spec(specs_, eh);
|
|
} else {
|
|
FMT_ASSERT(false, "float support disabled");
|
|
}
|
|
break;
|
|
case internal::type::double_type:
|
|
if (internal::const_check(FMT_USE_DOUBLE)) {
|
|
internal::parse_float_type_spec(specs_, eh);
|
|
} else {
|
|
FMT_ASSERT(false, "double support disabled");
|
|
}
|
|
break;
|
|
case internal::type::long_double_type:
|
|
if (internal::const_check(FMT_USE_LONG_DOUBLE)) {
|
|
internal::parse_float_type_spec(specs_, eh);
|
|
} else {
|
|
FMT_ASSERT(false, "long double support disabled");
|
|
}
|
|
break;
|
|
case internal::type::cstring_type:
|
|
internal::handle_cstring_type_spec(
|
|
specs_.type, internal::cstring_type_checker<decltype(eh)>(eh));
|
|
break;
|
|
case internal::type::string_type:
|
|
internal::check_string_type_spec(specs_.type, eh);
|
|
break;
|
|
case internal::type::pointer_type:
|
|
internal::check_pointer_type_spec(specs_.type, eh);
|
|
break;
|
|
case internal::type::custom_type:
|
|
// Custom format specifiers should be checked in parse functions of
|
|
// formatter specializations.
|
|
break;
|
|
}
|
|
return it;
|
|
}
|
|
|
|
template <typename FormatContext>
|
|
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
internal::handle_dynamic_spec<internal::width_checker>(
|
|
specs_.width, specs_.width_ref, ctx);
|
|
internal::handle_dynamic_spec<internal::precision_checker>(
|
|
specs_.precision, specs_.precision_ref, ctx);
|
|
using range_type =
|
|
internal::output_range<typename FormatContext::iterator,
|
|
typename FormatContext::char_type>;
|
|
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
|
|
internal::make_arg<FormatContext>(val));
|
|
}
|
|
|
|
private:
|
|
internal::dynamic_format_specs<Char> specs_;
|
|
};
|
|
|
|
#define FMT_FORMAT_AS(Type, Base) \
|
|
template <typename Char> \
|
|
struct formatter<Type, Char> : formatter<Base, Char> { \
|
|
template <typename FormatContext> \
|
|
auto format(Type const& val, FormatContext& ctx) -> decltype(ctx.out()) { \
|
|
return formatter<Base, Char>::format(val, ctx); \
|
|
} \
|
|
}
|
|
|
|
FMT_FORMAT_AS(signed char, int);
|
|
FMT_FORMAT_AS(unsigned char, unsigned);
|
|
FMT_FORMAT_AS(short, int);
|
|
FMT_FORMAT_AS(unsigned short, unsigned);
|
|
FMT_FORMAT_AS(long, long long);
|
|
FMT_FORMAT_AS(unsigned long, unsigned long long);
|
|
FMT_FORMAT_AS(Char*, const Char*);
|
|
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
|
|
FMT_FORMAT_AS(std::nullptr_t, const void*);
|
|
FMT_FORMAT_AS(internal::std_string_view<Char>, basic_string_view<Char>);
|
|
|
|
template <typename Char>
|
|
struct formatter<void*, Char> : formatter<const void*, Char> {
|
|
template <typename FormatContext>
|
|
auto format(void* val, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
return formatter<const void*, Char>::format(val, ctx);
|
|
}
|
|
};
|
|
|
|
template <typename Char, size_t N>
|
|
struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
|
|
template <typename FormatContext>
|
|
auto format(const Char* val, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
return formatter<basic_string_view<Char>, Char>::format(val, ctx);
|
|
}
|
|
};
|
|
|
|
// A formatter for types known only at run time such as variant alternatives.
|
|
//
|
|
// Usage:
|
|
// using variant = std::variant<int, std::string>;
|
|
// template <>
|
|
// struct formatter<variant>: dynamic_formatter<> {
|
|
// void format(buffer &buf, const variant &v, context &ctx) {
|
|
// visit([&](const auto &val) { format(buf, val, ctx); }, v);
|
|
// }
|
|
// };
|
|
template <typename Char = char> class dynamic_formatter {
|
|
private:
|
|
struct null_handler : internal::error_handler {
|
|
void on_align(align_t) {}
|
|
void on_plus() {}
|
|
void on_minus() {}
|
|
void on_space() {}
|
|
void on_hash() {}
|
|
};
|
|
|
|
public:
|
|
template <typename ParseContext>
|
|
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
format_str_ = ctx.begin();
|
|
// Checks are deferred to formatting time when the argument type is known.
|
|
internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
|
|
return parse_format_specs(ctx.begin(), ctx.end(), handler);
|
|
}
|
|
|
|
template <typename T, typename FormatContext>
|
|
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
handle_specs(ctx);
|
|
internal::specs_checker<null_handler> checker(
|
|
null_handler(),
|
|
internal::mapped_type_constant<T, FormatContext>::value);
|
|
checker.on_align(specs_.align);
|
|
switch (specs_.sign) {
|
|
case sign::none:
|
|
break;
|
|
case sign::plus:
|
|
checker.on_plus();
|
|
break;
|
|
case sign::minus:
|
|
checker.on_minus();
|
|
break;
|
|
case sign::space:
|
|
checker.on_space();
|
|
break;
|
|
}
|
|
if (specs_.alt) checker.on_hash();
|
|
if (specs_.precision >= 0) checker.end_precision();
|
|
using range = internal::output_range<typename FormatContext::iterator,
|
|
typename FormatContext::char_type>;
|
|
visit_format_arg(arg_formatter<range>(ctx, nullptr, &specs_),
|
|
internal::make_arg<FormatContext>(val));
|
|
return ctx.out();
|
|
}
|
|
|
|
private:
|
|
template <typename Context> void handle_specs(Context& ctx) {
|
|
internal::handle_dynamic_spec<internal::width_checker>(
|
|
specs_.width, specs_.width_ref, ctx);
|
|
internal::handle_dynamic_spec<internal::precision_checker>(
|
|
specs_.precision, specs_.precision_ref, ctx);
|
|
}
|
|
|
|
internal::dynamic_format_specs<Char> specs_;
|
|
const Char* format_str_;
|
|
};
|
|
|
|
template <typename Range, typename Char>
|
|
typename basic_format_context<Range, Char>::format_arg
|
|
basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
|
|
map_.init(args_);
|
|
format_arg arg = map_.find(name);
|
|
if (arg.type() == internal::type::none_type)
|
|
this->on_error("argument not found");
|
|
return arg;
|
|
}
|
|
|
|
template <typename Char, typename ErrorHandler>
|
|
FMT_CONSTEXPR void advance_to(
|
|
basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
|
|
ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
|
|
}
|
|
|
|
template <typename ArgFormatter, typename Char, typename Context>
|
|
struct format_handler : internal::error_handler {
|
|
using range = typename ArgFormatter::range;
|
|
|
|
format_handler(range r, basic_string_view<Char> str,
|
|
basic_format_args<Context> format_args,
|
|
internal::locale_ref loc)
|
|
: parse_context(str), context(r.begin(), format_args, loc) {}
|
|
|
|
void on_text(const Char* begin, const Char* end) {
|
|
auto size = internal::to_unsigned(end - begin);
|
|
auto out = context.out();
|
|
auto&& it = internal::reserve(out, size);
|
|
it = std::copy_n(begin, size, it);
|
|
context.advance_to(out);
|
|
}
|
|
|
|
void get_arg(int id) { arg = internal::get_arg(context, id); }
|
|
|
|
void on_arg_id() { get_arg(parse_context.next_arg_id()); }
|
|
void on_arg_id(int id) {
|
|
parse_context.check_arg_id(id);
|
|
get_arg(id);
|
|
}
|
|
void on_arg_id(basic_string_view<Char> id) { arg = context.arg(id); }
|
|
|
|
void on_replacement_field(const Char* p) {
|
|
advance_to(parse_context, p);
|
|
context.advance_to(
|
|
visit_format_arg(ArgFormatter(context, &parse_context), arg));
|
|
}
|
|
|
|
const Char* on_format_specs(const Char* begin, const Char* end) {
|
|
advance_to(parse_context, begin);
|
|
internal::custom_formatter<Context> f(parse_context, context);
|
|
if (visit_format_arg(f, arg)) return parse_context.begin();
|
|
basic_format_specs<Char> specs;
|
|
using internal::specs_handler;
|
|
using parse_context_t = basic_format_parse_context<Char>;
|
|
internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
|
|
specs_handler<parse_context_t, Context>(specs, parse_context, context),
|
|
arg.type());
|
|
begin = parse_format_specs(begin, end, handler);
|
|
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
|
advance_to(parse_context, begin);
|
|
context.advance_to(
|
|
visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
|
|
return begin;
|
|
}
|
|
|
|
basic_format_parse_context<Char> parse_context;
|
|
Context context;
|
|
basic_format_arg<Context> arg;
|
|
};
|
|
|
|
/** Formats arguments and writes the output to the range. */
|
|
template <typename ArgFormatter, typename Char, typename Context>
|
|
typename Context::iterator vformat_to(
|
|
typename ArgFormatter::range out, basic_string_view<Char> format_str,
|
|
basic_format_args<Context> args,
|
|
internal::locale_ref loc = internal::locale_ref()) {
|
|
format_handler<ArgFormatter, Char, Context> h(out, format_str, args, loc);
|
|
internal::parse_format_string<false>(format_str, h);
|
|
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_;
|
|
friend struct formatter<bytes>;
|
|
|
|
public:
|
|
explicit bytes(string_view data) : data_(data) {}
|
|
};
|
|
|
|
template <> struct formatter<bytes> {
|
|
template <typename ParseContext>
|
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
using handler_type = internal::dynamic_specs_handler<ParseContext>;
|
|
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
|
internal::type::string_type);
|
|
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
|
internal::check_string_type_spec(specs_.type, ctx.error_handler());
|
|
return it;
|
|
}
|
|
|
|
template <typename FormatContext>
|
|
auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
internal::handle_dynamic_spec<internal::width_checker>(
|
|
specs_.width, specs_.width_ref, ctx);
|
|
internal::handle_dynamic_spec<internal::precision_checker>(
|
|
specs_.precision, specs_.precision_ref, ctx);
|
|
using range_type =
|
|
internal::output_range<typename FormatContext::iterator, char>;
|
|
internal::basic_writer<range_type> writer(range_type(ctx.out()));
|
|
writer.write_bytes(b.data_, specs_);
|
|
return writer.out();
|
|
}
|
|
|
|
private:
|
|
internal::dynamic_format_specs<char> specs_;
|
|
};
|
|
|
|
template <typename It, typename Char> struct arg_join : internal::view {
|
|
It begin;
|
|
It end;
|
|
basic_string_view<Char> sep;
|
|
|
|
arg_join(It b, It e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
|
|
};
|
|
|
|
template <typename It, typename Char>
|
|
struct formatter<arg_join<It, Char>, Char>
|
|
: formatter<typename std::iterator_traits<It>::value_type, Char> {
|
|
template <typename FormatContext>
|
|
auto format(const arg_join<It, Char>& value, FormatContext& ctx)
|
|
-> decltype(ctx.out()) {
|
|
using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
|
|
auto it = value.begin;
|
|
auto out = ctx.out();
|
|
if (it != value.end) {
|
|
out = base::format(*it++, ctx);
|
|
while (it != value.end) {
|
|
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
|
ctx.advance_to(out);
|
|
out = base::format(*it++, ctx);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
};
|
|
|
|
/**
|
|
Returns an object that formats the iterator range `[begin, end)` with elements
|
|
separated by `sep`.
|
|
*/
|
|
template <typename It>
|
|
arg_join<It, char> join(It begin, It end, string_view sep) {
|
|
return {begin, end, sep};
|
|
}
|
|
|
|
template <typename It>
|
|
arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
|
|
return {begin, end, sep};
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Returns an object that formats `range` with elements separated by `sep`.
|
|
|
|
**Example**::
|
|
|
|
std::vector<int> v = {1, 2, 3};
|
|
fmt::print("{}", fmt::join(v, ", "));
|
|
// Output: "1, 2, 3"
|
|
|
|
``fmt::join`` applies passed format specifiers to the range elements::
|
|
|
|
fmt::print("{:02}", fmt::join(v, ", "));
|
|
// Output: "01, 02, 03"
|
|
\endrst
|
|
*/
|
|
template <typename Range>
|
|
arg_join<internal::iterator_t<const Range>, char> join(const Range& range,
|
|
string_view sep) {
|
|
return join(std::begin(range), std::end(range), sep);
|
|
}
|
|
|
|
template <typename Range>
|
|
arg_join<internal::iterator_t<const Range>, wchar_t> join(const Range& range,
|
|
wstring_view sep) {
|
|
return join(std::begin(range), std::end(range), sep);
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Converts *value* to ``std::string`` using the default format for type *T*.
|
|
|
|
**Example**::
|
|
|
|
#include <fmt/format.h>
|
|
|
|
std::string answer = fmt::to_string(42);
|
|
\endrst
|
|
*/
|
|
template <typename T> inline std::string to_string(const T& value) {
|
|
return format("{}", value);
|
|
}
|
|
|
|
/**
|
|
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
|
*/
|
|
template <typename T> inline std::wstring to_wstring(const T& value) {
|
|
return format(L"{}", value);
|
|
}
|
|
|
|
template <typename Char, std::size_t SIZE>
|
|
std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
|
|
return std::basic_string<Char>(buf.data(), buf.size());
|
|
}
|
|
|
|
template <typename Char>
|
|
typename buffer_context<Char>::iterator internal::vformat_to(
|
|
internal::buffer<Char>& buf, basic_string_view<Char> format_str,
|
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
using range = buffer_range<Char>;
|
|
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str),
|
|
args);
|
|
}
|
|
|
|
template <typename S, typename Char = char_t<S>,
|
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
inline typename buffer_context<Char>::iterator vformat_to(
|
|
internal::buffer<Char>& buf, const S& format_str,
|
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
return internal::vformat_to(buf, to_string_view(format_str), args);
|
|
}
|
|
|
|
template <typename S, typename... Args, std::size_t SIZE = inline_buffer_size,
|
|
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
|
inline typename buffer_context<Char>::iterator format_to(
|
|
basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) {
|
|
internal::check_format_string<Args...>(format_str);
|
|
using context = buffer_context<Char>;
|
|
return internal::vformat_to(buf, to_string_view(format_str),
|
|
make_format_args<context>(args...));
|
|
}
|
|
|
|
template <typename OutputIt, typename Char = char>
|
|
using format_context_t = basic_format_context<OutputIt, Char>;
|
|
|
|
template <typename OutputIt, typename Char = char>
|
|
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,
|
|
format_args_t<type_identity_t<OutputIt>, char_t<S>> args) {
|
|
using range = internal::output_range<OutputIt, char_t<S>>;
|
|
return vformat_to<arg_formatter<range>>(range(out),
|
|
to_string_view(format_str), args);
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Formats arguments, writes the result to the output iterator ``out`` and returns
|
|
the iterator past the end of the output range.
|
|
|
|
**Example**::
|
|
|
|
std::vector<char> out;
|
|
fmt::format_to(std::back_inserter(out), "{}", 42);
|
|
\endrst
|
|
*/
|
|
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) {
|
|
internal::check_format_string<Args...>(format_str);
|
|
using context = format_context_t<OutputIt, char_t<S>>;
|
|
return vformat_to(out, to_string_view(format_str),
|
|
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...));
|
|
}
|
|
|
|
template <typename Char>
|
|
inline std::basic_string<Char> internal::vformat(
|
|
basic_string_view<Char> format_str,
|
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
basic_memory_buffer<Char> buffer;
|
|
internal::vformat_to(buffer, format_str, args);
|
|
return to_string(buffer);
|
|
}
|
|
|
|
/**
|
|
Returns the number of characters in the output of
|
|
``format(format_str, args...)``.
|
|
*/
|
|
template <typename... Args>
|
|
inline std::size_t formatted_size(string_view format_str, const Args&... args) {
|
|
return format_to(internal::counting_iterator(), format_str, args...).count();
|
|
}
|
|
|
|
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
|
|
void vprint(std::FILE* f, basic_string_view<Char> format_str,
|
|
wformat_args args) {
|
|
wmemory_buffer buffer;
|
|
internal::vformat_to(buffer, format_str, args);
|
|
buffer.push_back(L'\0');
|
|
if (std::fputws(buffer.data(), f) == -1)
|
|
FMT_THROW(system_error(errno, "cannot write to file"));
|
|
}
|
|
|
|
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
|
|
void vprint(basic_string_view<Char> format_str, wformat_args args) {
|
|
vprint(stdout, format_str, args);
|
|
}
|
|
|
|
#if FMT_USE_USER_DEFINED_LITERALS
|
|
namespace internal {
|
|
|
|
# if FMT_USE_UDL_TEMPLATE
|
|
template <typename Char, Char... CHARS> class udl_formatter {
|
|
public:
|
|
template <typename... Args>
|
|
std::basic_string<Char> operator()(Args&&... args) const {
|
|
FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
|
|
FMT_CONSTEXPR_DECL bool invalid_format =
|
|
do_check_format_string<Char, error_handler, remove_cvref_t<Args>...>(
|
|
basic_string_view<Char>(s, sizeof...(CHARS)));
|
|
(void)invalid_format;
|
|
return format(s, std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
# else
|
|
template <typename Char> struct udl_formatter {
|
|
basic_string_view<Char> str;
|
|
|
|
template <typename... Args>
|
|
std::basic_string<Char> operator()(Args&&... args) const {
|
|
return format(str, std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
# endif // FMT_USE_UDL_TEMPLATE
|
|
|
|
template <typename Char> struct udl_arg {
|
|
basic_string_view<Char> str;
|
|
|
|
template <typename T> named_arg<T, Char> operator=(T&& value) const {
|
|
return {str, std::forward<T>(value)};
|
|
}
|
|
};
|
|
|
|
// Converts string literals to basic_string_view.
|
|
template <typename Char, size_t N>
|
|
FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
|
|
const Char (&s)[N]) {
|
|
// Remove trailing null character if needed. Won't be present if this is used
|
|
// with raw character array (i.e. not defined as a string).
|
|
return {s,
|
|
N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
|
|
}
|
|
|
|
// Converts string_view to basic_string_view.
|
|
template <typename Char>
|
|
FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
|
|
const std_string_view<Char>& s) {
|
|
return {s.data(), s.size()};
|
|
}
|
|
} // namespace internal
|
|
|
|
inline namespace literals {
|
|
# if FMT_USE_UDL_TEMPLATE
|
|
# pragma GCC diagnostic push
|
|
# if FMT_CLANG_VERSION
|
|
# pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
|
|
# endif
|
|
template <typename Char, Char... CHARS>
|
|
FMT_CONSTEXPR internal::udl_formatter<Char, CHARS...> operator""_format() {
|
|
return {};
|
|
}
|
|
# pragma GCC diagnostic pop
|
|
# else
|
|
/**
|
|
\rst
|
|
User-defined literal equivalent of :func:`fmt::format`.
|
|
|
|
**Example**::
|
|
|
|
using namespace fmt::literals;
|
|
std::string message = "The answer is {}"_format(42);
|
|
\endrst
|
|
*/
|
|
FMT_CONSTEXPR internal::udl_formatter<char> operator"" _format(const char* s,
|
|
std::size_t n) {
|
|
return {{s, n}};
|
|
}
|
|
FMT_CONSTEXPR internal::udl_formatter<wchar_t> operator"" _format(
|
|
const wchar_t* s, std::size_t n) {
|
|
return {{s, n}};
|
|
}
|
|
# endif // FMT_USE_UDL_TEMPLATE
|
|
|
|
/**
|
|
\rst
|
|
User-defined literal equivalent of :func:`fmt::arg`.
|
|
|
|
**Example**::
|
|
|
|
using namespace fmt::literals;
|
|
fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
|
|
\endrst
|
|
*/
|
|
FMT_CONSTEXPR internal::udl_arg<char> operator"" _a(const char* s,
|
|
std::size_t n) {
|
|
return {{s, n}};
|
|
}
|
|
FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
|
|
std::size_t n) {
|
|
return {{s, n}};
|
|
}
|
|
} // namespace literals
|
|
#endif // FMT_USE_USER_DEFINED_LITERALS
|
|
FMT_END_NAMESPACE
|
|
|
|
#define FMT_STRING_IMPL(s, ...) \
|
|
[] { \
|
|
/* Use a macro-like name to avoid shadowing warnings. */ \
|
|
struct FMT_COMPILE_STRING : fmt::compile_string { \
|
|
using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
|
|
FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \
|
|
operator fmt::basic_string_view<char_type>() const { \
|
|
return fmt::internal::compile_string_to_view<char_type>(s); \
|
|
} \
|
|
}; \
|
|
return FMT_COMPILE_STRING(); \
|
|
}()
|
|
|
|
/**
|
|
\rst
|
|
Constructs a compile-time format string from a string literal *s*.
|
|
|
|
**Example**::
|
|
|
|
// A compile-time error because 'd' is an invalid specifier for strings.
|
|
std::string s = format(FMT_STRING("{:d}"), "foo");
|
|
\endrst
|
|
*/
|
|
#define FMT_STRING(s) FMT_STRING_IMPL(s, )
|
|
|
|
#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
|
|
# define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
|
|
#endif
|
|
|
|
#ifdef FMT_HEADER_ONLY
|
|
# define FMT_FUNC inline
|
|
# include "format-inl.h"
|
|
#else
|
|
# define FMT_FUNC
|
|
#endif
|
|
|
|
#endif // FMT_FORMAT_H_
|