diff --git a/.gitignore b/.gitignore index 40ae7c56b..01e0a7232 100644 --- a/.gitignore +++ b/.gitignore @@ -97,6 +97,10 @@ local.properties .project .cproject +# vcpkg integration +/vcpkg/ +/vcpkg-ports/ + # ================== # diff --git a/apps/installer/includes/os_configs/windows.sh b/apps/installer/includes/os_configs/windows.sh index ae69640b0..6f49052a3 100644 --- a/apps/installer/includes/os_configs/windows.sh +++ b/apps/installer/includes/os_configs/windows.sh @@ -24,6 +24,6 @@ fi choco install -y --skip-checksums "${INSTALL_ARGS[@]}" cmake.install -y --installargs 'ADD_CMAKE_TO_PATH=System' choco install -y --skip-checksums "${INSTALL_ARGS[@]}" visualstudio2022-workload-nativedesktop -choco install -y --skip-checksums "${INSTALL_ARGS[@]}" openssl --force --version=3.6.1 +choco install -y --skip-checksums "${INSTALL_ARGS[@]}" openssl --force --version=3.6.2 choco install -y --skip-checksums "${INSTALL_ARGS[@]}" boost-msvc-14.3 --force --version=1.87.0 choco install -y --skip-checksums "${INSTALL_ARGS[@]}" mysql --force --version=8.4.6 diff --git a/data/sql/updates/db_characters/2026_04_12_00.sql b/data/sql/updates/db_characters/2026_04_12_00.sql new file mode 100644 index 000000000..92343a870 --- /dev/null +++ b/data/sql/updates/db_characters/2026_04_12_00.sql @@ -0,0 +1,6 @@ +-- DB update 2026_04_03_00 -> 2026_04_12_00 +-- Flush all respawn times to ensure a clean slate for the spawn group system. +-- Compat-mode creatures/GOs will respawn naturally on next grid load; +-- non-compat spawns will be handled by ProcessRespawns(). +TRUNCATE TABLE `creature_respawn`; +TRUNCATE TABLE `gameobject_respawn`; diff --git a/data/sql/updates/db_world/2026_04_11_00.sql b/data/sql/updates/db_world/2026_04_11_00.sql new file mode 100644 index 000000000..f49ecf7aa --- /dev/null +++ b/data/sql/updates/db_world/2026_04_11_00.sql @@ -0,0 +1,4 @@ +-- DB update 2026_04_10_02 -> 2026_04_11_00 +-- +UPDATE `waypoint_data` SET `delay` = 32000 WHERE `id` = 1281350 AND `point` = 1; +UPDATE `waypoint_data` SET `delay` = 38000 WHERE `id` = 1281350 AND `point` = 8; diff --git a/data/sql/updates/db_world/2026_04_12_00.sql b/data/sql/updates/db_world/2026_04_12_00.sql new file mode 100644 index 000000000..c67f3b686 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_12_00.sql @@ -0,0 +1,4 @@ +-- DB update 2026_04_11_00 -> 2026_04_12_00 +UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~ 0x00040000 WHERE `entry` = 24722; +UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~ (0x00000100|0x00000200) WHERE `entry` = 25552; +UPDATE `creature_template_addon` SET `auras` = '25900' WHERE `entry` IN (24722, 25552); diff --git a/data/sql/updates/db_world/2026_04_12_01.sql b/data/sql/updates/db_world/2026_04_12_01.sql new file mode 100644 index 000000000..555fd5aa5 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_12_01.sql @@ -0,0 +1,5 @@ +-- DB update 2026_04_12_00 -> 2026_04_12_01 +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 2795900) AND (`source_type` = 9) AND (`id` = 9); +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`event_param5`,`event_param6`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_param4`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(2795900,9,9,0,0,0,100,0,0,0,0,0,0,0,19,256,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Your Inner Turmoil - Actionlist - Remove Flags Immune To Players'); diff --git a/data/sql/updates/db_world/2026_04_12_02.sql b/data/sql/updates/db_world/2026_04_12_02.sql new file mode 100644 index 000000000..6cf5a6c7a --- /dev/null +++ b/data/sql/updates/db_world/2026_04_12_02.sql @@ -0,0 +1,154 @@ +-- DB update 2026_04_12_01 -> 2026_04_12_02 + +-- Armorer Orkuruk +DELETE FROM `waypoints` WHERE `entry` = 25274; +DELETE FROM `waypoint_data` WHERE (`id` IN (2527400)); +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(2527400, 1, 2761.85, 6187.82, 83.448, NULL, 0, 0, 0, 100, 0), +(2527400, 2, 2763.1, 6203.83, 83.4409, NULL, 0, 0, 0, 100, 0), +(2527400, 3, 2765.37, 6209.65, 83.4451, NULL, 0, 0, 0, 100, 0), +(2527400, 4, 2772.92, 6217.22, 83.4475, NULL, 0, 0, 0, 100, 0), +(2527400, 5, 2766.39, 6211.48, 83.437, NULL, 0, 0, 0, 100, 0), +(2527400, 6, 2762.96, 6202.62, 83.4409, NULL, 0, 0, 0, 100, 0), +(2527400, 7, 2762.31, 6187.53, 83.4546, NULL, 0, 0, 0, 100, 0), +(2527400, 8, 2765.22, 6178.21, 83.4565, NULL, 0, 0, 0, 100, 0), +(2527400, 9, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 10, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 11, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 12, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 13, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 14, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 15, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 16, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 17, 2764.09, 6182.25, 83.4576, NULL, 0, 0, 0, 100, 0), +(2527400, 18, 2762.03, 6188.66, 83.4549, NULL, 0, 0, 0, 100, 0), +(2527400, 19, 2763.21, 6203.82, 83.4422, NULL, 0, 0, 0, 100, 0), +(2527400, 20, 2766.87, 6211.5, 83.4422, NULL, 0, 0, 0, 100, 0), +(2527400, 21, 2773.88, 6218.09, 83.4497, NULL, 0, 0, 0, 100, 0), +(2527400, 22, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 23, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 24, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 25, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 26, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 27, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 28, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 29, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 30, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 31, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 32, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 33, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 34, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 35, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 36, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 37, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 38, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 39, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 40, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 41, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 42, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 43, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 44, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 45, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 46, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 47, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 48, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 49, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 50, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 51, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 52, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 53, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 54, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 55, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 56, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 57, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 58, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 59, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 60, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 61, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 62, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 63, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 64, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 65, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 66, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 67, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 68, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 69, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 70, 2766.07, 6210.93, 83.4391, NULL, 0, 0, 0, 100, 0), +(2527400, 71, 2762.53, 6207.16, 83.4206, NULL, 0, 0, 0, 100, 0), +(2527400, 72, 2762.16, 6187.79, 83.4531, NULL, 0, 0, 0, 100, 0), +(2527400, 73, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0), +(2527400, 74, 2761.85, 6187.82, 83.448, NULL, 0, 0, 0, 100, 0), +(2527400, 75, 2763.1, 6203.83, 83.4409, NULL, 0, 0, 0, 100, 0), +(2527400, 76, 2765.37, 6209.65, 83.4451, NULL, 0, 0, 0, 100, 0), +(2527400, 77, 2772.92, 6217.22, 83.4475, NULL, 0, 0, 0, 100, 0), +(2527400, 78, 2766.39, 6211.48, 83.437, NULL, 0, 0, 0, 100, 0), +(2527400, 79, 2762.96, 6202.62, 83.4409, NULL, 0, 0, 0, 100, 0), +(2527400, 80, 2762.31, 6187.53, 83.4546, NULL, 0, 0, 0, 100, 0), +(2527400, 81, 2765.22, 6178.21, 83.4565, NULL, 0, 0, 0, 100, 0), +(2527400, 82, 2762.24, 6188.36, 83.4575, NULL, 0, 0, 0, 100, 0), +(2527400, 83, 2763.33, 6204.33, 83.4432, NULL, 0, 0, 0, 100, 0), +(2527400, 84, 2766.61, 6211.59, 83.4381, NULL, 0, 0, 0, 100, 0), +(2527400, 85, 2773.5, 6217.87, 83.4484, NULL, 0, 0, 0, 100, 0), +(2527400, 86, 2765.7, 6210.09, 83.4438, NULL, 0, 0, 0, 100, 0), +(2527400, 87, 2762.77, 6202.1, 83.4395, NULL, 0, 0, 0, 100, 0), +(2527400, 88, 2762.54, 6187.41, 83.4578, NULL, 0, 0, 0, 100, 0), +(2527400, 89, 2765.46, 6178.01, 83.4598, NULL, 0, 0, 0, 100, 0); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 25274; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 25274); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(25274, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 232, 2527400, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Armorer Orkuruk - On Reset - Start Path 2527400'), +(25274, 0, 1, 2, 108, 0, 100, 0, 17, 2527400, 0, 0, 0, 0, 235, 15000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Armorer Orkuruk - On Point 17 of Path 2527400 Reached - Pause Movement'), +(25274, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 3.38169, 'Armorer Orkuruk - On Point 17 of Path 2527400 Reached - Set Orientation 3.38169'), +(25274, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2527400, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Armorer Orkuruk - On Point 17 of Path 2527400 Reached - Run Script'), +(25274, 0, 4, 5, 108, 0, 100, 0, 71, 2527400, 0, 0, 0, 0, 235, 15000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Armorer Orkuruk - On Point 71 of Path 2527400 Reached - Pause Movement'), +(25274, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 2.88465, 'Armorer Orkuruk - On Point 71 of Path 2527400 Reached - Set Orientation 2.88465'), +(25274, 0, 6, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2527400, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Armorer Orkuruk - On Point 71 of Path 2527400 Reached - Run Script'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2527400); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(2527400, 9, 0, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Armorer Orkuruk - Actionlist - Play Emote 5'), +(2527400, 9, 1, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 1, 0, 10000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Armorer Orkuruk - Actionlist - Say Line 0'); + +-- Innkeeper Thulfram +DELETE FROM `waypoints` WHERE `entry` = 774400; +DELETE FROM `waypoint_data` WHERE (`id` IN (774400)); +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(774400, 1, 399.622, -2118.75, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 2, 398.372, -2113.75, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 3, 398.122, -2112.5, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 4, 396.622, -2104.75, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 5, 396.19, -2103.14, 131.792, NULL, 0, 0, 0, 100, 0), +(774400, 6, 395.44, -2100.64, 131.792, NULL, 0, 0, 0, 100, 0), +(774400, 7, 394.19, -2094.14, 131.792, NULL, 0, 0, 0, 100, 0), +(774400, 8, 393.69, -2092.39, 131.792, NULL, 0, 0, 0, 100, 0), +(774400, 9, 393.223, -2090.41, 131.734, NULL, 0, 0, 0, 100, 0), +(774400, 10, 392.723, -2089.16, 131.734, NULL, 0, 0, 0, 100, 0), +(774400, 11, 392.473, -2087.91, 131.734, NULL, 0, 0, 0, 100, 0), +(774400, 12, 392.223, -2086.91, 131.734, NULL, 0, 0, 0, 100, 0), +(774400, 13, 390.917, -2081.43, 131.801, NULL, 0, 0, 0, 100, 0), +(774400, 14, 391.848, -2085.33, 131.707, NULL, 0, 0, 0, 100, 0), +(774400, 15, 392.434, -2087.05, 131.937, NULL, 0, 0, 0, 100, 0), +(774400, 16, 393.184, -2089.55, 131.937, NULL, 0, 0, 0, 100, 0), +(774400, 17, 393.434, -2090.8, 131.937, NULL, 0, 0, 0, 100, 0), +(774400, 18, 393.684, -2092.3, 131.937, NULL, 0, 0, 0, 100, 0), +(774400, 19, 393.934, -2093.8, 131.937, NULL, 0, 0, 0, 100, 0), +(774400, 20, 394.684, -2097.05, 131.937, NULL, 0, 0, 0, 100, 0), +(774400, 21, 395.69, -2100.93, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 22, 396.44, -2103.43, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 23, 396.69, -2104.43, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 24, 398.19, -2112.43, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 25, 398.19, -2113.18, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 26, 398.44, -2113.93, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 27, 399.44, -2118.68, 131.918, NULL, 0, 0, 0, 100, 0), +(774400, 28, 399.725, -2119.74, 131.668, NULL, 0, 0, 0, 100, 0); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 7744; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 7744); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(7744, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Innkeeper Thulfram - On Respawn - Set Reactstate Defensive'), +(7744, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 232, 774400, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Innkeeper Thulfram - On Respawn - Start Path 774400'), +(7744, 0, 2, 3, 62, 0, 100, 0, 345, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Innkeeper Thulfram - On Gossip Option 0 Selected - Close Gossip'), +(7744, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 134, 24751, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Innkeeper Thulfram - On Gossip Option 0 Selected - Invoker Cast \'Trick or Treat\''); diff --git a/data/sql/updates/db_world/2026_04_12_03.sql b/data/sql/updates/db_world/2026_04_12_03.sql new file mode 100644 index 000000000..378689b21 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_12_03.sql @@ -0,0 +1,3 @@ +-- DB update 2026_04_12_02 -> 2026_04_12_03 +-- Add SPELL_ATTR0_CU_DONT_BREAK_STEALTH (0x40) to Distract (1725) +UPDATE `spell_custom_attr` SET `attributes` = `attributes` | 0x40 WHERE `spell_id` = 1725; diff --git a/data/sql/updates/db_world/2026_04_12_04.sql b/data/sql/updates/db_world/2026_04_12_04.sql new file mode 100644 index 000000000..0057f570c --- /dev/null +++ b/data/sql/updates/db_world/2026_04_12_04.sql @@ -0,0 +1,3 @@ +-- DB update 2026_04_12_03 -> 2026_04_12_04 +-- Remove Bone Shield spell script (double charge consumption bug) +DELETE FROM `spell_script_names` WHERE `spell_id` = 49222 AND `ScriptName` = 'spell_dk_bone_shield'; diff --git a/data/sql/updates/db_world/2026_04_12_05.sql b/data/sql/updates/db_world/2026_04_12_05.sql new file mode 100644 index 000000000..368c905b7 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_12_05.sql @@ -0,0 +1,69 @@ +-- DB update 2026_04_12_04 -> 2026_04_12_05 +-- +DROP TABLE IF EXISTS `spawn_group_template`; +CREATE TABLE `spawn_group_template` ( + `groupId` int(10) unsigned NOT NULL, + `groupName` varchar(100) NOT NULL, + `groupFlags` int(10) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`groupId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +DROP TABLE IF EXISTS `spawn_group`; +CREATE TABLE `spawn_group` ( + `groupId` int(10) unsigned NOT NULL, + `spawnType` tinyint(3) unsigned NOT NULL, + `spawnId` int(10) unsigned NOT NULL, + PRIMARY KEY (`groupId`,`spawnType`,`spawnId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Insert default spawn groups +DELETE FROM `spawn_group_template` WHERE `groupId` IN (0, 1); +INSERT INTO `spawn_group_template` (`groupId`, `groupName`, `groupFlags`) VALUES +(0, 'Default Group', 0x01), -- SYSTEM (dynamic respawn by default) +(1, 'Legacy Group', 0x03); -- SYSTEM | COMPATIBILITY_MODE + +-- Register spawn group commands +DELETE FROM `command` WHERE `name` IN ('list respawns', 'npc spawngroup', 'npc despawngroup', 'gobject spawngroup', 'gobject despawngroup', 'reload spawn_group'); +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('list respawns', 2, 'Syntax: .list respawns\r\nShows all pending creature and gameobject respawns on the current map.'), +('npc spawngroup', 3, 'Syntax: .npc spawngroup #groupId\r\nSpawns all creatures in the given spawn group.'), +('npc despawngroup', 3, 'Syntax: .npc despawngroup #groupId\r\nDespawns all creatures in the given spawn group.'), +('gobject spawngroup', 3, 'Syntax: .gobject spawngroup #groupId\r\nSpawns all gameobjects in the given spawn group.'), +('gobject despawngroup', 3, 'Syntax: .gobject despawngroup #groupId\r\nDespawns all gameobjects in the given spawn group.'), +('reload spawn_group', 3, 'Syntax: .reload spawn_group\r\nReloads the spawn_group_template and spawn_group tables.'); + +-- Spawn group localized strings +DELETE FROM `acore_string` WHERE `entry` BETWEEN 35411 AND 35424; +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(35411, 'Cannot manually spawn system group {} ({}).', '시스템 그룹 {} ({})을(를) 수동으로 생성할 수 없습니다.', 'Impossible de faire apparaître manuellement le groupe système {} ({}).', 'Systemgruppe {} ({}) kann nicht manuell gespawnt werden.', '无法手动生成系统组 {} ({})。', '無法手動生成系統組 {} ({})。', 'No se puede generar manualmente el grupo del sistema {} ({}).', 'No se puede generar manualmente el grupo del sistema {} ({}).', 'Невозможно вручную создать системную группу {} ({}).'), +(35412, 'Spawn group {} ({}) spawned successfully.', '스폰 그룹 {} ({})이(가) 성공적으로 생성되었습니다.', 'Le groupe d''apparition {} ({}) a été créé avec succès.', 'Spawngruppe {} ({}) erfolgreich erstellt.', '刷新组 {} ({}) 已成功生成。', '重生組 {} ({}) 已成功生成。', 'Grupo de aparición {} ({}) generado correctamente.', 'Grupo de aparición {} ({}) generado correctamente.', 'Группа спавна {} ({}) успешно создана.'), +(35413, 'Failed to spawn group {} ({}).', '스폰 그룹 {} ({}) 생성에 실패했습니다.', 'Échec de l''apparition du groupe {} ({}).', 'Spawngruppe {} ({}) konnte nicht erstellt werden.', '生成组 {} ({}) 失败。', '生成組 {} ({}) 失敗。', 'Error al generar el grupo {} ({}).', 'Error al generar el grupo {} ({}).', 'Не удалось создать группу спавна {} ({}).'), +(35414, 'Cannot manually despawn system group {} ({}).', '시스템 그룹 {} ({})을(를) 수동으로 제거할 수 없습니다.', 'Impossible de retirer manuellement le groupe système {} ({}).', 'Systemgruppe {} ({}) kann nicht manuell entfernt werden.', '无法手动移除系统组 {} ({})。', '無法手動移除系統組 {} ({})。', 'No se puede eliminar manualmente el grupo del sistema {} ({}).', 'No se puede eliminar manualmente el grupo del sistema {} ({}).', 'Невозможно вручную удалить системную группу {} ({}).'), +(35415, 'Spawn group {} ({}) despawned successfully.', '스폰 그룹 {} ({})이(가) 성공적으로 제거되었습니다.', 'Le groupe d''apparition {} ({}) a été retiré avec succès.', 'Spawngruppe {} ({}) erfolgreich entfernt.', '刷新组 {} ({}) 已成功移除。', '重生組 {} ({}) 已成功移除。', 'Grupo de aparición {} ({}) eliminado correctamente.', 'Grupo de aparición {} ({}) eliminado correctamente.', 'Группа спавна {} ({}) успешно удалена.'), +(35416, 'Failed to despawn group {} ({}).', '스폰 그룹 {} ({}) 제거에 실패했습니다.', 'Échec du retrait du groupe {} ({}).', 'Spawngruppe {} ({}) konnte nicht entfernt werden.', '移除组 {} ({}) 失败。', '移除組 {} ({}) 失敗。', 'Error al eliminar el grupo {} ({}).', 'Error al eliminar el grupo {} ({}).', 'Не удалось удалить группу спавна {} ({}).'), +(35419, 'Pending creature respawns on map {} (instance {}):', '맵 {} (인스턴스 {})의 대기 중인 생물 리스폰:', 'Réapparitions de créatures en attente sur la carte {} (instance {}) :', 'Ausstehende Kreatur-Respawns auf Karte {} (Instanz {}):', '地图 {} (副本 {}) 上待处理的生物重生:', '地圖 {} (副本 {}) 上待處理的生物重生:', 'Reapariciones de criaturas pendientes en mapa {} (instancia {}):', 'Reapariciones de criaturas pendientes en mapa {} (instancia {}):', 'Ожидающие респауны существ на карте {} (инстанс {}):'), +(35420, ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}초', ' DB GUID : {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}с'), +(35421, 'Pending gameobject respawns:', '대기 중인 게임오브젝트 리스폰:', 'Réapparitions de game objects en attente :', 'Ausstehende Spielobjekt-Respawns:', '待处理的游戏对象重生:', '待處理的遊戲物件重生:', 'Reapariciones de objetos pendientes:', 'Reapariciones de objetos pendientes:', 'Ожидающие респауны игровых объектов:'), +(35422, ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}초', ' DB GUID : {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}с'), +(35423, ' ... and more (limited to 50)', ' ... 그 외 다수 (50개로 제한)', ' ... et plus (limité à 50)', ' ... und mehr (auf 50 begrenzt)', ' ... 以及更多(限制为50)', ' ... 以及更多(限制為50)', ' ... y más (limitado a 50)', ' ... y más (limitado a 50)', ' ... и ещё (ограничено 50)'), +(35424, 'Spawn group {} not found.', '스폰 그룹 {}을(를) 찾을 수 없습니다.', 'Groupe d''apparition {} introuvable.', 'Spawngruppe {} nicht gefunden.', '刷新组 {} 未找到。', '重生組 {} 未找到。', 'Grupo de aparición {} no encontrado.', 'Grupo de aparición {} no encontrado.', 'Группа спавна {} не найдена.'); + +-- Register pool debug commands +DELETE FROM `command` WHERE `name` IN ('pool info', 'pool lookup'); +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('pool info', 2, 'Syntax: .pool info #poolId\r\nShows pool details: description, max active, all creature/gameobject/sub-pool members with active/inactive status.'), +('pool lookup', 2, 'Syntax: .pool lookup\r\nTarget a creature or stand near a gameobject to find which pool it belongs to and its current spawn status.'); + +-- Pool debug command localized strings +DELETE FROM `acore_string` WHERE `entry` BETWEEN 35425 AND 35434; +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(35425, 'Pool {} does not exist.', '풀 {}이(가) 존재하지 않습니다.', 'Le pool {} n''existe pas.', 'Pool {} existiert nicht.', '池 {} 不存在。', '池 {} 不存在。', 'El pool {} no existe.', 'El pool {} no existe.', 'Пул {} не существует.'), +(35426, '=== Pool {} | {} | max active: {} | spawned: {} ===', '=== 풀 {} | {} | 최대 활성: {} | 생성됨: {} ===', '=== Pool {} | {} | max actifs : {} | apparus : {} ===', '=== Pool {} | {} | max. aktiv: {} | gespawnt: {} ===', '=== 池 {} | {} | 最大活跃: {} | 已生成: {} ===', '=== 池 {} | {} | 最大活躍: {} | 已生成: {} ===', '=== Pool {} | {} | máx activos: {} | generados: {} ===', '=== Pool {} | {} | máx activos: {} | generados: {} ===', '=== Пул {} | {} | макс. активных: {} | создано: {} ==='), +(35427, ' {} ({} total):', ' {} (총 {}):', ' {} ({} au total) :', ' {} ({} insgesamt):', ' {}(共 {}):', ' {}(共 {}):', ' {} ({} en total):', ' {} ({} en total):', ' {} (всего {}):'), +(35428, ' [{}] GUID {} | {} (entry {}) | map {} ({}, {}, {})', ' [{}] GUID {} | {} (항목 {}) | 맵 {} ({}, {}, {})', ' [{}] GUID {} | {} (entrée {}) | carte {} ({}, {}, {})', ' [{}] GUID {} | {} (Eintrag {}) | Karte {} ({}, {}, {})', ' [{}] GUID {} | {} (条目 {}) | 地图 {} ({}, {}, {})', ' [{}] GUID {} | {} (條目 {}) | 地圖 {} ({}, {}, {})', ' [{}] GUID {} | {} (entrada {}) | mapa {} ({}, {}, {})', ' [{}] GUID {} | {} (entrada {}) | mapa {} ({}, {}, {})', ' [{}] GUID {} | {} (запись {}) | карта {} ({}, {}, {})'), +(35429, ' Sub-pools ({} total):', ' 하위 풀 (총 {}):', ' Sous-pools ({} au total) :', ' Unter-Pools ({} insgesamt):', ' 子池(共 {}):', ' 子池(共 {}):', ' Sub-pools ({} en total):', ' Sub-pools ({} en total):', ' Под-пулы (всего {}):'), +(35430, ' [{}] Pool {} | {}', ' [{}] 풀 {} | {}', ' [{}] Pool {} | {}', ' [{}] Pool {} | {}', ' [{}] 池 {} | {}', ' [{}] 池 {} | {}', ' [{}] Pool {} | {}', ' [{}] Pool {} | {}', ' [{}] Пул {} | {}'), +(35431, '{} (GUID {}) is in pool {}. Status: {}', '{} (GUID {})이(가) 풀 {}에 있습니다. 상태: {}', '{} (GUID {}) est dans le pool {}. Statut : {}', '{} (GUID {}) ist in Pool {}. Status: {}', '{} (GUID {}) 在池 {} 中。状态:{}', '{} (GUID {}) 在池 {} 中。狀態:{}', '{} (GUID {}) está en el pool {}. Estado: {}', '{} (GUID {}) está en el pool {}. Estado: {}', '{} (GUID {}) в пуле {}. Статус: {}'), +(35432, '{} (GUID {}) is not in any pool.', '{} (GUID {})은(는) 어떤 풀에도 속해 있지 않습니다.', '{} (GUID {}) n''est dans aucun pool.', '{} (GUID {}) ist in keinem Pool.', '{} (GUID {}) 不在任何池中。', '{} (GUID {}) 不在任何池中。', '{} (GUID {}) no está en ningún pool.', '{} (GUID {}) no está en ningún pool.', '{} (GUID {}) не в пуле.'), +(35433, ' Use ''.pool info {}'' for full details.', ' 전체 세부정보는 ''.pool info {}''를 사용하세요.', ' Utilisez ''.pool info {}'' pour les détails complets.', ' Verwenden Sie ''.pool info {}'' für vollständige Details.', ' 使用".pool info {}"查看完整详情。', ' 使用「.pool info {}」查看完整詳情。', ' Use ''.pool info {}'' para ver los detalles completos.', ' Use ''.pool info {}'' para ver los detalles completos.', ' Используйте ''.pool info {}'' для полной информации.'), +(35434, 'No creature selected and no nearby gameobject found. Target a creature or stand near a gameobject.', '선택된 생물이 없고 근처 게임오브젝트도 없습니다. 생물을 대상으로 지정하거나 게임오브젝트 근처에 서세요.', 'Aucune créature sélectionnée et aucun objet de jeu à proximité. Ciblez une créature ou approchez-vous d''un objet de jeu.', 'Keine Kreatur ausgewählt und kein Spielobjekt in der Nähe gefunden. Wählen Sie eine Kreatur oder stehen Sie neben einem Spielobjekt.', '未选择生物且附近没有游戏对象。请选择一个生物或站在游戏对象旁边。', '未選擇生物且附近沒有遊戲物件。請選擇一個生物或站在遊戲物件旁邊。', 'No hay criatura seleccionada ni objeto cercano. Seleccione una criatura o acérquese a un objeto.', 'No hay criatura seleccionada ni objeto cercano. Seleccione una criatura o acérquese a un objeto.', 'Существо не выбрано и рядом нет игровых объектов. Выберите существо или встаньте рядом с объектом.'); diff --git a/data/sql/updates/db_world/2026_04_12_06.sql b/data/sql/updates/db_world/2026_04_12_06.sql new file mode 100644 index 000000000..d8b93cd5d --- /dev/null +++ b/data/sql/updates/db_world/2026_04_12_06.sql @@ -0,0 +1,31 @@ +-- DB update 2026_04_12_05 -> 2026_04_12_06 + +-- Shadron +UPDATE `creature_template` SET `lootid` = 30451 WHERE (`entry` = 30451); +UPDATE `creature_template` SET `lootid` = 31520 WHERE (`entry` = 31520); + +DELETE FROM `creature_loot_template` WHERE (`Entry` IN (30451, 31520)) AND (`Item` IN (47241)); +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(30451, 47241, 0, 100, 0, 1, 0, 1, 1, 'Emblem of Triumph'), +(31520, 47241, 0, 100, 0, 1, 0, 1, 1, 'Emblem of Triumph'); + +-- Vesperon +UPDATE `creature_template` SET `lootid` = 30449 WHERE (`entry` = 30449); +UPDATE `creature_template` SET `lootid` = 31535 WHERE (`entry` = 31535); + +DELETE FROM `creature_loot_template` WHERE (`Entry` IN (30449, 31535)) AND (`Item` IN (47241)); +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(30449, 47241, 0, 100, 0, 1, 0, 1, 1, 'Emblem of Triumph'), +(31535, 47241, 0, 100, 0, 1, 0, 1, 1, 'Emblem of Triumph'); + +-- Tenebron +UPDATE `creature_template` SET `lootid` = 30452 WHERE (`entry` = 30452); +UPDATE `creature_template` SET `lootid` = 31534 WHERE (`entry` = 31534); + +DELETE FROM `creature_loot_template` WHERE (`Entry` IN (30452, 31534)) AND (`Item` IN (47241)); +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(30452, 47241, 0, 100, 0, 1, 0, 1, 1, 'Emblem of Triumph'), +(31534, 47241, 0, 100, 0, 1, 0, 1, 1, 'Emblem of Triumph'); + +-- Reduce Emblem of Triump (Sartharion 10M). +UPDATE `creature_loot_template` SET `MinCount` = 1, `MaxCount` = 1 WHERE (`Entry` = 28860) AND (`Item` IN (47241)); diff --git a/data/sql/updates/db_world/2026_04_14_00.sql b/data/sql/updates/db_world/2026_04_14_00.sql new file mode 100644 index 000000000..fe06aa17d --- /dev/null +++ b/data/sql/updates/db_world/2026_04_14_00.sql @@ -0,0 +1,42 @@ +-- DB update 2026_04_12_06 -> 2026_04_14_00 +-- Move npc_vh_sinclari (30658) and go_vh_activation_crystal (193611) from C++ to DB + +UPDATE `creature_template` SET `gossip_menu_id` = 9997, `AIName` = 'SmartAI', `ScriptName` = '' WHERE `entry` = 30658; + +-- (9997, 13853) already exists for NOT_STARTED +DELETE FROM `gossip_menu` WHERE `MenuID` = 9997 AND `TextID` IN (14271, 13910); +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(9997, 14271), +(9997, 13910); + +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 9997 AND `OptionID` = 1; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) +VALUES (9997, 1, 0, 'I''m not fighting, so send me in now!', 33204, 1, 1, 0, 0, 0, 0, '', 0, 0); + +-- Gossip menu text conditions (CONDITION_INSTANCE_INFO on DATA_ENCOUNTER_STATUS) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 9997; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 9997, 13853, 0, 0, 13, 0, 30, 0, 0, 0, 0, 0, '', 'Sinclari - Show text 13853 when encounter NOT_STARTED'), +(14, 9997, 14271, 0, 0, 13, 0, 30, 1, 0, 0, 0, 0, '', 'Sinclari - Show text 14271 when encounter IN_PROGRESS'), +(14, 9997, 13910, 0, 0, 13, 0, 30, 2, 0, 0, 0, 0, '', 'Sinclari - Show text 13910 when encounter DONE'); + +-- Gossip option conditions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 9997; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 9997, 0, 0, 0, 13, 0, 30, 0, 0, 0, 0, 0, '', 'Sinclari - Show option 0 (start) only when NOT_STARTED'), +(15, 9997, 1, 0, 0, 13, 0, 30, 1, 0, 0, 0, 0, '', 'Sinclari - Show option 1 (late join) only when IN_PROGRESS'); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 30658 AND `source_type` = 0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(30658, 0, 0, 0, 62, 0, 100, 0, 9998, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sinclari - On Gossip Select (Start) - Close Gossip'), +(30658, 0, 1, 0, 62, 0, 100, 0, 9998, 0, 0, 0, 0, 0, 223, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sinclari - On Gossip Select (Start) - DoAction ACTION_START_INSTANCE on Instance'), +(30658, 0, 2, 0, 62, 0, 100, 0, 9997, 1, 0, 0, 0, 0, 62, 608, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 1830.531006, 803.939758, 44.340508, 6.281611, 'Sinclari - On Gossip Select (Late Join) - Teleport Player'), +(30658, 0, 3, 0, 62, 0, 100, 0, 9997, 1, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sinclari - On Gossip Select (Late Join) - Close Gossip'); + +-- Activation Crystal: invoker casts spell 57804 → SPELL_EFFECT_SEND_EVENT(20001) → ProcessEvent +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI', `ScriptName` = '' WHERE `entry` = 193611; + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 193611 AND `source_type` = 1; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(193611, 1, 0, 0, 64, 0, 100, 0, 0, 0, 0, 0, 0, 0, 134, 57804, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Activation Crystal - On Gossip Hello - Invoker Cast Crystal Activation'), +(193611, 1, 1, 0, 64, 0, 100, 0, 0, 0, 0, 0, 0, 0, 105, 16, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Activation Crystal - On Gossip Hello - Add GO_FLAG_NOT_SELECTABLE'); diff --git a/data/sql/updates/db_world/2026_04_15_00.sql b/data/sql/updates/db_world/2026_04_15_00.sql new file mode 100644 index 000000000..3d5f86e22 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_15_00.sql @@ -0,0 +1,3 @@ +-- DB update 2026_04_14_00 -> 2026_04_15_00 +-- Erekem Guards: add UNIT_FLAG_IMMUNE_TO_PC (0x100) +UPDATE `creature_template` SET `unit_flags` = `unit_flags` | 0x100 WHERE `entry` IN (29395, 31513); diff --git a/data/sql/updates/db_world/2026_04_15_01.sql b/data/sql/updates/db_world/2026_04_15_01.sql new file mode 100644 index 000000000..dc2541de6 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_15_01.sql @@ -0,0 +1,7 @@ +-- DB update 2026_04_15_00 -> 2026_04_15_01 + +-- Set respawn time on 0 (17 - Force Despawn). +UPDATE `smart_scripts` SET `action_param2` = 0 WHERE (`entryorguid` = 29686) AND (`source_type` = 0) AND (`id` IN (7)); + +-- Set Text Type on 12 (say). +UPDATE `creature_text` SET `Type` = 12 WHERE (`CreatureID` = 29686) AND (`GroupID` = 0); diff --git a/data/sql/updates/db_world/2026_04_16_00.sql b/data/sql/updates/db_world/2026_04_16_00.sql new file mode 100644 index 000000000..fae06a234 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_16_00.sql @@ -0,0 +1,11 @@ +-- DB update 2026_04_15_01 -> 2026_04_16_00 +-- +-- Ball of Flames Proc (ICC Prince Valanar): +-- ensure the spell script is registered so the new AuraScript +-- decrements a stack each time the ball hits a player. +DELETE FROM `spell_script_names` WHERE `spell_id` IN (71756, 72782, 72783, 72784) AND `ScriptName`='spell_taldaram_ball_of_inferno_flame'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(71756, 'spell_taldaram_ball_of_inferno_flame'), +(72782, 'spell_taldaram_ball_of_inferno_flame'), +(72783, 'spell_taldaram_ball_of_inferno_flame'), +(72784, 'spell_taldaram_ball_of_inferno_flame'); diff --git a/data/sql/updates/db_world/2026_04_17_00.sql b/data/sql/updates/db_world/2026_04_17_00.sql new file mode 100644 index 000000000..b2d6e71c9 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_17_00.sql @@ -0,0 +1,4 @@ +-- DB update 2026_04_16_00 -> 2026_04_17_00 +-- +UPDATE `creature_template` SET `flags_extra` = `flags_extra` & ~0x1 + WHERE `entry` IN (30449, 30451, 30452, 31520, 31534, 31535); diff --git a/data/sql/updates/db_world/2026_04_17_01.sql b/data/sql/updates/db_world/2026_04_17_01.sql new file mode 100644 index 000000000..5391a72a6 --- /dev/null +++ b/data/sql/updates/db_world/2026_04_17_01.sql @@ -0,0 +1,6 @@ +-- DB update 2026_04_17_00 -> 2026_04_17_01 +-- Onslaught Knight (27206): Add On Evade -> Despawn +-- Prevents ejected riders from piling up when warhorses respawn +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 27206) AND (`source_type` = 0) AND (`id` IN (2)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(27206, 0, 2, 0, 7, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Knight - On Evade - Despawn Instant'); diff --git a/deps/PackageList.txt b/deps/PackageList.txt index 70e9f695c..a0a57b6c3 100644 --- a/deps/PackageList.txt +++ b/deps/PackageList.txt @@ -80,7 +80,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse {fmt} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams. https://github.com/fmtlib/fmt - Version: 7.1.3 + Version: 12.1.0 https://github.com/fmtlib/fmt/releases/tag/12.1.0 fkYAML (A C++ header-only YAML library) https://github.com/fktn-k/fkYAML diff --git a/deps/fmt/CMakeLists.txt b/deps/fmt/CMakeLists.txt index a20b7788e..fb3a403c6 100644 --- a/deps/fmt/CMakeLists.txt +++ b/deps/fmt/CMakeLists.txt @@ -8,67 +8,31 @@ # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -include(CheckSymbolExists) +add_library(fmt STATIC) -set(strtod_l_headers stdlib.h) - -if (APPLE) - set(strtod_l_headers ${strtod_l_headers} xlocale.h) -endif () - -if(WIN32) - check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) -else() - check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) -endif() - -function(add_headers VAR) - set(headers ${${VAR}}) - foreach (header ${ARGN}) - set(headers ${headers} include/fmt/${header}) - endforeach() - set(${VAR} ${headers} PARENT_SCOPE) -endfunction() - -# Define the fmt library, its includes and the needed defines. -add_headers(FMT_HEADERS - args.h - chrono.h - color.h - compile.h - core.h - format.h - format-inl.h - os.h - ostream.h - printf.h - ranges.h - std.h - xchar.h) - -set(FMT_SOURCES +target_sources(fmt + PRIVATE + include/fmt/args.h + include/fmt/base.h + include/fmt/chrono.h + include/fmt/color.h + include/fmt/compile.h + include/fmt/core.h + include/fmt/format.h + include/fmt/format-inl.h + include/fmt/os.h + include/fmt/ostream.h + include/fmt/printf.h + include/fmt/ranges.h + include/fmt/std.h + include/fmt/xchar.h src/format.cc src/os.cc) -add_library(fmt STATIC ${FMT_SOURCES} ${FMT_HEADERS}) - -if (HAVE_STRTOD_L) - target_compile_definitions(fmt - PUBLIC - FMT_LOCALE) -endif() - -# Define FMT_CONSTEVAL for current using in c++20 -target_compile_definitions(fmt - PUBLIC - FMT_CONSTEVAL=) - target_include_directories(fmt PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - + include) target_link_libraries(fmt PRIVATE acore-dependency-interface) @@ -76,4 +40,4 @@ target_link_libraries(fmt set_target_properties(fmt PROPERTIES FOLDER - "deps") \ No newline at end of file + "deps") diff --git a/deps/fmt/ChangeLog.md b/deps/fmt/ChangeLog.md new file mode 100644 index 000000000..e96f9df8c --- /dev/null +++ b/deps/fmt/ChangeLog.md @@ -0,0 +1,3299 @@ +# 12.1.0 - 2025-10-29 + +- Optimized `buffer::append`, resulting in up to ~16% improvement on spdlog + benchmarks (https://github.com/fmtlib/fmt/pull/4541). Thanks @fyrsta7. + +- Worked around an ABI incompatibility in `std::locale_ref` between clang and + gcc (https://github.com/fmtlib/fmt/issues/4573). + +- Made `std::variant` and `std::expected` formatters work with `format_as` + (https://github.com/fmtlib/fmt/issues/4574, + https://github.com/fmtlib/fmt/pull/4575). Thanks @phprus. + +- Made `fmt::join` work with C++ modules + (https://github.com/fmtlib/fmt/issues/4379, + https://github.com/fmtlib/fmt/pull/4577). Thanks @Arghnews. + +- Exported `fmt::is_compiled_string` and `operator""_cf` from the module + (https://github.com/fmtlib/fmt/pull/4544). Thanks @CrackedMatter. + +- Fixed a compatibility issue with C++ modules in clang + (https://github.com/fmtlib/fmt/pull/4548). Thanks @tsarn. + +- Added support for cv-qualified types to the `std::optional` formatter + (https://github.com/fmtlib/fmt/issues/4561, + https://github.com/fmtlib/fmt/pull/4562). Thanks @OleksandrKvl. + +- Added demangling support (used in exception and `std::type_info` formatters) + for libc++ and clang-cl + (https://github.com/fmtlib/fmt/issues/4542, + https://github.com/fmtlib/fmt/pull/4560, + https://github.com/fmtlib/fmt/issues/4568, + https://github.com/fmtlib/fmt/pull/4571). + Thanks @FatihBAKIR and @rohitsutreja. + +- Switched to global `malloc`/`free` to enable allocator customization + (https://github.com/fmtlib/fmt/issues/4569, + https://github.com/fmtlib/fmt/pull/4570). Thanks @rohitsutreja. + +- Made the `FMT_USE_CONSTEVAL` macro configurable by users + (https://github.com/fmtlib/fmt/pull/4546). Thanks @SnapperTT. + +- Fixed compilation with locales disabled in the header-only mode + (https://github.com/fmtlib/fmt/issues/4550). + +- Fixed compilation with clang 21 and `-std=c++20` + (https://github.com/fmtlib/fmt/issues/4552). + +- Fixed a dynamic linking issue with clang-cl + (https://github.com/fmtlib/fmt/issues/4576, + https://github.com/fmtlib/fmt/pull/4584). Thanks @FatihBAKIR. + +- Fixed a warning suppression leakage on gcc + (https://github.com/fmtlib/fmt/pull/4588). Thanks @ZedThree. + +- Made more internal color APIs `constexpr` + (https://github.com/fmtlib/fmt/pull/4581). Thanks @ishani. + +- Fixed compatibility with clang as a host compiler for NVCC + (https://github.com/fmtlib/fmt/pull/4564). Thanks @valgur. + +- Fixed various warnings and lint issues + (https://github.com/fmtlib/fmt/issues/4565, + https://github.com/fmtlib/fmt/pull/4572, + https://github.com/fmtlib/fmt/pull/4557). + Thanks @LiangHuDream and @teruyamato0731. + +- Improved documentation + (https://github.com/fmtlib/fmt/issues/4549, + https://github.com/fmtlib/fmt/pull/4551, + https://github.com/fmtlib/fmt/issues/4566, + https://github.com/fmtlib/fmt/pull/4567, + https://github.com/fmtlib/fmt/pull/4578,). + Thanks @teruyamato0731, @petersteneteg and @zimmerman-dev. + +# 12.0.0 - 2025-09-17 + +- Optimized the default floating point formatting + (https://github.com/fmtlib/fmt/issues/3675, + https://github.com/fmtlib/fmt/issues/4516). In particular, formatting a + `double` with format string compilation into a stack allocated buffer is + more than 60% faster in version 12.0 compared to 11.2 according to + [dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark): + + ``` + Function Time (ns) Speedup + fmt11 34.471 1.00x + fmt12 21.000 1.64x + ``` + + + +- Added `constexpr` support to `fmt::format`. For example: + + ```c++ + #include + + using namespace fmt::literals; + std::string s = fmt::format(""_cf, 42); + ``` + + now works at compile time provided that `std::string` supports `constexpr` + (https://github.com/fmtlib/fmt/issues/3403, + https://github.com/fmtlib/fmt/pull/4456). Thanks @msvetkin. + +- Added `FMT_STATIC_FORMAT` that allows formatting into a string of the exact + required size at compile time. + + For example: + + ```c++ + #include + + constexpr auto s = FMT_STATIC_FORMAT("{}", 42); + ``` + + compiles to just + + ```s + __ZL1s: + .asciiz "42" + ``` + + It can be accessed as a C string with `s.c_str()` or as a string view with + `s.str()`. + +- Improved C++20 module support + (https://github.com/fmtlib/fmt/pull/4451, + https://github.com/fmtlib/fmt/pull/4459, + https://github.com/fmtlib/fmt/pull/4476, + https://github.com/fmtlib/fmt/pull/4488, + https://github.com/fmtlib/fmt/issues/4491, + https://github.com/fmtlib/fmt/pull/4495). + Thanks @arBmind, @tkhyn, @Mishura4, @anonymouspc and @autoantwort. + +- Switched to using estimated display width in precision. For example: + + ```c++ + fmt::print("|{:.4}|\n|1234|\n", "🐱🐱🐱"); + ``` + + prints + + ![](https://github.com/user-attachments/assets/6c4446b3-13eb-43b9-b74a-b4543540ad6a) + + because `🐱` has an estimated width of 2 + (https://github.com/fmtlib/fmt/issues/4272, + https://github.com/fmtlib/fmt/pull/4443, + https://github.com/fmtlib/fmt/pull/4475). + Thanks @nikhilreddydev and @localspook. + +- Fix interaction between debug presentation, precision, and width for strings + (https://github.com/fmtlib/fmt/pull/4478). Thanks @localspook. + +- Implemented allocator propagation on `basic_memory_buffer` move + (https://github.com/fmtlib/fmt/issues/4487, + https://github.com/fmtlib/fmt/pull/4490). Thanks @toprakmurat. + +- Fixed an ambiguity between `std::reference_wrapper` and `format_as` + formatters (https://github.com/fmtlib/fmt/issues/4424, + https://github.com/fmtlib/fmt/pull/4434). Thanks @jeremy-rifkin. + +- Removed the following deprecated APIs: + + - `has_formatter`: use `is_formattable` instead, + - `basic_format_args::parse_context_type`, + `basic_format_args::formatter_type` and similar aliases in context types, + - wide stream overload of `fmt::printf`, + - wide stream overloads of `fmt::print` that take text styles, + - `is_*char` traits, + - `fmt::localtime`. + +- Deprecated wide overloads of `fmt::fprintf` and `fmt::sprintf`. + +- Improved diagnostics for the incorrect usage of `fmt::ptr` + (https://github.com/fmtlib/fmt/pull/4453). Thanks @TobiSchluter. + +- Made handling of ANSI escape sequences more efficient + (https://github.com/fmtlib/fmt/pull/4511, + https://github.com/fmtlib/fmt/pull/4528). + Thanks @localspook and @Anas-Hamdane. + +- Fixed a buffer overflow on all emphasis flags set + (https://github.com/fmtlib/fmt/pull/4498). Thanks @dominicpoeschko. + +- Fixed an integer overflow for precision close to the max `int` value. + +- Fixed compatibility with WASI (https://github.com/fmtlib/fmt/issues/4496, + https://github.com/fmtlib/fmt/pull/4497). Thanks @whitequark. + +- Fixed `back_insert_iterator` detection, preventing a fallback on slower path + that handles arbitrary iterators (https://github.com/fmtlib/fmt/issues/4454). + +- Fixed handling of invalid glibc `FILE` buffers + (https://github.com/fmtlib/fmt/issues/4469). + +- Added `wchar_t` support to the `std::byte` formatter + (https://github.com/fmtlib/fmt/issues/4479, + https://github.com/fmtlib/fmt/pull/4480). Thanks @phprus. + +- Changed component prefix from `fmt-` to `fmt_` for compatibility with + NSIS/CPack on Windows, e.g. `fmt-doc` changed to `fmt_doc` + (https://github.com/fmtlib/fmt/issues/4441, + https://github.com/fmtlib/fmt/pull/4442). Thanks @n-stein. + +- Added the `FMT_CUSTOM_ASSERT_FAIL` macro to simplify providing a custom + `fmt::assert_fail` implementation (https://github.com/fmtlib/fmt/pull/4505). + Thanks @HazardyKnusperkeks. + +- Switched to `FMT_THROW` on reporting format errors so that it can be + overriden by users when exceptions are disabled + (https://github.com/fmtlib/fmt/pull/4521). Thanks @HazardyKnusperkeks. + +- Improved master project detection and disabled install targets when using + {fmt} as a subproject by default (https://github.com/fmtlib/fmt/pull/4536). + Thanks @crueter. + +- Made various code improvements + (https://github.com/fmtlib/fmt/pull/4445, + https://github.com/fmtlib/fmt/pull/4448, + https://github.com/fmtlib/fmt/pull/4473, + https://github.com/fmtlib/fmt/pull/4522). + Thanks @localspook, @tchaikov and @way4sahil. + +- Added Conan instructions to the docs + (https://github.com/fmtlib/fmt/pull/4537). Thanks @uilianries. + +- Removed Bazel files to avoid issues with downstream packaging + (https://github.com/fmtlib/fmt/pull/4530). Thanks @mering. + +- Added more entries for generated files to `.gitignore` + (https://github.com/fmtlib/fmt/pull/4355, + https://github.com/fmtlib/fmt/pull/4512). + Thanks @dinomight and @localspook. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/pull/4447, + https://github.com/fmtlib/fmt/issues/4470, + https://github.com/fmtlib/fmt/pull/4474, + https://github.com/fmtlib/fmt/pull/4477, + https://github.com/fmtlib/fmt/pull/4471, + https://github.com/fmtlib/fmt/pull/4483, + https://github.com/fmtlib/fmt/pull/4515, + https://github.com/fmtlib/fmt/issues/4533, + https://github.com/fmtlib/fmt/pull/4534). + Thanks @dodomorandi, @localspook, @remyjette, @Tomek-Stolarczyk, @Mishura4, + @mattiasljungstrom and @FatihBAKIR. + +# 11.2.0 - 2025-05-03 + +- Added the `s` specifier for `std::error_code`. It allows formatting an error + message as a string. For example: + + ```c++ + #include + + int main() { + auto ec = std::make_error_code(std::errc::no_such_file_or_directory); + fmt::print("{:s}\n", ec); + } + ``` + + prints + + ``` + No such file or directory + ``` + (The actual message is platform-specific.) + +- Fixed formatting of `std::chrono::local_time` and `tm` + (https://github.com/fmtlib/fmt/issues/3815, + https://github.com/fmtlib/fmt/issues/4350). + For example ([godbolt](https://www.godbolt.org/z/8o4b1PPn5)): + + ```c++ + #include + + int main() { + std::chrono::zoned_time zt( + std::chrono::current_zone(), + std::chrono::system_clock::now()); + fmt::print("{}", zt.get_local_time()); + } + ``` + + is now formatted consistenly across platforms. + +- Added diagnostics for cases when timezone information is not available. + For example: + + ```c++ + fmt::print("{:Z}", std::chrono::local_seconds()); + ``` + + now gives a compile-time error. + +- Deprecated `fmt::localtime` in favor of `std::localtime`. + +- Fixed compilation with GCC 15 and C++20 modules enabled + (https://github.com/fmtlib/fmt/pull/4347). Thanks @tkhyn. + +- Fixed handling of named arguments in format specs + (https://github.com/fmtlib/fmt/issues/4360, + https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight. + +- Added error reporting for duplicate named arguments + (https://github.com/fmtlib/fmt/issues/4282, + https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight. + +- Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0` + (https://github.com/fmtlib/fmt/issues/4375, + https://github.com/fmtlib/fmt/issues/4394). + +- Optimized `text_style` using bit packing + (https://github.com/fmtlib/fmt/pull/4363). Thanks @localspook. + +- Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180, + https://github.com/fmtlib/fmt/pull/4383). Thanks @localspook. + +- Fixed a flush issue in `fmt::print` when using libstdc++ + (https://github.com/fmtlib/fmt/issues/4398). + +- Fixed `fmt::println` usage with `FMT_ENFORCE_COMPILE_STRING` and legacy + compile-time checks (https://github.com/fmtlib/fmt/pull/4407). + Thanks @madmaxoft. + +- Removed legacy header `fmt/core.h` from docs + (https://github.com/fmtlib/fmt/pull/4421, + https://github.com/fmtlib/fmt/pull/4422). Thanks @krzysztofkortas. + +- Worked around limitations of `__builtin_strlen` during constant evaluation + (https://github.com/fmtlib/fmt/issues/4423, + https://github.com/fmtlib/fmt/pull/4429). Thanks @BRevzin. + +- Worked around a bug in MSVC v141 (https://github.com/fmtlib/fmt/issues/4412, + https://github.com/fmtlib/fmt/pull/4413). Thanks @hirohira9119. + +- Removed the `fmt_detail` namespace + (https://github.com/fmtlib/fmt/issues/4324). + +- Removed specializations of `std::is_floating_point` in tests + (https://github.com/fmtlib/fmt/issues/4417). + +- Fixed a CMake error when setting `CMAKE_MODULE_PATH` in the pedantic mode + (https://github.com/fmtlib/fmt/pull/4426). Thanks @rlalik. + +- Updated the Bazel config (https://github.com/fmtlib/fmt/pull/4400). + Thanks @Vertexwahn. + +# 11.1.4 - 2025-02-26 + +- Fixed ABI compatibility with earlier 11.x versions on Windows + (https://github.com/fmtlib/fmt/issues/4359). + +- Improved the logic of switching between fixed and exponential format for + `float` (https://github.com/fmtlib/fmt/issues/3649). + +- Moved `is_compiled_string` to the public API + (https://github.com/fmtlib/fmt/issues/4335, + https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb. + +- Simplified implementation of `operator""_cf` + (https://github.com/fmtlib/fmt/pull/4349). Thanks @localspook. + +- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329). + Thanks @localspook. + +- Fixed handling of BMI paths with the Ninja generator + (https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn. + +- Fixed gcc 8.3 compile errors (https://github.com/fmtlib/fmt/issues/4331, + https://github.com/fmtlib/fmt/pull/4336). Thanks @sergiud. + +- Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/pull/4356). + Thanks @dinomight. + +# 11.1.3 - 2025-01-25 + +- Fixed compilation on GCC 9.4 (https://github.com/fmtlib/fmt/issues/4313). + +- Worked around an internal compiler error when using C++20 modules with GCC + 14.2 and earlier (https://github.com/fmtlib/fmt/issues/4295). + +- Worked around a bug in GCC 6 (https://github.com/fmtlib/fmt/issues/4318). + +- Fixed an issue caused by instantiating `formatter` + (https://github.com/fmtlib/fmt/issues/4303, + https://github.com/fmtlib/fmt/pull/4325). Thanks @timsong-cpp. + +- Fixed formatting into `std::ostreambuf_iterator` when using format string + compilation (https://github.com/fmtlib/fmt/issues/4309, + https://github.com/fmtlib/fmt/pull/4312). Thanks @phprus. + +- Restored a constraint on the map formatter so that it correctly reports as + unformattable when the element is (https://github.com/fmtlib/fmt/pull/4326). + Thanks @timsong-cpp. + +- Reduced the size of format specs (https://github.com/fmtlib/fmt/issues/4298). + +- Readded `args()` to `fmt::format_context` + (https://github.com/fmtlib/fmt/issues/4307, + https://github.com/fmtlib/fmt/pull/4310). Thanks @Erroneous1. + +- Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/issues/4314, + https://github.com/fmtlib/fmt/pull/4322). Thanks @ZehMatt. + +- Fixed a pedantic mode error in the CMake config + (https://github.com/fmtlib/fmt/pull/4327). Thanks @rlalik. + +# 11.1.2 - 2025-01-12 + +- Fixed ABI compatibility with earlier 11.x versions + (https://github.com/fmtlib/fmt/issues/4292). + +- Added `wchar_t` support to the `std::bitset` formatter + (https://github.com/fmtlib/fmt/issues/4285, + https://github.com/fmtlib/fmt/pull/4286, + https://github.com/fmtlib/fmt/issues/4289, + https://github.com/fmtlib/fmt/pull/4290). Thanks @phprus. + +- Prefixed CMake components with `fmt-` to simplify usage of {fmt} via + `add_subdirectory` (https://github.com/fmtlib/fmt/issues/4283). + +- Updated docs for meson (https://github.com/fmtlib/fmt/pull/4291). + Thanks @trim21. + +- Fixed a compilation error in chrono on nvcc + (https://github.com/fmtlib/fmt/issues/4297, + https://github.com/fmtlib/fmt/pull/4301). Thanks @breyerml. + +- Fixed various warnings + (https://github.com/fmtlib/fmt/pull/4288, + https://github.com/fmtlib/fmt/pull/4299). Thanks @GamesTrap and @edo9300. + +# 11.1.1 - 2024-12-27 + +- Fixed ABI compatibility with earlier 11.x versions + (https://github.com/fmtlib/fmt/issues/4278). + +- Defined CMake components (`core` and `doc`) to allow docs to be installed + separately (https://github.com/fmtlib/fmt/pull/4276). + Thanks @carlsmedstad. + +# 11.1.0 - 2024-12-25 + +- Improved C++20 module support + (https://github.com/fmtlib/fmt/issues/4081, + https://github.com/fmtlib/fmt/pull/4083, + https://github.com/fmtlib/fmt/pull/4084, + https://github.com/fmtlib/fmt/pull/4152, + https://github.com/fmtlib/fmt/issues/4153, + https://github.com/fmtlib/fmt/pull/4169, + https://github.com/fmtlib/fmt/issues/4190, + https://github.com/fmtlib/fmt/issues/4234, + https://github.com/fmtlib/fmt/pull/4239). + Thanks @kamrann and @Arghnews. + +- Reduced debug (unoptimized) binary code size and the number of template + instantiations when passing formatting arguments. For example, unoptimized + binary code size for `fmt::print("{}", 42)` was reduced by ~40% on GCC and + ~60% on clang (x86-64). + + GCC: + - Before: 161 instructions of which 105 are in reusable functions + ([godbolt](https://www.godbolt.org/z/s9bGoo4ze)). + - After: 116 instructions of which 60 are in reusable functions + ([godbolt](https://www.godbolt.org/z/r7GGGxMs6)). + + Clang: + - Before: 310 instructions of which 251 are in reusable functions + ([godbolt](https://www.godbolt.org/z/Ts88b7M9o)). + - After: 194 instructions of which 135 are in reusable functions + ([godbolt](https://www.godbolt.org/z/vcrjP8ceW)). + +- Added an experimental `fmt::writer` API that can be used for writing to + different destinations such as files or strings + (https://github.com/fmtlib/fmt/issues/2354). + For example ([godbolt](https://www.godbolt.org/z/rWoKfbP7e)): + + ```c++ + #include + + void write_text(fmt::writer w) { + w.print("The answer is {}.", 42); + } + + int main() { + // Write to FILE. + write_text(stdout); + + // Write to fmt::ostream. + auto f = fmt::output_file("myfile"); + write_text(f); + + // Write to std::string. + auto sb = fmt::string_buffer(); + write_text(sb); + std::string s = sb.str(); + } + ``` + +- Added width and alignment support to the formatter of `std::error_code`. + +- Made `std::expected` formattable + (https://github.com/fmtlib/fmt/issues/4145, + https://github.com/fmtlib/fmt/pull/4148). + For example ([godbolt](https://www.godbolt.org/z/hrj5c6G86)): + + ```c++ + fmt::print("{}", std::expected()); + ``` + + prints + + ``` + expected() + ``` + + Thanks @phprus. + +- Made `fmt::is_formattable` SFINAE-friendly + (https://github.com/fmtlib/fmt/issues/4147). + +- Added support for `_BitInt` formatting when using clang + (https://github.com/fmtlib/fmt/issues/4007, + https://github.com/fmtlib/fmt/pull/4072, + https://github.com/fmtlib/fmt/issues/4140, + https://github.com/fmtlib/fmt/issues/4173, + https://github.com/fmtlib/fmt/pull/4176). + For example ([godbolt](https://www.godbolt.org/z/KWjbWec5z)): + + ```c++ + using int42 = _BitInt(42); + fmt::print("{}", int42(100)); + ``` + + Thanks @Arghnews. + +- Added the `n` specifier for tuples and pairs + (https://github.com/fmtlib/fmt/pull/4107). Thanks @someonewithpc. + +- Added support for tuple-like types to `fmt::join` + (https://github.com/fmtlib/fmt/issues/4226, + https://github.com/fmtlib/fmt/pull/4230). Thanks @phprus. + +- Made more types formattable at compile time + (https://github.com/fmtlib/fmt/pull/4127). Thanks @AnthonyVH. + +- Implemented a more efficient compile-time `fmt::formatted_size` + (https://github.com/fmtlib/fmt/issues/4102, + https://github.com/fmtlib/fmt/pull/4103). Thanks @phprus. + +- Fixed compile-time formatting of some string types + (https://github.com/fmtlib/fmt/pull/4065). Thanks @torshepherd. + +- Made compiled version of `fmt::format_to` work with + `std::back_insert_iterator>` + (https://github.com/fmtlib/fmt/issues/4206, + https://github.com/fmtlib/fmt/pull/4211). Thanks @phprus. + +- Added a formatter for `std::reference_wrapper` + (https://github.com/fmtlib/fmt/pull/4163, + https://github.com/fmtlib/fmt/pull/4164). Thanks @yfeldblum and @phprus. + +- Added experimental padding support (glibc `strftime` extension) to `%m`, `%j` + and `%Y` (https://github.com/fmtlib/fmt/pull/4161). Thanks @KKhanhH. + +- Made microseconds formatted as `us` instead of `µs` if the Unicode support is + disabled (https://github.com/fmtlib/fmt/issues/4088). + +- Fixed an unreleased regression in transcoding of surrogate pairs + (https://github.com/fmtlib/fmt/issues/4094, + https://github.com/fmtlib/fmt/pull/4095). Thanks @phprus. + +- Made `fmt::appender` satisfy `std::output_iterator` concept + (https://github.com/fmtlib/fmt/issues/4092, + https://github.com/fmtlib/fmt/pull/4093). Thanks @phprus. + +- Made `std::iterator_traits` standard-conforming + (https://github.com/fmtlib/fmt/pull/4185). Thanks @CaseyCarter. + +- Made it easier to reuse `fmt::formatter` for types with + an implicit conversion to `std::string_view` + (https://github.com/fmtlib/fmt/issues/4036, + https://github.com/fmtlib/fmt/pull/4055). Thanks @Arghnews. + +- Made it possible to disable `` use via `FMT_CPP_LIB_FILESYSTEM` + for compatibility with some video game console SDKs, e.g. Nintendo Switch SDK + (https://github.com/fmtlib/fmt/issues/4257, + https://github.com/fmtlib/fmt/pull/4258, + https://github.com/fmtlib/fmt/pull/4259). Thanks @W4RH4WK and @phprus. + +- Fixed compatibility with platforms that use 80-bit `long double` + (https://github.com/fmtlib/fmt/issues/4245, + https://github.com/fmtlib/fmt/pull/4246). Thanks @jsirpoma. + +- Added support for UTF-32 code units greater than `0xFFFF` in fill + (https://github.com/fmtlib/fmt/issues/4201). + +- Fixed handling of legacy encodings on Windows with GCC + (https://github.com/fmtlib/fmt/issues/4162). + +- Made `fmt::to_string` take `fmt::basic_memory_buffer` by const reference + (https://github.com/fmtlib/fmt/issues/4261, + https://github.com/fmtlib/fmt/pull/4262). Thanks @sascha-devel. + +- Added `fmt::dynamic_format_arg_store::size` + (https://github.com/fmtlib/fmt/pull/4270). Thanks @hannes-harnisch. + +- Removed the ability to control locale usage via an undocumented + `FMT_STATIC_THOUSANDS_SEPARATOR` in favor of `FMT_USE_LOCALE`. + +- Renamed `FMT_EXCEPTIONS` to `FMT_USE_EXCEPTIONS` for consistency with other + similar macros. + +- Improved include directory ordering to reduce the chance of including + incorrect headers when using multiple versions of {fmt} + (https://github.com/fmtlib/fmt/pull/4116). Thanks @cdzhan. + +- Made it possible to compile a subset of {fmt} without the C++ runtime. + +- Improved documentation and README + (https://github.com/fmtlib/fmt/pull/4066, + https://github.com/fmtlib/fmt/issues/4117, + https://github.com/fmtlib/fmt/issues/4203, + https://github.com/fmtlib/fmt/pull/4235). Thanks @zyctree and @nikola-sh. + +- Improved the documentation generator (https://github.com/fmtlib/fmt/pull/4110, + https://github.com/fmtlib/fmt/pull/4115). Thanks @rturrado. + +- Improved CI (https://github.com/fmtlib/fmt/pull/4155, + https://github.com/fmtlib/fmt/pull/4151). Thanks @phprus. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/issues/2708, + https://github.com/fmtlib/fmt/issues/4091, + https://github.com/fmtlib/fmt/issues/4109, + https://github.com/fmtlib/fmt/issues/4113, + https://github.com/fmtlib/fmt/issues/4125, + https://github.com/fmtlib/fmt/issues/4129, + https://github.com/fmtlib/fmt/pull/4130, + https://github.com/fmtlib/fmt/pull/4131, + https://github.com/fmtlib/fmt/pull/4132, + https://github.com/fmtlib/fmt/issues/4133, + https://github.com/fmtlib/fmt/issues/4144, + https://github.com/fmtlib/fmt/issues/4150, + https://github.com/fmtlib/fmt/issues/4158, + https://github.com/fmtlib/fmt/pull/4159, + https://github.com/fmtlib/fmt/issues/4160, + https://github.com/fmtlib/fmt/pull/4170, + https://github.com/fmtlib/fmt/issues/4177, + https://github.com/fmtlib/fmt/pull/4187, + https://github.com/fmtlib/fmt/pull/4188, + https://github.com/fmtlib/fmt/pull/4194, + https://github.com/fmtlib/fmt/pull/4200, + https://github.com/fmtlib/fmt/issues/4205, + https://github.com/fmtlib/fmt/issues/4207, + https://github.com/fmtlib/fmt/pull/4208, + https://github.com/fmtlib/fmt/pull/4210, + https://github.com/fmtlib/fmt/issues/4220, + https://github.com/fmtlib/fmt/issues/4231, + https://github.com/fmtlib/fmt/issues/4232, + https://github.com/fmtlib/fmt/pull/4233, + https://github.com/fmtlib/fmt/pull/4236, + https://github.com/fmtlib/fmt/pull/4267, + https://github.com/fmtlib/fmt/pull/4271). + Thanks @torsten48, @Arghnews, @tinfoilboy, @aminya, @Ottani, @zeroomega, + @c4v4, @kongy, @vinayyadav3016, @sergio-nsk, @phprus and @YexuanXiao. + +# 11.0.2 - 2024-07-20 + +- Fixed compatibility with non-POSIX systems + (https://github.com/fmtlib/fmt/issues/4054, + https://github.com/fmtlib/fmt/issues/4060). + +- Fixed performance regressions when using `std::back_insert_iterator` with + `fmt::format_to` (https://github.com/fmtlib/fmt/issues/4070). + +- Fixed handling of `std::generator` and move-only iterators + (https://github.com/fmtlib/fmt/issues/4053, + https://github.com/fmtlib/fmt/pull/4057). Thanks @Arghnews. + +- Made `formatter::parse` work with types convertible to + `std::string_view` (https://github.com/fmtlib/fmt/issues/4036, + https://github.com/fmtlib/fmt/pull/4055). Thanks @Arghnews. + +- Made `volatile void*` formattable + (https://github.com/fmtlib/fmt/issues/4049, + https://github.com/fmtlib/fmt/pull/4056). Thanks @Arghnews. + +- Made `Glib::ustring` not be confused with `std::string` + (https://github.com/fmtlib/fmt/issues/4052). + +- Made `fmt::context` iterator compatible with STL algorithms that rely on + iterator category (https://github.com/fmtlib/fmt/issues/4079). + +# 11.0.1 - 2024-07-05 + +- Fixed version number in the inline namespace + (https://github.com/fmtlib/fmt/issues/4047). + +- Fixed disabling Unicode support via CMake + (https://github.com/fmtlib/fmt/issues/4051). + +- Fixed deprecated `visit_format_arg` (https://github.com/fmtlib/fmt/pull/4043). + Thanks @nebkat. + +- Fixed handling of a sign and improved the `std::complex` formater + (https://github.com/fmtlib/fmt/pull/4034, + https://github.com/fmtlib/fmt/pull/4050). Thanks @tesch1 and @phprus. + +- Fixed ADL issues in `fmt::printf` when using C++20 + (https://github.com/fmtlib/fmt/pull/4042). Thanks @toge. + +- Removed a redundant check in the formatter for `std::expected` + (https://github.com/fmtlib/fmt/pull/4040). Thanks @phprus. + +# 11.0.0 - 2024-07-01 + +- Added `fmt/base.h` which provides a subset of the API with minimal include + dependencies and enough functionality to replace all uses of the `printf` + family of functions. This brings the compile time of code using {fmt} much + closer to the equivalent `printf` code as shown on the following benchmark + that compiles 100 source files: + + | Method | Compile Time (s) | + |--------------|------------------| + | printf | 1.6 | + | IOStreams | 25.9 | + | fmt 10.x | 19.0 | + | fmt 11.0 | 4.8 | + | tinyformat | 29.1 | + | Boost Format | 55.0 | + + This gives almost 4x improvement in build speed compared to version 10. + Note that the benchmark is purely formatting code and includes. In real + projects the difference from `printf` will be smaller partly because common + standard headers will be included in almost any translation unit (TU) anyway. + In particular, in every case except `printf` above ~1s is spent in total on + including `` in all TUs. + +- Optimized includes in other headers such as `fmt/format.h` which is now + roughly equivalent to the old `fmt/core.h` in terms of build speed. + +- Migrated the documentation at https://fmt.dev/ from Sphinx to MkDocs. + +- Improved C++20 module support + (https://github.com/fmtlib/fmt/issues/3990, + https://github.com/fmtlib/fmt/pull/3991, + https://github.com/fmtlib/fmt/issues/3993, + https://github.com/fmtlib/fmt/pull/3994, + https://github.com/fmtlib/fmt/pull/3997, + https://github.com/fmtlib/fmt/pull/3998, + https://github.com/fmtlib/fmt/pull/4004, + https://github.com/fmtlib/fmt/pull/4005, + https://github.com/fmtlib/fmt/pull/4006, + https://github.com/fmtlib/fmt/pull/4013, + https://github.com/fmtlib/fmt/pull/4027, + https://github.com/fmtlib/fmt/pull/4029). In particular, native CMake support + for modules is now used if available. Thanks @yujincheng08 and @matt77hias. + +- Added an option to replace standard includes with `import std` enabled via + the `FMT_IMPORT_STD` macro (https://github.com/fmtlib/fmt/issues/3921, + https://github.com/fmtlib/fmt/pull/3928). Thanks @matt77hias. + +- Exported `fmt::range_format`, `fmt::range_format_kind` and + `fmt::compiled_string` from the `fmt` module + (https://github.com/fmtlib/fmt/pull/3970, + https://github.com/fmtlib/fmt/pull/3999). + Thanks @matt77hias and @yujincheng08. + +- Improved integration with stdio in `fmt::print`, enabling direct writes + into a C stream buffer in common cases. This may give significant + performance improvements ranging from tens of percent to [2x]( + https://stackoverflow.com/a/78457454/471164) and eliminates dynamic memory + allocations on the buffer level. It is currently enabled for built-in and + string types with wider availability coming up in future releases. + + For example, it gives ~24% improvement on a [simple benchmark]( + https://isocpp.org/files/papers/P3107R5.html#perf) compiled with Apple clang + version 15.0.0 (clang-1500.1.0.2.5) and run on macOS 14.2.1: + + ``` + ------------------------------------------------------- + Benchmark Time CPU Iterations + ------------------------------------------------------- + printf 81.8 ns 81.5 ns 8496899 + fmt::print (10.x) 63.8 ns 61.9 ns 11524151 + fmt::print (11.0) 51.3 ns 51.0 ns 13846580 + ``` + +- Improved safety of `fmt::format_to` when writing to an array + (https://github.com/fmtlib/fmt/pull/3805). + For example ([godbolt](https://www.godbolt.org/z/cYrn8dWY8)): + + ```c++ + auto volkswagen = char[4]; + auto result = fmt::format_to(volkswagen, "elephant"); + ``` + + no longer results in a buffer overflow. Instead the output will be truncated + and you can get the end iterator and whether truncation occurred from the + `result` object. Thanks @ThePhD. + +- Enabled Unicode support by default in MSVC, bringing it on par with other + compilers and making it unnecessary for users to enable it explicitly. + Most of {fmt} is encoding-agnostic but this prevents mojibake in places + where encoding matters such as path formatting and terminal output. + You can control the Unicode support via the CMake `FMT_UNICODE` option. + Note that some {fmt} packages such as the one in vcpkg have already been + compiled with Unicode enabled. + +- Added a formatter for `std::expected` + (https://github.com/fmtlib/fmt/pull/3834). Thanks @dominicpoeschko. + +- Added a formatter for `std::complex` + (https://github.com/fmtlib/fmt/issues/1467, + https://github.com/fmtlib/fmt/issues/3886, + https://github.com/fmtlib/fmt/pull/3892, + https://github.com/fmtlib/fmt/pull/3900). Thanks @phprus. + +- Added a formatter for `std::type_info` + (https://github.com/fmtlib/fmt/pull/3978). Thanks @matt77hias. + +- Specialized `formatter` for `std::basic_string` types with custom traits + and allocators (https://github.com/fmtlib/fmt/issues/3938, + https://github.com/fmtlib/fmt/pull/3943). Thanks @dieram3. + +- Added formatters for `std::chrono::day`, `std::chrono::month`, + `std::chrono::year` and `std::chrono::year_month_day` + (https://github.com/fmtlib/fmt/issues/3758, + https://github.com/fmtlib/fmt/issues/3772, + https://github.com/fmtlib/fmt/pull/3906, + https://github.com/fmtlib/fmt/pull/3913). For example: + + ```c++ + #include + #include + + int main() { + fmt::print(fg(fmt::color::green), "{}\n", std::chrono::day(7)); + } + ``` + + prints a green day: + + image + + Thanks @zivshek. + +- Fixed handling of precision in `%S` (https://github.com/fmtlib/fmt/issues/3794, + https://github.com/fmtlib/fmt/pull/3814). Thanks @js324. + +- Added support for the `-` specifier (glibc `strftime` extension) to day of + the month (`%d`) and week of the year (`%W`, `%U`, `%V`) specifiers + (https://github.com/fmtlib/fmt/pull/3976). Thanks @ZaheenJ. + +- Fixed the scope of the `-` extension in chrono formatting so that it doesn't + apply to subsequent specifiers (https://github.com/fmtlib/fmt/issues/3811, + https://github.com/fmtlib/fmt/pull/3812). Thanks @phprus. + +- Improved handling of `time_point::min()` + (https://github.com/fmtlib/fmt/issues/3282). + +- Added support for character range formatting + (https://github.com/fmtlib/fmt/issues/3857, + https://github.com/fmtlib/fmt/pull/3863). Thanks @js324. + +- Added `string` and `debug_string` range formatters + (https://github.com/fmtlib/fmt/pull/3973, + https://github.com/fmtlib/fmt/pull/4024). Thanks @matt77hias. + +- Enabled ADL for `begin` and `end` in `fmt::join` + (https://github.com/fmtlib/fmt/issues/3813, + https://github.com/fmtlib/fmt/pull/3824). Thanks @bbolli. + +- Made contiguous iterator optimizations apply to `std::basic_string` iterators + (https://github.com/fmtlib/fmt/pull/3798). Thanks @phprus. + +- Added support for ranges with mutable `begin` and `end` + (https://github.com/fmtlib/fmt/issues/3752, + https://github.com/fmtlib/fmt/pull/3800, + https://github.com/fmtlib/fmt/pull/3955). Thanks @tcbrindle and @Arghnews. + +- Added support for move-only iterators to `fmt::join` + (https://github.com/fmtlib/fmt/issues/3802, + https://github.com/fmtlib/fmt/pull/3946). Thanks @Arghnews. + +- Moved range and iterator overloads of `fmt::join` to `fmt/ranges.h`, next + to other overloads. + +- Fixed handling of types with `begin` returning `void` such as Eigen matrices + (https://github.com/fmtlib/fmt/issues/3839, + https://github.com/fmtlib/fmt/pull/3964). Thanks @Arghnews. + +- Added an `fmt::formattable` concept (https://github.com/fmtlib/fmt/pull/3974). + Thanks @matt77hias. + +- Added support for `__float128` (https://github.com/fmtlib/fmt/issues/3494). + +- Fixed rounding issues when formatting `long double` with fixed precision + (https://github.com/fmtlib/fmt/issues/3539). + +- Made `fmt::isnan` not trigger floating-point exception for NaN values + (https://github.com/fmtlib/fmt/issues/3948, + https://github.com/fmtlib/fmt/pull/3951). Thanks @alexdewar. + +- Removed dependency on `` for `std::allocator_traits` when possible + (https://github.com/fmtlib/fmt/pull/3804). Thanks @phprus. + +- Enabled compile-time checks in formatting functions that take text colors and + styles. + +- Deprecated wide stream overloads of `fmt::print` that take text styles. + +- Made format string compilation work with clang 12 and later despite + only partial non-type template parameter support + (https://github.com/fmtlib/fmt/issues/4000, + https://github.com/fmtlib/fmt/pull/4001). Thanks @yujincheng08. + +- Made `fmt::iterator_buffer`'s move constructor `noexcept` + (https://github.com/fmtlib/fmt/pull/3808). Thanks @waywardmonkeys. + +- Started enforcing that `formatter::format` is const for compatibility + with `std::format` (https://github.com/fmtlib/fmt/issues/3447). + +- Added `fmt::basic_format_arg::visit` and deprecated `fmt::visit_format_arg`. + +- Made `fmt::basic_string_view` not constructible from `nullptr` for + consistency with `std::string_view` in C++23 + (https://github.com/fmtlib/fmt/pull/3846). Thanks @dalle. + +- Fixed `fmt::group_digits` for negative integers + (https://github.com/fmtlib/fmt/issues/3891, + https://github.com/fmtlib/fmt/pull/3901). Thanks @phprus. + +- Fixed handling of negative ids in `fmt::basic_format_args::get` + (https://github.com/fmtlib/fmt/pull/3945). Thanks @marlenecota. + +- Fixed handling of a buffer boundary on flush + (https://github.com/fmtlib/fmt/issues/4229). + +- Improved named argument validation + (https://github.com/fmtlib/fmt/issues/3817). + +- Disabled copy construction/assignment for `fmt::format_arg_store` and + fixed moved construction (https://github.com/fmtlib/fmt/pull/3833). + Thanks @ivafanas. + +- Worked around a locale issue in RHEL/devtoolset + (https://github.com/fmtlib/fmt/issues/3858, + https://github.com/fmtlib/fmt/pull/3859). Thanks @g199209. + +- Added RTTI detection for MSVC (https://github.com/fmtlib/fmt/pull/3821, + https://github.com/fmtlib/fmt/pull/3963). Thanks @edo9300. + +- Migrated the documentation from Sphinx to MkDocs. + +- Improved documentation and README + (https://github.com/fmtlib/fmt/issues/3775, + https://github.com/fmtlib/fmt/pull/3784, + https://github.com/fmtlib/fmt/issues/3788, + https://github.com/fmtlib/fmt/pull/3789, + https://github.com/fmtlib/fmt/pull/3793, + https://github.com/fmtlib/fmt/issues/3818, + https://github.com/fmtlib/fmt/pull/3820, + https://github.com/fmtlib/fmt/pull/3822, + https://github.com/fmtlib/fmt/pull/3843, + https://github.com/fmtlib/fmt/pull/3890, + https://github.com/fmtlib/fmt/issues/3894, + https://github.com/fmtlib/fmt/pull/3895, + https://github.com/fmtlib/fmt/pull/3905, + https://github.com/fmtlib/fmt/issues/3942, + https://github.com/fmtlib/fmt/pull/4008). + Thanks @zencatalyst, WolleTD, @tupaschoal, @Dobiasd, @frank-weinberg, @bbolli, + @phprus, @waywardmonkeys, @js324 and @tchaikov. + +- Improved CI and tests + (https://github.com/fmtlib/fmt/issues/3878, + https://github.com/fmtlib/fmt/pull/3883, + https://github.com/fmtlib/fmt/issues/3897, + https://github.com/fmtlib/fmt/pull/3979, + https://github.com/fmtlib/fmt/pull/3980, + https://github.com/fmtlib/fmt/pull/3988, + https://github.com/fmtlib/fmt/pull/4010, + https://github.com/fmtlib/fmt/pull/4012, + https://github.com/fmtlib/fmt/pull/4038). + Thanks @vgorrX, @waywardmonkeys, @tchaikov and @phprus. + +- Fixed buffer overflow when using format string compilation with debug format + and `std::back_insert_iterator` (https://github.com/fmtlib/fmt/issues/3795, + https://github.com/fmtlib/fmt/pull/3797). Thanks @phprus. + +- Improved Bazel support + (https://github.com/fmtlib/fmt/pull/3792, + https://github.com/fmtlib/fmt/pull/3801, + https://github.com/fmtlib/fmt/pull/3962, + https://github.com/fmtlib/fmt/pull/3965). Thanks @Vertexwahn. + +- Improved/fixed the CMake config + (https://github.com/fmtlib/fmt/issues/3777, + https://github.com/fmtlib/fmt/pull/3783, + https://github.com/fmtlib/fmt/issues/3847, + https://github.com/fmtlib/fmt/pull/3907). Thanks @phprus and @xTachyon. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/issues/3685, + https://github.com/fmtlib/fmt/issues/3769, + https://github.com/fmtlib/fmt/issues/3796, + https://github.com/fmtlib/fmt/issues/3803, + https://github.com/fmtlib/fmt/pull/3806, + https://github.com/fmtlib/fmt/pull/3807, + https://github.com/fmtlib/fmt/issues/3809, + https://github.com/fmtlib/fmt/pull/3810, + https://github.com/fmtlib/fmt/issues/3830, + https://github.com/fmtlib/fmt/pull/3832, + https://github.com/fmtlib/fmt/issues/3835, + https://github.com/fmtlib/fmt/pull/3844, + https://github.com/fmtlib/fmt/issues/3854, + https://github.com/fmtlib/fmt/pull/3856, + https://github.com/fmtlib/fmt/pull/3865, + https://github.com/fmtlib/fmt/pull/3866, + https://github.com/fmtlib/fmt/pull/3880, + https://github.com/fmtlib/fmt/issues/3881, + https://github.com/fmtlib/fmt/issues/3884, + https://github.com/fmtlib/fmt/issues/3898, + https://github.com/fmtlib/fmt/pull/3899, + https://github.com/fmtlib/fmt/pull/3909, + https://github.com/fmtlib/fmt/pull/3917, + https://github.com/fmtlib/fmt/pull/3923, + https://github.com/fmtlib/fmt/pull/3924, + https://github.com/fmtlib/fmt/issues/3925, + https://github.com/fmtlib/fmt/pull/3930, + https://github.com/fmtlib/fmt/pull/3931, + https://github.com/fmtlib/fmt/pull/3933, + https://github.com/fmtlib/fmt/issues/3935, + https://github.com/fmtlib/fmt/pull/3937, + https://github.com/fmtlib/fmt/pull/3967, + https://github.com/fmtlib/fmt/pull/3968, + https://github.com/fmtlib/fmt/pull/3972, + https://github.com/fmtlib/fmt/pull/3983, + https://github.com/fmtlib/fmt/issues/3992, + https://github.com/fmtlib/fmt/pull/3995, + https://github.com/fmtlib/fmt/pull/4009, + https://github.com/fmtlib/fmt/pull/4023). + Thanks @hmbj, @phprus, @res2k, @Baardi, @matt77hias, @waywardmonkeys, @hmbj, + @yakra, @prlw1, @Arghnews, @mtillmann0, @ShifftC, @eepp, @jimmy-park and + @ChristianGebhardt. + +# 10.2.1 - 2024-01-04 + +- Fixed ABI compatibility with earlier 10.x versions + (https://github.com/fmtlib/fmt/issues/3785, + https://github.com/fmtlib/fmt/pull/3786). Thanks @saraedum. + +# 10.2.0 - 2024-01-01 + +- Added support for the `%j` specifier (the number of days) for + `std::chrono::duration` (https://github.com/fmtlib/fmt/issues/3643, + https://github.com/fmtlib/fmt/pull/3732). Thanks @intelfx. + +- Added support for the chrono suffix for days and changed + the suffix for minutes from "m" to the correct "min" + (https://github.com/fmtlib/fmt/issues/3662, + https://github.com/fmtlib/fmt/pull/3664). + For example ([godbolt](https://godbolt.org/z/9KhMnq9ba)): + + ```c++ + #include + + int main() { + fmt::print("{}\n", std::chrono::days(42)); // prints "42d" + } + ``` + + Thanks @Richardk2n. + +- Fixed an overflow in `std::chrono::time_point` formatting with large dates + (https://github.com/fmtlib/fmt/issues/3725, + https://github.com/fmtlib/fmt/pull/3727). Thanks @cschreib. + +- Added a formatter for `std::source_location` + (https://github.com/fmtlib/fmt/pull/3730). + For example ([godbolt](https://godbolt.org/z/YajfKjhhr)): + + ```c++ + #include + #include + + int main() { + fmt::print("{}\n", std::source_location::current()); + } + ``` + + prints + + ``` + /app/example.cpp:5:51: int main() + ``` + + Thanks @felix642. + +- Added a formatter for `std::bitset` + (https://github.com/fmtlib/fmt/pull/3660). + For example ([godbolt](https://godbolt.org/z/bdEaGeYxe)): + + ```c++ + #include + #include + + int main() { + fmt::print("{}\n", std::bitset<6>(42)); // prints "101010" + } + ``` + + Thanks @muggenhor. + +- Added an experimental `nested_formatter` that provides an easy way of + applying a formatter to one or more subobjects while automatically handling + width, fill and alignment. For example: + + ```c++ + #include + + struct point { + double x, y; + }; + + template <> + struct fmt::formatter : nested_formatter { + auto format(point p, format_context& ctx) const { + return write_padded(ctx, [=](auto out) { + return format_to(out, "({}, {})", nested(p.x), nested(p.y)); + }); + } + }; + + int main() { + fmt::print("[{:>20.2f}]", point{1, 2}); + } + ``` + + prints + + ``` + [ (1.00, 2.00)] + ``` + +- Added the generic representation (`g`) to `std::filesystem::path` + (https://github.com/fmtlib/fmt/issues/3715, + https://github.com/fmtlib/fmt/pull/3729). For example: + + ```c++ + #include + #include + + int main() { + fmt::print("{:g}\n", std::filesystem::path("C:\\foo")); + } + ``` + + prints `"C:/foo"` on Windows. + + Thanks @js324. + +- Made `format_as` work with references + (https://github.com/fmtlib/fmt/pull/3739). Thanks @tchaikov. + +- Fixed formatting of invalid UTF-8 with precision + (https://github.com/fmtlib/fmt/issues/3284). + +- Fixed an inconsistency between `fmt::to_string` and `fmt::format` + (https://github.com/fmtlib/fmt/issues/3684). + +- Disallowed unsafe uses of `fmt::styled` + (https://github.com/fmtlib/fmt/issues/3625): + + ```c++ + auto s = fmt::styled(std::string("dangle"), fmt::emphasis::bold); + fmt::print("{}\n", s); // compile error + ``` + + Pass `fmt::styled(...)` as a parameter instead. + +- Added a null check when formatting a C string with the `s` specifier + (https://github.com/fmtlib/fmt/issues/3706). + +- Disallowed the `c` specifier for `bool` + (https://github.com/fmtlib/fmt/issues/3726, + https://github.com/fmtlib/fmt/pull/3734). Thanks @js324. + +- Made the default formatting unlocalized in `fmt::ostream_formatter` for + consistency with the rest of the library + (https://github.com/fmtlib/fmt/issues/3460). + +- Fixed localized formatting in bases other than decimal + (https://github.com/fmtlib/fmt/issues/3693, + https://github.com/fmtlib/fmt/pull/3750). Thanks @js324. + +- Fixed a performance regression in experimental `fmt::ostream::print` + (https://github.com/fmtlib/fmt/issues/3674). + +- Added synchronization with the underlying output stream when writing to + the Windows console + (https://github.com/fmtlib/fmt/pull/3668, + https://github.com/fmtlib/fmt/issues/3688, + https://github.com/fmtlib/fmt/pull/3689). + Thanks @Roman-Koshelev and @dimztimz. + +- Changed to only export `format_error` when {fmt} is built as a shared + library (https://github.com/fmtlib/fmt/issues/3626, + https://github.com/fmtlib/fmt/pull/3627). Thanks @phprus. + +- Made `fmt::streamed` `constexpr`. + (https://github.com/fmtlib/fmt/pull/3650). Thanks @muggenhor. + +- Made `fmt::format_int` `constexpr` + (https://github.com/fmtlib/fmt/issues/4031, + https://github.com/fmtlib/fmt/pull/4032). Thanks @dixlorenz. + +- Enabled `consteval` on older versions of MSVC + (https://github.com/fmtlib/fmt/pull/3757). Thanks @phprus. + +- Added an option to build without `wchar_t` support on Windows + (https://github.com/fmtlib/fmt/issues/3631, + https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. + +- Improved build and CI configuration + (https://github.com/fmtlib/fmt/pull/3679, + https://github.com/fmtlib/fmt/issues/3701, + https://github.com/fmtlib/fmt/pull/3702, + https://github.com/fmtlib/fmt/pull/3749). + Thanks @jcar87, @pklima and @tchaikov. + +- Fixed various warnings, compilation and test issues + (https://github.com/fmtlib/fmt/issues/3607, + https://github.com/fmtlib/fmt/pull/3610, + https://github.com/fmtlib/fmt/pull/3624, + https://github.com/fmtlib/fmt/pull/3630, + https://github.com/fmtlib/fmt/pull/3634, + https://github.com/fmtlib/fmt/pull/3638, + https://github.com/fmtlib/fmt/issues/3645, + https://github.com/fmtlib/fmt/issues/3646, + https://github.com/fmtlib/fmt/pull/3647, + https://github.com/fmtlib/fmt/pull/3652, + https://github.com/fmtlib/fmt/issues/3654, + https://github.com/fmtlib/fmt/pull/3663, + https://github.com/fmtlib/fmt/issues/3670, + https://github.com/fmtlib/fmt/pull/3680, + https://github.com/fmtlib/fmt/issues/3694, + https://github.com/fmtlib/fmt/pull/3695, + https://github.com/fmtlib/fmt/pull/3699, + https://github.com/fmtlib/fmt/issues/3705, + https://github.com/fmtlib/fmt/issues/3710, + https://github.com/fmtlib/fmt/issues/3712, + https://github.com/fmtlib/fmt/pull/3713, + https://github.com/fmtlib/fmt/issues/3714, + https://github.com/fmtlib/fmt/pull/3716, + https://github.com/fmtlib/fmt/pull/3723, + https://github.com/fmtlib/fmt/issues/3738, + https://github.com/fmtlib/fmt/issues/3740, + https://github.com/fmtlib/fmt/pull/3741, + https://github.com/fmtlib/fmt/pull/3743, + https://github.com/fmtlib/fmt/issues/3745, + https://github.com/fmtlib/fmt/pull/3747, + https://github.com/fmtlib/fmt/pull/3748, + https://github.com/fmtlib/fmt/pull/3751, + https://github.com/fmtlib/fmt/pull/3754, + https://github.com/fmtlib/fmt/pull/3755, + https://github.com/fmtlib/fmt/issues/3760, + https://github.com/fmtlib/fmt/pull/3762, + https://github.com/fmtlib/fmt/issues/3763, + https://github.com/fmtlib/fmt/pull/3764, + https://github.com/fmtlib/fmt/issues/3774, + https://github.com/fmtlib/fmt/pull/3779). + Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, + @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad, @hotwatermorning, + @cptFracassa, @kuguma, @PeterJohnson, @H1X4Dev, @asantoni, @eltociear, + @msimberg, @tchaikov, @waywardmonkeys. + +- Improved documentation and README + (https://github.com/fmtlib/fmt/issues/2086, + https://github.com/fmtlib/fmt/issues/3637, + https://github.com/fmtlib/fmt/pull/3642, + https://github.com/fmtlib/fmt/pull/3653, + https://github.com/fmtlib/fmt/pull/3655, + https://github.com/fmtlib/fmt/pull/3661, + https://github.com/fmtlib/fmt/issues/3673, + https://github.com/fmtlib/fmt/pull/3677, + https://github.com/fmtlib/fmt/pull/3737, + https://github.com/fmtlib/fmt/issues/3742, + https://github.com/fmtlib/fmt/pull/3744). + Thanks @idzm, @perlun, @joycebrum, @fennewald, @reinhardt1053, @GeorgeLS. + +- Updated CI dependencies + (https://github.com/fmtlib/fmt/pull/3615, + https://github.com/fmtlib/fmt/pull/3622, + https://github.com/fmtlib/fmt/pull/3623, + https://github.com/fmtlib/fmt/pull/3666, + https://github.com/fmtlib/fmt/pull/3696, + https://github.com/fmtlib/fmt/pull/3697, + https://github.com/fmtlib/fmt/pull/3759, + https://github.com/fmtlib/fmt/pull/3782). + +# 10.1.1 - 2023-08-28 + +- Added formatters for `std::atomic` and `atomic_flag` + (https://github.com/fmtlib/fmt/pull/3574, + https://github.com/fmtlib/fmt/pull/3594). + Thanks @wangzw and @AlexGuteniev. +- Fixed an error about partial specialization of `formatter` + after instantiation when compiled with gcc and C++20 + (https://github.com/fmtlib/fmt/issues/3584). +- Fixed compilation as a C++20 module with gcc and clang + (https://github.com/fmtlib/fmt/issues/3587, + https://github.com/fmtlib/fmt/pull/3597, + https://github.com/fmtlib/fmt/pull/3605). + Thanks @MathewBensonCode. +- Made `fmt::to_string` work with types that have `format_as` + overloads (https://github.com/fmtlib/fmt/pull/3575). Thanks @phprus. +- Made `formatted_size` work with integral format specifiers at + compile time (https://github.com/fmtlib/fmt/pull/3591). + Thanks @elbeno. +- Fixed a warning about the `no_unique_address` attribute on clang-cl + (https://github.com/fmtlib/fmt/pull/3599). Thanks @lukester1975. +- Improved compatibility with the legacy GBK encoding + (https://github.com/fmtlib/fmt/issues/3598, + https://github.com/fmtlib/fmt/pull/3599). Thanks @YuHuanTin. +- Added OpenSSF Scorecard analysis + (https://github.com/fmtlib/fmt/issues/3530, + https://github.com/fmtlib/fmt/pull/3571). Thanks @joycebrum. +- Updated CI dependencies + (https://github.com/fmtlib/fmt/pull/3591, + https://github.com/fmtlib/fmt/pull/3592, + https://github.com/fmtlib/fmt/pull/3593, + https://github.com/fmtlib/fmt/pull/3602). + +# 10.1.0 - 2023-08-12 + +- Optimized format string compilation resulting in up to 40% speed up + in compiled `format_to` and \~4x speed up in compiled `format_to_n` + on a concatenation benchmark + (https://github.com/fmtlib/fmt/issues/3133, + https://github.com/fmtlib/fmt/issues/3484). + + {fmt} 10.0: + + --------------------------------------------------------- + Benchmark Time CPU Iterations + --------------------------------------------------------- + BM_format_to 78.9 ns 78.9 ns 8881746 + BM_format_to_n 568 ns 568 ns 1232089 + + {fmt} 10.1: + + --------------------------------------------------------- + Benchmark Time CPU Iterations + --------------------------------------------------------- + BM_format_to 54.9 ns 54.9 ns 12727944 + BM_format_to_n 133 ns 133 ns 5257795 + +- Optimized storage of an empty allocator in `basic_memory_buffer` + (https://github.com/fmtlib/fmt/pull/3485). Thanks @Minty-Meeo. + +- Added formatters for proxy references to elements of + `std::vector` and `std::bitset` + (https://github.com/fmtlib/fmt/issues/3567, + https://github.com/fmtlib/fmt/pull/3570). For example + ([godbolt](https://godbolt.org/z/zYb79Pvn8)): + + ```c++ + #include + #include + + int main() { + auto v = std::vector{true}; + fmt::print("{}", v[0]); + } + ``` + + Thanks @phprus and @felix642. + +- Fixed an ambiguous formatter specialization for containers that look + like container adaptors such as `boost::flat_set` + (https://github.com/fmtlib/fmt/issues/3556, + https://github.com/fmtlib/fmt/pull/3561). Thanks @5chmidti. + +- Fixed compilation when formatting durations not convertible from + `std::chrono::seconds` + (https://github.com/fmtlib/fmt/pull/3430). Thanks @patlkli. + +- Made the `formatter` specialization for `char*` const-correct + (https://github.com/fmtlib/fmt/pull/3432). Thanks @timsong-cpp. + +- Made `{}` and `{:}` handled consistently during compile-time checks + (https://github.com/fmtlib/fmt/issues/3526). + +- Disallowed passing temporaries to `make_format_args` to improve API + safety by preventing dangling references. + +- Improved the compile-time error for unformattable types + (https://github.com/fmtlib/fmt/pull/3478). Thanks @BRevzin. + +- Improved the floating-point formatter + (https://github.com/fmtlib/fmt/pull/3448, + https://github.com/fmtlib/fmt/pull/3450). + Thanks @florimond-collette. + +- Fixed handling of precision for `long double` larger than 64 bits. + (https://github.com/fmtlib/fmt/issues/3539, + https://github.com/fmtlib/fmt/issues/3564). + +- Made floating-point and chrono tests less platform-dependent + (https://github.com/fmtlib/fmt/issues/3337, + https://github.com/fmtlib/fmt/issues/3433, + https://github.com/fmtlib/fmt/pull/3434). Thanks @phprus. + +- Removed the remnants of the Grisu floating-point formatter that has + been replaced by Dragonbox in earlier versions. + +- Added `throw_format_error` to the public API + (https://github.com/fmtlib/fmt/pull/3551). Thanks @mjerabek. + +- Made `FMT_THROW` assert even if assertions are disabled when + compiling with exceptions disabled + (https://github.com/fmtlib/fmt/issues/3418, + https://github.com/fmtlib/fmt/pull/3439). Thanks @BRevzin. + +- Made `format_as` and `std::filesystem::path` formatter work with + exotic code unit types. + (https://github.com/fmtlib/fmt/pull/3457, + https://github.com/fmtlib/fmt/pull/3476). Thanks @gix and @hmbj. + +- Added support for the `?` format specifier to + `std::filesystem::path` and made the default unescaped for + consistency with strings. + +- Deprecated the wide stream overload of `printf`. + +- Removed unused `basic_printf_parse_context`. + +- Improved RTTI detection used when formatting exceptions + (https://github.com/fmtlib/fmt/pull/3468). Thanks @danakj. + +- Improved compatibility with VxWorks7 + (https://github.com/fmtlib/fmt/pull/3467). Thanks @wenshan1. + +- Improved documentation + (https://github.com/fmtlib/fmt/issues/3174, + https://github.com/fmtlib/fmt/issues/3423, + https://github.com/fmtlib/fmt/pull/3454, + https://github.com/fmtlib/fmt/issues/3458, + https://github.com/fmtlib/fmt/pull/3461, + https://github.com/fmtlib/fmt/issues/3487, + https://github.com/fmtlib/fmt/pull/3515). + Thanks @zencatalyst, @rlalik and @mikecrowe. + +- Improved build and CI configurations + (https://github.com/fmtlib/fmt/issues/3449, + https://github.com/fmtlib/fmt/pull/3451, + https://github.com/fmtlib/fmt/pull/3452, + https://github.com/fmtlib/fmt/pull/3453, + https://github.com/fmtlib/fmt/pull/3459, + https://github.com/fmtlib/fmt/issues/3481, + https://github.com/fmtlib/fmt/pull/3486, + https://github.com/fmtlib/fmt/issues/3489, + https://github.com/fmtlib/fmt/pull/3496, + https://github.com/fmtlib/fmt/issues/3517, + https://github.com/fmtlib/fmt/pull/3523, + https://github.com/fmtlib/fmt/pull/3563). + Thanks @joycebrum, @glebm, @phprus, @petrmanek, @setoye and @abouvier. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/issues/3408, + https://github.com/fmtlib/fmt/issues/3424, + https://github.com/fmtlib/fmt/issues/3444, + https://github.com/fmtlib/fmt/pull/3446, + https://github.com/fmtlib/fmt/pull/3475, + https://github.com/fmtlib/fmt/pull/3482, + https://github.com/fmtlib/fmt/issues/3492, + https://github.com/fmtlib/fmt/pull/3493, + https://github.com/fmtlib/fmt/pull/3508, + https://github.com/fmtlib/fmt/issues/3509, + https://github.com/fmtlib/fmt/issues/3533, + https://github.com/fmtlib/fmt/pull/3542, + https://github.com/fmtlib/fmt/issues/3543, + https://github.com/fmtlib/fmt/issues/3540, + https://github.com/fmtlib/fmt/pull/3544, + https://github.com/fmtlib/fmt/issues/3548, + https://github.com/fmtlib/fmt/pull/3549, + https://github.com/fmtlib/fmt/pull/3550, + https://github.com/fmtlib/fmt/pull/3552). + Thanks @adesitter, @hmbj, @Minty-Meeo, @phprus, @TobiSchluter, + @kieranclancy, @alexeedm, @jurihock, @Ozomahtli and @razaqq. + +# 10.0.0 - 2023-05-09 + +- Replaced Grisu with a new floating-point formatting algorithm for + given precision (https://github.com/fmtlib/fmt/issues/3262, + https://github.com/fmtlib/fmt/issues/2750, + https://github.com/fmtlib/fmt/pull/3269, + https://github.com/fmtlib/fmt/pull/3276). The new algorithm + is based on Dragonbox already used for the shortest representation + and gives substantial performance improvement: + + ![](https://user-images.githubusercontent.com/33922675/211956670-84891a09-6867-47d9-82fc-3230da7abe0f.png) + + - Red: new algorithm + - Green: new algorithm with `FMT_USE_FULL_CACHE_DRAGONBOX` defined + to 1 + - Blue: old algorithm + + Thanks @jk-jeon. + +- Replaced `snprintf`-based hex float formatter with an internal + implementation (https://github.com/fmtlib/fmt/pull/3179, + https://github.com/fmtlib/fmt/pull/3203). This removes the + last usage of `s(n)printf` in {fmt}. Thanks @phprus. + +- Fixed alignment of floating-point numbers with localization + (https://github.com/fmtlib/fmt/issues/3263, + https://github.com/fmtlib/fmt/pull/3272). Thanks @ShawnZhong. + +- Made handling of `#` consistent with `std::format`. + +- Improved C++20 module support + (https://github.com/fmtlib/fmt/pull/3134, + https://github.com/fmtlib/fmt/pull/3254, + https://github.com/fmtlib/fmt/pull/3386, + https://github.com/fmtlib/fmt/pull/3387, + https://github.com/fmtlib/fmt/pull/3388, + https://github.com/fmtlib/fmt/pull/3392, + https://github.com/fmtlib/fmt/pull/3397, + https://github.com/fmtlib/fmt/pull/3399, + https://github.com/fmtlib/fmt/pull/3400). + Thanks @laitingsheng, @Orvid and @DanielaE. + +- Switched to the [modules CMake library](https://github.com/vitaut/modules) + which allows building {fmt} as a C++20 module with clang: + + CXX=clang++ cmake -DFMT_MODULE=ON . + make + +- Made `format_as` work with any user-defined type and not just enums. + For example ([godbolt](https://godbolt.org/z/b7rqhq5Kh)): + + ```c++ + #include + + struct floaty_mc_floatface { + double value; + }; + + auto format_as(floaty_mc_floatface f) { return f.value; } + + int main() { + fmt::print("{:8}\n", floaty_mc_floatface{0.42}); // prints " 0.42" + } + ``` + +- Removed deprecated implicit conversions for enums and conversions to + primitive types for compatibility with `std::format` and to prevent + potential ODR violations. Use `format_as` instead. + +- Added support for fill, align and width to the time point formatter + (https://github.com/fmtlib/fmt/issues/3237, + https://github.com/fmtlib/fmt/pull/3260, + https://github.com/fmtlib/fmt/pull/3275). For example + ([godbolt](https://godbolt.org/z/rKP6MGz6c)): + + ```c++ + #include + + int main() { + // prints " 2023" + fmt::print("{:>8%Y}\n", std::chrono::system_clock::now()); + } + ``` + + Thanks @ShawnZhong. + +- Implemented formatting of subseconds + (https://github.com/fmtlib/fmt/issues/2207, + https://github.com/fmtlib/fmt/issues/3117, + https://github.com/fmtlib/fmt/pull/3115, + https://github.com/fmtlib/fmt/pull/3143, + https://github.com/fmtlib/fmt/pull/3144, + https://github.com/fmtlib/fmt/pull/3349). For example + ([godbolt](https://godbolt.org/z/45738oGEo)): + + ```c++ + #include + + int main() { + // prints 01.234567 + fmt::print("{:%S}\n", std::chrono::microseconds(1234567)); + } + ``` + + Thanks @patrickroocks @phprus and @BRevzin. + +- Added precision support to `%S` + (https://github.com/fmtlib/fmt/pull/3148). Thanks @SappyJoy + +- Added support for `std::utc_time` + (https://github.com/fmtlib/fmt/issues/3098, + https://github.com/fmtlib/fmt/pull/3110). Thanks @patrickroocks. + +- Switched formatting of `std::chrono::system_clock` from local time + to UTC for compatibility with the standard + (https://github.com/fmtlib/fmt/issues/3199, + https://github.com/fmtlib/fmt/pull/3230). Thanks @ned14. + +- Added support for `%Ez` and `%Oz` to chrono formatters. + (https://github.com/fmtlib/fmt/issues/3220, + https://github.com/fmtlib/fmt/pull/3222). Thanks @phprus. + +- Improved validation of format specifiers for `std::chrono::duration` + (https://github.com/fmtlib/fmt/issues/3219, + https://github.com/fmtlib/fmt/pull/3232). Thanks @ShawnZhong. + +- Fixed formatting of time points before the epoch + (https://github.com/fmtlib/fmt/issues/3117, + https://github.com/fmtlib/fmt/pull/3261). For example + ([godbolt](https://godbolt.org/z/f7bcznb3W)): + + ```c++ + #include + + int main() { + auto t = std::chrono::system_clock::from_time_t(0) - + std::chrono::milliseconds(250); + fmt::print("{:%S}\n", t); // prints 59.750000000 + } + ``` + + Thanks @ShawnZhong. + +- Experimental: implemented glibc extension for padding seconds, + minutes and hours + (https://github.com/fmtlib/fmt/issues/2959, + https://github.com/fmtlib/fmt/pull/3271). Thanks @ShawnZhong. + +- Added a formatter for `std::exception` + (https://github.com/fmtlib/fmt/issues/2977, + https://github.com/fmtlib/fmt/issues/3012, + https://github.com/fmtlib/fmt/pull/3062, + https://github.com/fmtlib/fmt/pull/3076, + https://github.com/fmtlib/fmt/pull/3119). For example + ([godbolt](https://godbolt.org/z/8xoWGs9e4)): + + ```c++ + #include + #include + + int main() { + try { + std::vector().at(0); + } catch(const std::exception& e) { + fmt::print("{}", e); + } + } + ``` + + prints: + + vector::_M_range_check: __n (which is 0) >= this->size() (which is 0) + + on libstdc++. Thanks @zach2good and @phprus. + +- Moved `std::error_code` formatter from `fmt/os.h` to `fmt/std.h`. + (https://github.com/fmtlib/fmt/pull/3125). Thanks @phprus. + +- Added formatters for standard container adapters: + `std::priority_queue`, `std::queue` and `std::stack` + (https://github.com/fmtlib/fmt/issues/3215, + https://github.com/fmtlib/fmt/pull/3279). For example + ([godbolt](https://godbolt.org/z/74h1xY9qK)): + + ```c++ + #include + #include + #include + + int main() { + auto s = std::stack>(); + for (auto b: {true, false, true}) s.push(b); + fmt::print("{}\n", s); // prints [true, false, true] + } + ``` + + Thanks @ShawnZhong. + +- Added a formatter for `std::optional` to `fmt/std.h` + (https://github.com/fmtlib/fmt/issues/1367, + https://github.com/fmtlib/fmt/pull/3303). + Thanks @tom-huntington. + +- Fixed formatting of valueless by exception variants + (https://github.com/fmtlib/fmt/pull/3347). Thanks @TheOmegaCarrot. + +- Made `fmt::ptr` accept `unique_ptr` with a custom deleter + (https://github.com/fmtlib/fmt/pull/3177). Thanks @hmbj. + +- Fixed formatting of noncopyable ranges and nested ranges of chars + (https://github.com/fmtlib/fmt/pull/3158 + https://github.com/fmtlib/fmt/issues/3286, + https://github.com/fmtlib/fmt/pull/3290). Thanks @BRevzin. + +- Fixed issues with formatting of paths and ranges of paths + (https://github.com/fmtlib/fmt/issues/3319, + https://github.com/fmtlib/fmt/pull/3321 + https://github.com/fmtlib/fmt/issues/3322). Thanks @phprus. + +- Improved handling of invalid Unicode in paths. + +- Enabled compile-time checks on Apple clang 14 and later + (https://github.com/fmtlib/fmt/pull/3331). Thanks @cloyce. + +- Improved compile-time checks of named arguments + (https://github.com/fmtlib/fmt/issues/3105, + https://github.com/fmtlib/fmt/pull/3214). Thanks @rbrich. + +- Fixed formatting when both alignment and `0` are given + (https://github.com/fmtlib/fmt/issues/3236, + https://github.com/fmtlib/fmt/pull/3248). Thanks @ShawnZhong. + +- Improved Unicode support in the experimental file API on Windows + (https://github.com/fmtlib/fmt/issues/3234, + https://github.com/fmtlib/fmt/pull/3293). Thanks @Fros1er. + +- Unified UTF transcoding + (https://github.com/fmtlib/fmt/pull/3416). Thanks @phprus. + +- Added support for UTF-8 digit separators via an experimental locale + facet (https://github.com/fmtlib/fmt/issues/1861). For + example ([godbolt](https://godbolt.org/z/f7bcznb3W)): + + ```c++ + auto loc = std::locale( + std::locale(), new fmt::format_facet("’")); + auto s = fmt::format(loc, "{:L}", 1000); + ``` + + where `’` is U+2019 used as a digit separator in the de_CH locale. + +- Added an overload of `formatted_size` that takes a locale + (https://github.com/fmtlib/fmt/issues/3084, + https://github.com/fmtlib/fmt/pull/3087). Thanks @gerboengels. + +- Removed the deprecated `FMT_DEPRECATED_OSTREAM`. + +- Fixed a UB when using a null `std::string_view` with + `fmt::to_string` or format string compilation + (https://github.com/fmtlib/fmt/issues/3241, + https://github.com/fmtlib/fmt/pull/3244). Thanks @phprus. + +- Added `starts_with` to the fallback `string_view` implementation + (https://github.com/fmtlib/fmt/pull/3080). Thanks @phprus. + +- Added `fmt::basic_format_string::get()` for compatibility with + `basic_format_string` + (https://github.com/fmtlib/fmt/pull/3111). Thanks @huangqinjin. + +- Added `println` for compatibility with C++23 + (https://github.com/fmtlib/fmt/pull/3267). Thanks @ShawnZhong. + +- Renamed the `FMT_EXPORT` macro for shared library usage to + `FMT_LIB_EXPORT`. + +- Improved documentation + (https://github.com/fmtlib/fmt/issues/3108, + https://github.com/fmtlib/fmt/issues/3169, + https://github.com/fmtlib/fmt/pull/3243). + https://github.com/fmtlib/fmt/pull/3404, + https://github.com/fmtlib/fmt/pull/4002). + Thanks @Cleroth, @Vertexwahn and @yujincheng08. + +- Improved build configuration and tests + (https://github.com/fmtlib/fmt/pull/3118, + https://github.com/fmtlib/fmt/pull/3120, + https://github.com/fmtlib/fmt/pull/3188, + https://github.com/fmtlib/fmt/issues/3189, + https://github.com/fmtlib/fmt/pull/3198, + https://github.com/fmtlib/fmt/pull/3205, + https://github.com/fmtlib/fmt/pull/3207, + https://github.com/fmtlib/fmt/pull/3210, + https://github.com/fmtlib/fmt/pull/3240, + https://github.com/fmtlib/fmt/pull/3256, + https://github.com/fmtlib/fmt/pull/3264, + https://github.com/fmtlib/fmt/issues/3299, + https://github.com/fmtlib/fmt/pull/3302, + https://github.com/fmtlib/fmt/pull/3312, + https://github.com/fmtlib/fmt/issues/3317, + https://github.com/fmtlib/fmt/pull/3328, + https://github.com/fmtlib/fmt/pull/3333, + https://github.com/fmtlib/fmt/pull/3369, + https://github.com/fmtlib/fmt/issues/3373, + https://github.com/fmtlib/fmt/pull/3395, + https://github.com/fmtlib/fmt/pull/3406, + https://github.com/fmtlib/fmt/pull/3411). + Thanks @dimztimz, @phprus, @DavidKorczynski, @ChrisThrasher, + @FrancoisCarouge, @kennyweiss, @luzpaz, @codeinred, @Mixaill, @joycebrum, + @kevinhwang and @Vertexwahn. + +- Fixed a regression in handling empty format specifiers after a colon + (`{:}`) (https://github.com/fmtlib/fmt/pull/3086). Thanks @oxidase. + +- Worked around a broken implementation of + `std::is_constant_evaluated` in some versions of libstdc++ on clang + (https://github.com/fmtlib/fmt/issues/3247, + https://github.com/fmtlib/fmt/pull/3281). Thanks @phprus. + +- Fixed formatting of volatile variables + (https://github.com/fmtlib/fmt/pull/3068). + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/pull/3057, + https://github.com/fmtlib/fmt/pull/3066, + https://github.com/fmtlib/fmt/pull/3072, + https://github.com/fmtlib/fmt/pull/3082, + https://github.com/fmtlib/fmt/pull/3091, + https://github.com/fmtlib/fmt/issues/3092, + https://github.com/fmtlib/fmt/pull/3093, + https://github.com/fmtlib/fmt/pull/3095, + https://github.com/fmtlib/fmt/issues/3096, + https://github.com/fmtlib/fmt/pull/3097, + https://github.com/fmtlib/fmt/issues/3128, + https://github.com/fmtlib/fmt/pull/3129, + https://github.com/fmtlib/fmt/pull/3137, + https://github.com/fmtlib/fmt/pull/3139, + https://github.com/fmtlib/fmt/issues/3140, + https://github.com/fmtlib/fmt/pull/3142, + https://github.com/fmtlib/fmt/issues/3149, + https://github.com/fmtlib/fmt/pull/3150, + https://github.com/fmtlib/fmt/issues/3154, + https://github.com/fmtlib/fmt/issues/3163, + https://github.com/fmtlib/fmt/issues/3178, + https://github.com/fmtlib/fmt/pull/3184, + https://github.com/fmtlib/fmt/pull/3196, + https://github.com/fmtlib/fmt/issues/3204, + https://github.com/fmtlib/fmt/pull/3206, + https://github.com/fmtlib/fmt/pull/3208, + https://github.com/fmtlib/fmt/issues/3213, + https://github.com/fmtlib/fmt/pull/3216, + https://github.com/fmtlib/fmt/issues/3224, + https://github.com/fmtlib/fmt/issues/3226, + https://github.com/fmtlib/fmt/issues/3228, + https://github.com/fmtlib/fmt/pull/3229, + https://github.com/fmtlib/fmt/pull/3259, + https://github.com/fmtlib/fmt/issues/3274, + https://github.com/fmtlib/fmt/issues/3287, + https://github.com/fmtlib/fmt/pull/3288, + https://github.com/fmtlib/fmt/issues/3292, + https://github.com/fmtlib/fmt/pull/3295, + https://github.com/fmtlib/fmt/pull/3296, + https://github.com/fmtlib/fmt/issues/3298, + https://github.com/fmtlib/fmt/issues/3325, + https://github.com/fmtlib/fmt/pull/3326, + https://github.com/fmtlib/fmt/issues/3334, + https://github.com/fmtlib/fmt/issues/3342, + https://github.com/fmtlib/fmt/pull/3343, + https://github.com/fmtlib/fmt/issues/3351, + https://github.com/fmtlib/fmt/pull/3352, + https://github.com/fmtlib/fmt/pull/3362, + https://github.com/fmtlib/fmt/issues/3365, + https://github.com/fmtlib/fmt/pull/3366, + https://github.com/fmtlib/fmt/pull/3374, + https://github.com/fmtlib/fmt/issues/3377, + https://github.com/fmtlib/fmt/pull/3378, + https://github.com/fmtlib/fmt/issues/3381, + https://github.com/fmtlib/fmt/pull/3398, + https://github.com/fmtlib/fmt/pull/3413, + https://github.com/fmtlib/fmt/issues/3415). + Thanks @phprus, @gsjaardema, @NewbieOrange, @EngineLessCC, @asmaloney, + @HazardyKnusperkeks, @sergiud, @Youw, @thesmurph, @czudziakm, + @Roman-Koshelev, @chronoxor, @ShawnZhong, @russelltg, @glebm, @tmartin-gh, + @Zhaojun-Liu, @louiswins and @mogemimi. + +# 9.1.0 - 2022-08-27 + +- `fmt::formatted_size` now works at compile time + (https://github.com/fmtlib/fmt/pull/3026). For example + ([godbolt](https://godbolt.org/z/1MW5rMdf8)): + + ```c++ + #include + + int main() { + using namespace fmt::literals; + constexpr size_t n = fmt::formatted_size("{}"_cf, 42); + fmt::print("{}\n", n); // prints 2 + } + ``` + + Thanks @marksantaniello. + +- Fixed handling of invalid UTF-8 + (https://github.com/fmtlib/fmt/pull/3038, + https://github.com/fmtlib/fmt/pull/3044, + https://github.com/fmtlib/fmt/pull/3056). + Thanks @phprus and @skeeto. + +- Improved Unicode support in `ostream` overloads of `print` + (https://github.com/fmtlib/fmt/pull/2994, + https://github.com/fmtlib/fmt/pull/3001, + https://github.com/fmtlib/fmt/pull/3025). Thanks @dimztimz. + +- Fixed handling of the sign specifier in localized formatting on + systems with 32-bit `wchar_t` + (https://github.com/fmtlib/fmt/issues/3041). + +- Added support for wide streams to `fmt::streamed` + (https://github.com/fmtlib/fmt/pull/2994). Thanks @phprus. + +- Added the `n` specifier that disables the output of delimiters when + formatting ranges (https://github.com/fmtlib/fmt/pull/2981, + https://github.com/fmtlib/fmt/pull/2983). For example + ([godbolt](https://godbolt.org/z/roKqGdj8c)): + + ```c++ + #include + #include + + int main() { + auto v = std::vector{1, 2, 3}; + fmt::print("{:n}\n", v); // prints 1, 2, 3 + } + ``` + + Thanks @BRevzin. + +- Worked around problematic `std::string_view` constructors introduced + in C++23 (https://github.com/fmtlib/fmt/issues/3030, + https://github.com/fmtlib/fmt/issues/3050). Thanks @strega-nil-ms. + +- Improve handling (exclusion) of recursive ranges + (https://github.com/fmtlib/fmt/issues/2968, + https://github.com/fmtlib/fmt/pull/2974). Thanks @Dani-Hub. + +- Improved error reporting in format string compilation + (https://github.com/fmtlib/fmt/issues/3055). + +- Improved the implementation of + [Dragonbox](https://github.com/jk-jeon/dragonbox), the algorithm + used for the default floating-point formatting + (https://github.com/fmtlib/fmt/pull/2984). Thanks @jk-jeon. + +- Fixed issues with floating-point formatting on exotic platforms. + +- Improved the implementation of chrono formatting + (https://github.com/fmtlib/fmt/pull/3010). Thanks @phprus. + +- Improved documentation + (https://github.com/fmtlib/fmt/pull/2966, + https://github.com/fmtlib/fmt/pull/3009, + https://github.com/fmtlib/fmt/issues/3020, + https://github.com/fmtlib/fmt/pull/3037). + Thanks @mwinterb, @jcelerier and @remiburtin. + +- Improved build configuration + (https://github.com/fmtlib/fmt/pull/2991, + https://github.com/fmtlib/fmt/pull/2995, + https://github.com/fmtlib/fmt/issues/3004, + https://github.com/fmtlib/fmt/pull/3007, + https://github.com/fmtlib/fmt/pull/3040). + Thanks @dimztimz and @hwhsu1231. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/issues/2969, + https://github.com/fmtlib/fmt/pull/2971, + https://github.com/fmtlib/fmt/issues/2975, + https://github.com/fmtlib/fmt/pull/2982, + https://github.com/fmtlib/fmt/pull/2985, + https://github.com/fmtlib/fmt/issues/2988, + https://github.com/fmtlib/fmt/issues/2989, + https://github.com/fmtlib/fmt/issues/3000, + https://github.com/fmtlib/fmt/issues/3006, + https://github.com/fmtlib/fmt/issues/3014, + https://github.com/fmtlib/fmt/issues/3015, + https://github.com/fmtlib/fmt/pull/3021, + https://github.com/fmtlib/fmt/issues/3023, + https://github.com/fmtlib/fmt/pull/3024, + https://github.com/fmtlib/fmt/pull/3029, + https://github.com/fmtlib/fmt/pull/3043, + https://github.com/fmtlib/fmt/issues/3052, + https://github.com/fmtlib/fmt/pull/3053, + https://github.com/fmtlib/fmt/pull/3054). + Thanks @h-friederich, @dimztimz, @olupton, @bernhardmgruber and @phprus. + +# 9.0.0 - 2022-07-04 + +- Switched to the internal floating point formatter for all decimal + presentation formats. In particular this results in consistent + rounding on all platforms and removing the `s[n]printf` fallback for + decimal FP formatting. + +- Compile-time floating point formatting no longer requires the + header-only mode. For example + ([godbolt](https://godbolt.org/z/G37PTeG3b)): + + ```c++ + #include + #include + + consteval auto compile_time_dtoa(double value) -> std::array { + auto result = std::array(); + fmt::format_to(result.data(), FMT_COMPILE("{}"), value); + return result; + } + + constexpr auto answer = compile_time_dtoa(0.42); + ``` + + works with the default settings. + +- Improved the implementation of + [Dragonbox](https://github.com/jk-jeon/dragonbox), the algorithm + used for the default floating-point formatting + (https://github.com/fmtlib/fmt/pull/2713, + https://github.com/fmtlib/fmt/pull/2750). Thanks @jk-jeon. + +- Made `fmt::to_string` work with `__float128`. This uses the internal + FP formatter and works even on system without `__float128` support + in `[s]printf`. + +- Disabled automatic `std::ostream` insertion operator (`operator<<`) + discovery when `fmt/ostream.h` is included to prevent ODR + violations. You can get the old behavior by defining + `FMT_DEPRECATED_OSTREAM` but this will be removed in the next major + release. Use `fmt::streamed` or `fmt::ostream_formatter` to enable + formatting via `std::ostream` instead. + +- Added `fmt::ostream_formatter` that can be used to write `formatter` + specializations that perform formatting via `std::ostream`. For + example ([godbolt](https://godbolt.org/z/5sEc5qMsf)): + + ```c++ + #include + + struct date { + int year, month, day; + + friend std::ostream& operator<<(std::ostream& os, const date& d) { + return os << d.year << '-' << d.month << '-' << d.day; + } + }; + + template <> struct fmt::formatter : ostream_formatter {}; + + std::string s = fmt::format("The date is {}", date{2012, 12, 9}); + // s == "The date is 2012-12-9" + ``` + +- Added the `fmt::streamed` function that takes an object and formats + it via `std::ostream`. For example + ([godbolt](https://godbolt.org/z/5G3346G1f)): + + ```c++ + #include + #include + + int main() { + fmt::print("Current thread id: {}\n", + fmt::streamed(std::this_thread::get_id())); + } + ``` + + Note that `fmt/std.h` provides a `formatter` specialization for + `std::thread::id` so you don\'t need to format it via + `std::ostream`. + +- Deprecated implicit conversions of unscoped enums to integers for + consistency with scoped enums. + +- Added an argument-dependent lookup based `format_as` extension API + to simplify formatting of enums. + +- Added experimental `std::variant` formatting support + (https://github.com/fmtlib/fmt/pull/2941). For example + ([godbolt](https://godbolt.org/z/KG9z6cq68)): + + ```c++ + #include + #include + + int main() { + auto v = std::variant(42); + fmt::print("{}\n", v); + } + ``` + + prints: + + variant(42) + + Thanks @jehelset. + +- Added experimental `std::filesystem::path` formatting support + (https://github.com/fmtlib/fmt/issues/2865, + https://github.com/fmtlib/fmt/pull/2902, + https://github.com/fmtlib/fmt/issues/2917, + https://github.com/fmtlib/fmt/pull/2918). For example + ([godbolt](https://godbolt.org/z/o44dMexEb)): + + ```c++ + #include + #include + + int main() { + fmt::print("There is no place like {}.", std::filesystem::path("/home")); + } + ``` + + prints: + + There is no place like "/home". + + Thanks @phprus. + +- Added a `std::thread::id` formatter to `fmt/std.h`. For example + ([godbolt](https://godbolt.org/z/j1azbYf3E)): + + ```c++ + #include + #include + + int main() { + fmt::print("Current thread id: {}\n", std::this_thread::get_id()); + } + ``` + +- Added `fmt::styled` that applies a text style to an individual + argument (https://github.com/fmtlib/fmt/pull/2793). For + example ([godbolt](https://godbolt.org/z/vWGW7v5M6)): + + ```c++ + #include + #include + + int main() { + auto now = std::chrono::system_clock::now(); + fmt::print( + "[{}] {}: {}\n", + fmt::styled(now, fmt::emphasis::bold), + fmt::styled("error", fg(fmt::color::red)), + "something went wrong"); + } + ``` + + prints + + ![](https://user-images.githubusercontent.com/576385/175071215-12809244-dab0-4005-96d8-7cd911c964d5.png) + + Thanks @rbrugo. + +- Made `fmt::print` overload for text styles correctly handle UTF-8 + (https://github.com/fmtlib/fmt/issues/2681, + https://github.com/fmtlib/fmt/pull/2701). Thanks @AlexGuteniev. + +- Fixed Unicode handling when writing to an ostream. + +- Added support for nested specifiers to range formatting + (https://github.com/fmtlib/fmt/pull/2673). For example + ([godbolt](https://godbolt.org/z/xd3Gj38cf)): + + ```c++ + #include + #include + + int main() { + fmt::print("{::#x}\n", std::vector{10, 20, 30}); + } + ``` + + prints `[0xa, 0x14, 0x1e]`. + + Thanks @BRevzin. + +- Implemented escaping of wide strings in ranges + (https://github.com/fmtlib/fmt/pull/2904). Thanks @phprus. + +- Added support for ranges with `begin` / `end` found via the + argument-dependent lookup + (https://github.com/fmtlib/fmt/pull/2807). Thanks @rbrugo. + +- Fixed formatting of certain kinds of ranges of ranges + (https://github.com/fmtlib/fmt/pull/2787). Thanks @BRevzin. + +- Fixed handling of maps with element types other than `std::pair` + (https://github.com/fmtlib/fmt/pull/2944). Thanks @BrukerJWD. + +- Made tuple formatter enabled only if elements are formattable + (https://github.com/fmtlib/fmt/issues/2939, + https://github.com/fmtlib/fmt/pull/2940). Thanks @jehelset. + +- Made `fmt::join` compatible with format string compilation + (https://github.com/fmtlib/fmt/issues/2719, + https://github.com/fmtlib/fmt/pull/2720). Thanks @phprus. + +- Made compile-time checks work with named arguments of custom types + and `std::ostream` `print` overloads + (https://github.com/fmtlib/fmt/issues/2816, + https://github.com/fmtlib/fmt/issues/2817, + https://github.com/fmtlib/fmt/pull/2819). Thanks @timsong-cpp. + +- Removed `make_args_checked` because it is no longer needed for + compile-time checks + (https://github.com/fmtlib/fmt/pull/2760). Thanks @phprus. + +- Removed the following deprecated APIs: `_format`, `arg_join`, the + `format_to` overload that takes a memory buffer, `[v]fprintf` that + takes an `ostream`. + +- Removed the deprecated implicit conversion of `[const] signed char*` + and `[const] unsigned char*` to C strings. + +- Removed the deprecated `fmt/locale.h`. + +- Replaced the deprecated `fileno()` with `descriptor()` in + `buffered_file`. + +- Moved `to_string_view` to the `detail` namespace since it\'s an + implementation detail. + +- Made access mode of a created file consistent with `fopen` by + setting `S_IWGRP` and `S_IWOTH` + (https://github.com/fmtlib/fmt/pull/2733). Thanks @arogge. + +- Removed a redundant buffer resize when formatting to `std::ostream` + (https://github.com/fmtlib/fmt/issues/2842, + https://github.com/fmtlib/fmt/pull/2843). Thanks @jcelerier. + +- Made precision computation for strings consistent with width + (https://github.com/fmtlib/fmt/issues/2888). + +- Fixed handling of locale separators in floating point formatting + (https://github.com/fmtlib/fmt/issues/2830). + +- Made sign specifiers work with `__int128_t` + (https://github.com/fmtlib/fmt/issues/2773). + +- Improved support for systems such as CHERI with extra data stored in + pointers (https://github.com/fmtlib/fmt/pull/2932). + Thanks @davidchisnall. + +- Improved documentation + (https://github.com/fmtlib/fmt/pull/2706, + https://github.com/fmtlib/fmt/pull/2712, + https://github.com/fmtlib/fmt/pull/2789, + https://github.com/fmtlib/fmt/pull/2803, + https://github.com/fmtlib/fmt/pull/2805, + https://github.com/fmtlib/fmt/pull/2815, + https://github.com/fmtlib/fmt/pull/2924). + Thanks @BRevzin, @Pokechu22, @setoye, @rtobar, @rbrugo, @anoonD and + @leha-bot. + +- Improved build configuration + (https://github.com/fmtlib/fmt/pull/2766, + https://github.com/fmtlib/fmt/pull/2772, + https://github.com/fmtlib/fmt/pull/2836, + https://github.com/fmtlib/fmt/pull/2852, + https://github.com/fmtlib/fmt/pull/2907, + https://github.com/fmtlib/fmt/pull/2913, + https://github.com/fmtlib/fmt/pull/2914). + Thanks @kambala-decapitator, @mattiasljungstrom, @kieselnb, @nathannaveen + and @Vertexwahn. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/issues/2408, + https://github.com/fmtlib/fmt/issues/2507, + https://github.com/fmtlib/fmt/issues/2697, + https://github.com/fmtlib/fmt/issues/2715, + https://github.com/fmtlib/fmt/issues/2717, + https://github.com/fmtlib/fmt/pull/2722, + https://github.com/fmtlib/fmt/pull/2724, + https://github.com/fmtlib/fmt/pull/2725, + https://github.com/fmtlib/fmt/issues/2726, + https://github.com/fmtlib/fmt/pull/2728, + https://github.com/fmtlib/fmt/pull/2732, + https://github.com/fmtlib/fmt/issues/2738, + https://github.com/fmtlib/fmt/pull/2742, + https://github.com/fmtlib/fmt/issues/2744, + https://github.com/fmtlib/fmt/issues/2745, + https://github.com/fmtlib/fmt/issues/2746, + https://github.com/fmtlib/fmt/issues/2754, + https://github.com/fmtlib/fmt/pull/2755, + https://github.com/fmtlib/fmt/issues/2757, + https://github.com/fmtlib/fmt/pull/2758, + https://github.com/fmtlib/fmt/issues/2761, + https://github.com/fmtlib/fmt/pull/2762, + https://github.com/fmtlib/fmt/issues/2763, + https://github.com/fmtlib/fmt/pull/2765, + https://github.com/fmtlib/fmt/issues/2769, + https://github.com/fmtlib/fmt/pull/2770, + https://github.com/fmtlib/fmt/issues/2771, + https://github.com/fmtlib/fmt/issues/2777, + https://github.com/fmtlib/fmt/pull/2779, + https://github.com/fmtlib/fmt/pull/2782, + https://github.com/fmtlib/fmt/pull/2783, + https://github.com/fmtlib/fmt/issues/2794, + https://github.com/fmtlib/fmt/issues/2796, + https://github.com/fmtlib/fmt/pull/2797, + https://github.com/fmtlib/fmt/pull/2801, + https://github.com/fmtlib/fmt/pull/2802, + https://github.com/fmtlib/fmt/issues/2808, + https://github.com/fmtlib/fmt/issues/2818, + https://github.com/fmtlib/fmt/pull/2819, + https://github.com/fmtlib/fmt/issues/2829, + https://github.com/fmtlib/fmt/issues/2835, + https://github.com/fmtlib/fmt/issues/2848, + https://github.com/fmtlib/fmt/issues/2860, + https://github.com/fmtlib/fmt/pull/2861, + https://github.com/fmtlib/fmt/pull/2882, + https://github.com/fmtlib/fmt/issues/2886, + https://github.com/fmtlib/fmt/issues/2891, + https://github.com/fmtlib/fmt/pull/2892, + https://github.com/fmtlib/fmt/issues/2895, + https://github.com/fmtlib/fmt/issues/2896, + https://github.com/fmtlib/fmt/pull/2903, + https://github.com/fmtlib/fmt/issues/2906, + https://github.com/fmtlib/fmt/issues/2908, + https://github.com/fmtlib/fmt/pull/2909, + https://github.com/fmtlib/fmt/issues/2920, + https://github.com/fmtlib/fmt/pull/2922, + https://github.com/fmtlib/fmt/pull/2927, + https://github.com/fmtlib/fmt/pull/2929, + https://github.com/fmtlib/fmt/issues/2936, + https://github.com/fmtlib/fmt/pull/2937, + https://github.com/fmtlib/fmt/pull/2938, + https://github.com/fmtlib/fmt/pull/2951, + https://github.com/fmtlib/fmt/issues/2954, + https://github.com/fmtlib/fmt/pull/2957, + https://github.com/fmtlib/fmt/issues/2958, + https://github.com/fmtlib/fmt/pull/2960). + Thanks @matrackif @Tobi823, @ivan-volnov, @VasiliPupkin256, + @federico-busato, @barcharcraz, @jk-jeon, @HazardyKnusperkeks, @dalboris, + @seanm, @gsjaardema, @timsong-cpp, @seanm, @frithrah, @chronoxor, @Agga, + @madmaxoft, @JurajX, @phprus and @Dani-Hub. + +# 8.1.1 - 2022-01-06 + +- Restored ABI compatibility with version 8.0.x + (https://github.com/fmtlib/fmt/issues/2695, + https://github.com/fmtlib/fmt/pull/2696). Thanks @saraedum. +- Fixed chrono formatting on big endian systems + (https://github.com/fmtlib/fmt/issues/2698, + https://github.com/fmtlib/fmt/pull/2699). + Thanks @phprus and @xvitaly. +- Fixed a linkage error with mingw + (https://github.com/fmtlib/fmt/issues/2691, + https://github.com/fmtlib/fmt/pull/2692). Thanks @rbberger. + +# 8.1.0 - 2022-01-02 + +- Optimized chrono formatting + (https://github.com/fmtlib/fmt/pull/2500, + https://github.com/fmtlib/fmt/pull/2537, + https://github.com/fmtlib/fmt/issues/2541, + https://github.com/fmtlib/fmt/pull/2544, + https://github.com/fmtlib/fmt/pull/2550, + https://github.com/fmtlib/fmt/pull/2551, + https://github.com/fmtlib/fmt/pull/2576, + https://github.com/fmtlib/fmt/issues/2577, + https://github.com/fmtlib/fmt/pull/2586, + https://github.com/fmtlib/fmt/pull/2591, + https://github.com/fmtlib/fmt/pull/2594, + https://github.com/fmtlib/fmt/pull/2602, + https://github.com/fmtlib/fmt/pull/2617, + https://github.com/fmtlib/fmt/issues/2628, + https://github.com/fmtlib/fmt/pull/2633, + https://github.com/fmtlib/fmt/issues/2670, + https://github.com/fmtlib/fmt/pull/2671). + + Processing of some specifiers such as `%z` and `%Y` is now up to + 10-20 times faster, for example on GCC 11 with libstdc++: + + ---------------------------------------------------------------------------- + Benchmark Before After + ---------------------------------------------------------------------------- + FMTFormatter_z 261 ns 26.3 ns + FMTFormatterCompile_z 246 ns 11.6 ns + FMTFormatter_Y 263 ns 26.1 ns + FMTFormatterCompile_Y 244 ns 10.5 ns + ---------------------------------------------------------------------------- + + Thanks @phprus and @toughengineer. + +- Implemented subsecond formatting for chrono durations + (https://github.com/fmtlib/fmt/pull/2623). For example + ([godbolt](https://godbolt.org/z/es7vWTETe)): + + ```c++ + #include + + int main() { + fmt::print("{:%S}", std::chrono::milliseconds(1234)); + } + ``` + + prints \"01.234\". + + Thanks @matrackif. + +- Fixed handling of precision 0 when formatting chrono durations + (https://github.com/fmtlib/fmt/issues/2587, + https://github.com/fmtlib/fmt/pull/2588). Thanks @lukester1975. + +- Fixed an overflow on invalid inputs in the `tm` formatter + (https://github.com/fmtlib/fmt/pull/2564). Thanks @phprus. + +- Added `fmt::group_digits` that formats integers with a non-localized + digit separator (comma) for groups of three digits. For example + ([godbolt](https://godbolt.org/z/TxGxG9Poq)): + + ```c++ + #include + + int main() { + fmt::print("{} dollars", fmt::group_digits(1000000)); + } + ``` + + prints \"1,000,000 dollars\". + +- Added support for faint, conceal, reverse and blink text styles + (https://github.com/fmtlib/fmt/pull/2394): + + + + Thanks @benit8 and @data-man. + +- Added experimental support for compile-time floating point + formatting (https://github.com/fmtlib/fmt/pull/2426, + https://github.com/fmtlib/fmt/pull/2470). It is currently + limited to the header-only mode. Thanks @alexezeder. + +- Added UDL-based named argument support to compile-time format string + checks (https://github.com/fmtlib/fmt/issues/2640, + https://github.com/fmtlib/fmt/pull/2649). For example + ([godbolt](https://godbolt.org/z/ohGbbvonv)): + + ```c++ + #include + + int main() { + using namespace fmt::literals; + fmt::print("{answer:s}", "answer"_a=42); + } + ``` + + gives a compile-time error on compilers with C++20 `consteval` and + non-type template parameter support (gcc 10+) because `s` is not a + valid format specifier for an integer. + + Thanks @alexezeder. + +- Implemented escaping of string range elements. For example + ([godbolt](https://godbolt.org/z/rKvM1vKf3)): + + ```c++ + #include + #include + + int main() { + fmt::print("{}", std::vector{"\naan"}); + } + ``` + + is now printed as: + + ["\naan"] + + instead of: + + [" + aan"] + +- Added an experimental `?` specifier for escaping strings. + (https://github.com/fmtlib/fmt/pull/2674). Thanks @BRevzin. + +- Switched to JSON-like representation of maps and sets for + consistency with Python\'s `str.format`. For example + ([godbolt](https://godbolt.org/z/seKjoY9W5)): + + ```c++ + #include + #include + + int main() { + fmt::print("{}", std::map{{"answer", 42}}); + } + ``` + + is now printed as: + + {"answer": 42} + +- Extended `fmt::join` to support C++20-only ranges + (https://github.com/fmtlib/fmt/pull/2549). Thanks @BRevzin. + +- Optimized handling of non-const-iterable ranges and implemented + initial support for non-const-formattable types. + +- Disabled implicit conversions of scoped enums to integers that was + accidentally introduced in earlier versions + (https://github.com/fmtlib/fmt/pull/1841). + +- Deprecated implicit conversion of `[const] signed char*` and + `[const] unsigned char*` to C strings. + +- Deprecated `_format`, a legacy UDL-based format API + (https://github.com/fmtlib/fmt/pull/2646). Thanks @alexezeder. + +- Marked `format`, `formatted_size` and `to_string` as `[[nodiscard]]` + (https://github.com/fmtlib/fmt/pull/2612). @0x8000-0000. + +- Added missing diagnostic when trying to format function and member + pointers as well as objects convertible to pointers which is + explicitly disallowed + (https://github.com/fmtlib/fmt/issues/2598, + https://github.com/fmtlib/fmt/pull/2609, + https://github.com/fmtlib/fmt/pull/2610). Thanks @AlexGuteniev. + +- Optimized writing to a contiguous buffer with `format_to_n` + (https://github.com/fmtlib/fmt/pull/2489). Thanks @Roman-Koshelev. + +- Optimized writing to non-`char` buffers + (https://github.com/fmtlib/fmt/pull/2477). Thanks @Roman-Koshelev. + +- Decimal point is now localized when using the `L` specifier. + +- Improved floating point formatter implementation + (https://github.com/fmtlib/fmt/pull/2498, + https://github.com/fmtlib/fmt/pull/2499). Thanks @Roman-Koshelev. + +- Fixed handling of very large precision in fixed format + (https://github.com/fmtlib/fmt/pull/2616). + +- Made a table of cached powers used in FP formatting static + (https://github.com/fmtlib/fmt/pull/2509). Thanks @jk-jeon. + +- Resolved a lookup ambiguity with C++20 format-related functions due + to ADL (https://github.com/fmtlib/fmt/issues/2639, + https://github.com/fmtlib/fmt/pull/2641). Thanks @mkurdej. + +- Removed unnecessary inline namespace qualification + (https://github.com/fmtlib/fmt/issues/2642, + https://github.com/fmtlib/fmt/pull/2643). Thanks @mkurdej. + +- Implemented argument forwarding in `format_to_n` + (https://github.com/fmtlib/fmt/issues/2462, + https://github.com/fmtlib/fmt/pull/2463). Thanks @owent. + +- Fixed handling of implicit conversions in `fmt::to_string` and + format string compilation + (https://github.com/fmtlib/fmt/issues/2565). + +- Changed the default access mode of files created by + `fmt::output_file` to `-rw-r--r--` for consistency with `fopen` + (https://github.com/fmtlib/fmt/issues/2530). + +- Make `fmt::ostream::flush` public + (https://github.com/fmtlib/fmt/issues/2435). + +- Improved C++14/17 attribute detection + (https://github.com/fmtlib/fmt/pull/2615). Thanks @AlexGuteniev. + +- Improved `consteval` detection for MSVC + (https://github.com/fmtlib/fmt/pull/2559). Thanks @DanielaE. + +- Improved documentation + (https://github.com/fmtlib/fmt/issues/2406, + https://github.com/fmtlib/fmt/pull/2446, + https://github.com/fmtlib/fmt/issues/2493, + https://github.com/fmtlib/fmt/issues/2513, + https://github.com/fmtlib/fmt/pull/2515, + https://github.com/fmtlib/fmt/issues/2522, + https://github.com/fmtlib/fmt/pull/2562, + https://github.com/fmtlib/fmt/pull/2575, + https://github.com/fmtlib/fmt/pull/2606, + https://github.com/fmtlib/fmt/pull/2620, + https://github.com/fmtlib/fmt/issues/2676). + Thanks @sobolevn, @UnePierre, @zhsj, @phprus, @ericcurtin and @Lounarok. + +- Improved fuzzers and added a fuzzer for chrono timepoint formatting + (https://github.com/fmtlib/fmt/pull/2461, + https://github.com/fmtlib/fmt/pull/2469). @pauldreik, + +- Added the `FMT_SYSTEM_HEADERS` CMake option setting which marks + {fmt}\'s headers as system. It can be used to suppress warnings + (https://github.com/fmtlib/fmt/issues/2644, + https://github.com/fmtlib/fmt/pull/2651). Thanks @alexezeder. + +- Added the Bazel build system support + (https://github.com/fmtlib/fmt/pull/2505, + https://github.com/fmtlib/fmt/pull/2516). Thanks @Vertexwahn. + +- Improved build configuration and tests + (https://github.com/fmtlib/fmt/issues/2437, + https://github.com/fmtlib/fmt/pull/2558, + https://github.com/fmtlib/fmt/pull/2648, + https://github.com/fmtlib/fmt/pull/2650, + https://github.com/fmtlib/fmt/pull/2663, + https://github.com/fmtlib/fmt/pull/2677). + Thanks @DanielaE, @alexezeder and @phprus. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/pull/2353, + https://github.com/fmtlib/fmt/pull/2356, + https://github.com/fmtlib/fmt/pull/2399, + https://github.com/fmtlib/fmt/issues/2408, + https://github.com/fmtlib/fmt/pull/2414, + https://github.com/fmtlib/fmt/pull/2427, + https://github.com/fmtlib/fmt/pull/2432, + https://github.com/fmtlib/fmt/pull/2442, + https://github.com/fmtlib/fmt/pull/2434, + https://github.com/fmtlib/fmt/issues/2439, + https://github.com/fmtlib/fmt/pull/2447, + https://github.com/fmtlib/fmt/pull/2450, + https://github.com/fmtlib/fmt/issues/2455, + https://github.com/fmtlib/fmt/issues/2465, + https://github.com/fmtlib/fmt/issues/2472, + https://github.com/fmtlib/fmt/issues/2474, + https://github.com/fmtlib/fmt/pull/2476, + https://github.com/fmtlib/fmt/issues/2478, + https://github.com/fmtlib/fmt/issues/2479, + https://github.com/fmtlib/fmt/issues/2481, + https://github.com/fmtlib/fmt/pull/2482, + https://github.com/fmtlib/fmt/pull/2483, + https://github.com/fmtlib/fmt/issues/2490, + https://github.com/fmtlib/fmt/pull/2491, + https://github.com/fmtlib/fmt/pull/2510, + https://github.com/fmtlib/fmt/pull/2518, + https://github.com/fmtlib/fmt/issues/2528, + https://github.com/fmtlib/fmt/pull/2529, + https://github.com/fmtlib/fmt/pull/2539, + https://github.com/fmtlib/fmt/issues/2540, + https://github.com/fmtlib/fmt/pull/2545, + https://github.com/fmtlib/fmt/pull/2555, + https://github.com/fmtlib/fmt/issues/2557, + https://github.com/fmtlib/fmt/issues/2570, + https://github.com/fmtlib/fmt/pull/2573, + https://github.com/fmtlib/fmt/pull/2582, + https://github.com/fmtlib/fmt/issues/2605, + https://github.com/fmtlib/fmt/pull/2611, + https://github.com/fmtlib/fmt/pull/2647, + https://github.com/fmtlib/fmt/issues/2627, + https://github.com/fmtlib/fmt/pull/2630, + https://github.com/fmtlib/fmt/issues/2635, + https://github.com/fmtlib/fmt/issues/2638, + https://github.com/fmtlib/fmt/issues/2653, + https://github.com/fmtlib/fmt/issues/2654, + https://github.com/fmtlib/fmt/issues/2661, + https://github.com/fmtlib/fmt/pull/2664, + https://github.com/fmtlib/fmt/pull/2684). + Thanks @DanielaE, @mwinterb, @cdacamar, @TrebledJ, @bodomartin, @cquammen, + @white238, @mmarkeloff, @palacaze, @jcelerier, @mborn-adi, @BrukerJWD, + @spyridon97, @phprus, @oliverlee, @joshessman-llnl, @akohlmey, @timkalu, + @olupton, @Acretock, @alexezeder, @andrewcorrigan, @lucpelletier and + @HazardyKnusperkeks. + +# 8.0.1 - 2021-07-02 + +- Fixed the version number in the inline namespace + (https://github.com/fmtlib/fmt/issues/2374). +- Added a missing presentation type check for `std::string` + (https://github.com/fmtlib/fmt/issues/2402). +- Fixed a linkage error when mixing code built with clang and gcc + (https://github.com/fmtlib/fmt/issues/2377). +- Fixed documentation issues + (https://github.com/fmtlib/fmt/pull/2396, + https://github.com/fmtlib/fmt/issues/2403, + https://github.com/fmtlib/fmt/issues/2406). Thanks @mkurdej. +- Removed dead code in FP formatter ( + https://github.com/fmtlib/fmt/pull/2398). Thanks @javierhonduco. +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/issues/2351, + https://github.com/fmtlib/fmt/issues/2359, + https://github.com/fmtlib/fmt/pull/2365, + https://github.com/fmtlib/fmt/issues/2368, + https://github.com/fmtlib/fmt/pull/2370, + https://github.com/fmtlib/fmt/pull/2376, + https://github.com/fmtlib/fmt/pull/2381, + https://github.com/fmtlib/fmt/pull/2382, + https://github.com/fmtlib/fmt/issues/2386, + https://github.com/fmtlib/fmt/pull/2389, + https://github.com/fmtlib/fmt/pull/2395, + https://github.com/fmtlib/fmt/pull/2397, + https://github.com/fmtlib/fmt/issues/2400, + https://github.com/fmtlib/fmt/issues/2401, + https://github.com/fmtlib/fmt/pull/2407). + Thanks @zx2c4, @AidanSun05, @mattiasljungstrom, @joemmett, @erengy, + @patlkli, @gsjaardema and @phprus. + +# 8.0.0 - 2021-06-21 + +- Enabled compile-time format string checks by default. For example + ([godbolt](https://godbolt.org/z/sMxcohGjz)): + + ```c++ + #include + + int main() { + fmt::print("{:d}", "I am not a number"); + } + ``` + + gives a compile-time error on compilers with C++20 `consteval` + support (gcc 10+, clang 11+) because `d` is not a valid format + specifier for a string. + + To pass a runtime string wrap it in `fmt::runtime`: + + ```c++ + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + ``` + +- Added compile-time formatting + (https://github.com/fmtlib/fmt/pull/2019, + https://github.com/fmtlib/fmt/pull/2044, + https://github.com/fmtlib/fmt/pull/2056, + https://github.com/fmtlib/fmt/pull/2072, + https://github.com/fmtlib/fmt/pull/2075, + https://github.com/fmtlib/fmt/issues/2078, + https://github.com/fmtlib/fmt/pull/2129, + https://github.com/fmtlib/fmt/pull/2326). For example + ([godbolt](https://godbolt.org/z/Mxx9d89jM)): + + ```c++ + #include + + consteval auto compile_time_itoa(int value) -> std::array { + auto result = std::array(); + fmt::format_to(result.data(), FMT_COMPILE("{}"), value); + return result; + } + + constexpr auto answer = compile_time_itoa(42); + ``` + + Most of the formatting functionality is available at compile time + with a notable exception of floating-point numbers and pointers. + Thanks @alexezeder. + +- Optimized handling of format specifiers during format string + compilation. For example, hexadecimal formatting (`"{:x}"`) is now + 3-7x faster than before when using `format_to` with format string + compilation and a stack-allocated buffer + (https://github.com/fmtlib/fmt/issues/1944). + + Before (7.1.3): + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + FMTCompileOld/0 15.5 ns 15.5 ns 43302898 + FMTCompileOld/42 16.6 ns 16.6 ns 43278267 + FMTCompileOld/273123 18.7 ns 18.6 ns 37035861 + FMTCompileOld/9223372036854775807 19.4 ns 19.4 ns 35243000 + ---------------------------------------------------------------------------- + + After (8.x): + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + FMTCompileNew/0 1.99 ns 1.99 ns 360523686 + FMTCompileNew/42 2.33 ns 2.33 ns 279865664 + FMTCompileNew/273123 3.72 ns 3.71 ns 190230315 + FMTCompileNew/9223372036854775807 5.28 ns 5.26 ns 130711631 + ---------------------------------------------------------------------------- + + It is even faster than `std::to_chars` from libc++ compiled with + clang on macOS: + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + ToChars/0 4.42 ns 4.41 ns 160196630 + ToChars/42 5.00 ns 4.98 ns 140735201 + ToChars/273123 7.26 ns 7.24 ns 95784130 + ToChars/9223372036854775807 8.77 ns 8.75 ns 75872534 + ---------------------------------------------------------------------------- + + In other cases, especially involving `std::string` construction, the + speed up is usually lower because handling format specifiers takes a + smaller fraction of the total time. + +- Added the `_cf` user-defined literal to represent a compiled format + string. It can be used instead of the `FMT_COMPILE` macro + (https://github.com/fmtlib/fmt/pull/2043, + https://github.com/fmtlib/fmt/pull/2242): + + ```c++ + #include + + using namespace fmt::literals; + auto s = fmt::format(FMT_COMPILE("{}"), 42); // 🙁 not modern + auto s = fmt::format("{}"_cf, 42); // 🙂 modern as hell + ``` + + It requires compiler support for class types in non-type template + parameters (a C++20 feature) which is available in GCC 9.3+. + Thanks @alexezeder. + +- Format string compilation now requires `format` functions of + `formatter` specializations for user-defined types to be `const`: + + ```c++ + template <> struct fmt::formatter: formatter { + template + auto format(my_type obj, FormatContext& ctx) const { // Note const here. + // ... + } + }; + ``` + +- Added UDL-based named argument support to format string compilation + (https://github.com/fmtlib/fmt/pull/2243, + https://github.com/fmtlib/fmt/pull/2281). For example: + + ```c++ + #include + + using namespace fmt::literals; + auto s = fmt::format(FMT_COMPILE("{answer}"), "answer"_a = 42); + ``` + + Here the argument named \"answer\" is resolved at compile time with + no runtime overhead. Thanks @alexezeder. + +- Added format string compilation support to `fmt::print` + (https://github.com/fmtlib/fmt/issues/2280, + https://github.com/fmtlib/fmt/pull/2304). Thanks @alexezeder. + +- Added initial support for compiling {fmt} as a C++20 module + (https://github.com/fmtlib/fmt/pull/2235, + https://github.com/fmtlib/fmt/pull/2240, + https://github.com/fmtlib/fmt/pull/2260, + https://github.com/fmtlib/fmt/pull/2282, + https://github.com/fmtlib/fmt/pull/2283, + https://github.com/fmtlib/fmt/pull/2288, + https://github.com/fmtlib/fmt/pull/2298, + https://github.com/fmtlib/fmt/pull/2306, + https://github.com/fmtlib/fmt/pull/2307, + https://github.com/fmtlib/fmt/pull/2309, + https://github.com/fmtlib/fmt/pull/2318, + https://github.com/fmtlib/fmt/pull/2324, + https://github.com/fmtlib/fmt/pull/2332, + https://github.com/fmtlib/fmt/pull/2340). Thanks @DanielaE. + +- Made symbols private by default reducing shared library size + (https://github.com/fmtlib/fmt/pull/2301). For example + there was a \~15% reported reduction on one platform. Thanks @sergiud. + +- Optimized includes making the result of preprocessing `fmt/format.h` + \~20% smaller with libstdc++/C++20 and slightly improving build + times (https://github.com/fmtlib/fmt/issues/1998). + +- Added support of ranges with non-const `begin` / `end` + (https://github.com/fmtlib/fmt/pull/1953). Thanks @kitegi. + +- Added support of `std::byte` and other formattable types to + `fmt::join` (https://github.com/fmtlib/fmt/issues/1981, + https://github.com/fmtlib/fmt/issues/2040, + https://github.com/fmtlib/fmt/pull/2050, + https://github.com/fmtlib/fmt/issues/2262). For example: + + ```c++ + #include + #include + #include + + int main() { + auto bytes = std::vector{std::byte(4), std::byte(2)}; + fmt::print("{}", fmt::join(bytes, "")); + } + ``` + + prints \"42\". + + Thanks @kamibo. + +- Implemented the default format for `std::chrono::system_clock` + (https://github.com/fmtlib/fmt/issues/2319, + https://github.com/fmtlib/fmt/pull/2345). For example: + + ```c++ + #include + + int main() { + fmt::print("{}", std::chrono::system_clock::now()); + } + ``` + + prints \"2021-06-18 15:22:00\" (the output depends on the current + date and time). Thanks @sunmy2019. + +- Made more chrono specifiers locale independent by default. Use the + `'L'` specifier to get localized formatting. For example: + + ```c++ + #include + + int main() { + std::locale::global(std::locale("ru_RU.UTF-8")); + auto monday = std::chrono::weekday(1); + fmt::print("{}\n", monday); // prints "Mon" + fmt::print("{:L}\n", monday); // prints "пн" + } + ``` + +- Improved locale handling in chrono formatting + (https://github.com/fmtlib/fmt/issues/2337, + https://github.com/fmtlib/fmt/pull/2349, + https://github.com/fmtlib/fmt/pull/2350). Thanks @phprus. + +- Deprecated `fmt/locale.h` moving the formatting functions that take + a locale to `fmt/format.h` (`char`) and `fmt/xchar` (other + overloads). This doesn\'t introduce a dependency on `` so + there is virtually no compile time effect. + +- Deprecated an undocumented `format_to` overload that takes + `basic_memory_buffer`. + +- Made parameter order in `vformat_to` consistent with `format_to` + (https://github.com/fmtlib/fmt/issues/2327). + +- Added support for time points with arbitrary durations + (https://github.com/fmtlib/fmt/issues/2208). For example: + + ```c++ + #include + + int main() { + using tp = std::chrono::time_point< + std::chrono::system_clock, std::chrono::seconds>; + fmt::print("{:%S}", tp(std::chrono::seconds(42))); + } + ``` + + prints \"42\". + +- Formatting floating-point numbers no longer produces trailing zeros + by default for consistency with `std::format`. For example: + + ```c++ + #include + + int main() { + fmt::print("{0:.3}", 1.1); + } + ``` + + prints \"1.1\". Use the `'#'` specifier to keep trailing zeros. + +- Dropped a limit on the number of elements in a range and replaced + `{}` with `[]` as range delimiters for consistency with Python\'s + `str.format`. + +- The `'L'` specifier for locale-specific numeric formatting can now + be combined with presentation specifiers as in `std::format`. For + example: + + ```c++ + #include + #include + + int main() { + std::locale::global(std::locale("fr_FR.UTF-8")); + fmt::print("{0:.2Lf}", 0.42); + } + ``` + + prints \"0,42\". The deprecated `'n'` specifier has been removed. + +- Made the `0` specifier ignored for infinity and NaN + (https://github.com/fmtlib/fmt/issues/2305, + https://github.com/fmtlib/fmt/pull/2310). Thanks @Liedtke. + +- Made the hexfloat formatting use the right alignment by default + (https://github.com/fmtlib/fmt/issues/2308, + https://github.com/fmtlib/fmt/pull/2317). Thanks @Liedtke. + +- Removed the deprecated numeric alignment (`'='`). Use the `'0'` + specifier instead. + +- Removed the deprecated `fmt/posix.h` header that has been replaced + with `fmt/os.h`. + +- Removed the deprecated `format_to_n_context`, `format_to_n_args` and + `make_format_to_n_args`. They have been replaced with + `format_context`, `` format_args` and ``make_format_args\`\` + respectively. + +- Moved `wchar_t`-specific functions and types to `fmt/xchar.h`. You + can define `FMT_DEPRECATED_INCLUDE_XCHAR` to automatically include + `fmt/xchar.h` from `fmt/format.h` but this will be disabled in the + next major release. + +- Fixed handling of the `'+'` specifier in localized formatting + (https://github.com/fmtlib/fmt/issues/2133). + +- Added support for the `'s'` format specifier that gives textual + representation of `bool` + (https://github.com/fmtlib/fmt/issues/2094, + https://github.com/fmtlib/fmt/pull/2109). For example: + + ```c++ + #include + + int main() { + fmt::print("{:s}", true); + } + ``` + + prints \"true\". Thanks @powercoderlol. + +- Made `fmt::ptr` work with function pointers + (https://github.com/fmtlib/fmt/pull/2131). For example: + + ```c++ + #include + + int main() { + fmt::print("My main: {}\n", fmt::ptr(main)); + } + ``` + + Thanks @mikecrowe. + +- The undocumented support for specializing `formatter` for pointer + types has been removed. + +- Fixed `fmt::formatted_size` with format string compilation + (https://github.com/fmtlib/fmt/pull/2141, + https://github.com/fmtlib/fmt/pull/2161). Thanks @alexezeder. + +- Fixed handling of empty format strings during format string + compilation (https://github.com/fmtlib/fmt/issues/2042): + + ```c++ + auto s = fmt::format(FMT_COMPILE("")); + ``` + + Thanks @alexezeder. + +- Fixed handling of enums in `fmt::to_string` + (https://github.com/fmtlib/fmt/issues/2036). + +- Improved width computation + (https://github.com/fmtlib/fmt/issues/2033, + https://github.com/fmtlib/fmt/issues/2091). For example: + + ```c++ + #include + + int main() { + fmt::print("{:-<10}{}\n", "你好", "世界"); + fmt::print("{:-<10}{}\n", "hello", "world"); + } + ``` + + prints + + ![](https://user-images.githubusercontent.com/576385/119840373-cea3ca80-beb9-11eb-91e0-54266c48e181.png) + + on a modern terminal. + +- The experimental fast output stream (`fmt::ostream`) is now + truncated by default for consistency with `fopen` + (https://github.com/fmtlib/fmt/issues/2018). For example: + + ```c++ + #include + + int main() { + fmt::ostream out1 = fmt::output_file("guide"); + out1.print("Zaphod"); + out1.close(); + fmt::ostream out2 = fmt::output_file("guide"); + out2.print("Ford"); + } + ``` + + writes \"Ford\" to the file \"guide\". To preserve the old file + content if any pass `fmt::file::WRONLY | fmt::file::CREATE` flags to + `fmt::output_file`. + +- Fixed moving of `fmt::ostream` that holds buffered data + (https://github.com/fmtlib/fmt/issues/2197, + https://github.com/fmtlib/fmt/pull/2198). Thanks @vtta. + +- Replaced the `fmt::system_error` exception with a function of the + same name that constructs `std::system_error` + (https://github.com/fmtlib/fmt/issues/2266). + +- Replaced the `fmt::windows_error` exception with a function of the + same name that constructs `std::system_error` with the category + returned by `fmt::system_category()` + (https://github.com/fmtlib/fmt/issues/2274, + https://github.com/fmtlib/fmt/pull/2275). The latter is + similar to `std::system_category` but correctly handles UTF-8. + Thanks @phprus. + +- Replaced `fmt::error_code` with `std::error_code` and made it + formattable (https://github.com/fmtlib/fmt/issues/2269, + https://github.com/fmtlib/fmt/pull/2270, + https://github.com/fmtlib/fmt/pull/2273). Thanks @phprus. + +- Added speech synthesis support + (https://github.com/fmtlib/fmt/pull/2206). + +- Made `format_to` work with a memory buffer that has a custom + allocator (https://github.com/fmtlib/fmt/pull/2300). + Thanks @voxmea. + +- Added `Allocator::max_size` support to `basic_memory_buffer`. + (https://github.com/fmtlib/fmt/pull/1960). Thanks @phprus. + +- Added wide string support to `fmt::join` + (https://github.com/fmtlib/fmt/pull/2236). Thanks @crbrz. + +- Made iterators passed to `formatter` specializations via a format + context satisfy C++20 `std::output_iterator` requirements + (https://github.com/fmtlib/fmt/issues/2156, + https://github.com/fmtlib/fmt/pull/2158, + https://github.com/fmtlib/fmt/issues/2195, + https://github.com/fmtlib/fmt/pull/2204). Thanks @randomnetcat. + +- Optimized the `printf` implementation + (https://github.com/fmtlib/fmt/pull/1982, + https://github.com/fmtlib/fmt/pull/1984, + https://github.com/fmtlib/fmt/pull/2016, + https://github.com/fmtlib/fmt/pull/2164). + Thanks @rimathia and @moiwi. + +- Improved detection of `constexpr` `char_traits` + (https://github.com/fmtlib/fmt/pull/2246, + https://github.com/fmtlib/fmt/pull/2257). Thanks @phprus. + +- Fixed writing to `stdout` when it is redirected to `NUL` on Windows + (https://github.com/fmtlib/fmt/issues/2080). + +- Fixed exception propagation from iterators + (https://github.com/fmtlib/fmt/issues/2097). + +- Improved `strftime` error handling + (https://github.com/fmtlib/fmt/issues/2238, + https://github.com/fmtlib/fmt/pull/2244). Thanks @yumeyao. + +- Stopped using deprecated GCC UDL template extension. + +- Added `fmt/args.h` to the install target + (https://github.com/fmtlib/fmt/issues/2096). + +- Error messages are now passed to assert when exceptions are disabled + (https://github.com/fmtlib/fmt/pull/2145). Thanks @NobodyXu. + +- Added the `FMT_MASTER_PROJECT` CMake option to control build and + install targets when {fmt} is included via `add_subdirectory` + (https://github.com/fmtlib/fmt/issues/2098, + https://github.com/fmtlib/fmt/pull/2100). + Thanks @randomizedthinking. + +- Improved build configuration + (https://github.com/fmtlib/fmt/pull/2026, + https://github.com/fmtlib/fmt/pull/2122). + Thanks @luncliff and @ibaned. + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/issues/1947, + https://github.com/fmtlib/fmt/pull/1959, + https://github.com/fmtlib/fmt/pull/1963, + https://github.com/fmtlib/fmt/pull/1965, + https://github.com/fmtlib/fmt/issues/1966, + https://github.com/fmtlib/fmt/pull/1974, + https://github.com/fmtlib/fmt/pull/1975, + https://github.com/fmtlib/fmt/pull/1990, + https://github.com/fmtlib/fmt/issues/2000, + https://github.com/fmtlib/fmt/pull/2001, + https://github.com/fmtlib/fmt/issues/2002, + https://github.com/fmtlib/fmt/issues/2004, + https://github.com/fmtlib/fmt/pull/2006, + https://github.com/fmtlib/fmt/pull/2009, + https://github.com/fmtlib/fmt/pull/2010, + https://github.com/fmtlib/fmt/issues/2038, + https://github.com/fmtlib/fmt/issues/2039, + https://github.com/fmtlib/fmt/issues/2047, + https://github.com/fmtlib/fmt/pull/2053, + https://github.com/fmtlib/fmt/issues/2059, + https://github.com/fmtlib/fmt/pull/2065, + https://github.com/fmtlib/fmt/pull/2067, + https://github.com/fmtlib/fmt/pull/2068, + https://github.com/fmtlib/fmt/pull/2073, + https://github.com/fmtlib/fmt/issues/2103, + https://github.com/fmtlib/fmt/issues/2105, + https://github.com/fmtlib/fmt/pull/2106, + https://github.com/fmtlib/fmt/pull/2107, + https://github.com/fmtlib/fmt/issues/2116, + https://github.com/fmtlib/fmt/pull/2117, + https://github.com/fmtlib/fmt/issues/2118, + https://github.com/fmtlib/fmt/pull/2119, + https://github.com/fmtlib/fmt/issues/2127, + https://github.com/fmtlib/fmt/pull/2128, + https://github.com/fmtlib/fmt/issues/2140, + https://github.com/fmtlib/fmt/issues/2142, + https://github.com/fmtlib/fmt/pull/2143, + https://github.com/fmtlib/fmt/pull/2144, + https://github.com/fmtlib/fmt/issues/2147, + https://github.com/fmtlib/fmt/issues/2148, + https://github.com/fmtlib/fmt/issues/2149, + https://github.com/fmtlib/fmt/pull/2152, + https://github.com/fmtlib/fmt/pull/2160, + https://github.com/fmtlib/fmt/issues/2170, + https://github.com/fmtlib/fmt/issues/2175, + https://github.com/fmtlib/fmt/issues/2176, + https://github.com/fmtlib/fmt/pull/2177, + https://github.com/fmtlib/fmt/issues/2178, + https://github.com/fmtlib/fmt/pull/2179, + https://github.com/fmtlib/fmt/issues/2180, + https://github.com/fmtlib/fmt/issues/2181, + https://github.com/fmtlib/fmt/pull/2183, + https://github.com/fmtlib/fmt/issues/2184, + https://github.com/fmtlib/fmt/issues/2185, + https://github.com/fmtlib/fmt/pull/2186, + https://github.com/fmtlib/fmt/pull/2187, + https://github.com/fmtlib/fmt/pull/2190, + https://github.com/fmtlib/fmt/pull/2192, + https://github.com/fmtlib/fmt/pull/2194, + https://github.com/fmtlib/fmt/pull/2205, + https://github.com/fmtlib/fmt/issues/2210, + https://github.com/fmtlib/fmt/pull/2211, + https://github.com/fmtlib/fmt/pull/2215, + https://github.com/fmtlib/fmt/pull/2216, + https://github.com/fmtlib/fmt/pull/2218, + https://github.com/fmtlib/fmt/pull/2220, + https://github.com/fmtlib/fmt/issues/2228, + https://github.com/fmtlib/fmt/pull/2229, + https://github.com/fmtlib/fmt/pull/2230, + https://github.com/fmtlib/fmt/issues/2233, + https://github.com/fmtlib/fmt/pull/2239, + https://github.com/fmtlib/fmt/issues/2248, + https://github.com/fmtlib/fmt/issues/2252, + https://github.com/fmtlib/fmt/pull/2253, + https://github.com/fmtlib/fmt/pull/2255, + https://github.com/fmtlib/fmt/issues/2261, + https://github.com/fmtlib/fmt/issues/2278, + https://github.com/fmtlib/fmt/issues/2284, + https://github.com/fmtlib/fmt/pull/2287, + https://github.com/fmtlib/fmt/pull/2289, + https://github.com/fmtlib/fmt/pull/2290, + https://github.com/fmtlib/fmt/pull/2293, + https://github.com/fmtlib/fmt/issues/2295, + https://github.com/fmtlib/fmt/pull/2296, + https://github.com/fmtlib/fmt/pull/2297, + https://github.com/fmtlib/fmt/issues/2311, + https://github.com/fmtlib/fmt/pull/2313, + https://github.com/fmtlib/fmt/pull/2315, + https://github.com/fmtlib/fmt/issues/2320, + https://github.com/fmtlib/fmt/pull/2321, + https://github.com/fmtlib/fmt/pull/2323, + https://github.com/fmtlib/fmt/issues/2328, + https://github.com/fmtlib/fmt/pull/2329, + https://github.com/fmtlib/fmt/pull/2333, + https://github.com/fmtlib/fmt/pull/2338, + https://github.com/fmtlib/fmt/pull/2341). + Thanks @darklukee, @fagg, @killerbot242, @jgopel, @yeswalrus, @Finkman, + @HazardyKnusperkeks, @dkavolis, @concatime, @chronoxor, @summivox, @yNeo, + @Apache-HB, @alexezeder, @toojays, @Brainy0207, @vadz, @imsherlock, @phprus, + @white238, @yafshar, @BillyDonahue, @jstaahl, @denchat, @DanielaE, + @ilyakurdyukov, @ilmai, @JessyDL, @sergiud, @mwinterb, @sven-herrmann, + @jmelas, @twoixter, @crbrz and @upsj. + +- Improved documentation + (https://github.com/fmtlib/fmt/issues/1986, + https://github.com/fmtlib/fmt/pull/2051, + https://github.com/fmtlib/fmt/issues/2057, + https://github.com/fmtlib/fmt/pull/2081, + https://github.com/fmtlib/fmt/issues/2084, + https://github.com/fmtlib/fmt/pull/2312). + Thanks @imba-tjd, @0x416c69 and @mordante. + +- Continuous integration and test improvements + (https://github.com/fmtlib/fmt/issues/1969, + https://github.com/fmtlib/fmt/pull/1991, + https://github.com/fmtlib/fmt/pull/2020, + https://github.com/fmtlib/fmt/pull/2110, + https://github.com/fmtlib/fmt/pull/2114, + https://github.com/fmtlib/fmt/issues/2196, + https://github.com/fmtlib/fmt/pull/2217, + https://github.com/fmtlib/fmt/pull/2247, + https://github.com/fmtlib/fmt/pull/2256, + https://github.com/fmtlib/fmt/pull/2336, + https://github.com/fmtlib/fmt/pull/2346). + Thanks @jgopel, @alexezeder and @DanielaE. + +The change log for versions 0.8.0 - 7.1.3 is available [here]( +doc/ChangeLog-old.md). diff --git a/deps/fmt/ChangeLog.rst b/deps/fmt/ChangeLog.rst deleted file mode 100644 index 62fc8a8b0..000000000 --- a/deps/fmt/ChangeLog.rst +++ /dev/null @@ -1,5922 +0,0 @@ -10.1.1 - 2023-08-28 -------------------- - -* Added formatters for ``std::atomic`` and ``atomic_flag`` - (`#3574 `_, - `#3594 `_). - Thanks `@wangzw (Zhanwei Wang) `_ and - `@AlexGuteniev (Alex Guteniev) `_. - -* Fixed an error about partial specialization of ``formatter`` - after instantiation when compiled with gcc and C++20 - (`#3584 `_). - -* Fixed compilation as a C++20 module with gcc and clang - (`#3587 `_, - `#3597 `_, - `#3605 `_). Thanks - `@MathewBensonCode (Mathew Benson) `_. - -* Made ``fmt::to_string`` work with types that have ``format_as`` overloads - (`#3575 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Made ``formatted_size`` work with integral format specifiers at compile time - (`#3591 `_). - Thanks `@elbeno (Ben Deane) `_. - -* Fixed a warning about the ``no_unique_address`` attribute on clang-cl - (`#3599 `_). - Thanks `@lukester1975 `_. - -* Improved compatibility with the legacy GBK encoding - (`#3598 `_, - `#3599 `_). - Thanks `@YuHuanTin `_. - -* Added OpenSSF Scorecard analysis - (`#3530 `_, - `#3571 `_). - Thanks `@joycebrum (Joyce) `_. - -* Updated CI dependencies - (`#3591 `_, - `#3592 `_, - `#3593 `_, - `#3602 `_). - -10.1.0 - 2023-08-12 -------------------- - -* Optimized format string compilation resulting in up to 40% speed up in - compiled ``format_to`` and ~4x speed up in compiled ``format_to_n`` on a - concatenation benchmark (`#3133 `_, - `#3484 `_). - - {fmt} 10.0:: - - --------------------------------------------------------- - Benchmark Time CPU Iterations - --------------------------------------------------------- - BM_format_to 78.9 ns 78.9 ns 8881746 - BM_format_to_n 568 ns 568 ns 1232089 - - {fmt} 10.1:: - - --------------------------------------------------------- - Benchmark Time CPU Iterations - --------------------------------------------------------- - BM_format_to 54.9 ns 54.9 ns 12727944 - BM_format_to_n 133 ns 133 ns 5257795 - -* Optimized storage of an empty allocator in ``basic_memory_buffer`` - (`#3485 `_). - Thanks `@Minty-Meeo `_. - -* Added formatters for proxy references to elements of ``std::vector`` and - ``std::bitset`` (`#3567 `_, - `#3570 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - auto v = std::vector{true}; - fmt::print("{}", v[0]); - } - - Thanks `@phprus (Vladislav Shchapov) `_ and - `@felix642 (Félix-Antoine Constantin) `_. - -* Fixed an ambiguous formatter specialization for containers that look like - container adaptors such as ``boost::flat_set`` - (`#3556 `_, - `#3561 `_). - Thanks `@5chmidti `_. - -* Fixed compilation when formatting durations not convertible from - ``std::chrono::seconds`` (`#3430 `_). - Thanks `@patlkli (Patrick Geltinger) `_. - -* Made the ``formatter`` specialization for ``char*`` const-correct - (`#3432 `_). - Thanks `@timsong-cpp `_. - -* Made ``{}`` and ``{:}`` handled consistently during compile-time checks - (`#3526 `_). - -* Disallowed passing temporaries to ``make_format_args`` to improve API safety - by preventing dangling references. - -* Improved the compile-time error for unformattable types - (`#3478 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Improved the floating-point formatter - (`#3448 `_, - `#3450 `_). - Thanks `@florimond-collette (Florimond Collette) - `_. - -* Fixed handling of precision for ``long double`` larger than 64 bits. - (`#3539 `_, - `#3564 `_). - -* Made floating-point and chrono tests less platform-dependent - (`#3337 `_, - `#3433 `_, - `#3434 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Removed the remnants of the Grisu floating-point formatter that has been - replaced by Dragonbox in earlier versions. - -* Added ``throw_format_error`` to the public API - (`#3551 `_). - Thanks `@mjerabek (Martin Jeřábek) `_. - -* Made ``FMT_THROW`` assert even if assertions are disabled when compiling with - exceptions disabled (`#3418 `_, - `#3439 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Made ``format_as`` and ``std::filesystem::path`` formatter work with exotic - code unit types. - (`#3457 `_, - `#3476 `_). - Thanks `@gix (Nico Rieck) `_, - `@hmbj (Hans-Martin B. Jensen) `_. - -* Added support for the ``?`` format specifier to ``std::filesystem::path`` and - made the default unescaped for consistency with strings. - -* Deprecated the wide stream overload of ``printf``. - -* Removed unused ``basic_printf_parse_context``. - -* Improved RTTI detection used when formatting exceptions - (`#3468 `_). - Thanks `@danakj (Dana Jansens) `_. - -* Improved compatibility with VxWorks7 - (`#3467 `_). - Thanks `@wenshan1 (Bin Lan) `_. - -* Improved documentation - (`#3174 `_, - `#3423 `_, - `#3454 `_, - `#3458 `_, - `#3461 `_, - `#3487 `_, - `#3515 `_). - Thanks `@zencatalyst (Kasra Hashemi) `_, - `@rlalik `_, - `@mikecrowe (Mike Crowe) `_. - -* Improved build and CI configurations - (`#3449 `_, - `#3451 `_, - `#3452 `_, - `#3453 `_, - `#3459 `_, - `#3481 `_, - `#3486 `_, - `#3489 `_, - `#3496 `_, - `#3517 `_, - `#3523 `_, - `#3563 `_). - Thanks `@joycebrum (Joyce) `_, - `@glebm (Gleb Mazovetskiy) `_, - `@phprus (Vladislav Shchapov) `_, - `@petrmanek (Petr Mánek) `_, - `@setoye (Alta) `_, - `@abouvier (Alexandre Bouvier) `_. - -* Fixed various warnings and compilation issues - (`#3408 `_, - `#3424 `_, - `#3444 `_, - `#3446 `_, - `#3475 `_, - `#3482 `_, - `#3492 `_, - `#3493 `_, - `#3508 `_, - `#3509 `_, - `#3533 `_, - `#3542 `_, - `#3543 `_, - `#3540 `_, - `#3544 `_, - `#3548 `_, - `#3549 `_, - `#3550 `_, - `#3552 `_). - Thanks `@adesitter (Arnaud Desitter) `_, - `@hmbj (Hans-Martin B. Jensen) `_, - `@Minty-Meeo `_, - `@phprus (Vladislav Shchapov) `_, - `@TobiSchluter (Tobias Schlüter) `_, - `@kieranclancy (Kieran Clancy) `_, - `@alexeedm (Dmitry Alexeev) `_, - `@jurihock (Jürgen Hock) `_, - `@Ozomahtli `_, - `@razaqq `_. - -10.0.0 - 2023-05-09 -------------------- - -* Replaced Grisu with a new floating-point formatting algorithm for given - precision (`#3262 `_, - `#2750 `_, - `#3269 `_, - `#3276 `_). - The new algorithm is based on Dragonbox already used for the - shortest representation and gives substantial performance improvement: - - .. image:: https://user-images.githubusercontent.com/33922675/ - 211956670-84891a09-6867-47d9-82fc-3230da7abe0f.png - - * Red: new algorithm - * Green: new algorithm with ``FMT_USE_FULL_CACHE_DRAGONBOX`` defined to 1 - * Blue: old algorithm - - Thanks `@jk-jeon (Junekey Jeon) `_. - -* Replaced ``snprintf``-based hex float formatter with an internal - implementation (`#3179 `_, - `#3203 `_). - This removes the last usage of ``s(n)printf`` in {fmt}. - Thanks `@phprus (Vladislav Shchapov) `_. - -* Fixed alignment of floating-point numbers with localization - (`#3263 `_, - `#3272 `_). - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Made handling of ``#`` consistent with ``std::format``. - -* Improved C++20 module support - (`#3134 `_, - `#3254 `_, - `#3386 `_, - `#3387 `_, - `#3388 `_, - `#3392 `_, - `#3397 `_, - `#3399 `_, - `#3400 `_). - Thanks `@laitingsheng (Tinson Lai) `_, - `@Orvid (Orvid King) `_, - `@DanielaE (Daniela Engert) `_. - Switched to the `modules CMake library `_ - which allows building {fmt} as a C++20 module with clang:: - - CXX=clang++ cmake -DFMT_MODULE=ON . - make - -* Made ``format_as`` work with any user-defined type and not just enums. - For example (`godbolt `__): - - .. code:: c++ - - #include - - struct floaty_mc_floatface { - double value; - }; - - auto format_as(floaty_mc_floatface f) { return f.value; } - - int main() { - fmt::print("{:8}\n", floaty_mc_floatface{0.42}); // prints " 0.42" - } - -* Removed deprecated implicit conversions for enums and conversions to primitive - types for compatibility with ``std::format`` and to prevent potential ODR - violations. Use ``format_as`` instead. - -* Added support for fill, align and width to the time point formatter - (`#3237 `_, - `#3260 `_, - `#3275 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - // prints " 2023" - fmt::print("{:>8%Y}\n", std::chrono::system_clock::now()); - } - - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Implemented formatting of subseconds - (`#2207 `_, - `#3117 `_, - `#3115 `_, - `#3143 `_, - `#3144 `_, - `#3349 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - // prints 01.234567 - fmt::print("{:%S}\n", std::chrono::microseconds(1234567)); - } - - Thanks `@patrickroocks (Patrick Roocks) `_ - `@phprus (Vladislav Shchapov) `_, - `@BRevzin (Barry Revzin) `_. - -* Added precision support to ``%S`` - (`#3148 `_). - Thanks `@SappyJoy (Stepan Ponomaryov) `_ - -* Added support for ``std::utc_time`` - (`#3098 `_, - `#3110 `_). - Thanks `@patrickroocks (Patrick Roocks) `_. - -* Switched formatting of ``std::chrono::system_clock`` from local time to UTC - for compatibility with the standard - (`#3199 `_, - `#3230 `_). - Thanks `@ned14 (Niall Douglas) `_. - -* Added support for ``%Ez`` and ``%Oz`` to chrono formatters. - (`#3220 `_, - `#3222 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Improved validation of format specifiers for ``std::chrono::duration`` - (`#3219 `_, - `#3232 `_). - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Fixed formatting of time points before the epoch - (`#3117 `_, - `#3261 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - auto t = std::chrono::system_clock::from_time_t(0) - - std::chrono::milliseconds(250); - fmt::print("{:%S}\n", t); // prints 59.750000000 - } - - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Experimental: implemented glibc extension for padding seconds, minutes and - hours (`#2959 `_, - `#3271 `_). - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Added a formatter for ``std::exception`` - (`#2977 `_, - `#3012 `_, - `#3062 `_, - `#3076 `_, - `#3119 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - try { - std::vector().at(0); - } catch(const std::exception& e) { - fmt::print("{}", e); - } - } - - prints:: - - vector::_M_range_check: __n (which is 0) >= this->size() (which is 0) - - on libstdc++. - Thanks `@zach2good (Zach Toogood) `_ and - `@phprus (Vladislav Shchapov) `_. - -* Moved ``std::error_code`` formatter from ``fmt/os.h`` to ``fmt/std.h``. - (`#3125 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added formatters for standard container adapters: ``std::priority_queue``, - ``std::queue`` and ``std::stack`` - (`#3215 `_, - `#3279 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - #include - - int main() { - auto s = std::stack>(); - for (auto b: {true, false, true}) s.push(b); - fmt::print("{}\n", s); // prints [true, false, true] - } - - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Added a formatter for ``std::optional`` to ``fmt/std.h``. - Thanks `@tom-huntington `_. - -* Fixed formatting of valueless by exception variants - (`#3347 `_). - Thanks `@TheOmegaCarrot `_. - -* Made ``fmt::ptr`` accept ``unique_ptr`` with a custom deleter - (`#3177 `_). - Thanks `@hmbj (Hans-Martin B. Jensen) `_. - -* Fixed formatting of noncopyable ranges and nested ranges of chars - (`#3158 `_ - `#3286 `_, - `#3290 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Fixed issues with formatting of paths and ranges of paths - (`#3319 `_, - `#3321 `_ - `#3322 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Improved handling of invalid Unicode in paths. - -* Enabled compile-time checks on Apple clang 14 and later - (`#3331 `_). - Thanks `@cloyce (Cloyce D. Spradling) `_. - -* Improved compile-time checks of named arguments - (`#3105 `_, - `#3214 `_). - Thanks `@rbrich (Radek Brich) `_. - -* Fixed formatting when both alignment and ``0`` are given - (`#3236 `_, - `#3248 `_). - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Improved Unicode support in the experimental file API on Windows - (`#3234 `_, - `#3293 `_). - Thanks `@Fros1er (Froster) `_. - -* Unified UTF transcoding - (`#3416 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added support for UTF-8 digit separators via an experimental locale facet - (`#1861 `_). - For example (`godbolt `__): - - .. code:: c++ - - auto loc = std::locale( - std::locale(), new fmt::format_facet("’")); - auto s = fmt::format(loc, "{:L}", 1000); - - where ``’`` is U+2019 used as a digit separator in the de_CH locale. - -* Added an overload of ``formatted_size`` that takes a locale - (`#3084 `_, - `#3087 `_). - Thanks `@gerboengels `_. - -* Removed the deprecated ``FMT_DEPRECATED_OSTREAM``. - -* Fixed a UB when using a null ``std::string_view`` with ``fmt::to_string`` - or format string compilation - (`#3241 `_, - `#3244 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added ``starts_with`` to the fallback ``string_view`` implementation - (`#3080 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added ``fmt::basic_format_string::get()`` for compatibility with - ``basic_format_string`` (`#3111 `_). - Thanks `@huangqinjin `_. - -* Added ``println`` for compatibility with C++23 - (`#3267 `_). - Thanks `@ShawnZhong (Shawn Zhong) `_. - -* Renamed the ``FMT_EXPORT`` macro for shared library usage to - ``FMT_LIB_EXPORT``. - -* Improved documentation - (`#3108 `_, - `#3169 `_, - `#3243 `_). - `#3404 `_). - Thanks `@Cleroth `_ and - `@Vertexwahn `_. - -* Improved build configuration and tests - (`#3118 `_, - `#3120 `_, - `#3188 `_, - `#3189 `_, - `#3198 `_, - `#3205 `_, - `#3207 `_, - `#3210 `_, - `#3240 `_, - `#3256 `_, - `#3264 `_, - `#3299 `_, - `#3302 `_, - `#3312 `_, - `#3317 `_, - `#3328 `_, - `#3333 `_, - `#3369 `_, - `#3373 `_, - `#3395 `_, - `#3406 `_, - `#3411 `_). - Thanks `@dimztimz (Dimitrij Mijoski) `_, - `@phprus (Vladislav Shchapov) `_, - `@DavidKorczynski `_, - `@ChrisThrasher (Chris Thrasher) `_, - `@FrancoisCarouge (François Carouge) `_, - `@kennyweiss (Kenny Weiss) `_, - `@luzpaz `_, - `@codeinred (Alecto Irene Perez) `_, - `@Mixaill (Mikhail Paulyshka) `_, - `@joycebrum (Joyce) `_, - `@kevinhwang (Kevin Hwang) `_, - `@Vertexwahn `_. - -* Fixed a regression in handling empty format specifiers after a colon (``{:}``) - (`#3086 `_). - Thanks `@oxidase (Michael Krasnyk) `_. - -* Worked around a broken implementation of ``std::is_constant_evaluated`` in - some versions of libstdc++ on clang - (`#3247 `_, - `#3281 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Fixed formatting of volatile variables - (`#3068 `_). - -* Fixed various warnings and compilation issues - (`#3057 `_, - `#3066 `_, - `#3072 `_, - `#3082 `_, - `#3091 `_, - `#3092 `_, - `#3093 `_, - `#3095 `_, - `#3096 `_, - `#3097 `_, - `#3128 `_, - `#3129 `_, - `#3137 `_, - `#3139 `_, - `#3140 `_, - `#3142 `_, - `#3149 `_, - `#3150 `_, - `#3154 `_, - `#3163 `_, - `#3178 `_, - `#3184 `_, - `#3196 `_, - `#3204 `_, - `#3206 `_, - `#3208 `_, - `#3213 `_, - `#3216 `_, - `#3224 `_, - `#3226 `_, - `#3228 `_, - `#3229 `_, - `#3259 `_, - `#3274 `_, - `#3287 `_, - `#3288 `_, - `#3292 `_, - `#3295 `_, - `#3296 `_, - `#3298 `_, - `#3325 `_, - `#3326 `_, - `#3334 `_, - `#3342 `_, - `#3343 `_, - `#3351 `_, - `#3352 `_, - `#3362 `_, - `#3365 `_, - `#3366 `_, - `#3374 `_, - `#3377 `_, - `#3378 `_, - `#3381 `_, - `#3398 `_, - `#3413 `_, - `#3415 `_). - Thanks `@phprus (Vladislav Shchapov) `_, - `@gsjaardema (Greg Sjaardema) `_, - `@NewbieOrange `_, - `@EngineLessCC (VivyaCC) `_, - `@asmaloney (Andy Maloney) `_, - `@HazardyKnusperkeks (Björn Schäpers) - `_, - `@sergiud (Sergiu Deitsch) `_, - `@Youw (Ihor Dutchak) `_, - `@thesmurph `_, - `@czudziakm (Maksymilian Czudziak) `_, - `@Roman-Koshelev `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@ShawnZhong (Shawn Zhong) `_, - `@russelltg (Russell Greene) `_, - `@glebm (Gleb Mazovetskiy) `_, - `@tmartin-gh `_, - `@Zhaojun-Liu (June Liu) `_, - `@louiswins (Louis Wilson) `_, - `@mogemimi `_. - -9.1.0 - 2022-08-27 ------------------- - -* ``fmt::formatted_size`` now works at compile time - (`#3026 `_). For example - (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - using namespace fmt::literals; - constexpr size_t n = fmt::formatted_size("{}"_cf, 42); - fmt::print("{}\n", n); // prints 2 - } - - Thanks `@marksantaniello (Mark Santaniello) - `_. - -* Fixed handling of invalid UTF-8 - (`#3038 `_, - `#3044 `_, - `#3056 `_). - Thanks `@phprus (Vladislav Shchapov) `_ and - `@skeeto (Christopher Wellons) `_. - -* Improved Unicode support in ``ostream`` overloads of ``print`` - (`#2994 `_, - `#3001 `_, - `#3025 `_). - Thanks `@dimztimz (Dimitrij Mijoski) `_. - -* Fixed handling of the sign specifier in localized formatting on systems with - 32-bit ``wchar_t`` (`#3041 `_). - -* Added support for wide streams to ``fmt::streamed`` - (`#2994 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added the ``n`` specifier that disables the output of delimiters when - formatting ranges (`#2981 `_, - `#2983 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - auto v = std::vector{1, 2, 3}; - fmt::print("{:n}\n", v); // prints 1, 2, 3 - } - - Thanks `@BRevzin (Barry Revzin) `_. - -* Worked around problematic ``std::string_view`` constructors introduced in - C++23 (`#3030 `_, - `#3050 `_). - Thanks `@strega-nil-ms (nicole mazzuca) `_. - -* Improve handling (exclusion) of recursive ranges - (`#2968 `_, - `#2974 `_). - Thanks `@Dani-Hub (Daniel Krügler) `_. - -* Improved error reporting in format string compilation - (`#3055 `_). - -* Improved the implementation of - `Dragonbox `_, the algorithm used for - the default floating-point formatting - (`#2984 `_). - Thanks `@jk-jeon (Junekey Jeon) `_. - -* Fixed issues with floating-point formatting on exotic platforms. - -* Improved the implementation of chrono formatting - (`#3010 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Improved documentation - (`#2966 `_, - `#3009 `_, - `#3020 `_, - `#3037 `_). - Thanks `@mwinterb `_, - `@jcelerier (Jean-Michaël Celerier) `_ - and `@remiburtin (Rémi Burtin) `_. - -* Improved build configuration - (`#2991 `_, - `#2995 `_, - `#3004 `_, - `#3007 `_, - `#3040 `_). - Thanks `@dimztimz (Dimitrij Mijoski) `_ and - `@hwhsu1231 (Haowei Hsu) `_. - -* Fixed various warnings and compilation issues - (`#2969 `_, - `#2971 `_, - `#2975 `_, - `#2982 `_, - `#2985 `_, - `#2988 `_, - `#2989 `_, - `#3000 `_, - `#3006 `_, - `#3014 `_, - `#3015 `_, - `#3021 `_, - `#3023 `_, - `#3024 `_, - `#3029 `_, - `#3043 `_, - `#3052 `_, - `#3053 `_, - `#3054 `_). - Thanks `@h-friederich (Hannes Friederich) `_, - `@dimztimz (Dimitrij Mijoski) `_, - `@olupton (Olli Lupton) `_, - `@bernhardmgruber (Bernhard Manfred Gruber) - `_, - `@phprus (Vladislav Shchapov) `_. - -9.0.0 - 2022-07-04 ------------------- - -* Switched to the internal floating point formatter for all decimal presentation - formats. In particular this results in consistent rounding on all platforms - and removing the ``s[n]printf`` fallback for decimal FP formatting. - -* Compile-time floating point formatting no longer requires the header-only - mode. For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - consteval auto compile_time_dtoa(double value) -> std::array { - auto result = std::array(); - fmt::format_to(result.data(), FMT_COMPILE("{}"), value); - return result; - } - - constexpr auto answer = compile_time_dtoa(0.42); - - works with the default settings. - -* Improved the implementation of - `Dragonbox `_, the algorithm used for - the default floating-point formatting - (`#2713 `_, - `#2750 `_). - Thanks `@jk-jeon (Junekey Jeon) `_. - -* Made ``fmt::to_string`` work with ``__float128``. This uses the internal - FP formatter and works even on system without ``__float128`` support in - ``[s]printf``. - -* Disabled automatic ``std::ostream`` insertion operator (``operator<<``) - discovery when ``fmt/ostream.h`` is included to prevent ODR violations. - You can get the old behavior by defining ``FMT_DEPRECATED_OSTREAM`` but this - will be removed in the next major release. Use ``fmt::streamed`` or - ``fmt::ostream_formatter`` to enable formatting via ``std::ostream`` instead. - -* Added ``fmt::ostream_formatter`` that can be used to write ``formatter`` - specializations that perform formatting via ``std::ostream``. - For example (`godbolt `__): - - .. code:: c++ - - #include - - struct date { - int year, month, day; - - friend std::ostream& operator<<(std::ostream& os, const date& d) { - return os << d.year << '-' << d.month << '-' << d.day; - } - }; - - template <> struct fmt::formatter : ostream_formatter {}; - - std::string s = fmt::format("The date is {}", date{2012, 12, 9}); - // s == "The date is 2012-12-9" - -* Added the ``fmt::streamed`` function that takes an object and formats it - via ``std::ostream``. - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - fmt::print("Current thread id: {}\n", - fmt::streamed(std::this_thread::get_id())); - } - - Note that ``fmt/std.h`` provides a ``formatter`` specialization for - ``std::thread::id`` so you don't need to format it via ``std::ostream``. - -* Deprecated implicit conversions of unscoped enums to integers for consistency - with scoped enums. - -* Added an argument-dependent lookup based ``format_as`` extension API to - simplify formatting of enums. - -* Added experimental ``std::variant`` formatting support - (`#2941 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - auto v = std::variant(42); - fmt::print("{}\n", v); - } - - prints:: - - variant(42) - - Thanks `@jehelset `_. - -* Added experimental ``std::filesystem::path`` formatting support - (`#2865 `_, - `#2902 `_, - `#2917 `_, - `#2918 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - fmt::print("There is no place like {}.", std::filesystem::path("/home")); - } - - prints:: - - There is no place like "/home". - - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added a ``std::thread::id`` formatter to ``fmt/std.h``. - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - fmt::print("Current thread id: {}\n", std::this_thread::get_id()); - } - -* Added ``fmt::styled`` that applies a text style to an individual argument - (`#2793 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - auto now = std::chrono::system_clock::now(); - fmt::print( - "[{}] {}: {}\n", - fmt::styled(now, fmt::emphasis::bold), - fmt::styled("error", fg(fmt::color::red)), - "something went wrong"); - } - - prints - - .. image:: https://user-images.githubusercontent.com/576385/ - 175071215-12809244-dab0-4005-96d8-7cd911c964d5.png - - Thanks `@rbrugo (Riccardo Brugo) `_. - -* Made ``fmt::print`` overload for text styles correctly handle UTF-8 - (`#2681 `_, - `#2701 `_). - Thanks `@AlexGuteniev (Alex Guteniev) `_. - -* Fixed Unicode handling when writing to an ostream. - -* Added support for nested specifiers to range formatting - (`#2673 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - fmt::print("{::#x}\n", std::vector{10, 20, 30}); - } - - prints ``[0xa, 0x14, 0x1e]``. - - Thanks `@BRevzin (Barry Revzin) `_. - -* Implemented escaping of wide strings in ranges - (`#2904 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added support for ranges with ``begin`` / ``end`` found via the - argument-dependent lookup - (`#2807 `_). - Thanks `@rbrugo (Riccardo Brugo) `_. - -* Fixed formatting of certain kinds of ranges of ranges - (`#2787 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Fixed handling of maps with element types other than ``std::pair`` - (`#2944 `_). - Thanks `@BrukerJWD (Jonathan W) `_. - -* Made tuple formatter enabled only if elements are formattable - (`#2939 `_, - `#2940 `_). - Thanks `@jehelset `_. - -* Made ``fmt::join`` compatible with format string compilation - (`#2719 `_, - `#2720 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Made compile-time checks work with named arguments of custom types and - ``std::ostream`` ``print`` overloads - (`#2816 `_, - `#2817 `_, - `#2819 `_). - Thanks `@timsong-cpp `_. - -* Removed ``make_args_checked`` because it is no longer needed for compile-time - checks (`#2760 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Removed the following deprecated APIs: ``_format``, ``arg_join``, - the ``format_to`` overload that takes a memory buffer, - ``[v]fprintf`` that takes an ``ostream``. - -* Removed the deprecated implicit conversion of ``[const] signed char*`` and - ``[const] unsigned char*`` to C strings. - -* Removed the deprecated ``fmt/locale.h``. - -* Replaced the deprecated ``fileno()`` with ``descriptor()`` in - ``buffered_file``. - -* Moved ``to_string_view`` to the ``detail`` namespace since it's an - implementation detail. - -* Made access mode of a created file consistent with ``fopen`` by setting - ``S_IWGRP`` and ``S_IWOTH`` - (`#2733 `_). - Thanks `@arogge (Andreas Rogge) `_. - -* Removed a redundant buffer resize when formatting to ``std::ostream`` - (`#2842 `_, - `#2843 `_). - Thanks `@jcelerier (Jean-Michaël Celerier) `_. - -* Made precision computation for strings consistent with width - (`#2888 `_). - -* Fixed handling of locale separators in floating point formatting - (`#2830 `_). - -* Made sign specifiers work with ``__int128_t`` - (`#2773 `_). - -* Improved support for systems such as CHERI with extra data stored in pointers - (`#2932 `_). - Thanks `@davidchisnall (David Chisnall) `_. - -* Improved documentation - (`#2706 `_, - `#2712 `_, - `#2789 `_, - `#2803 `_, - `#2805 `_, - `#2815 `_, - `#2924 `_). - Thanks `@BRevzin (Barry Revzin) `_, - `@Pokechu22 `_, - `@setoye (Alta) `_, - `@rtobar `_, - `@rbrugo (Riccardo Brugo) `_, - `@anoonD (cre) `_, - `@leha-bot (Alex) `_. - -* Improved build configuration - (`#2766 `_, - `#2772 `_, - `#2836 `_, - `#2852 `_, - `#2907 `_, - `#2913 `_, - `#2914 `_). - Thanks `@kambala-decapitator (Andrey Filipenkov) - `_, - `@mattiasljungstrom (Mattias Ljungström) - `_, - `@kieselnb (Nick Kiesel) `_, - `@nathannaveen `_, - `@Vertexwahn `_. - -* Fixed various warnings and compilation issues - (`#2408 `_, - `#2507 `_, - `#2697 `_, - `#2715 `_, - `#2717 `_, - `#2722 `_, - `#2724 `_, - `#2725 `_, - `#2726 `_, - `#2728 `_, - `#2732 `_, - `#2738 `_, - `#2742 `_, - `#2744 `_, - `#2745 `_, - `#2746 `_, - `#2754 `_, - `#2755 `_, - `#2757 `_, - `#2758 `_, - `#2761 `_, - `#2762 `_, - `#2763 `_, - `#2765 `_, - `#2769 `_, - `#2770 `_, - `#2771 `_, - `#2777 `_, - `#2779 `_, - `#2782 `_, - `#2783 `_, - `#2794 `_, - `#2796 `_, - `#2797 `_, - `#2801 `_, - `#2802 `_, - `#2808 `_, - `#2818 `_, - `#2819 `_, - `#2829 `_, - `#2835 `_, - `#2848 `_, - `#2860 `_, - `#2861 `_, - `#2882 `_, - `#2886 `_, - `#2891 `_, - `#2892 `_, - `#2895 `_, - `#2896 `_, - `#2903 `_, - `#2906 `_, - `#2908 `_, - `#2909 `_, - `#2920 `_, - `#2922 `_, - `#2927 `_, - `#2929 `_, - `#2936 `_, - `#2937 `_, - `#2938 `_, - `#2951 `_, - `#2954 `_, - `#2957 `_, - `#2958 `_, - `#2960 `_). - Thanks `@matrackif `_ - `@Tobi823 (Tobias Hellmann) `_, - `@ivan-volnov (Ivan Volnov) `_, - `@VasiliPupkin256 `_, - `@federico-busato (Federico) `_, - `@barcharcraz (Charlie Barto) `_, - `@jk-jeon (Junekey Jeon) `_, - `@HazardyKnusperkeks (Björn Schäpers) - `_, - `@dalboris (Boris Dalstein) `_, - `@seanm (Sean McBride) `_, - `@gsjaardema (Greg Sjaardema) `_, - `@timsong-cpp `_, - `@seanm (Sean McBride) `_, - `@frithrah `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@Agga `_, - `@madmaxoft (Mattes D) `_, - `@JurajX (Juraj) `_, - `@phprus (Vladislav Shchapov) `_, - `@Dani-Hub (Daniel Krügler) `_. - -8.1.1 - 2022-01-06 ------------------- - -* Restored ABI compatibility with version 8.0.x - (`#2695 `_, - `#2696 `_). - Thanks `@saraedum (Julian Rüth) `_. - -* Fixed chrono formatting on big endian systems - (`#2698 `_, - `#2699 `_). - Thanks `@phprus (Vladislav Shchapov) `_ and - `@xvitaly (Vitaly Zaitsev) `_. - -* Fixed a linkage error with mingw - (`#2691 `_, - `#2692 `_). - Thanks `@rbberger (Richard Berger) `_. - -8.1.0 - 2022-01-02 ------------------- - -* Optimized chrono formatting - (`#2500 `_, - `#2537 `_, - `#2541 `_, - `#2544 `_, - `#2550 `_, - `#2551 `_, - `#2576 `_, - `#2577 `_, - `#2586 `_, - `#2591 `_, - `#2594 `_, - `#2602 `_, - `#2617 `_, - `#2628 `_, - `#2633 `_, - `#2670 `_, - `#2671 `_). - - Processing of some specifiers such as ``%z`` and ``%Y`` is now up to 10-20 - times faster, for example on GCC 11 with libstdc++:: - - ---------------------------------------------------------------------------- - Benchmark Before After - ---------------------------------------------------------------------------- - FMTFormatter_z 261 ns 26.3 ns - FMTFormatterCompile_z 246 ns 11.6 ns - FMTFormatter_Y 263 ns 26.1 ns - FMTFormatterCompile_Y 244 ns 10.5 ns - ---------------------------------------------------------------------------- - - Thanks `@phprus (Vladislav Shchapov) `_ and - `@toughengineer (Pavel Novikov) `_. - -* Implemented subsecond formatting for chrono durations - (`#2623 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - fmt::print("{:%S}", std::chrono::milliseconds(1234)); - } - - prints "01.234". - - Thanks `@matrackif `_. - -* Fixed handling of precision 0 when formatting chrono durations - (`#2587 `_, - `#2588 `_). - Thanks `@lukester1975 `_. - -* Fixed an overflow on invalid inputs in the ``tm`` formatter - (`#2564 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added ``fmt::group_digits`` that formats integers with a non-localized digit - separator (comma) for groups of three digits. - For example (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - fmt::print("{} dollars", fmt::group_digits(1000000)); - } - - prints "1,000,000 dollars". - -* Added support for faint, conceal, reverse and blink text styles - (`#2394 `_): - - https://user-images.githubusercontent.com/576385/147710227-c68f5317-f8fa-42c3-9123-7c4ba3c398cb.mp4 - - Thanks `@benit8 (Benoît Lormeau) `_ and - `@data-man (Dmitry Atamanov) `_. - -* Added experimental support for compile-time floating point formatting - (`#2426 `_, - `#2470 `_). - It is currently limited to the header-only mode. - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Added UDL-based named argument support to compile-time format string checks - (`#2640 `_, - `#2649 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - using namespace fmt::literals; - fmt::print("{answer:s}", "answer"_a=42); - } - - gives a compile-time error on compilers with C++20 ``consteval`` and non-type - template parameter support (gcc 10+) because ``s`` is not a valid format - specifier for an integer. - - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Implemented escaping of string range elements. - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - fmt::print("{}", std::vector{"\naan"}); - } - - is now printed as:: - - ["\naan"] - - instead of:: - - [" - aan"] - -* Added an experimental ``?`` specifier for escaping strings. - (`#2674 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Switched to JSON-like representation of maps and sets for consistency with - Python's ``str.format``. - For example (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - fmt::print("{}", std::map{{"answer", 42}}); - } - - is now printed as:: - - {"answer": 42} - -* Extended ``fmt::join`` to support C++20-only ranges - (`#2549 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Optimized handling of non-const-iterable ranges and implemented initial - support for non-const-formattable types. - -* Disabled implicit conversions of scoped enums to integers that was - accidentally introduced in earlier versions - (`#1841 `_). - -* Deprecated implicit conversion of ``[const] signed char*`` and - ``[const] unsigned char*`` to C strings. - -* Deprecated ``_format``, a legacy UDL-based format API - (`#2646 `_). - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Marked ``format``, ``formatted_size`` and ``to_string`` as ``[[nodiscard]]`` - (`#2612 `_). - `@0x8000-0000 (Florin Iucha) `_. - -* Added missing diagnostic when trying to format function and member pointers - as well as objects convertible to pointers which is explicitly disallowed - (`#2598 `_, - `#2609 `_, - `#2610 `_). - Thanks `@AlexGuteniev (Alex Guteniev) `_. - -* Optimized writing to a contiguous buffer with ``format_to_n`` - (`#2489 `_). - Thanks `@Roman-Koshelev `_. - -* Optimized writing to non-``char`` buffers - (`#2477 `_). - Thanks `@Roman-Koshelev `_. - -* Decimal point is now localized when using the ``L`` specifier. - -* Improved floating point formatter implementation - (`#2498 `_, - `#2499 `_). - Thanks `@Roman-Koshelev `_. - -* Fixed handling of very large precision in fixed format - (`#2616 `_). - -* Made a table of cached powers used in FP formatting static - (`#2509 `_). - Thanks `@jk-jeon (Junekey Jeon) `_. - -* Resolved a lookup ambiguity with C++20 format-related functions due to ADL - (`#2639 `_, - `#2641 `_). - Thanks `@mkurdej (Marek Kurdej) `_. - -* Removed unnecessary inline namespace qualification - (`#2642 `_, - `#2643 `_). - Thanks `@mkurdej (Marek Kurdej) `_. - -* Implemented argument forwarding in ``format_to_n`` - (`#2462 `_, - `#2463 `_). - Thanks `@owent (WenTao Ou) `_. - -* Fixed handling of implicit conversions in ``fmt::to_string`` and format string - compilation (`#2565 `_). - -* Changed the default access mode of files created by ``fmt::output_file`` to - ``-rw-r--r--`` for consistency with ``fopen`` - (`#2530 `_). - -* Make ``fmt::ostream::flush`` public - (`#2435 `_). - -* Improved C++14/17 attribute detection - (`#2615 `_). - Thanks `@AlexGuteniev (Alex Guteniev) `_. - -* Improved ``consteval`` detection for MSVC - (`#2559 `_). - Thanks `@DanielaE (Daniela Engert) `_. - -* Improved documentation - (`#2406 `_, - `#2446 `_, - `#2493 `_, - `#2513 `_, - `#2515 `_, - `#2522 `_, - `#2562 `_, - `#2575 `_, - `#2606 `_, - `#2620 `_, - `#2676 `_). - Thanks `@sobolevn (Nikita Sobolev) `_, - `@UnePierre (Max FERGER) `_, - `@zhsj `_, - `@phprus (Vladislav Shchapov) `_, - `@ericcurtin (Eric Curtin) `_, - `@Lounarok `_. - -* Improved fuzzers and added a fuzzer for chrono timepoint formatting - (`#2461 `_, - `#2469 `_). - `@pauldreik (Paul Dreik) `_, - -* Added the ``FMT_SYSTEM_HEADERS`` CMake option setting which marks {fmt}'s - headers as system. It can be used to suppress warnings - (`#2644 `_, - `#2651 `_). - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Added the Bazel build system support - (`#2505 `_, - `#2516 `_). - Thanks `@Vertexwahn `_. - -* Improved build configuration and tests - (`#2437 `_, - `#2558 `_, - `#2648 `_, - `#2650 `_, - `#2663 `_, - `#2677 `_). - Thanks `@DanielaE (Daniela Engert) `_, - `@alexezeder (Alexey Ochapov) `_, - `@phprus (Vladislav Shchapov) `_. - -* Fixed various warnings and compilation issues - (`#2353 `_, - `#2356 `_, - `#2399 `_, - `#2408 `_, - `#2414 `_, - `#2427 `_, - `#2432 `_, - `#2442 `_, - `#2434 `_, - `#2439 `_, - `#2447 `_, - `#2450 `_, - `#2455 `_, - `#2465 `_, - `#2472 `_, - `#2474 `_, - `#2476 `_, - `#2478 `_, - `#2479 `_, - `#2481 `_, - `#2482 `_, - `#2483 `_, - `#2490 `_, - `#2491 `_, - `#2510 `_, - `#2518 `_, - `#2528 `_, - `#2529 `_, - `#2539 `_, - `#2540 `_, - `#2545 `_, - `#2555 `_, - `#2557 `_, - `#2570 `_, - `#2573 `_, - `#2582 `_, - `#2605 `_, - `#2611 `_, - `#2647 `_, - `#2627 `_, - `#2630 `_, - `#2635 `_, - `#2638 `_, - `#2653 `_, - `#2654 `_, - `#2661 `_, - `#2664 `_, - `#2684 `_). - Thanks `@DanielaE (Daniela Engert) `_, - `@mwinterb `_, - `@cdacamar (Cameron DaCamara) `_, - `@TrebledJ (Johnathan) `_, - `@bodomartin (brm) `_, - `@cquammen (Cory Quammen) `_, - `@white238 (Chris White) `_, - `@mmarkeloff (Max) `_, - `@palacaze (Pierre-Antoine Lacaze) `_, - `@jcelerier (Jean-Michaël Celerier) `_, - `@mborn-adi (Mathias Born) `_, - `@BrukerJWD (Jonathan W) `_, - `@spyridon97 (Spiros Tsalikis) `_, - `@phprus (Vladislav Shchapov) `_, - `@oliverlee (Oliver Lee) `_, - `@joshessman-llnl (Josh Essman) `_, - `@akohlmey (Axel Kohlmeyer) `_, - `@timkalu `_, - `@olupton (Olli Lupton) `_, - `@Acretock `_, - `@alexezeder (Alexey Ochapov) `_, - `@andrewcorrigan (Andrew Corrigan) `_, - `@lucpelletier `_, - `@HazardyKnusperkeks (Björn Schäpers) - `_. - -8.0.1 - 2021-07-02 ------------------- - -* Fixed the version number in the inline namespace - (`#2374 `_). - -* Added a missing presentation type check for ``std::string`` - (`#2402 `_). - -* Fixed a linkage error when mixing code built with clang and gcc - (`#2377 `_). - -* Fixed documentation issues - (`#2396 `_, - `#2403 `_, - `#2406 `_). - Thanks `@mkurdej (Marek Kurdej) `_. - -* Removed dead code in FP formatter ( - `#2398 `_). - Thanks `@javierhonduco (Javier Honduvilla Coto) - `_. - -* Fixed various warnings and compilation issues - (`#2351 `_, - `#2359 `_, - `#2365 `_, - `#2368 `_, - `#2370 `_, - `#2376 `_, - `#2381 `_, - `#2382 `_, - `#2386 `_, - `#2389 `_, - `#2395 `_, - `#2397 `_, - `#2400 `_, - `#2401 `_, - `#2407 `_). - Thanks `@zx2c4 (Jason A. Donenfeld) `_, - `@AidanSun05 (Aidan Sun) `_, - `@mattiasljungstrom (Mattias Ljungström) - `_, - `@joemmett (Jonathan Emmett) `_, - `@erengy (Eren Okka) `_, - `@patlkli (Patrick Geltinger) `_, - `@gsjaardema (Greg Sjaardema) `_, - `@phprus (Vladislav Shchapov) `_. - -8.0.0 - 2021-06-21 ------------------- - -* Enabled compile-time format string checks by default. - For example (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - fmt::print("{:d}", "I am not a number"); - } - - gives a compile-time error on compilers with C++20 ``consteval`` support - (gcc 10+, clang 11+) because ``d`` is not a valid format specifier for a - string. - - To pass a runtime string wrap it in ``fmt::runtime``: - - .. code:: c++ - - fmt::print(fmt::runtime("{:d}"), "I am not a number"); - -* Added compile-time formatting - (`#2019 `_, - `#2044 `_, - `#2056 `_, - `#2072 `_, - `#2075 `_, - `#2078 `_, - `#2129 `_, - `#2326 `_). - For example (`godbolt `__): - - .. code:: c++ - - #include - - consteval auto compile_time_itoa(int value) -> std::array { - auto result = std::array(); - fmt::format_to(result.data(), FMT_COMPILE("{}"), value); - return result; - } - - constexpr auto answer = compile_time_itoa(42); - - Most of the formatting functionality is available at compile time with a - notable exception of floating-point numbers and pointers. - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Optimized handling of format specifiers during format string compilation. - For example, hexadecimal formatting (``"{:x}"``) is now 3-7x faster than - before when using ``format_to`` with format string compilation and a - stack-allocated buffer (`#1944 `_). - - Before (7.1.3):: - - ---------------------------------------------------------------------------- - Benchmark Time CPU Iterations - ---------------------------------------------------------------------------- - FMTCompileOld/0 15.5 ns 15.5 ns 43302898 - FMTCompileOld/42 16.6 ns 16.6 ns 43278267 - FMTCompileOld/273123 18.7 ns 18.6 ns 37035861 - FMTCompileOld/9223372036854775807 19.4 ns 19.4 ns 35243000 - ---------------------------------------------------------------------------- - - After (8.x):: - - ---------------------------------------------------------------------------- - Benchmark Time CPU Iterations - ---------------------------------------------------------------------------- - FMTCompileNew/0 1.99 ns 1.99 ns 360523686 - FMTCompileNew/42 2.33 ns 2.33 ns 279865664 - FMTCompileNew/273123 3.72 ns 3.71 ns 190230315 - FMTCompileNew/9223372036854775807 5.28 ns 5.26 ns 130711631 - ---------------------------------------------------------------------------- - - It is even faster than ``std::to_chars`` from libc++ compiled with clang on - macOS:: - - ---------------------------------------------------------------------------- - Benchmark Time CPU Iterations - ---------------------------------------------------------------------------- - ToChars/0 4.42 ns 4.41 ns 160196630 - ToChars/42 5.00 ns 4.98 ns 140735201 - ToChars/273123 7.26 ns 7.24 ns 95784130 - ToChars/9223372036854775807 8.77 ns 8.75 ns 75872534 - ---------------------------------------------------------------------------- - - In other cases, especially involving ``std::string`` construction, the - speed up is usually lower because handling format specifiers takes a smaller - fraction of the total time. - -* Added the ``_cf`` user-defined literal to represent a compiled format string. - It can be used instead of the ``FMT_COMPILE`` macro - (`#2043 `_, - `#2242 `_): - - .. code:: c++ - - #include - - using namespace fmt::literals; - auto s = fmt::format(FMT_COMPILE("{}"), 42); // 🙁 not modern - auto s = fmt::format("{}"_cf, 42); // 🙂 modern as hell - - It requires compiler support for class types in non-type template parameters - (a C++20 feature) which is available in GCC 9.3+. - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Format string compilation now requires ``format`` functions of ``formatter`` - specializations for user-defined types to be ``const``: - - .. code:: c++ - - template <> struct fmt::formatter: formatter { - template - auto format(my_type obj, FormatContext& ctx) const { // Note const here. - // ... - } - }; - -* Added UDL-based named argument support to format string compilation - (`#2243 `_, - `#2281 `_). For example: - - .. code:: c++ - - #include - - using namespace fmt::literals; - auto s = fmt::format(FMT_COMPILE("{answer}"), "answer"_a = 42); - - Here the argument named "answer" is resolved at compile time with no - runtime overhead. - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Added format string compilation support to ``fmt::print`` - (`#2280 `_, - `#2304 `_). - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Added initial support for compiling {fmt} as a C++20 module - (`#2235 `_, - `#2240 `_, - `#2260 `_, - `#2282 `_, - `#2283 `_, - `#2288 `_, - `#2298 `_, - `#2306 `_, - `#2307 `_, - `#2309 `_, - `#2318 `_, - `#2324 `_, - `#2332 `_, - `#2340 `_). - Thanks `@DanielaE (Daniela Engert) `_. - -* Made symbols private by default reducing shared library size - (`#2301 `_). For example there was - a ~15% reported reduction on one platform. - Thanks `@sergiud (Sergiu Deitsch) `_. - -* Optimized includes making the result of preprocessing ``fmt/format.h`` - ~20% smaller with libstdc++/C++20 and slightly improving build times - (`#1998 `_). - -* Added support of ranges with non-const ``begin`` / ``end`` - (`#1953 `_). - Thanks `@kitegi (sarah) `_. - -* Added support of ``std::byte`` and other formattable types to ``fmt::join`` - (`#1981 `_, - `#2040 `_, - `#2050 `_, - `#2262 `_). For example: - - .. code:: c++ - - #include - #include - #include - - int main() { - auto bytes = std::vector{std::byte(4), std::byte(2)}; - fmt::print("{}", fmt::join(bytes, "")); - } - - prints "42". - - Thanks `@kamibo (Camille Bordignon) `_. - -* Implemented the default format for ``std::chrono::system_clock`` - (`#2319 `_, - `#2345 `_). For example: - - .. code:: c++ - - #include - - int main() { - fmt::print("{}", std::chrono::system_clock::now()); - } - - prints "2021-06-18 15:22:00" (the output depends on the current date and - time). Thanks `@sunmy2019 `_. - -* Made more chrono specifiers locale independent by default. Use the ``'L'`` - specifier to get localized formatting. For example: - - .. code:: c++ - - #include - - int main() { - std::locale::global(std::locale("ru_RU.UTF-8")); - auto monday = std::chrono::weekday(1); - fmt::print("{}\n", monday); // prints "Mon" - fmt::print("{:L}\n", monday); // prints "пн" - } - -* Improved locale handling in chrono formatting - (`#2337 `_, - `#2349 `_, - `#2350 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Deprecated ``fmt/locale.h`` moving the formatting functions that take a - locale to ``fmt/format.h`` (``char``) and ``fmt/xchar`` (other overloads). - This doesn't introduce a dependency on ```` so there is virtually no - compile time effect. - -* Deprecated an undocumented ``format_to`` overload that takes - ``basic_memory_buffer``. - -* Made parameter order in ``vformat_to`` consistent with ``format_to`` - (`#2327 `_). - -* Added support for time points with arbitrary durations - (`#2208 `_). For example: - - .. code:: c++ - - #include - - int main() { - using tp = std::chrono::time_point< - std::chrono::system_clock, std::chrono::seconds>; - fmt::print("{:%S}", tp(std::chrono::seconds(42))); - } - - prints "42". - -* Formatting floating-point numbers no longer produces trailing zeros by default - for consistency with ``std::format``. For example: - - .. code:: c++ - - #include - - int main() { - fmt::print("{0:.3}", 1.1); - } - - prints "1.1". Use the ``'#'`` specifier to keep trailing zeros. - -* Dropped a limit on the number of elements in a range and replaced ``{}`` with - ``[]`` as range delimiters for consistency with Python's ``str.format``. - -* The ``'L'`` specifier for locale-specific numeric formatting can now be - combined with presentation specifiers as in ``std::format``. For example: - - .. code:: c++ - - #include - #include - - int main() { - std::locale::global(std::locale("fr_FR.UTF-8")); - fmt::print("{0:.2Lf}", 0.42); - } - - prints "0,42". The deprecated ``'n'`` specifier has been removed. - -* Made the ``0`` specifier ignored for infinity and NaN - (`#2305 `_, - `#2310 `_). - Thanks `@Liedtke (Matthias Liedtke) `_. - -* Made the hexfloat formatting use the right alignment by default - (`#2308 `_, - `#2317 `_). - Thanks `@Liedtke (Matthias Liedtke) `_. - -* Removed the deprecated numeric alignment (``'='``). Use the ``'0'`` specifier - instead. - -* Removed the deprecated ``fmt/posix.h`` header that has been replaced with - ``fmt/os.h``. - -* Removed the deprecated ``format_to_n_context``, ``format_to_n_args`` and - ``make_format_to_n_args``. They have been replaced with ``format_context``, - ``format_args` and ``make_format_args`` respectively. - -* Moved ``wchar_t``-specific functions and types to ``fmt/xchar.h``. - You can define ``FMT_DEPRECATED_INCLUDE_XCHAR`` to automatically include - ``fmt/xchar.h`` from ``fmt/format.h`` but this will be disabled in the next - major release. - -* Fixed handling of the ``'+'`` specifier in localized formatting - (`#2133 `_). - -* Added support for the ``'s'`` format specifier that gives textual - representation of ``bool`` - (`#2094 `_, - `#2109 `_). For example: - - .. code:: c++ - - #include - - int main() { - fmt::print("{:s}", true); - } - - prints "true". - Thanks `@powercoderlol (Ivan Polyakov) `_. - -* Made ``fmt::ptr`` work with function pointers - (`#2131 `_). For example: - - .. code:: c++ - - #include - - int main() { - fmt::print("My main: {}\n", fmt::ptr(main)); - } - - Thanks `@mikecrowe (Mike Crowe) `_. - -* The undocumented support for specializing ``formatter`` for pointer types - has been removed. - -* Fixed ``fmt::formatted_size`` with format string compilation - (`#2141 `_, - `#2161 `_). - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Fixed handling of empty format strings during format string compilation - (`#2042 `_): - - .. code:: c++ - - auto s = fmt::format(FMT_COMPILE("")); - - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Fixed handling of enums in ``fmt::to_string`` - (`#2036 `_). - -* Improved width computation - (`#2033 `_, - `#2091 `_). For example: - - .. code:: c++ - - #include - - int main() { - fmt::print("{:-<10}{}\n", "你好", "世界"); - fmt::print("{:-<10}{}\n", "hello", "world"); - } - - prints - - .. image:: https://user-images.githubusercontent.com/576385/ - 119840373-cea3ca80-beb9-11eb-91e0-54266c48e181.png - - on a modern terminal. - -* The experimental fast output stream (``fmt::ostream``) is now truncated by - default for consistency with ``fopen`` - (`#2018 `_). For example: - - .. code:: c++ - - #include - - int main() { - fmt::ostream out1 = fmt::output_file("guide"); - out1.print("Zaphod"); - out1.close(); - fmt::ostream out2 = fmt::output_file("guide"); - out2.print("Ford"); - } - - writes "Ford" to the file "guide". To preserve the old file content if any - pass ``fmt::file::WRONLY | fmt::file::CREATE`` flags to ``fmt::output_file``. - -* Fixed moving of ``fmt::ostream`` that holds buffered data - (`#2197 `_, - `#2198 `_). - Thanks `@vtta `_. - -* Replaced the ``fmt::system_error`` exception with a function of the same - name that constructs ``std::system_error`` - (`#2266 `_). - -* Replaced the ``fmt::windows_error`` exception with a function of the same - name that constructs ``std::system_error`` with the category returned by - ``fmt::system_category()`` - (`#2274 `_, - `#2275 `_). - The latter is similar to ``std::sytem_category`` but correctly handles UTF-8. - Thanks `@phprus (Vladislav Shchapov) `_. - -* Replaced ``fmt::error_code`` with ``std::error_code`` and made it formattable - (`#2269 `_, - `#2270 `_, - `#2273 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added speech synthesis support - (`#2206 `_). - -* Made ``format_to`` work with a memory buffer that has a custom allocator - (`#2300 `_). - Thanks `@voxmea `_. - -* Added ``Allocator::max_size`` support to ``basic_memory_buffer``. - (`#1960 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Added wide string support to ``fmt::join`` - (`#2236 `_). - Thanks `@crbrz `_. - -* Made iterators passed to ``formatter`` specializations via a format context - satisfy C++20 ``std::output_iterator`` requirements - (`#2156 `_, - `#2158 `_, - `#2195 `_, - `#2204 `_). - Thanks `@randomnetcat (Jason Cobb) `_. - -* Optimized the ``printf`` implementation - (`#1982 `_, - `#1984 `_, - `#2016 `_, - `#2164 `_). - Thanks `@rimathia `_ and - `@moiwi `_. - -* Improved detection of ``constexpr`` ``char_traits`` - (`#2246 `_, - `#2257 `_). - Thanks `@phprus (Vladislav Shchapov) `_. - -* Fixed writing to ``stdout`` when it is redirected to ``NUL`` on Windows - (`#2080 `_). - -* Fixed exception propagation from iterators - (`#2097 `_). - -* Improved ``strftime`` error handling - (`#2238 `_, - `#2244 `_). - Thanks `@yumeyao `_. - -* Stopped using deprecated GCC UDL template extension. - -* Added ``fmt/args.h`` to the install target - (`#2096 `_). - -* Error messages are now passed to assert when exceptions are disabled - (`#2145 `_). - Thanks `@NobodyXu (Jiahao XU) `_. - -* Added the ``FMT_MASTER_PROJECT`` CMake option to control build and install - targets when {fmt} is included via ``add_subdirectory`` - (`#2098 `_, - `#2100 `_). - Thanks `@randomizedthinking `_. - -* Improved build configuration - (`#2026 `_, - `#2122 `_). - Thanks `@luncliff (Park DongHa) `_ and - `@ibaned (Dan Ibanez) `_. - -* Fixed various warnings and compilation issues - (`#1947 `_, - `#1959 `_, - `#1963 `_, - `#1965 `_, - `#1966 `_, - `#1974 `_, - `#1975 `_, - `#1990 `_, - `#2000 `_, - `#2001 `_, - `#2002 `_, - `#2004 `_, - `#2006 `_, - `#2009 `_, - `#2010 `_, - `#2038 `_, - `#2039 `_, - `#2047 `_, - `#2053 `_, - `#2059 `_, - `#2065 `_, - `#2067 `_, - `#2068 `_, - `#2073 `_, - `#2103 `_, - `#2105 `_, - `#2106 `_, - `#2107 `_, - `#2116 `_, - `#2117 `_, - `#2118 `_, - `#2119 `_, - `#2127 `_, - `#2128 `_, - `#2140 `_, - `#2142 `_, - `#2143 `_, - `#2144 `_, - `#2147 `_, - `#2148 `_, - `#2149 `_, - `#2152 `_, - `#2160 `_, - `#2170 `_, - `#2175 `_, - `#2176 `_, - `#2177 `_, - `#2178 `_, - `#2179 `_, - `#2180 `_, - `#2181 `_, - `#2183 `_, - `#2184 `_, - `#2185 `_, - `#2186 `_, - `#2187 `_, - `#2190 `_, - `#2192 `_, - `#2194 `_, - `#2205 `_, - `#2210 `_, - `#2211 `_, - `#2215 `_, - `#2216 `_, - `#2218 `_, - `#2220 `_, - `#2228 `_, - `#2229 `_, - `#2230 `_, - `#2233 `_, - `#2239 `_, - `#2248 `_, - `#2252 `_, - `#2253 `_, - `#2255 `_, - `#2261 `_, - `#2278 `_, - `#2284 `_, - `#2287 `_, - `#2289 `_, - `#2290 `_, - `#2293 `_, - `#2295 `_, - `#2296 `_, - `#2297 `_, - `#2311 `_, - `#2313 `_, - `#2315 `_, - `#2320 `_, - `#2321 `_, - `#2323 `_, - `#2328 `_, - `#2329 `_, - `#2333 `_, - `#2338 `_, - `#2341 `_). - Thanks `@darklukee `_, - `@fagg (Ashton Fagg) `_, - `@killerbot242 (Lieven de Cock) `_, - `@jgopel (Jonathan Gopel) `_, - `@yeswalrus (Walter Gray) `_, - `@Finkman `_, - `@HazardyKnusperkeks (Björn Schäpers) `_, - `@dkavolis (Daumantas Kavolis) `_, - `@concatime (Issam Maghni) `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@summivox (Yin Zhong) `_, - `@yNeo `_, - `@Apache-HB (Elliot) `_, - `@alexezeder (Alexey Ochapov) `_, - `@toojays (John Steele Scott) `_, - `@Brainy0207 `_, - `@vadz (VZ) `_, - `@imsherlock (Ryan Sherlock) `_, - `@phprus (Vladislav Shchapov) `_, - `@white238 (Chris White) `_, - `@yafshar (Yaser Afshar) `_, - `@BillyDonahue (Billy Donahue) `_, - `@jstaahl `_, - `@denchat `_, - `@DanielaE (Daniela Engert) `_, - `@ilyakurdyukov (Ilya Kurdyukov) `_, - `@ilmai `_, - `@JessyDL (Jessy De Lannoit) `_, - `@sergiud (Sergiu Deitsch) `_, - `@mwinterb `_, - `@sven-herrmann `_, - `@jmelas (John Melas) `_, - `@twoixter (Jose Miguel Pérez) `_, - `@crbrz `_, - `@upsj (Tobias Ribizel) `_. - -* Improved documentation - (`#1986 `_, - `#2051 `_, - `#2057 `_, - `#2081 `_, - `#2084 `_, - `#2312 `_). - Thanks `@imba-tjd (谭九鼎) `_, - `@0x416c69 (AlιAѕѕaѕѕιN) `_, - `@mordante `_. - -* Continuous integration and test improvements - (`#1969 `_, - `#1991 `_, - `#2020 `_, - `#2110 `_, - `#2114 `_, - `#2196 `_, - `#2217 `_, - `#2247 `_, - `#2256 `_, - `#2336 `_, - `#2346 `_). - Thanks `@jgopel (Jonathan Gopel) `_, - `@alexezeder (Alexey Ochapov) `_ and - `@DanielaE (Daniela Engert) `_. - -7.1.3 - 2020-11-24 ------------------- - -* Fixed handling of buffer boundaries in ``format_to_n`` - (`#1996 `_, - `#2029 `_). - -* Fixed linkage errors when linking with a shared library - (`#2011 `_). - -* Reintroduced ostream support to range formatters - (`#2014 `_). - -* Worked around an issue with mixing std versions in gcc - (`#2017 `_). - -7.1.2 - 2020-11-04 ------------------- - -* Fixed floating point formatting with large precision - (`#1976 `_). - -7.1.1 - 2020-11-01 ------------------- - -* Fixed ABI compatibility with 7.0.x - (`#1961 `_). - -* Added the ``FMT_ARM_ABI_COMPATIBILITY`` macro to work around ABI - incompatibility between GCC and Clang on ARM - (`#1919 `_). - -* Worked around a SFINAE bug in GCC 8 - (`#1957 `_). - -* Fixed linkage errors when building with GCC's LTO - (`#1955 `_). - -* Fixed a compilation error when building without ``__builtin_clz`` or equivalent - (`#1968 `_). - Thanks `@tohammer (Tobias Hammer) `_. - -* Fixed a sign conversion warning - (`#1964 `_). - Thanks `@OptoCloud `_. - -7.1.0 - 2020-10-25 ------------------- - -* Switched from `Grisu3 - `_ - to `Dragonbox `_ for the default - floating-point formatting which gives the shortest decimal representation - with round-trip guarantee and correct rounding - (`#1882 `_, - `#1887 `_, - `#1894 `_). This makes {fmt} up to - 20-30x faster than common implementations of ``std::ostringstream`` and - ``sprintf`` on `dtoa-benchmark `_ - and faster than double-conversion and Ryū: - - .. image:: https://user-images.githubusercontent.com/576385/ - 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png - - It is possible to get even better performance at the cost of larger binary - size by compiling with the ``FMT_USE_FULL_CACHE_DRAGONBOX`` macro set to 1. - - Thanks `@jk-jeon (Junekey Jeon) `_. - -* Added an experimental unsynchronized file output API which, together with - `format string compilation `_, - can give `5-9 times speed up compared to fprintf - `_ - on common platforms (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - auto f = fmt::output_file("guide"); - f.print("The answer is {}.", 42); - } - -* Added a formatter for ``std::chrono::time_point`` - (`#1819 `_, - `#1837 `_). For example - (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - auto now = std::chrono::system_clock::now(); - fmt::print("The time is {:%H:%M:%S}.\n", now); - } - - Thanks `@adamburgess (Adam Burgess) `_. - -* Added support for ranges with non-const ``begin``/``end`` to ``fmt::join`` - (`#1784 `_, - `#1786 `_). For example - (`godbolt `__): - - .. code:: c++ - - #include - #include - - int main() { - using std::literals::string_literals::operator""s; - auto strs = std::array{"a"s, "bb"s, "ccc"s}; - auto range = strs | ranges::views::filter( - [] (const std::string &x) { return x.size() != 2; } - ); - fmt::print("{}\n", fmt::join(range, "")); - } - - prints "accc". - - Thanks `@tonyelewis (Tony E Lewis) `_. - -* Added a ``memory_buffer::append`` overload that takes a range - (`#1806 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Improved handling of single code units in ``FMT_COMPILE``. For example: - - .. code:: c++ - - #include - - char* f(char* buf) { - return fmt::format_to(buf, FMT_COMPILE("x{}"), 42); - } - - compiles to just (`godbolt `__): - - .. code:: asm - - _Z1fPc: - movb $120, (%rdi) - xorl %edx, %edx - cmpl $42, _ZN3fmt2v76detail10basic_dataIvE23zero_or_powers_of_10_32E+8(%rip) - movl $3, %eax - seta %dl - subl %edx, %eax - movzwl _ZN3fmt2v76detail10basic_dataIvE6digitsE+84(%rip), %edx - cltq - addq %rdi, %rax - movw %dx, -2(%rax) - ret - - Here a single ``mov`` instruction writes ``'x'`` (``$120``) to the output - buffer. - -* Added dynamic width support to format string compilation - (`#1809 `_). - -* Improved error reporting for unformattable types: now you'll get the type name - directly in the error message instead of the note: - - .. code:: c++ - - #include - - struct how_about_no {}; - - int main() { - fmt::print("{}", how_about_no()); - } - - Error (`godbolt `__): - - ``fmt/core.h:1438:3: error: static_assert failed due to requirement - 'fmt::v7::formattable()' "Cannot format an argument. - To make type T formattable provide a formatter specialization: - https://fmt.dev/latest/api.html#udt" - ...`` - -* Added the `make_args_checked `_ - function template that allows you to write formatting functions with - compile-time format string checks and avoid binary code bloat - (`godbolt `__): - - .. code:: c++ - - void vlog(const char* file, int line, fmt::string_view format, - fmt::format_args args) { - fmt::print("{}: {}: ", file, line); - fmt::vprint(format, args); - } - - template - void log(const char* file, int line, const S& format, Args&&... args) { - vlog(file, line, format, - fmt::make_args_checked(format, args...)); - } - - #define MY_LOG(format, ...) \ - log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__) - - MY_LOG("invalid squishiness: {}", 42); - -* Replaced ``snprintf`` fallback with a faster internal IEEE 754 ``float`` and - ``double`` formatter for arbitrary precision. For example - (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - fmt::print("{:.500}\n", 4.9406564584124654E-324); - } - - prints - - ``4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599237979656469544571773092665671035593979639877479601078187812630071319031140452784581716784898210368871863605699873072305000638740915356498438731247339727316961514003171538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332452475714786901472678015935523861155013480352649347201937902681071074917033322268447533357208324319360923829e-324``. - -* Made ``format_to_n`` and ``formatted_size`` part of the `core API - `__ - (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - char buffer[10]; - auto result = fmt::format_to_n(buffer, sizeof(buffer), "{}", 42); - } - -* Added ``fmt::format_to_n`` overload with format string compilation - (`#1764 `_, - `#1767 `_, - `#1869 `_). For example - (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - char buffer[8]; - fmt::format_to_n(buffer, sizeof(buffer), FMT_COMPILE("{}"), 42); - } - - Thanks `@Kurkin (Dmitry Kurkin) `_, - `@alexezeder (Alexey Ochapov) `_. - -* Added ``fmt::format_to`` overload that take ``text_style`` - (`#1593 `_, - `#1842 `_, - `#1843 `_). For example - (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - std::string out; - fmt::format_to(std::back_inserter(out), - fmt::emphasis::bold | fg(fmt::color::red), - "The answer is {}.", 42); - } - - Thanks `@Naios (Denis Blank) `_. - -* Made the ``'#'`` specifier emit trailing zeros in addition to the decimal - point (`#1797 `_). For example - (`godbolt `__): - - .. code:: c++ - - #include - - int main() { - fmt::print("{:#.2g}", 0.5); - } - - prints ``0.50``. - -* Changed the default floating point format to not include ``.0`` for - consistency with ``std::format`` and ``std::to_chars`` - (`#1893 `_, - `#1943 `_). It is possible to get - the decimal point and trailing zero with the ``#`` specifier. - -* Fixed an issue with floating-point formatting that could result in addition of - a non-significant trailing zero in rare cases e.g. ``1.00e-34`` instead of - ``1.0e-34`` (`#1873 `_, - `#1917 `_). - -* Made ``fmt::to_string`` fallback on ``ostream`` insertion operator if - the ``formatter`` specialization is not provided - (`#1815 `_, - `#1829 `_). - Thanks `@alexezeder (Alexey Ochapov) `_. - -* Added support for the append mode to the experimental file API and - improved ``fcntl.h`` detection. - (`#1847 `_, - `#1848 `_). - Thanks `@t-wiser `_. - -* Fixed handling of types that have both an implicit conversion operator and - an overloaded ``ostream`` insertion operator - (`#1766 `_). - -* Fixed a slicing issue in an internal iterator type - (`#1822 `_). - Thanks `@BRevzin (Barry Revzin) `_. - -* Fixed an issue in locale-specific integer formatting - (`#1927 `_). - -* Fixed handling of exotic code unit types - (`#1870 `_, - `#1932 `_). - -* Improved ``FMT_ALWAYS_INLINE`` - (`#1878 `_). - Thanks `@jk-jeon (Junekey Jeon) `_. - -* Removed dependency on ``windows.h`` - (`#1900 `_). - Thanks `@bernd5 (Bernd Baumanns) `_. - -* Optimized counting of decimal digits on MSVC - (`#1890 `_). - Thanks `@mwinterb `_. - -* Improved documentation - (`#1772 `_, - `#1775 `_, - `#1792 `_, - `#1838 `_, - `#1888 `_, - `#1918 `_, - `#1939 `_). - Thanks `@leolchat (Léonard Gérard) `_, - `@pepsiman (Malcolm Parsons) `_, - `@Klaim (Joël Lamotte) `_, - `@ravijanjam (Ravi J) `_, - `@francesco-st `_, - `@udnaan (Adnan) `_. - -* Added the ``FMT_REDUCE_INT_INSTANTIATIONS`` CMake option that reduces the - binary code size at the cost of some integer formatting performance. This can - be useful for extremely memory-constrained embedded systems - (`#1778 `_, - `#1781 `_). - Thanks `@kammce (Khalil Estell) `_. - -* Added the ``FMT_USE_INLINE_NAMESPACES`` macro to control usage of inline - namespaces (`#1945 `_). - Thanks `@darklukee `_. - -* Improved build configuration - (`#1760 `_, - `#1770 `_, - `#1779 `_, - `#1783 `_, - `#1823 `_). - Thanks `@dvetutnev (Dmitriy Vetutnev) `_, - `@xvitaly (Vitaly Zaitsev) `_, - `@tambry (Raul Tambre) `_, - `@medithe `_, - `@martinwuehrer (Martin Wührer) `_. - -* Fixed various warnings and compilation issues - (`#1790 `_, - `#1802 `_, - `#1808 `_, - `#1810 `_, - `#1811 `_, - `#1812 `_, - `#1814 `_, - `#1816 `_, - `#1817 `_, - `#1818 `_, - `#1825 `_, - `#1836 `_, - `#1855 `_, - `#1856 `_, - `#1860 `_, - `#1877 `_, - `#1879 `_, - `#1880 `_, - `#1896 `_, - `#1897 `_, - `#1898 `_, - `#1904 `_, - `#1908 `_, - `#1911 `_, - `#1912 `_, - `#1928 `_, - `#1929 `_, - `#1935 `_, - `#1937 `_, - `#1942 `_, - `#1949 `_). - Thanks `@TheQwertiest `_, - `@medithe `_, - `@martinwuehrer (Martin Wührer) `_, - `@n16h7hunt3r `_, - `@Othereum (Seokjin Lee) `_, - `@gsjaardema (Greg Sjaardema) `_, - `@AlexanderLanin (Alexander Lanin) `_, - `@gcerretani (Giovanni Cerretani) `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@noizefloor (Jan Schwers) `_, - `@akohlmey (Axel Kohlmeyer) `_, - `@jk-jeon (Junekey Jeon) `_, - `@rimathia `_, - `@rglarix (Riccardo Ghetta (larix)) `_, - `@moiwi `_, - `@heckad (Kazantcev Andrey) `_, - `@MarcDirven `_. - `@BartSiwek (Bart Siwek) `_, - `@darklukee `_. - -7.0.3 - 2020-08-06 ------------------- - -* Worked around broken ``numeric_limits`` for 128-bit integers - (`#1787 `_). - -* Added error reporting on missing named arguments - (`#1796 `_). - -* Stopped using 128-bit integers with clang-cl - (`#1800 `_). - Thanks `@Kingcom `_. - -* Fixed issues in locale-specific integer formatting - (`#1782 `_, - `#1801 `_). - -7.0.2 - 2020-07-29 ------------------- - -* Worked around broken ``numeric_limits`` for 128-bit integers - (`#1725 `_). - -* Fixed compatibility with CMake 3.4 - (`#1779 `_). - -* Fixed handling of digit separators in locale-specific formatting - (`#1782 `_). - -7.0.1 - 2020-07-07 ------------------- - -* Updated the inline version namespace name. - -* Worked around a gcc bug in mangling of alias templates - (`#1753 `_). - -* Fixed a linkage error on Windows - (`#1757 `_). - Thanks `@Kurkin (Dmitry Kurkin) `_. - -* Fixed minor issues with the documentation. - -7.0.0 - 2020-07-05 ------------------- - -* Reduced the library size. For example, on macOS a stripped test binary - statically linked with {fmt} `shrank from ~368k to less than 100k - `_. - -* Added a simpler and more efficient `format string compilation API - `_: - - .. code:: c++ - - #include - - // Converts 42 into std::string using the most efficient method and no - // runtime format string processing. - std::string s = fmt::format(FMT_COMPILE("{}"), 42); - - The old ``fmt::compile`` API is now deprecated. - -* Optimized integer formatting: ``format_to`` with format string compilation - and a stack-allocated buffer is now `faster than to_chars on both - libc++ and libstdc++ - `_. - -* Optimized handling of small format strings. For example, - - .. code:: c++ - - fmt::format("Result: {}: ({},{},{},{})", str1, str2, str3, str4, str5) - - is now ~40% faster (`#1685 `_). - -* Applied extern templates to improve compile times when using the core API - and ``fmt/format.h`` (`#1452 `_). - For example, on macOS with clang the compile time of a test translation unit - dropped from 2.3s to 0.3s with ``-O2`` and from 0.6s to 0.3s with the default - settings (``-O0``). - - Before (``-O2``):: - - % time c++ -c test.cc -I include -std=c++17 -O2 - c++ -c test.cc -I include -std=c++17 -O2 2.22s user 0.08s system 99% cpu 2.311 total - - After (``-O2``):: - - % time c++ -c test.cc -I include -std=c++17 -O2 - c++ -c test.cc -I include -std=c++17 -O2 0.26s user 0.04s system 98% cpu 0.303 total - - Before (default):: - - % time c++ -c test.cc -I include -std=c++17 - c++ -c test.cc -I include -std=c++17 0.53s user 0.06s system 98% cpu 0.601 total - - After (default):: - - % time c++ -c test.cc -I include -std=c++17 - c++ -c test.cc -I include -std=c++17 0.24s user 0.06s system 98% cpu 0.301 total - - It is still recommended to use ``fmt/core.h`` instead of ``fmt/format.h`` but - the compile time difference is now smaller. Thanks - `@alex3d `_ for the suggestion. - -* Named arguments are now stored on stack (no dynamic memory allocations) and - the compiled code is more compact and efficient. For example - - .. code:: c++ - - #include - - int main() { - fmt::print("The answer is {answer}\n", fmt::arg("answer", 42)); - } - - compiles to just (`godbolt `__) - - .. code:: asm - - .LC0: - .string "answer" - .LC1: - .string "The answer is {answer}\n" - main: - sub rsp, 56 - mov edi, OFFSET FLAT:.LC1 - mov esi, 23 - movabs rdx, 4611686018427387905 - lea rax, [rsp+32] - lea rcx, [rsp+16] - mov QWORD PTR [rsp+8], 1 - mov QWORD PTR [rsp], rax - mov DWORD PTR [rsp+16], 42 - mov QWORD PTR [rsp+32], OFFSET FLAT:.LC0 - mov DWORD PTR [rsp+40], 0 - call fmt::v6::vprint(fmt::v6::basic_string_view, - fmt::v6::format_args) - xor eax, eax - add rsp, 56 - ret - - .L.str.1: - .asciz "answer" - -* Implemented compile-time checks for dynamic width and precision - (`#1614 `_): - - .. code:: c++ - - #include - - int main() { - fmt::print(FMT_STRING("{0:{1}}"), 42); - } - - now gives a compilation error because argument 1 doesn't exist:: - - In file included from test.cc:1: - include/fmt/format.h:2726:27: error: constexpr variable 'invalid_format' must be - initialized by a constant expression - FMT_CONSTEXPR_DECL bool invalid_format = - ^ - ... - include/fmt/core.h:569:26: note: in call to - '&checker(s, {}).context_->on_error(&"argument not found"[0])' - if (id >= num_args_) on_error("argument not found"); - ^ - -* Added sentinel support to ``fmt::join`` - (`#1689 `_) - - .. code:: c++ - - struct zstring_sentinel {}; - bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } - bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } - - struct zstring { - const char* p; - const char* begin() const { return p; } - zstring_sentinel end() const { return {}; } - }; - - auto s = fmt::format("{}", fmt::join(zstring{"hello"}, "_")); - // s == "h_e_l_l_o" - - Thanks `@BRevzin (Barry Revzin) `_. - -* Added support for named arguments, ``clear`` and ``reserve`` to - ``dynamic_format_arg_store`` - (`#1655 `_, - `#1663 `_, - `#1674 `_, - `#1677 `_). - Thanks `@vsolontsov-ll (Vladimir Solontsov) - `_. - -* Added support for the ``'c'`` format specifier to integral types for - compatibility with ``std::format`` - (`#1652 `_). - -* Replaced the ``'n'`` format specifier with ``'L'`` for compatibility with - ``std::format`` (`#1624 `_). - The ``'n'`` specifier can be enabled via the ``FMT_DEPRECATED_N_SPECIFIER`` - macro. - -* The ``'='`` format specifier is now disabled by default for compatibility with - ``std::format``. It can be enabled via the ``FMT_DEPRECATED_NUMERIC_ALIGN`` - macro. - -* Removed the following deprecated APIs: - - * ``FMT_STRING_ALIAS`` and ``fmt`` macros - replaced by ``FMT_STRING`` - * ``fmt::basic_string_view::char_type`` - replaced by - ``fmt::basic_string_view::value_type`` - * ``convert_to_int`` - * ``format_arg_store::types`` - * ``*parse_context`` - replaced by ``*format_parse_context`` - * ``FMT_DEPRECATED_INCLUDE_OS`` - * ``FMT_DEPRECATED_PERCENT`` - incompatible with ``std::format`` - * ``*writer`` - replaced by compiled format API - -* Renamed the ``internal`` namespace to ``detail`` - (`#1538 `_). The former is still - provided as an alias if the ``FMT_USE_INTERNAL`` macro is defined. - -* Improved compatibility between ``fmt::printf`` with the standard specs - (`#1595 `_, - `#1682 `_, - `#1683 `_, - `#1687 `_, - `#1699 `_). - Thanks `@rimathia `_. - -* Fixed handling of ``operator<<`` overloads that use ``copyfmt`` - (`#1666 `_). - -* Added the ``FMT_OS`` CMake option to control inclusion of OS-specific APIs - in the fmt target. This can be useful for embedded platforms - (`#1654 `_, - `#1656 `_). - Thanks `@kwesolowski (Krzysztof Wesolowski) - `_. - -* Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ`` - macro to prevent interfering with fuzzing of projects using {fmt} - (`#1650 `_). - Thanks `@asraa (Asra Ali) `_. - -* Fixed compatibility with emscripten - (`#1636 `_, - `#1637 `_). - Thanks `@ArthurSonzogni (Arthur Sonzogni) - `_. - -* Improved documentation - (`#704 `_, - `#1643 `_, - `#1660 `_, - `#1681 `_, - `#1691 `_, - `#1706 `_, - `#1714 `_, - `#1721 `_, - `#1739 `_, - `#1740 `_, - `#1741 `_, - `#1751 `_). - Thanks `@senior7515 (Alexander Gallego) `_, - `@lsr0 (Lindsay Roberts) `_, - `@puetzk (Kevin Puetz) `_, - `@fpelliccioni (Fernando Pelliccioni) `_, - Alexey Kuzmenko, `@jelly (jelle van der Waa) `_, - `@claremacrae (Clare Macrae) `_, - `@jiapengwen (文佳鹏) `_, - `@gsjaardema (Greg Sjaardema) `_, - `@alexey-milovidov `_. - -* Implemented various build configuration fixes and improvements - (`#1603 `_, - `#1657 `_, - `#1702 `_, - `#1728 `_). - Thanks `@scramsby (Scott Ramsby) `_, - `@jtojnar (Jan Tojnar) `_, - `@orivej (Orivej Desh) `_, - `@flagarde `_. - -* Fixed various warnings and compilation issues - (`#1616 `_, - `#1620 `_, - `#1622 `_, - `#1625 `_, - `#1627 `_, - `#1628 `_, - `#1629 `_, - `#1631 `_, - `#1633 `_, - `#1649 `_, - `#1658 `_, - `#1661 `_, - `#1667 `_, - `#1668 `_, - `#1669 `_, - `#1692 `_, - `#1696 `_, - `#1697 `_, - `#1707 `_, - `#1712 `_, - `#1716 `_, - `#1722 `_, - `#1724 `_, - `#1729 `_, - `#1738 `_, - `#1742 `_, - `#1743 `_, - `#1744 `_, - `#1747 `_, - `#1750 `_). - Thanks `@gsjaardema (Greg Sjaardema) `_, - `@gabime (Gabi Melman) `_, - `@johnor (Johan) `_, - `@Kurkin (Dmitry Kurkin) `_, - `@invexed (James Beach) `_, - `@peterbell10 `_, - `@daixtrose (Markus Werle) `_, - `@petrutlucian94 (Lucian Petrut) `_, - `@Neargye (Daniil Goncharov) `_, - `@ambitslix (Attila M. Szilagyi) `_, - `@gabime (Gabi Melman) `_, - `@erthink (Leonid Yuriev) `_, - `@tohammer (Tobias Hammer) `_, - `@0x8000-0000 (Florin Iucha) `_. - -6.2.1 - 2020-05-09 ------------------- - -* Fixed ostream support in ``sprintf`` - (`#1631 `_). - -* Fixed type detection when using implicit conversion to ``string_view`` and - ostream ``operator<<`` inconsistently - (`#1662 `_). - -6.2.0 - 2020-04-05 ------------------- - -* Improved error reporting when trying to format an object of a non-formattable - type: - - .. code:: c++ - - fmt::format("{}", S()); - - now gives:: - - include/fmt/core.h:1015:5: error: static_assert failed due to requirement - 'formattable' "Cannot format argument. To make type T formattable provide a - formatter specialization: - https://fmt.dev/latest/api.html#formatting-user-defined-types" - static_assert( - ^ - ... - note: in instantiation of function template specialization - 'fmt::v6::format' requested here - fmt::format("{}", S()); - ^ - - if ``S`` is not formattable. - -* Reduced the library size by ~10%. - -* Always print decimal point if ``#`` is specified - (`#1476 `_, - `#1498 `_): - - .. code:: c++ - - fmt::print("{:#.0f}", 42.0); - - now prints ``42.`` - -* Implemented the ``'L'`` specifier for locale-specific numeric formatting to - improve compatibility with ``std::format``. The ``'n'`` specifier is now - deprecated and will be removed in the next major release. - -* Moved OS-specific APIs such as ``windows_error`` from ``fmt/format.h`` to - ``fmt/os.h``. You can define ``FMT_DEPRECATED_INCLUDE_OS`` to automatically - include ``fmt/os.h`` from ``fmt/format.h`` for compatibility but this will be - disabled in the next major release. - -* Added precision overflow detection in floating-point formatting. - -* Implemented detection of invalid use of ``fmt::arg``. - -* Used ``type_identity`` to block unnecessary template argument deduction. - Thanks Tim Song. - -* Improved UTF-8 handling - (`#1109 `_): - - .. code:: c++ - - fmt::print("┌{0:─^{2}}┐\n" - "│{1: ^{2}}│\n" - "└{0:─^{2}}┘\n", "", "Привет, мир!", 20); - - now prints:: - - ┌────────────────────┐ - │ Привет, мир! │ - └────────────────────┘ - - on systems that support Unicode. - -* Added experimental dynamic argument storage - (`#1170 `_, - `#1584 `_): - - .. code:: c++ - - fmt::dynamic_format_arg_store store; - store.push_back("answer"); - store.push_back(42); - fmt::vprint("The {} is {}.\n", store); - - prints:: - - The answer is 42. - - Thanks `@vsolontsov-ll (Vladimir Solontsov) - `_. - -* Made ``fmt::join`` accept ``initializer_list`` - (`#1591 `_). - Thanks `@Rapotkinnik (Nikolay Rapotkin) `_. - -* Fixed handling of empty tuples - (`#1588 `_). - -* Fixed handling of output iterators in ``format_to_n`` - (`#1506 `_). - -* Fixed formatting of ``std::chrono::duration`` types to wide output - (`#1533 `_). - Thanks `@zeffy (pilao) `_. - -* Added const ``begin`` and ``end`` overload to buffers - (`#1553 `_). - Thanks `@dominicpoeschko `_. - -* Added the ability to disable floating-point formatting via ``FMT_USE_FLOAT``, - ``FMT_USE_DOUBLE`` and ``FMT_USE_LONG_DOUBLE`` macros for extremely - memory-constrained embedded system - (`#1590 `_). - Thanks `@albaguirre (Alberto Aguirre) `_. - -* Made ``FMT_STRING`` work with ``constexpr`` ``string_view`` - (`#1589 `_). - Thanks `@scramsby (Scott Ramsby) `_. - -* Implemented a minor optimization in the format string parser - (`#1560 `_). - Thanks `@IkarusDeveloper `_. - -* Improved attribute detection - (`#1469 `_, - `#1475 `_, - `#1576 `_). - Thanks `@federico-busato (Federico) `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@refnum `_. - -* Improved documentation - (`#1481 `_, - `#1523 `_). - Thanks `@JackBoosY (Jack·Boos·Yu) `_, - `@imba-tjd (谭九鼎) `_. - -* Fixed symbol visibility on Linux when compiling with ``-fvisibility=hidden`` - (`#1535 `_). - Thanks `@milianw (Milian Wolff) `_. - -* Implemented various build configuration fixes and improvements - (`#1264 `_, - `#1460 `_, - `#1534 `_, - `#1536 `_, - `#1545 `_, - `#1546 `_, - `#1566 `_, - `#1582 `_, - `#1597 `_, - `#1598 `_). - Thanks `@ambitslix (Attila M. Szilagyi) `_, - `@jwillikers (Jordan Williams) `_, - `@stac47 (Laurent Stacul) `_. - -* Fixed various warnings and compilation issues - (`#1433 `_, - `#1461 `_, - `#1470 `_, - `#1480 `_, - `#1485 `_, - `#1492 `_, - `#1493 `_, - `#1504 `_, - `#1505 `_, - `#1512 `_, - `#1515 `_, - `#1516 `_, - `#1518 `_, - `#1519 `_, - `#1520 `_, - `#1521 `_, - `#1522 `_, - `#1524 `_, - `#1530 `_, - `#1531 `_, - `#1532 `_, - `#1539 `_, - `#1547 `_, - `#1548 `_, - `#1554 `_, - `#1567 `_, - `#1568 `_, - `#1569 `_, - `#1571 `_, - `#1573 `_, - `#1575 `_, - `#1581 `_, - `#1583 `_, - `#1586 `_, - `#1587 `_, - `#1594 `_, - `#1596 `_, - `#1604 `_, - `#1606 `_, - `#1607 `_, - `#1609 `_). - Thanks `@marti4d (Chris Martin) `_, - `@iPherian `_, - `@parkertomatoes `_, - `@gsjaardema (Greg Sjaardema) `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@DanielaE (Daniela Engert) `_, - `@torsten48 `_, - `@tohammer (Tobias Hammer) `_, - `@lefticus (Jason Turner) `_, - `@ryusakki (Haise) `_, - `@adnsv (Alex Denisov) `_, - `@fghzxm `_, - `@refnum `_, - `@pramodk (Pramod Kumbhar) `_, - `@Spirrwell `_, - `@scramsby (Scott Ramsby) `_. - -6.1.2 - 2019-12-11 ------------------- - -* Fixed ABI compatibility with ``libfmt.so.6.0.0`` - (`#1471 `_). - -* Fixed handling types convertible to ``std::string_view`` - (`#1451 `_). - Thanks `@denizevrenci (Deniz Evrenci) `_. - -* Made CUDA test an opt-in enabled via the ``FMT_CUDA_TEST`` CMake option. - -* Fixed sign conversion warnings - (`#1440 `_). - Thanks `@0x8000-0000 (Florin Iucha) `_. - -6.1.1 - 2019-12-04 ------------------- - -* Fixed shared library build on Windows - (`#1443 `_, - `#1445 `_, - `#1446 `_, - `#1450 `_). - Thanks `@egorpugin (Egor Pugin) `_, - `@bbolli (Beat Bolli) `_. - -* Added a missing decimal point in exponent notation with trailing zeros. - -* Removed deprecated ``format_arg_store::TYPES``. - -6.1.0 - 2019-12-01 ------------------- - -* {fmt} now formats IEEE 754 ``float`` and ``double`` using the shortest decimal - representation with correct rounding by default: - - .. code:: c++ - - #include - #include - - int main() { - fmt::print("{}", M_PI); - } - - prints ``3.141592653589793``. - -* Made the fast binary to decimal floating-point formatter the default, - simplified it and improved performance. {fmt} is now 15 times faster than - libc++'s ``std::ostringstream``, 11 times faster than ``printf`` and 10% - faster than double-conversion on `dtoa-benchmark - `_: - - ================== ========= ======= - Function Time (ns) Speedup - ================== ========= ======= - ostringstream 1,346.30 1.00x - ostrstream 1,195.74 1.13x - sprintf 995.08 1.35x - doubleconv 99.10 13.59x - fmt 88.34 15.24x - ================== ========= ======= - - .. image:: https://user-images.githubusercontent.com/576385/ - 69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png - -* {fmt} no longer converts ``float`` arguments to ``double``. In particular this - improves the default (shortest) representation of floats and makes - ``fmt::format`` consistent with ``std::format`` specs - (`#1336 `_, - `#1353 `_, - `#1360 `_, - `#1361 `_): - - .. code:: c++ - - fmt::print("{}", 0.1f); - - prints ``0.1`` instead of ``0.10000000149011612``. - - Thanks `@orivej (Orivej Desh) `_. - -* Made floating-point formatting output consistent with ``printf``/iostreams - (`#1376 `_, - `#1417 `_). - -* Added support for 128-bit integers - (`#1287 `_): - - .. code:: c++ - - fmt::print("{}", std::numeric_limits<__int128_t>::max()); - - prints ``170141183460469231731687303715884105727``. - - Thanks `@denizevrenci (Deniz Evrenci) `_. - -* The overload of ``print`` that takes ``text_style`` is now atomic, i.e. the - output from different threads doesn't interleave - (`#1351 `_). - Thanks `@tankiJong (Tanki Zhang) `_. - -* Made compile time in the header-only mode ~20% faster by reducing the number - of template instantiations. ``wchar_t`` overload of ``vprint`` was moved from - ``fmt/core.h`` to ``fmt/format.h``. - -* Added an overload of ``fmt::join`` that works with tuples - (`#1322 `_, - `#1330 `_): - - .. code:: c++ - - #include - #include - - int main() { - std::tuple t{'a', 1, 2.0f}; - fmt::print("{}", t); - } - - prints ``('a', 1, 2.0)``. - - Thanks `@jeremyong (Jeremy Ong) `_. - -* Changed formatting of octal zero with prefix from "00" to "0": - - .. code:: c++ - - fmt::print("{:#o}", 0); - - prints ``0``. - -* The locale is now passed to ostream insertion (``<<``) operators - (`#1406 `_): - - .. code:: c++ - - #include - #include - - struct S { - double value; - }; - - std::ostream& operator<<(std::ostream& os, S s) { - return os << s.value; - } - - int main() { - auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42}); - // s == "0,42" - } - - Thanks `@dlaugt (Daniel Laügt) `_. - -* Locale-specific number formatting now uses grouping - (`#1393 `_ - `#1394 `_). - Thanks `@skrdaniel `_. - -* Fixed handling of types with deleted implicit rvalue conversion to - ``const char**`` (`#1421 `_): - - .. code:: c++ - - struct mystring { - operator const char*() const&; - operator const char*() &; - operator const char*() const&& = delete; - operator const char*() && = delete; - }; - mystring str; - fmt::print("{}", str); // now compiles - -* Enums are now mapped to correct underlying types instead of ``int`` - (`#1286 `_). - Thanks `@agmt (Egor Seredin) `_. - -* Enum classes are no longer implicitly converted to ``int`` - (`#1424 `_). - -* Added ``basic_format_parse_context`` for consistency with C++20 - ``std::format`` and deprecated ``basic_parse_context``. - -* Fixed handling of UTF-8 in precision - (`#1389 `_, - `#1390 `_). - Thanks `@tajtiattila (Attila Tajti) `_. - -* {fmt} can now be installed on Linux, macOS and Windows with - `Conda `__ using its - `conda-forge `__ - `package `__ - (`#1410 `_):: - - conda install -c conda-forge fmt - - Thanks `@tdegeus (Tom de Geus) `_. - -* Added a CUDA test (`#1285 `_, - `#1317 `_). - Thanks `@luncliff (Park DongHa) `_ and - `@risa2000 `_. - -* Improved documentation (`#1276 `_, - `#1291 `_, - `#1296 `_, - `#1315 `_, - `#1332 `_, - `#1337 `_, - `#1395 `_ - `#1418 `_). - Thanks - `@waywardmonkeys (Bruce Mitchener) `_, - `@pauldreik (Paul Dreik) `_, - `@jackoalan (Jack Andersen) `_. - -* Various code improvements - (`#1358 `_, - `#1407 `_). - Thanks `@orivej (Orivej Desh) `_, - `@dpacbach (David P. Sicilia) `_, - -* Fixed compile-time format string checks for user-defined types - (`#1292 `_). - -* Worked around a false positive in ``unsigned-integer-overflow`` sanitizer - (`#1377 `_). - -* Fixed various warnings and compilation issues - (`#1273 `_, - `#1278 `_, - `#1280 `_, - `#1281 `_, - `#1288 `_, - `#1290 `_, - `#1301 `_, - `#1305 `_, - `#1306 `_, - `#1309 `_, - `#1312 `_, - `#1313 `_, - `#1316 `_, - `#1319 `_, - `#1320 `_, - `#1326 `_, - `#1328 `_, - `#1344 `_, - `#1345 `_, - `#1347 `_, - `#1349 `_, - `#1354 `_, - `#1362 `_, - `#1366 `_, - `#1364 `_, - `#1370 `_, - `#1371 `_, - `#1385 `_, - `#1388 `_, - `#1397 `_, - `#1414 `_, - `#1416 `_, - `#1422 `_ - `#1427 `_, - `#1431 `_, - `#1433 `_). - Thanks `@hhb `_, - `@gsjaardema (Greg Sjaardema) `_, - `@gabime (Gabi Melman) `_, - `@neheb (Rosen Penev) `_, - `@vedranmiletic (Vedran Miletić) `_, - `@dkavolis (Daumantas Kavolis) `_, - `@mwinterb `_, - `@orivej (Orivej Desh) `_, - `@denizevrenci (Deniz Evrenci) `_ - `@leonklingele `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@kent-tri `_, - `@0x8000-0000 (Florin Iucha) `_, - `@marti4d (Chris Martin) `_. - -6.0.0 - 2019-08-26 ------------------- - -* Switched to the `MIT license - `_ - with an optional exception that allows distributing binary code without - attribution. - -* Floating-point formatting is now locale-independent by default: - - .. code:: c++ - - #include - #include - - int main() { - std::locale::global(std::locale("ru_RU.UTF-8")); - fmt::print("value = {}", 4.2); - } - - prints "value = 4.2" regardless of the locale. - - For locale-specific formatting use the ``n`` specifier: - - .. code:: c++ - - std::locale::global(std::locale("ru_RU.UTF-8")); - fmt::print("value = {:n}", 4.2); - - prints "value = 4,2". - -* Added an experimental Grisu floating-point formatting algorithm - implementation (disabled by default). To enable it compile with the - ``FMT_USE_GRISU`` macro defined to 1: - - .. code:: c++ - - #define FMT_USE_GRISU 1 - #include - - auto s = fmt::format("{}", 4.2); // formats 4.2 using Grisu - - With Grisu enabled, {fmt} is 13x faster than ``std::ostringstream`` (libc++) - and 10x faster than ``sprintf`` on `dtoa-benchmark - `_ (`full results - `_): - - .. image:: https://user-images.githubusercontent.com/576385/ - 54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg - -* Separated formatting and parsing contexts for consistency with - `C++20 std::format `_, removing the - undocumented ``basic_format_context::parse_context()`` function. - -* Added `oss-fuzz `_ support - (`#1199 `_). - Thanks `@pauldreik (Paul Dreik) `_. - -* ``formatter`` specializations now always take precedence over ``operator<<`` - (`#952 `_): - - .. code:: c++ - - #include - #include - - struct S {}; - - std::ostream& operator<<(std::ostream& os, S) { - return os << 1; - } - - template <> - struct fmt::formatter : fmt::formatter { - auto format(S, format_context& ctx) { - return formatter::format(2, ctx); - } - }; - - int main() { - std::cout << S() << "\n"; // prints 1 using operator<< - fmt::print("{}\n", S()); // prints 2 using formatter - } - -* Introduced the experimental ``fmt::compile`` function that does format string - compilation (`#618 `_, - `#1169 `_, - `#1171 `_): - - .. code:: c++ - - #include - - auto f = fmt::compile("{}"); - std::string s = fmt::format(f, 42); // can be called multiple times to - // format different values - // s == "42" - - It moves the cost of parsing a format string outside of the format function - which can be beneficial when identically formatting many objects of the same - types. Thanks `@stryku (Mateusz Janek) `_. - -* Added experimental ``%`` format specifier that formats floating-point values - as percentages (`#1060 `_, - `#1069 `_, - `#1071 `_): - - .. code:: c++ - - auto s = fmt::format("{:.1%}", 0.42); // s == "42.0%" - - Thanks `@gawain-bolton (Gawain Bolton) `_. - -* Implemented precision for floating-point durations - (`#1004 `_, - `#1012 `_): - - .. code:: c++ - - auto s = fmt::format("{:.1}", std::chrono::duration(1.234)); - // s == 1.2s - - Thanks `@DanielaE (Daniela Engert) `_. - -* Implemented ``chrono`` format specifiers ``%Q`` and ``%q`` that give the value - and the unit respectively (`#1019 `_): - - .. code:: c++ - - auto value = fmt::format("{:%Q}", 42s); // value == "42" - auto unit = fmt::format("{:%q}", 42s); // unit == "s" - - Thanks `@DanielaE (Daniela Engert) `_. - -* Fixed handling of dynamic width in chrono formatter: - - .. code:: c++ - - auto s = fmt::format("{0:{1}%H:%M:%S}", std::chrono::seconds(12345), 12); - // ^ width argument index ^ width - // s == "03:25:45 " - - Thanks Howard Hinnant. - -* Removed deprecated ``fmt/time.h``. Use ``fmt/chrono.h`` instead. - -* Added ``fmt::format`` and ``fmt::vformat`` overloads that take ``text_style`` - (`#993 `_, - `#994 `_): - - .. code:: c++ - - #include - - std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), - "The answer is {}.", 42); - - Thanks `@Naios (Denis Blank) `_. - -* Removed the deprecated color API (``print_colored``). Use the new API, namely - ``print`` overloads that take ``text_style`` instead. - -* Made ``std::unique_ptr`` and ``std::shared_ptr`` formattable as pointers via - ``fmt::ptr`` (`#1121 `_): - - .. code:: c++ - - std::unique_ptr p = ...; - fmt::print("{}", fmt::ptr(p)); // prints p as a pointer - - Thanks `@sighingnow (Tao He) `_. - -* Made ``print`` and ``vprint`` report I/O errors - (`#1098 `_, - `#1099 `_). - Thanks `@BillyDonahue (Billy Donahue) `_. - -* Marked deprecated APIs with the ``[[deprecated]]`` attribute and removed - internal uses of deprecated APIs - (`#1022 `_). - Thanks `@eliaskosunen (Elias Kosunen) `_. - -* Modernized the codebase using more C++11 features and removing workarounds. - Most importantly, ``buffer_context`` is now an alias template, so - use ``buffer_context`` instead of ``buffer_context::type``. - These features require GCC 4.8 or later. - -* ``formatter`` specializations now always take precedence over implicit - conversions to ``int`` and the undocumented ``convert_to_int`` trait - is now deprecated. - -* Moved the undocumented ``basic_writer``, ``writer``, and ``wwriter`` types - to the ``internal`` namespace. - -* Removed deprecated ``basic_format_context::begin()``. Use ``out()`` instead. - -* Disallowed passing the result of ``join`` as an lvalue to prevent misuse. - -* Refactored the undocumented structs that represent parsed format specifiers - to simplify the API and allow multibyte fill. - -* Moved SFINAE to template parameters to reduce symbol sizes. - -* Switched to ``fputws`` for writing wide strings so that it's no longer - required to call ``_setmode`` on Windows - (`#1229 `_, - `#1243 `_). - Thanks `@jackoalan (Jack Andersen) `_. - -* Improved literal-based API - (`#1254 `_). - Thanks `@sylveon (Charles Milette) `_. - -* Added support for exotic platforms without ``uintptr_t`` such as IBM i - (AS/400) which has 128-bit pointers and only 64-bit integers - (`#1059 `_). - -* Added `Sublime Text syntax highlighting config - `_ - (`#1037 `_). - Thanks `@Kronuz (Germán Méndez Bravo) `_. - -* Added the ``FMT_ENFORCE_COMPILE_STRING`` macro to enforce the use of - compile-time format strings - (`#1231 `_). - Thanks `@jackoalan (Jack Andersen) `_. - -* Stopped setting ``CMAKE_BUILD_TYPE`` if {fmt} is a subproject - (`#1081 `_). - -* Various build improvements - (`#1039 `_, - `#1078 `_, - `#1091 `_, - `#1103 `_, - `#1177 `_). - Thanks `@luncliff (Park DongHa) `_, - `@jasonszang (Jason Shuo Zang) `_, - `@olafhering (Olaf Hering) `_, - `@Lecetem `_, - `@pauldreik (Paul Dreik) `_. - -* Improved documentation - (`#1049 `_, - `#1051 `_, - `#1083 `_, - `#1113 `_, - `#1114 `_, - `#1146 `_, - `#1180 `_, - `#1250 `_, - `#1252 `_, - `#1265 `_). - Thanks `@mikelui (Michael Lui) `_, - `@foonathan (Jonathan Müller) `_, - `@BillyDonahue (Billy Donahue) `_, - `@jwakely (Jonathan Wakely) `_, - `@kaisbe (Kais Ben Salah) `_, - `@sdebionne (Samuel Debionne) `_. - -* Fixed ambiguous formatter specialization in ``fmt/ranges.h`` - (`#1123 `_). - -* Fixed formatting of a non-empty ``std::filesystem::path`` which is an - infinitely deep range of its components - (`#1268 `_). - -* Fixed handling of general output iterators when formatting characters - (`#1056 `_, - `#1058 `_). - Thanks `@abolz (Alexander Bolz) `_. - -* Fixed handling of output iterators in ``formatter`` specialization for - ranges (`#1064 `_). - -* Fixed handling of exotic character types - (`#1188 `_). - -* Made chrono formatting work with exceptions disabled - (`#1062 `_). - -* Fixed DLL visibility issues - (`#1134 `_, - `#1147 `_). - Thanks `@denchat `_. - -* Disabled the use of UDL template extension on GCC 9 - (`#1148 `_). - -* Removed misplaced ``format`` compile-time checks from ``printf`` - (`#1173 `_). - -* Fixed issues in the experimental floating-point formatter - (`#1072 `_, - `#1129 `_, - `#1153 `_, - `#1155 `_, - `#1210 `_, - `#1222 `_). - Thanks `@alabuzhev (Alex Alabuzhev) `_. - -* Fixed bugs discovered by fuzzing or during fuzzing integration - (`#1124 `_, - `#1127 `_, - `#1132 `_, - `#1135 `_, - `#1136 `_, - `#1141 `_, - `#1142 `_, - `#1178 `_, - `#1179 `_, - `#1194 `_). - Thanks `@pauldreik (Paul Dreik) `_. - -* Fixed building tests on FreeBSD and Hurd - (`#1043 `_). - Thanks `@jackyf (Eugene V. Lyubimkin) `_. - -* Fixed various warnings and compilation issues - (`#998 `_, - `#1006 `_, - `#1008 `_, - `#1011 `_, - `#1025 `_, - `#1027 `_, - `#1028 `_, - `#1029 `_, - `#1030 `_, - `#1031 `_, - `#1054 `_, - `#1063 `_, - `#1068 `_, - `#1074 `_, - `#1075 `_, - `#1079 `_, - `#1086 `_, - `#1088 `_, - `#1089 `_, - `#1094 `_, - `#1101 `_, - `#1102 `_, - `#1105 `_, - `#1107 `_, - `#1115 `_, - `#1117 `_, - `#1118 `_, - `#1120 `_, - `#1123 `_, - `#1139 `_, - `#1140 `_, - `#1143 `_, - `#1144 `_, - `#1150 `_, - `#1151 `_, - `#1152 `_, - `#1154 `_, - `#1156 `_, - `#1159 `_, - `#1175 `_, - `#1181 `_, - `#1186 `_, - `#1187 `_, - `#1191 `_, - `#1197 `_, - `#1200 `_, - `#1203 `_, - `#1205 `_, - `#1206 `_, - `#1213 `_, - `#1214 `_, - `#1217 `_, - `#1228 `_, - `#1230 `_, - `#1232 `_, - `#1235 `_, - `#1236 `_, - `#1240 `_). - Thanks `@DanielaE (Daniela Engert) `_, - `@mwinterb `_, - `@eliaskosunen (Elias Kosunen) `_, - `@morinmorin `_, - `@ricco19 (Brian Ricciardelli) `_, - `@waywardmonkeys (Bruce Mitchener) `_, - `@chronoxor (Ivan Shynkarenka) `_, - `@remyabel `_, - `@pauldreik (Paul Dreik) `_, - `@gsjaardema (Greg Sjaardema) `_, - `@rcane (Ronny Krüger) `_, - `@mocabe `_, - `@denchat `_, - `@cjdb (Christopher Di Bella) `_, - `@HazardyKnusperkeks (Björn Schäpers) `_, - `@vedranmiletic (Vedran Miletić) `_, - `@jackoalan (Jack Andersen) `_, - `@DaanDeMeyer (Daan De Meyer) `_, - `@starkmapper (Mark Stapper) `_. - -5.3.0 - 2018-12-28 ------------------- - -* Introduced experimental chrono formatting support: - - .. code:: c++ - - #include - - int main() { - using namespace std::literals::chrono_literals; - fmt::print("Default format: {} {}\n", 42s, 100ms); - fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); - } - - prints:: - - Default format: 42s 100ms - strftime-like format: 03:15:30 - -* Added experimental support for emphasis (bold, italic, underline, - strikethrough), colored output to a file stream, and improved colored - formatting API - (`#961 `_, - `#967 `_, - `#973 `_): - - .. code:: c++ - - #include - - int main() { - print(fg(fmt::color::crimson) | fmt::emphasis::bold, - "Hello, {}!\n", "world"); - print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | - fmt::emphasis::underline, "Hello, {}!\n", "мир"); - print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, - "Hello, {}!\n", "世界"); - } - - prints the following on modern terminals with RGB color support: - - .. image:: https://user-images.githubusercontent.com/576385/ - 50405788-b66e7500-076e-11e9-9592-7324d1f951d8.png - - Thanks `@Rakete1111 (Nicolas) `_. - -* Added support for 4-bit terminal colors - (`#968 `_, - `#974 `_) - - .. code:: c++ - - #include - - int main() { - print(fg(fmt::terminal_color::red), "stop\n"); - } - - Note that these colors vary by terminal: - - .. image:: https://user-images.githubusercontent.com/576385/ - 50405925-dbfc7e00-0770-11e9-9b85-333fab0af9ac.png - - Thanks `@Rakete1111 (Nicolas) `_. - -* Parameterized formatting functions on the type of the format string - (`#880 `_, - `#881 `_, - `#883 `_, - `#885 `_, - `#897 `_, - `#920 `_). - Any object of type ``S`` that has an overloaded ``to_string_view(const S&)`` - returning ``fmt::string_view`` can be used as a format string: - - .. code:: c++ - - namespace my_ns { - inline string_view to_string_view(const my_string& s) { - return {s.data(), s.length()}; - } - } - - std::string message = fmt::format(my_string("The answer is {}."), 42); - - Thanks `@DanielaE (Daniela Engert) `_. - -* Made ``std::string_view`` work as a format string - (`#898 `_): - - .. code:: c++ - - auto message = fmt::format(std::string_view("The answer is {}."), 42); - - Thanks `@DanielaE (Daniela Engert) `_. - -* Added wide string support to compile-time format string checks - (`#924 `_): - - .. code:: c++ - - print(fmt(L"{:f}"), 42); // compile-time error: invalid type specifier - - Thanks `@XZiar `_. - -* Made colored print functions work with wide strings - (`#867 `_): - - .. code:: c++ - - #include - - int main() { - print(fg(fmt::color::red), L"{}\n", 42); - } - - Thanks `@DanielaE (Daniela Engert) `_. - -* Introduced experimental Unicode support - (`#628 `_, - `#891 `_): - - .. code:: c++ - - using namespace fmt::literals; - auto s = fmt::format("{:*^5}"_u, "🤡"_u); // s == "**🤡**"_u - -* Improved locale support: - - .. code:: c++ - - #include - - struct numpunct : std::numpunct { - protected: - char do_thousands_sep() const override { return '~'; } - }; - - std::locale loc; - auto s = fmt::format(std::locale(loc, new numpunct()), "{:n}", 1234567); - // s == "1~234~567" - -* Constrained formatting functions on proper iterator types - (`#921 `_). - Thanks `@DanielaE (Daniela Engert) `_. - -* Added ``make_printf_args`` and ``make_wprintf_args`` functions - (`#934 `_). - Thanks `@tnovotny `_. - -* Deprecated ``fmt::visit``, ``parse_context``, and ``wparse_context``. - Use ``fmt::visit_format_arg``, ``format_parse_context``, and - ``wformat_parse_context`` instead. - -* Removed undocumented ``basic_fixed_buffer`` which has been superseded by the - iterator-based API - (`#873 `_, - `#902 `_). - Thanks `@superfunc (hollywood programmer) `_. - -* Disallowed repeated leading zeros in an argument ID: - - .. code:: c++ - - fmt::print("{000}", 42); // error - -* Reintroduced support for gcc 4.4. - -* Fixed compilation on platforms with exotic ``double`` - (`#878 `_). - -* Improved documentation - (`#164 `_, - `#877 `_, - `#901 `_, - `#906 `_, - `#979 `_). - Thanks `@kookjr (Mathew Cucuzella) `_, - `@DarkDimius (Dmitry Petrashko) `_, - `@HecticSerenity `_. - -* Added pkgconfig support which makes it easier to consume the library from - meson and other build systems - (`#916 `_). - Thanks `@colemickens (Cole Mickens) `_. - -* Various build improvements - (`#909 `_, - `#926 `_, - `#937 `_, - `#953 `_, - `#959 `_). - Thanks `@tchaikov (Kefu Chai) `_, - `@luncliff (Park DongHa) `_, - `@AndreasSchoenle (Andreas Schönle) `_, - `@hotwatermorning `_, - `@Zefz (JohanJansen) `_. - -* Improved ``string_view`` construction performance - (`#914 `_). - Thanks `@gabime (Gabi Melman) `_. - -* Fixed non-matching char types - (`#895 `_). - Thanks `@DanielaE (Daniela Engert) `_. - -* Fixed ``format_to_n`` with ``std::back_insert_iterator`` - (`#913 `_). - Thanks `@DanielaE (Daniela Engert) `_. - -* Fixed locale-dependent formatting - (`#905 `_). - -* Fixed various compiler warnings and errors - (`#882 `_, - `#886 `_, - `#933 `_, - `#941 `_, - `#931 `_, - `#943 `_, - `#954 `_, - `#956 `_, - `#962 `_, - `#965 `_, - `#977 `_, - `#983 `_, - `#989 `_). - Thanks `@Luthaf (Guillaume Fraux) `_, - `@stevenhoving (Steven Hoving) `_, - `@christinaa (Kristina Brooks) `_, - `@lgritz (Larry Gritz) `_, - `@DanielaE (Daniela Engert) `_, - `@0x8000-0000 (Sign Bit) `_, - `@liuping1997 `_. - -5.2.1 - 2018-09-21 ------------------- - -* Fixed ``visit`` lookup issues on gcc 7 & 8 - (`#870 `_). - Thanks `@medithe `_. - -* Fixed linkage errors on older gcc. - -* Prevented ``fmt/range.h`` from specializing ``fmt::basic_string_view`` - (`#865 `_, - `#868 `_). - Thanks `@hhggit (dual) `_. - -* Improved error message when formatting unknown types - (`#872 `_). - Thanks `@foonathan (Jonathan Müller) `_, - -* Disabled templated user-defined literals when compiled under nvcc - (`#875 `_). - Thanks `@CandyGumdrop (Candy Gumdrop) `_, - -* Fixed ``format_to`` formatting to ``wmemory_buffer`` - (`#874 `_). - -5.2.0 - 2018-09-13 ------------------- - -* Optimized format string parsing and argument processing which resulted in up - to 5x speed up on long format strings and significant performance boost on - various benchmarks. For example, version 5.2 is 2.22x faster than 5.1 on - decimal integer formatting with ``format_to`` (macOS, clang-902.0.39.2): - - ================== ======= ======= - Method Time, s Speedup - ================== ======= ======= - fmt::format 5.1 0.58 - fmt::format 5.2 0.35 1.66x - fmt::format_to 5.1 0.51 - fmt::format_to 5.2 0.23 2.22x - sprintf 0.71 - std::to_string 1.01 - std::stringstream 1.73 - ================== ======= ======= - -* Changed the ``fmt`` macro from opt-out to opt-in to prevent name collisions. - To enable it define the ``FMT_STRING_ALIAS`` macro to 1 before including - ``fmt/format.h``: - - .. code:: c++ - - #define FMT_STRING_ALIAS 1 - #include - std::string answer = format(fmt("{}"), 42); - -* Added compile-time format string checks to ``format_to`` overload that takes - ``fmt::memory_buffer`` (`#783 `_): - - .. code:: c++ - - fmt::memory_buffer buf; - // Compile-time error: invalid type specifier. - fmt::format_to(buf, fmt("{:d}"), "foo"); - -* Moved experimental color support to ``fmt/color.h`` and enabled the - new API by default. The old API can be enabled by defining the - ``FMT_DEPRECATED_COLORS`` macro. - -* Added formatting support for types explicitly convertible to - ``fmt::string_view``: - - .. code:: c++ - - struct foo { - explicit operator fmt::string_view() const { return "foo"; } - }; - auto s = format("{}", foo()); - - In particular, this makes formatting function work with - ``folly::StringPiece``. - -* Implemented preliminary support for ``char*_t`` by replacing the ``format`` - function overloads with a single function template parameterized on the string - type. - -* Added support for dynamic argument lists - (`#814 `_, - `#819 `_). - Thanks `@MikePopoloski (Michael Popoloski) - `_. - -* Reduced executable size overhead for embedded targets using newlib nano by - making locale dependency optional - (`#839 `_). - Thanks `@teajay-fr (Thomas Benard) `_. - -* Keep ``noexcept`` specifier when exceptions are disabled - (`#801 `_, - `#810 `_). - Thanks `@qis (Alexej Harm) `_. - -* Fixed formatting of user-defined types providing ``operator<<`` with - ``format_to_n`` - (`#806 `_). - Thanks `@mkurdej (Marek Kurdej) `_. - -* Fixed dynamic linkage of new symbols - (`#808 `_). - -* Fixed global initialization issue - (`#807 `_): - - .. code:: c++ - - // This works on compilers with constexpr support. - static const std::string answer = fmt::format("{}", 42); - -* Fixed various compiler warnings and errors - (`#804 `_, - `#809 `_, - `#811 `_, - `#822 `_, - `#827 `_, - `#830 `_, - `#838 `_, - `#843 `_, - `#844 `_, - `#851 `_, - `#852 `_, - `#854 `_). - Thanks `@henryiii (Henry Schreiner) `_, - `@medithe `_, and - `@eliasdaler (Elias Daler) `_. - -5.1.0 - 2018-07-05 ------------------- - -* Added experimental support for RGB color output enabled with - the ``FMT_EXTENDED_COLORS`` macro: - - .. code:: c++ - - #define FMT_EXTENDED_COLORS - #define FMT_HEADER_ONLY // or compile fmt with FMT_EXTENDED_COLORS defined - #include - - fmt::print(fmt::color::steel_blue, "Some beautiful text"); - - The old API (the ``print_colored`` and ``vprint_colored`` functions and the - ``color`` enum) is now deprecated. - (`#762 `_ - `#767 `_). - thanks `@Remotion (Remo) `_. - -* Added quotes to strings in ranges and tuples - (`#766 `_). - Thanks `@Remotion (Remo) `_. - -* Made ``format_to`` work with ``basic_memory_buffer`` - (`#776 `_). - -* Added ``vformat_to_n`` and ``wchar_t`` overload of ``format_to_n`` - (`#764 `_, - `#769 `_). - -* Made ``is_range`` and ``is_tuple_like`` part of public (experimental) API - to allow specialization for user-defined types - (`#751 `_, - `#759 `_). - Thanks `@drrlvn (Dror Levin) `_. - -* Added more compilers to continuous integration and increased ``FMT_PEDANTIC`` - warning levels - (`#736 `_). - Thanks `@eliaskosunen (Elias Kosunen) `_. - -* Fixed compilation with MSVC 2013. - -* Fixed handling of user-defined types in ``format_to`` - (`#793 `_). - -* Forced linking of inline ``vformat`` functions into the library - (`#795 `_). - -* Fixed incorrect call to on_align in ``'{:}='`` - (`#750 `_). - -* Fixed floating-point formatting to a non-back_insert_iterator with sign & - numeric alignment specified - (`#756 `_). - -* Fixed formatting to an array with ``format_to_n`` - (`#778 `_). - -* Fixed formatting of more than 15 named arguments - (`#754 `_). - -* Fixed handling of compile-time strings when including ``fmt/ostream.h``. - (`#768 `_). - -* Fixed various compiler warnings and errors - (`#742 `_, - `#748 `_, - `#752 `_, - `#770 `_, - `#775 `_, - `#779 `_, - `#780 `_, - `#790 `_, - `#792 `_, - `#800 `_). - Thanks `@Remotion (Remo) `_, - `@gabime (Gabi Melman) `_, - `@foonathan (Jonathan Müller) `_, - `@Dark-Passenger (Dhruv Paranjape) `_, and - `@0x8000-0000 (Sign Bit) `_. - -5.0.0 - 2018-05-21 ------------------- - -* Added a requirement for partial C++11 support, most importantly variadic - templates and type traits, and dropped ``FMT_VARIADIC_*`` emulation macros. - Variadic templates are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). - For older compilers use {fmt} `version 4.x - `_ which continues to be - maintained and works with C++98 compilers. - -* Renamed symbols to follow standard C++ naming conventions and proposed a subset - of the library for standardization in `P0645R2 Text Formatting - `_. - -* Implemented ``constexpr`` parsing of format strings and `compile-time format - string checks - `_. For - example - - .. code:: c++ - - #include - - std::string s = format(fmt("{:d}"), "foo"); - - gives a compile-time error because ``d`` is an invalid specifier for strings - (`godbolt `__):: - - ... - :4:19: note: in instantiation of function template specialization 'fmt::v5::format' requested here - std::string s = format(fmt("{:d}"), "foo"); - ^ - format.h:1337:13: note: non-constexpr function 'on_error' cannot be used in a constant expression - handler.on_error("invalid type specifier"); - - Compile-time checks require relaxed ``constexpr`` (C++14 feature) support. If - the latter is not available, checks will be performed at runtime. - -* Separated format string parsing and formatting in the extension API to enable - compile-time format string processing. For example - - .. code:: c++ - - struct Answer {}; - - namespace fmt { - template <> - struct formatter { - constexpr auto parse(parse_context& ctx) { - auto it = ctx.begin(); - spec = *it; - if (spec != 'd' && spec != 's') - throw format_error("invalid specifier"); - return ++it; - } - - template - auto format(Answer, FormatContext& ctx) { - return spec == 's' ? - format_to(ctx.begin(), "{}", "fourty-two") : - format_to(ctx.begin(), "{}", 42); - } - - char spec = 0; - }; - } - - std::string s = format(fmt("{:x}"), Answer()); - - gives a compile-time error due to invalid format specifier (`godbolt - `__):: - - ... - :12:45: error: expression '' is not a constant expression - throw format_error("invalid specifier"); - -* Added `iterator support - `_: - - .. code:: c++ - - #include - #include - - std::vector out; - fmt::format_to(std::back_inserter(out), "{}", 42); - -* Added the `format_to_n - `_ - function that restricts the output to the specified number of characters - (`#298 `_): - - .. code:: c++ - - char out[4]; - fmt::format_to_n(out, sizeof(out), "{}", 12345); - // out == "1234" (without terminating '\0') - -* Added the `formatted_size - `_ - function for computing the output size: - - .. code:: c++ - - #include - - auto size = fmt::formatted_size("{}", 12345); // size == 5 - -* Improved compile times by reducing dependencies on standard headers and - providing a lightweight `core API `_: - - .. code:: c++ - - #include - - fmt::print("The answer is {}.", 42); - - See `Compile time and code bloat - `_. - -* Added the `make_format_args - `_ - function for capturing formatting arguments: - - .. code:: c++ - - // Prints formatted error message. - void vreport_error(const char *format, fmt::format_args args) { - fmt::print("Error: "); - fmt::vprint(format, args); - } - template - void report_error(const char *format, const Args & ... args) { - vreport_error(format, fmt::make_format_args(args...)); - } - -* Added the ``make_printf_args`` function for capturing ``printf`` arguments - (`#687 `_, - `#694 `_). - Thanks `@Kronuz (Germán Méndez Bravo) `_. - -* Added prefix ``v`` to non-variadic functions taking ``format_args`` to - distinguish them from variadic ones: - - .. code:: c++ - - std::string vformat(string_view format_str, format_args args); - - template - std::string format(string_view format_str, const Args & ... args); - -* Added experimental support for formatting ranges, containers and tuple-like - types in ``fmt/ranges.h`` (`#735 `_): - - .. code:: c++ - - #include - - std::vector v = {1, 2, 3}; - fmt::print("{}", v); // prints {1, 2, 3} - - Thanks `@Remotion (Remo) `_. - -* Implemented ``wchar_t`` date and time formatting - (`#712 `_): - - .. code:: c++ - - #include - - std::time_t t = std::time(nullptr); - auto s = fmt::format(L"The date is {:%Y-%m-%d}.", *std::localtime(&t)); - - Thanks `@DanielaE (Daniela Engert) `_. - -* Provided more wide string overloads - (`#724 `_). - Thanks `@DanielaE (Daniela Engert) `_. - -* Switched from a custom null-terminated string view class to ``string_view`` - in the format API and provided ``fmt::string_view`` which implements a subset - of ``std::string_view`` API for pre-C++17 systems. - -* Added support for ``std::experimental::string_view`` - (`#607 `_): - - .. code:: c++ - - #include - #include - - fmt::print("{}", std::experimental::string_view("foo")); - - Thanks `@virgiliofornazin (Virgilio Alexandre Fornazin) - `__. - -* Allowed mixing named and automatic arguments: - - .. code:: c++ - - fmt::format("{} {two}", 1, fmt::arg("two", 2)); - -* Removed the write API in favor of the `format API - `_ with compile-time handling of - format strings. - -* Disallowed formatting of multibyte strings into a wide character target - (`#606 `_). - -* Improved documentation - (`#515 `_, - `#614 `_, - `#617 `_, - `#661 `_, - `#680 `_). - Thanks `@ibell (Ian Bell) `_, - `@mihaitodor (Mihai Todor) `_, and - `@johnthagen `_. - -* Implemented more efficient handling of large number of format arguments. - -* Introduced an inline namespace for symbol versioning. - -* Added debug postfix ``d`` to the ``fmt`` library name - (`#636 `_). - -* Removed unnecessary ``fmt/`` prefix in includes - (`#397 `_). - Thanks `@chronoxor (Ivan Shynkarenka) `_. - -* Moved ``fmt/*.h`` to ``include/fmt/*.h`` to prevent irrelevant files and - directories appearing on the include search paths when fmt is used as a - subproject and moved source files to the ``src`` directory. - -* Added qmake project file ``support/fmt.pro`` - (`#641 `_). - Thanks `@cowo78 (Giuseppe Corbelli) `_. - -* Added Gradle build file ``support/build.gradle`` - (`#649 `_). - Thanks `@luncliff (Park DongHa) `_. - -* Removed ``FMT_CPPFORMAT`` CMake option. - -* Fixed a name conflict with the macro ``CHAR_WIDTH`` in glibc - (`#616 `_). - Thanks `@aroig (Abdó Roig-Maranges) `_. - -* Fixed handling of nested braces in ``fmt::join`` - (`#638 `_). - -* Added ``SOURCELINK_SUFFIX`` for compatibility with Sphinx 1.5 - (`#497 `_). - Thanks `@ginggs (Graham Inggs) `_. - -* Added a missing ``inline`` in the header-only mode - (`#626 `_). - Thanks `@aroig (Abdó Roig-Maranges) `_. - -* Fixed various compiler warnings - (`#640 `_, - `#656 `_, - `#679 `_, - `#681 `_, - `#705 `__, - `#715 `_, - `#717 `_, - `#720 `_, - `#723 `_, - `#726 `_, - `#730 `_, - `#739 `_). - Thanks `@peterbell10 `_, - `@LarsGullik `_, - `@foonathan (Jonathan Müller) `_, - `@eliaskosunen (Elias Kosunen) `_, - `@christianparpart (Christian Parpart) `_, - `@DanielaE (Daniela Engert) `_, - and `@mwinterb `_. - -* Worked around an MSVC bug and fixed several warnings - (`#653 `_). - Thanks `@alabuzhev (Alex Alabuzhev) `_. - -* Worked around GCC bug 67371 - (`#682 `_). - -* Fixed compilation with ``-fno-exceptions`` - (`#655 `_). - Thanks `@chenxiaolong (Andrew Gunnerson) `_. - -* Made ``constexpr remove_prefix`` gcc version check tighter - (`#648 `_). - -* Renamed internal type enum constants to prevent collision with poorly written - C libraries (`#644 `_). - -* Added detection of ``wostream operator<<`` - (`#650 `_). - -* Fixed compilation on OpenBSD - (`#660 `_). - Thanks `@hubslave `_. - -* Fixed compilation on FreeBSD 12 - (`#732 `_). - Thanks `@dankm `_. - -* Fixed compilation when there is a mismatch between ``-std`` options between - the library and user code - (`#664 `_). - -* Fixed compilation with GCC 7 and ``-std=c++11`` - (`#734 `_). - -* Improved generated binary code on GCC 7 and older - (`#668 `_). - -* Fixed handling of numeric alignment with no width - (`#675 `_). - -* Fixed handling of empty strings in UTF8/16 converters - (`#676 `_). - Thanks `@vgalka-sl (Vasili Galka) `_. - -* Fixed formatting of an empty ``string_view`` - (`#689 `_). - -* Fixed detection of ``string_view`` on libc++ - (`#686 `_). - -* Fixed DLL issues (`#696 `_). - Thanks `@sebkoenig `_. - -* Fixed compile checks for mixing narrow and wide strings - (`#690 `_). - -* Disabled unsafe implicit conversion to ``std::string`` - (`#729 `_). - -* Fixed handling of reused format specs (as in ``fmt::join``) for pointers - (`#725 `_). - Thanks `@mwinterb `_. - -* Fixed installation of ``fmt/ranges.h`` - (`#738 `_). - Thanks `@sv1990 `_. - -4.1.0 - 2017-12-20 ------------------- - -* Added ``fmt::to_wstring()`` in addition to ``fmt::to_string()`` - (`#559 `_). - Thanks `@alabuzhev (Alex Alabuzhev) `_. - -* Added support for C++17 ``std::string_view`` - (`#571 `_ and - `#578 `_). - Thanks `@thelostt (Mário Feroldi) `_ and - `@mwinterb `_. - -* Enabled stream exceptions to catch errors - (`#581 `_). - Thanks `@crusader-mike `_. - -* Allowed formatting of class hierarchies with ``fmt::format_arg()`` - (`#547 `_). - Thanks `@rollbear (Björn Fahller) `_. - -* Removed limitations on character types - (`#563 `_). - Thanks `@Yelnats321 (Elnar Dakeshov) `_. - -* Conditionally enabled use of ``std::allocator_traits`` - (`#583 `_). - Thanks `@mwinterb `_. - -* Added support for ``const`` variadic member function emulation with - ``FMT_VARIADIC_CONST`` (`#591 `_). - Thanks `@ludekvodicka (Ludek Vodicka) `_. - -* Various bugfixes: bad overflow check, unsupported implicit type conversion - when determining formatting function, test segfaults - (`#551 `_), ill-formed macros - (`#542 `_) and ambiguous overloads - (`#580 `_). - Thanks `@xylosper (Byoung-young Lee) `_. - -* Prevented warnings on MSVC (`#605 `_, - `#602 `_, and - `#545 `_), - clang (`#582 `_), - GCC (`#573 `_), - various conversion warnings (`#609 `_, - `#567 `_, - `#553 `_ and - `#553 `_), and added ``override`` and - ``[[noreturn]]`` (`#549 `_ and - `#555 `_). - Thanks `@alabuzhev (Alex Alabuzhev) `_, - `@virgiliofornazin (Virgilio Alexandre Fornazin) - `_, - `@alexanderbock (Alexander Bock) `_, - `@yumetodo `_, - `@VaderY (Császár Mátyás) `_, - `@jpcima (JP Cimalando) `_, - `@thelostt (Mário Feroldi) `_, and - `@Manu343726 (Manu Sánchez) `_. - -* Improved CMake: Used ``GNUInstallDirs`` to set installation location - (`#610 `_) and fixed warnings - (`#536 `_ and - `#556 `_). - Thanks `@mikecrowe (Mike Crowe) `_, - `@evgen231 `_ and - `@henryiii (Henry Schreiner) `_. - -4.0.0 - 2017-06-27 ------------------- - -* Removed old compatibility headers ``cppformat/*.h`` and CMake options - (`#527 `_). - Thanks `@maddinat0r (Alex Martin) `_. - -* Added ``string.h`` containing ``fmt::to_string()`` as alternative to - ``std::to_string()`` as well as other string writer functionality - (`#326 `_ and - `#441 `_): - - .. code:: c++ - - #include "fmt/string.h" - - std::string answer = fmt::to_string(42); - - Thanks to `@glebov-andrey (Andrey Glebov) - `_. - -* Moved ``fmt::printf()`` to new ``printf.h`` header and allowed ``%s`` as - generic specifier (`#453 `_), - made ``%.f`` more conformant to regular ``printf()`` - (`#490 `_), added custom writer - support (`#476 `_) and implemented - missing custom argument formatting - (`#339 `_ and - `#340 `_): - - .. code:: c++ - - #include "fmt/printf.h" - - // %s format specifier can be used with any argument type. - fmt::printf("%s", 42); - - Thanks `@mojoBrendan `_, - `@manylegged (Arthur Danskin) `_ and - `@spacemoose (Glen Stark) `_. - See also `#360 `_, - `#335 `_ and - `#331 `_. - -* Added ``container.h`` containing a ``BasicContainerWriter`` - to write to containers like ``std::vector`` - (`#450 `_). - Thanks `@polyvertex (Jean-Charles Lefebvre) `_. - -* Added ``fmt::join()`` function that takes a range and formats - its elements separated by a given string - (`#466 `_): - - .. code:: c++ - - #include "fmt/format.h" - - std::vector v = {1.2, 3.4, 5.6}; - // Prints "(+01.20, +03.40, +05.60)". - fmt::print("({:+06.2f})", fmt::join(v.begin(), v.end(), ", ")); - - Thanks `@olivier80 `_. - -* Added support for custom formatting specifications to simplify customization - of built-in formatting (`#444 `_). - Thanks `@polyvertex (Jean-Charles Lefebvre) `_. - See also `#439 `_. - -* Added ``fmt::format_system_error()`` for error code formatting - (`#323 `_ and - `#526 `_). - Thanks `@maddinat0r (Alex Martin) `_. - -* Added thread-safe ``fmt::localtime()`` and ``fmt::gmtime()`` - as replacement for the standard version to ``time.h`` - (`#396 `_). - Thanks `@codicodi `_. - -* Internal improvements to ``NamedArg`` and ``ArgLists`` - (`#389 `_ and - `#390 `_). - Thanks `@chronoxor `_. - -* Fixed crash due to bug in ``FormatBuf`` - (`#493 `_). - Thanks `@effzeh `_. See also - `#480 `_ and - `#491 `_. - -* Fixed handling of wide strings in ``fmt::StringWriter``. - -* Improved compiler error messages - (`#357 `_). - -* Fixed various warnings and issues with various compilers - (`#494 `_, - `#499 `_, - `#483 `_, - `#485 `_, - `#482 `_, - `#475 `_, - `#473 `_ and - `#414 `_). - Thanks `@chronoxor `_, - `@zhaohuaxishi `_, - `@pkestene (Pierre Kestener) `_, - `@dschmidt (Dominik Schmidt) `_ and - `@0x414c (Alexey Gorishny) `_ . - -* Improved CMake: targets are now namespaced - (`#511 `_ and - `#513 `_), supported header-only - ``printf.h`` (`#354 `_), fixed issue - with minimal supported library subset - (`#418 `_, - `#419 `_ and - `#420 `_). - Thanks `@bjoernthiel (Bjoern Thiel) `_, - `@niosHD (Mario Werner) `_, - `@LogicalKnight (Sean LK) `_ and - `@alabuzhev (Alex Alabuzhev) `_. - -* Improved documentation. Thanks to - `@pwm1234 (Phil) `_ for - `#393 `_. - -3.0.2 - 2017-06-14 ------------------- - -* Added ``FMT_VERSION`` macro - (`#411 `_). - -* Used ``FMT_NULL`` instead of literal ``0`` - (`#409 `_). - Thanks `@alabuzhev (Alex Alabuzhev) `_. - -* Added extern templates for ``format_float`` - (`#413 `_). - -* Fixed implicit conversion issue - (`#507 `_). - -* Fixed signbit detection (`#423 `_). - -* Fixed naming collision (`#425 `_). - -* Fixed missing intrinsic for C++/CLI - (`#457 `_). - Thanks `@calumr (Calum Robinson) `_ - -* Fixed Android detection (`#458 `_). - Thanks `@Gachapen (Magnus Bjerke Vik) `_. - -* Use lean ``windows.h`` if not in header-only mode - (`#503 `_). - Thanks `@Quentin01 (Quentin Buathier) `_. - -* Fixed issue with CMake exporting C++11 flag - (`#445 `_). - Thanks `@EricWF (Eric) `_. - -* Fixed issue with nvcc and MSVC compiler bug and MinGW - (`#505 `_). - -* Fixed DLL issues (`#469 `_ and - `#502 `_). - Thanks `@richardeakin (Richard Eakin) `_ and - `@AndreasSchoenle (Andreas Schönle) `_. - -* Fixed test compilation under FreeBSD - (`#433 `_). - -* Fixed various warnings (`#403 `_, - `#410 `_ and - `#510 `_). - Thanks `@Lecetem `_, - `@chenhayat (Chen Hayat) `_ and - `@trozen `_. - -* Worked around a broken ``__builtin_clz`` in clang with MS codegen - (`#519 `_). - -* Removed redundant include - (`#479 `_). - -* Fixed documentation issues. - -3.0.1 - 2016-11-01 ------------------- -* Fixed handling of thousands separator - (`#353 `_). - -* Fixed handling of ``unsigned char`` strings - (`#373 `_). - -* Corrected buffer growth when formatting time - (`#367 `_). - -* Removed warnings under MSVC and clang - (`#318 `_, - `#250 `_, also merged - `#385 `_ and - `#361 `_). - Thanks `@jcelerier (Jean-Michaël Celerier) `_ - and `@nmoehrle (Nils Moehrle) `_. - -* Fixed compilation issues under Android - (`#327 `_, - `#345 `_ and - `#381 `_), - FreeBSD (`#358 `_), - Cygwin (`#388 `_), - MinGW (`#355 `_) as well as other - issues (`#350 `_, - `#366 `_, - `#348 `_, - `#402 `_, - `#405 `_). - Thanks to `@dpantele (Dmitry) `_, - `@hghwng (Hugh Wang) `_, - `@arvedarved (Tilman Keskinöz) `_, - `@LogicalKnight (Sean) `_ and - `@JanHellwig (Jan Hellwig) `_. - -* Fixed some documentation issues and extended specification - (`#320 `_, - `#333 `_, - `#347 `_, - `#362 `_). - Thanks to `@smellman (Taro Matsuzawa aka. btm) - `_. - -3.0.0 - 2016-05-07 ------------------- - -* The project has been renamed from C++ Format (cppformat) to fmt for - consistency with the used namespace and macro prefix - (`#307 `_). - Library headers are now located in the ``fmt`` directory: - - .. code:: c++ - - #include "fmt/format.h" - - Including ``format.h`` from the ``cppformat`` directory is deprecated - but works via a proxy header which will be removed in the next major version. - - The documentation is now available at https://fmt.dev. - -* Added support for `strftime `_-like - `date and time formatting `_ - (`#283 `_): - - .. code:: c++ - - #include "fmt/time.h" - - std::time_t t = std::time(nullptr); - // Prints "The date is 2016-04-29." (with the current date) - fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t)); - -* ``std::ostream`` support including formatting of user-defined types that provide - overloaded ``operator<<`` has been moved to ``fmt/ostream.h``: - - .. code:: c++ - - #include "fmt/ostream.h" - - class Date { - int year_, month_, day_; - public: - Date(int year, int month, int day) : year_(year), month_(month), day_(day) {} - - friend std::ostream &operator<<(std::ostream &os, const Date &d) { - return os << d.year_ << '-' << d.month_ << '-' << d.day_; - } - }; - - std::string s = fmt::format("The date is {}", Date(2012, 12, 9)); - // s == "The date is 2012-12-9" - -* Added support for `custom argument formatters - `_ - (`#235 `_). - -* Added support for locale-specific integer formatting with the ``n`` specifier - (`#305 `_): - - .. code:: c++ - - std::setlocale(LC_ALL, "en_US.utf8"); - fmt::print("cppformat: {:n}\n", 1234567); // prints 1,234,567 - -* Sign is now preserved when formatting an integer with an incorrect ``printf`` - format specifier (`#265 `_): - - .. code:: c++ - - fmt::printf("%lld", -42); // prints -42 - - Note that it would be an undefined behavior in ``std::printf``. - -* Length modifiers such as ``ll`` are now optional in printf formatting - functions and the correct type is determined automatically - (`#255 `_): - - .. code:: c++ - - fmt::printf("%d", std::numeric_limits::max()); - - Note that it would be an undefined behavior in ``std::printf``. - -* Added initial support for custom formatters - (`#231 `_). - -* Fixed detection of user-defined literal support on Intel C++ compiler - (`#311 `_, - `#312 `_). - Thanks to `@dean0x7d (Dean Moldovan) `_ and - `@speth (Ray Speth) `_. - -* Reduced compile time - (`#243 `_, - `#249 `_, - `#317 `_): - - .. image:: https://cloud.githubusercontent.com/assets/4831417/11614060/ - b9e826d2-9c36-11e5-8666-d4131bf503ef.png - - .. image:: https://cloud.githubusercontent.com/assets/4831417/11614080/ - 6ac903cc-9c37-11e5-8165-26df6efae364.png - - Thanks to `@dean0x7d (Dean Moldovan) `_. - -* Compile test fixes (`#313 `_). - Thanks to `@dean0x7d (Dean Moldovan) `_. - -* Documentation fixes (`#239 `_, - `#248 `_, - `#252 `_, - `#258 `_, - `#260 `_, - `#301 `_, - `#309 `_). - Thanks to `@ReadmeCritic `_ - `@Gachapen (Magnus Bjerke Vik) `_ and - `@jwilk (Jakub Wilk) `_. - -* Fixed compiler and sanitizer warnings - (`#244 `_, - `#256 `_, - `#259 `_, - `#263 `_, - `#274 `_, - `#277 `_, - `#286 `_, - `#291 `_, - `#296 `_, - `#308 `_) - Thanks to `@mwinterb `_, - `@pweiskircher (Patrik Weiskircher) `_, - `@Naios `_. - -* Improved compatibility with Windows Store apps - (`#280 `_, - `#285 `_) - Thanks to `@mwinterb `_. - -* Added tests of compatibility with older C++ standards - (`#273 `_). - Thanks to `@niosHD `_. - -* Fixed Android build (`#271 `_). - Thanks to `@newnon `_. - -* Changed ``ArgMap`` to be backed by a vector instead of a map. - (`#261 `_, - `#262 `_). - Thanks to `@mwinterb `_. - -* Added ``fprintf`` overload that writes to a ``std::ostream`` - (`#251 `_). - Thanks to `nickhutchinson (Nicholas Hutchinson) `_. - -* Export symbols when building a Windows DLL - (`#245 `_). - Thanks to `macdems (Maciek Dems) `_. - -* Fixed compilation on Cygwin (`#304 `_). - -* Implemented a workaround for a bug in Apple LLVM version 4.2 of clang - (`#276 `_). - -* Implemented a workaround for Google Test bug - `#705 `_ on gcc 6 - (`#268 `_). - Thanks to `octoploid `_. - -* Removed Biicode support because the latter has been discontinued. - -2.1.1 - 2016-04-11 ------------------- - -* The install location for generated CMake files is now configurable via - the ``FMT_CMAKE_DIR`` CMake variable - (`#299 `_). - Thanks to `@niosHD `_. - -* Documentation fixes (`#252 `_). - -2.1.0 - 2016-03-21 ------------------- - -* Project layout and build system improvements - (`#267 `_): - - * The code have been moved to the ``cppformat`` directory. - Including ``format.h`` from the top-level directory is deprecated - but works via a proxy header which will be removed in the next - major version. - - * C++ Format CMake targets now have proper interface definitions. - - * Installed version of the library now supports the header-only - configuration. - - * Targets ``doc``, ``install``, and ``test`` are now disabled if C++ Format - is included as a CMake subproject. They can be enabled by setting - ``FMT_DOC``, ``FMT_INSTALL``, and ``FMT_TEST`` in the parent project. - - Thanks to `@niosHD `_. - -2.0.1 - 2016-03-13 ------------------- - -* Improved CMake find and package support - (`#264 `_). - Thanks to `@niosHD `_. - -* Fix compile error with Android NDK and mingw32 - (`#241 `_). - Thanks to `@Gachapen (Magnus Bjerke Vik) `_. - -* Documentation fixes - (`#248 `_, - `#260 `_). - -2.0.0 - 2015-12-01 ------------------- - -General -~~~~~~~ - -* [Breaking] Named arguments - (`#169 `_, - `#173 `_, - `#174 `_): - - .. code:: c++ - - fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); - - Thanks to `@jamboree `_. - -* [Experimental] User-defined literals for format and named arguments - (`#204 `_, - `#206 `_, - `#207 `_): - - .. code:: c++ - - using namespace fmt::literals; - fmt::print("The answer is {answer}.", "answer"_a=42); - - Thanks to `@dean0x7d (Dean Moldovan) `_. - -* [Breaking] Formatting of more than 16 arguments is now supported when using - variadic templates - (`#141 `_). - Thanks to `@Shauren `_. - -* Runtime width specification - (`#168 `_): - - .. code:: c++ - - fmt::format("{0:{1}}", 42, 5); // gives " 42" - - Thanks to `@jamboree `_. - -* [Breaking] Enums are now formatted with an overloaded ``std::ostream`` insertion - operator (``operator<<``) if available - (`#232 `_). - -* [Breaking] Changed default ``bool`` format to textual, "true" or "false" - (`#170 `_): - - .. code:: c++ - - fmt::print("{}", true); // prints "true" - - To print ``bool`` as a number use numeric format specifier such as ``d``: - - .. code:: c++ - - fmt::print("{:d}", true); // prints "1" - -* ``fmt::printf`` and ``fmt::sprintf`` now support formatting of ``bool`` with the - ``%s`` specifier giving textual output, "true" or "false" - (`#223 `_): - - .. code:: c++ - - fmt::printf("%s", true); // prints "true" - - Thanks to `@LarsGullik `_. - -* [Breaking] ``signed char`` and ``unsigned char`` are now formatted as integers by default - (`#217 `_). - -* [Breaking] Pointers to C strings can now be formatted with the ``p`` specifier - (`#223 `_): - - .. code:: c++ - - fmt::print("{:p}", "test"); // prints pointer value - - Thanks to `@LarsGullik `_. - -* [Breaking] ``fmt::printf`` and ``fmt::sprintf`` now print null pointers as ``(nil)`` - and null strings as ``(null)`` for consistency with glibc - (`#226 `_). - Thanks to `@LarsGullik `_. - -* [Breaking] ``fmt::(s)printf`` now supports formatting of objects of user-defined types - that provide an overloaded ``std::ostream`` insertion operator (``operator<<``) - (`#201 `_): - - .. code:: c++ - - fmt::printf("The date is %s", Date(2012, 12, 9)); - -* [Breaking] The ``Buffer`` template is now part of the public API and can be used - to implement custom memory buffers - (`#140 `_). - Thanks to `@polyvertex (Jean-Charles Lefebvre) `_. - -* [Breaking] Improved compatibility between ``BasicStringRef`` and - `std::experimental::basic_string_view - `_ - (`#100 `_, - `#159 `_, - `#183 `_): - - - Comparison operators now compare string content, not pointers - - ``BasicStringRef::c_str`` replaced by ``BasicStringRef::data`` - - ``BasicStringRef`` is no longer assumed to be null-terminated - - References to null-terminated strings are now represented by a new class, - ``BasicCStringRef``. - -* Dependency on pthreads introduced by Google Test is now optional - (`#185 `_). - -* New CMake options ``FMT_DOC``, ``FMT_INSTALL`` and ``FMT_TEST`` to control - generation of ``doc``, ``install`` and ``test`` targets respectively, on by default - (`#197 `_, - `#198 `_, - `#200 `_). - Thanks to `@maddinat0r (Alex Martin) `_. - -* ``noexcept`` is now used when compiling with MSVC2015 - (`#215 `_). - Thanks to `@dmkrepo (Dmitriy) `_. - -* Added an option to disable use of ``windows.h`` when ``FMT_USE_WINDOWS_H`` - is defined as 0 before including ``format.h`` - (`#171 `_). - Thanks to `@alfps (Alf P. Steinbach) `_. - -* [Breaking] ``windows.h`` is now included with ``NOMINMAX`` unless - ``FMT_WIN_MINMAX`` is defined. This is done to prevent breaking code using - ``std::min`` and ``std::max`` and only affects the header-only configuration - (`#152 `_, - `#153 `_, - `#154 `_). - Thanks to `@DevO2012 `_. - -* Improved support for custom character types - (`#171 `_). - Thanks to `@alfps (Alf P. Steinbach) `_. - -* Added an option to disable use of IOStreams when ``FMT_USE_IOSTREAMS`` - is defined as 0 before including ``format.h`` - (`#205 `_, - `#208 `_). - Thanks to `@JodiTheTigger `_. - -* Improved detection of ``isnan``, ``isinf`` and ``signbit``. - -Optimization -~~~~~~~~~~~~ - -* Made formatting of user-defined types more efficient with a custom stream buffer - (`#92 `_, - `#230 `_). - Thanks to `@NotImplemented `_. - -* Further improved performance of ``fmt::Writer`` on integer formatting - and fixed a minor regression. Now it is ~7% faster than ``karma::generate`` - on Karma's benchmark - (`#186 `_). - -* [Breaking] Reduced `compiled code size - `_ - (`#143 `_, - `#149 `_). - -Distribution -~~~~~~~~~~~~ - -* [Breaking] Headers are now installed in - ``${CMAKE_INSTALL_PREFIX}/include/cppformat`` - (`#178 `_). - Thanks to `@jackyf (Eugene V. Lyubimkin) `_. - -* [Breaking] Changed the library name from ``format`` to ``cppformat`` - for consistency with the project name and to avoid potential conflicts - (`#178 `_). - Thanks to `@jackyf (Eugene V. Lyubimkin) `_. - -* C++ Format is now available in `Debian `_ GNU/Linux - (`stretch `_, - `sid `_) and - derived distributions such as - `Ubuntu `_ 15.10 and later - (`#155 `_):: - - $ sudo apt-get install libcppformat1-dev - - Thanks to `@jackyf (Eugene V. Lyubimkin) `_. - -* `Packages for Fedora and RHEL `_ - are now available. Thanks to Dave Johansen. - -* C++ Format can now be installed via `Homebrew `_ on OS X - (`#157 `_):: - - $ brew install cppformat - - Thanks to `@ortho `_, Anatoliy Bulukin. - -Documentation -~~~~~~~~~~~~~ - -* Migrated from ReadTheDocs to GitHub Pages for better responsiveness - and reliability - (`#128 `_). - New documentation address is http://cppformat.github.io/. - - -* Added `Building the documentation - `_ - section to the documentation. - -* Documentation build script is now compatible with Python 3 and newer pip versions. - (`#189 `_, - `#209 `_). - Thanks to `@JodiTheTigger `_ and - `@xentec `_. - -* Documentation fixes and improvements - (`#36 `_, - `#75 `_, - `#125 `_, - `#160 `_, - `#161 `_, - `#162 `_, - `#165 `_, - `#210 `_). - Thanks to `@syohex (Syohei YOSHIDA) `_ and - bug reporters. - -* Fixed out-of-tree documentation build - (`#177 `_). - Thanks to `@jackyf (Eugene V. Lyubimkin) `_. - -Fixes -~~~~~ - -* Fixed ``initializer_list`` detection - (`#136 `_). - Thanks to `@Gachapen (Magnus Bjerke Vik) `_. - -* [Breaking] Fixed formatting of enums with numeric format specifiers in - ``fmt::(s)printf`` - (`#131 `_, - `#139 `_): - - .. code:: c++ - - enum { ANSWER = 42 }; - fmt::printf("%d", ANSWER); - - Thanks to `@Naios `_. - -* Improved compatibility with old versions of MinGW - (`#129 `_, - `#130 `_, - `#132 `_). - Thanks to `@cstamford (Christopher Stamford) `_. - -* Fixed a compile error on MSVC with disabled exceptions - (`#144 `_). - -* Added a workaround for broken implementation of variadic templates in MSVC2012 - (`#148 `_). - -* Placed the anonymous namespace within ``fmt`` namespace for the header-only - configuration - (`#171 `_). - Thanks to `@alfps (Alf P. Steinbach) `_. - -* Fixed issues reported by Coverity Scan - (`#187 `_, - `#192 `_). - -* Implemented a workaround for a name lookup bug in MSVC2010 - (`#188 `_). - -* Fixed compiler warnings - (`#95 `_, - `#96 `_, - `#114 `_, - `#135 `_, - `#142 `_, - `#145 `_, - `#146 `_, - `#158 `_, - `#163 `_, - `#175 `_, - `#190 `_, - `#191 `_, - `#194 `_, - `#196 `_, - `#216 `_, - `#218 `_, - `#220 `_, - `#229 `_, - `#233 `_, - `#234 `_, - `#236 `_, - `#281 `_, - `#289 `_). - Thanks to `@seanmiddleditch (Sean Middleditch) `_, - `@dixlorenz (Dix Lorenz) `_, - `@CarterLi (李通洲) `_, - `@Naios `_, - `@fmatthew5876 (Matthew Fioravante) `_, - `@LevskiWeng (Levski Weng) `_, - `@rpopescu `_, - `@gabime (Gabi Melman) `_, - `@cubicool (Jeremy Moles) `_, - `@jkflying (Julian Kent) `_, - `@LogicalKnight (Sean L) `_, - `@inguin (Ingo van Lil) `_ and - `@Jopie64 (Johan) `_. - -* Fixed portability issues (mostly causing test failures) on ARM, ppc64, ppc64le, - s390x and SunOS 5.11 i386 - (`#138 `_, - `#179 `_, - `#180 `_, - `#202 `_, - `#225 `_, - `Red Hat Bugzilla Bug 1260297 `_). - Thanks to `@Naios `_, - `@jackyf (Eugene V. Lyubimkin) `_ and Dave Johansen. - -* Fixed a name conflict with macro ``free`` defined in - ``crtdbg.h`` when ``_CRTDBG_MAP_ALLOC`` is set - (`#211 `_). - -* Fixed shared library build on OS X - (`#212 `_). - Thanks to `@dean0x7d (Dean Moldovan) `_. - -* Fixed an overload conflict on MSVC when ``/Zc:wchar_t-`` option is specified - (`#214 `_). - Thanks to `@slavanap (Vyacheslav Napadovsky) `_. - -* Improved compatibility with MSVC 2008 - (`#236 `_). - Thanks to `@Jopie64 (Johan) `_. - -* Improved compatibility with bcc32 - (`#227 `_). - -* Fixed ``static_assert`` detection on Clang - (`#228 `_). - Thanks to `@dean0x7d (Dean Moldovan) `_. - -1.1.0 - 2015-03-06 ------------------- - -* Added ``BasicArrayWriter``, a class template that provides operations for - formatting and writing data into a fixed-size array - (`#105 `_ and - `#122 `_): - - .. code:: c++ - - char buffer[100]; - fmt::ArrayWriter w(buffer); - w.write("The answer is {}", 42); - -* Added `0 A.D. `_ and `PenUltima Online (POL) - `_ to the list of notable projects using C++ Format. - -* C++ Format now uses MSVC intrinsics for better formatting performance - (`#115 `_, - `#116 `_, - `#118 `_ and - `#121 `_). - Previously these optimizations where only used on GCC and Clang. - Thanks to `@CarterLi `_ and - `@objectx `_. - -* CMake install target (`#119 `_). - Thanks to `@TrentHouliston `_. - - You can now install C++ Format with ``make install`` command. - -* Improved `Biicode `_ support - (`#98 `_ and - `#104 `_). Thanks to - `@MariadeAnton `_ and - `@franramirez688 `_. - -* Improved support for building with `Android NDK - `_ - (`#107 `_). - Thanks to `@newnon `_. - - The `android-ndk-example `_ - repository provides and example of using C++ Format with Android NDK: - - .. image:: https://raw.githubusercontent.com/fmtlib/android-ndk-example/ - master/screenshot.png - -* Improved documentation of ``SystemError`` and ``WindowsError`` - (`#54 `_). - -* Various code improvements - (`#110 `_, - `#111 `_ - `#112 `_). - Thanks to `@CarterLi `_. - -* Improved compile-time errors when formatting wide into narrow strings - (`#117 `_). - -* Fixed ``BasicWriter::write`` without formatting arguments when C++11 support - is disabled (`#109 `_). - -* Fixed header-only build on OS X with GCC 4.9 - (`#124 `_). - -* Fixed packaging issues (`#94 `_). - -* Added `changelog `_ - (`#103 `_). - -1.0.0 - 2015-02-05 ------------------- - -* Add support for a header-only configuration when ``FMT_HEADER_ONLY`` is - defined before including ``format.h``: - - .. code:: c++ - - #define FMT_HEADER_ONLY - #include "format.h" - -* Compute string length in the constructor of ``BasicStringRef`` - instead of the ``size`` method - (`#79 `_). - This eliminates size computation for string literals on reasonable optimizing - compilers. - -* Fix formatting of types with overloaded ``operator <<`` for ``std::wostream`` - (`#86 `_): - - .. code:: c++ - - fmt::format(L"The date is {0}", Date(2012, 12, 9)); - -* Fix linkage of tests on Arch Linux - (`#89 `_). - -* Allow precision specifier for non-float arguments - (`#90 `_): - - .. code:: c++ - - fmt::print("{:.3}\n", "Carpet"); // prints "Car" - -* Fix build on Android NDK - (`#93 `_) - -* Improvements to documentation build procedure. - -* Remove ``FMT_SHARED`` CMake variable in favor of standard `BUILD_SHARED_LIBS - `_. - -* Fix error handling in ``fmt::fprintf``. - -* Fix a number of warnings. - -0.12.0 - 2014-10-25 -------------------- - -* [Breaking] Improved separation between formatting and buffer management. - ``Writer`` is now a base class that cannot be instantiated directly. - The new ``MemoryWriter`` class implements the default buffer management - with small allocations done on stack. So ``fmt::Writer`` should be replaced - with ``fmt::MemoryWriter`` in variable declarations. - - Old code: - - .. code:: c++ - - fmt::Writer w; - - New code: - - .. code:: c++ - - fmt::MemoryWriter w; - - If you pass ``fmt::Writer`` by reference, you can continue to do so: - - .. code:: c++ - - void f(fmt::Writer &w); - - This doesn't affect the formatting API. - -* Support for custom memory allocators - (`#69 `_) - -* Formatting functions now accept `signed char` and `unsigned char` strings as - arguments (`#73 `_): - - .. code:: c++ - - auto s = format("GLSL version: {}", glGetString(GL_VERSION)); - -* Reduced code bloat. According to the new `benchmark results - `_, - cppformat is close to ``printf`` and by the order of magnitude better than - Boost Format in terms of compiled code size. - -* Improved appearance of the documentation on mobile by using the `Sphinx - Bootstrap theme `_: - - .. |old| image:: https://cloud.githubusercontent.com/assets/576385/4792130/ - cd256436-5de3-11e4-9a62-c077d0c2b003.png - - .. |new| image:: https://cloud.githubusercontent.com/assets/576385/4792131/ - cd29896c-5de3-11e4-8f59-cac952942bf0.png - - +-------+-------+ - | Old | New | - +-------+-------+ - | |old| | |new| | - +-------+-------+ - -0.11.0 - 2014-08-21 -------------------- - -* Safe printf implementation with a POSIX extension for positional arguments: - - .. code:: c++ - - fmt::printf("Elapsed time: %.2f seconds", 1.23); - fmt::printf("%1$s, %3$d %2$s", weekday, month, day); - -* Arguments of ``char`` type can now be formatted as integers - (Issue `#55 `_): - - .. code:: c++ - - fmt::format("0x{0:02X}", 'a'); - -* Deprecated parts of the API removed. - -* The library is now built and tested on MinGW with Appveyor in addition to - existing test platforms Linux/GCC, OS X/Clang, Windows/MSVC. - -0.10.0 - 2014-07-01 -------------------- - -**Improved API** - -* All formatting methods are now implemented as variadic functions instead - of using ``operator<<`` for feeding arbitrary arguments into a temporary - formatter object. This works both with C++11 where variadic templates are - used and with older standards where variadic functions are emulated by - providing lightweight wrapper functions defined with the ``FMT_VARIADIC`` - macro. You can use this macro for defining your own portable variadic - functions: - - .. code:: c++ - - void report_error(const char *format, const fmt::ArgList &args) { - fmt::print("Error: {}"); - fmt::print(format, args); - } - FMT_VARIADIC(void, report_error, const char *) - - report_error("file not found: {}", path); - - Apart from a more natural syntax, this also improves performance as there - is no need to construct temporary formatter objects and control arguments' - lifetimes. Because the wrapper functions are very lightweight, this doesn't - cause code bloat even in pre-C++11 mode. - -* Simplified common case of formatting an ``std::string``. Now it requires a - single function call: - - .. code:: c++ - - std::string s = format("The answer is {}.", 42); - - Previously it required 2 function calls: - - .. code:: c++ - - std::string s = str(Format("The answer is {}.") << 42); - - Instead of unsafe ``c_str`` function, ``fmt::Writer`` should be used directly - to bypass creation of ``std::string``: - - .. code:: c++ - - fmt::Writer w; - w.write("The answer is {}.", 42); - w.c_str(); // returns a C string - - This doesn't do dynamic memory allocation for small strings and is less error - prone as the lifetime of the string is the same as for ``std::string::c_str`` - which is well understood (hopefully). - -* Improved consistency in naming functions that are a part of the public API. - Now all public functions are lowercase following the standard library - conventions. Previously it was a combination of lowercase and - CapitalizedWords. - Issue `#50 `_. - -* Old functions are marked as deprecated and will be removed in the next - release. - -**Other Changes** - -* Experimental support for printf format specifications (work in progress): - - .. code:: c++ - - fmt::printf("The answer is %d.", 42); - std::string s = fmt::sprintf("Look, a %s!", "string"); - -* Support for hexadecimal floating point format specifiers ``a`` and ``A``: - - .. code:: c++ - - print("{:a}", -42.0); // Prints -0x1.5p+5 - print("{:A}", -42.0); // Prints -0X1.5P+5 - -* CMake option ``FMT_SHARED`` that specifies whether to build format as a - shared library (off by default). - -0.9.0 - 2014-05-13 ------------------- - -* More efficient implementation of variadic formatting functions. - -* ``Writer::Format`` now has a variadic overload: - - .. code:: c++ - - Writer out; - out.Format("Look, I'm {}!", "variadic"); - -* For efficiency and consistency with other overloads, variadic overload of - the ``Format`` function now returns ``Writer`` instead of ``std::string``. - Use the ``str`` function to convert it to ``std::string``: - - .. code:: c++ - - std::string s = str(Format("Look, I'm {}!", "variadic")); - -* Replaced formatter actions with output sinks: ``NoAction`` -> ``NullSink``, - ``Write`` -> ``FileSink``, ``ColorWriter`` -> ``ANSITerminalSink``. - This improves naming consistency and shouldn't affect client code unless - these classes are used directly which should be rarely needed. - -* Added ``ThrowSystemError`` function that formats a message and throws - ``SystemError`` containing the formatted message and system-specific error - description. For example, the following code - - .. code:: c++ - - FILE *f = fopen(filename, "r"); - if (!f) - ThrowSystemError(errno, "Failed to open file '{}'") << filename; - - will throw ``SystemError`` exception with description - "Failed to open file '': No such file or directory" if file - doesn't exist. - -* Support for AppVeyor continuous integration platform. - -* ``Format`` now throws ``SystemError`` in case of I/O errors. - -* Improve test infrastructure. Print functions are now tested by redirecting - the output to a pipe. - -0.8.0 - 2014-04-14 ------------------- - -* Initial release diff --git a/deps/fmt/LICENSE.rst b/deps/fmt/LICENSE similarity index 100% rename from deps/fmt/LICENSE.rst rename to deps/fmt/LICENSE diff --git a/deps/fmt/README.md b/deps/fmt/README.md new file mode 100644 index 000000000..612c10c55 --- /dev/null +++ b/deps/fmt/README.md @@ -0,0 +1,486 @@ +{fmt} + +[![image](https://github.com/fmtlib/fmt/workflows/linux/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux) +[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos) +[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows) +[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](https://www.bestpractices.dev/projects/8880) +[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt) +[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt) + +**{fmt}** is an open-source formatting library providing a fast and safe +alternative to C stdio and C++ iostreams. + +If you like this project, please consider donating to one of the funds +that help victims of the war in Ukraine: . + +[Documentation](https://fmt.dev) + +[Cheat Sheets](https://hackingcpp.com/cpp/libs/fmt.html) + +Q&A: ask questions on [StackOverflow with the tag +fmt](https://stackoverflow.com/questions/tagged/fmt). + +Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v). + +# Features + +- Simple [format API](https://fmt.dev/latest/api/) with positional + arguments for localization +- Implementation of [C++20 + std::format](https://en.cppreference.com/w/cpp/utility/format) and + [C++23 std::print](https://en.cppreference.com/w/cpp/io/print) +- [Format string syntax](https://fmt.dev/latest/syntax/) similar + to Python\'s + [format](https://docs.python.org/3/library/stdtypes.html#str.format) +- Fast IEEE 754 floating-point formatter with correct rounding, + shortness and round-trip guarantees using the + [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm +- Portable Unicode support +- Safe [printf + implementation](https://fmt.dev/latest/api/#printf-formatting) + including the POSIX extension for positional arguments +- Extensibility: [support for user-defined + types](https://fmt.dev/latest/api/#formatting-user-defined-types) +- High performance: faster than common standard library + implementations of `(s)printf`, iostreams, `to_string` and + `to_chars`, see [Speed tests](#speed-tests) and [Converting a + hundred million integers to strings per + second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html) +- Small code size both in terms of source code with the minimum + configuration consisting of just three files, `base.h`, `format.h` + and `format-inl.h`, and compiled code; see [Compile time and code + bloat](#compile-time-and-code-bloat) +- Reliability: the library has an extensive set of + [tests](https://github.com/fmtlib/fmt/tree/master/test) and is + [continuously fuzzed](https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1) +- Safety: the library is fully type-safe, errors in format strings can + be reported at compile time, automatic memory management prevents + buffer overflow errors +- Ease of use: small self-contained code base, no external + dependencies, permissive MIT + [license](https://github.com/fmtlib/fmt/blob/master/LICENSE) +- [Portability](https://fmt.dev/latest/#portability) with + consistent output across platforms and support for older compilers +- Clean warning-free codebase even on high warning levels such as + `-Wall -Wextra -pedantic` +- Locale independence by default +- Optional header-only configuration enabled with the + `FMT_HEADER_ONLY` macro + +See the [documentation](https://fmt.dev) for more details. + +# Examples + +**Print to stdout** ([run](https://godbolt.org/z/Tevcjh)) + +``` c++ +#include + +int main() { + fmt::print("Hello, world!\n"); +} +``` + +**Format a string** ([run](https://godbolt.org/z/oK8h33)) + +``` c++ +std::string s = fmt::format("The answer is {}.", 42); +// s == "The answer is 42." +``` + +**Format a string using positional arguments** +([run](https://godbolt.org/z/Yn7Txe)) + +``` c++ +std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); +// s == "I'd rather be happy than right." +``` + +**Print dates and times** ([run](https://godbolt.org/z/c31ExdY3W)) + +``` c++ +#include + +int main() { + auto now = std::chrono::system_clock::now(); + fmt::print("Date and time: {}\n", now); + fmt::print("Time: {:%H:%M}\n", now); +} +``` + +Output: + + Date and time: 2023-12-26 19:10:31.557195597 + Time: 19:10 + +**Print a container** ([run](https://godbolt.org/z/MxM1YqjE7)) + +``` c++ +#include +#include + +int main() { + std::vector v = {1, 2, 3}; + fmt::print("{}\n", v); +} +``` + +Output: + + [1, 2, 3] + +**Check a format string at compile time** + +``` c++ +std::string s = fmt::format("{:d}", "I am not a number"); +``` + +This gives a compile-time error in C++20 because `d` is an invalid +format specifier for a string. + +**Write a file from a single thread** + +``` c++ +#include + +int main() { + auto out = fmt::output_file("guide.txt"); + out.print("Don't {}", "Panic"); +} +``` + +This can be [5 to 9 times faster than +fprintf](http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html). + +**Print with colors and text styles** + +``` c++ +#include + +int main() { + fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, + "Hello, {}!\n", "world"); + fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | + fmt::emphasis::underline, "Olá, {}!\n", "Mundo"); + fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, + "你好{}!\n", "世界"); +} +``` + +Output on a modern terminal with Unicode support: + +![image](https://github.com/fmtlib/fmt/assets/%0A576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7) + +# Benchmarks + +## Speed tests + +| Library | Method | Run Time, s | +|-------------------|---------------|-------------| +| libc | printf | 0.91 | +| libc++ | std::ostream | 2.49 | +| {fmt} 9.1 | fmt::print | 0.74 | +| Boost Format 1.80 | boost::format | 6.26 | +| Folly Format | folly::format | 1.87 | + +{fmt} is the fastest of the benchmarked methods, \~20% faster than +`printf`. + +The above results were generated by building `tinyformat_test.cpp` on +macOS 12.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and +taking the best of three runs. In the test, the format string +`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000 +times with output sent to `/dev/null`; for further details refer to the +[source](https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc). + +{fmt} is up to 20-30x faster than `std::ostringstream` and `sprintf` on +IEEE754 `float` and `double` formatting +([dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark)) and faster +than [double-conversion](https://github.com/google/double-conversion) +and [ryu](https://github.com/ulfjack/ryu): + +[![image](https://user-images.githubusercontent.com/576385/95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png)](https://fmt.dev/unknown_mac64_clang12.0.html) + +## Compile time and code bloat + +The script [bloat-test.py][test] from [format-benchmark][bench] tests compile +time and code bloat for nontrivial projects. It generates 100 translation units +and uses `printf()` or its alternative five times in each to simulate a +medium-sized project. The resulting executable size and compile time (Apple +clang version 15.0.0 (clang-1500.1.0.2.5), macOS Sonoma, best of three) is shown +in the following tables. + +[test]: https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py +[bench]: https://github.com/fmtlib/format-benchmark + +**Optimized build (-O3)** + +| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | +|---------------|-----------------|----------------------|--------------------| +| printf | 1.6 | 54 | 50 | +| IOStreams | 25.9 | 98 | 84 | +| fmt 83652df | 4.8 | 54 | 50 | +| tinyformat | 29.1 | 161 | 136 | +| Boost Format | 55.0 | 530 | 317 | + +{fmt} is fast to compile and is comparable to `printf` in terms of per-call +binary size (within a rounding error on this system). + +**Non-optimized build** + +| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | +|---------------|-----------------|----------------------|--------------------| +| printf | 1.4 | 54 | 50 | +| IOStreams | 23.4 | 92 | 68 | +| {fmt} 83652df | 4.4 | 89 | 85 | +| tinyformat | 24.5 | 204 | 161 | +| Boost Format | 36.4 | 831 | 462 | + +`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries +to compare formatting function overhead only. Boost Format is a +header-only library so it doesn\'t provide any linkage options. + +## Running the tests + +Please refer to [Building the +library](https://fmt.dev/latest/get-started/#building-from-source) for +instructions on how to build the library and run the unit tests. + +Benchmarks reside in a separate repository, +[format-benchmarks](https://github.com/fmtlib/format-benchmark), so to +run the benchmarks you first need to clone this repository and generate +Makefiles with CMake: + + $ git clone --recursive https://github.com/fmtlib/format-benchmark.git + $ cd format-benchmark + $ cmake . + +Then you can run the speed test: + + $ make speed-test + +or the bloat test: + + $ make bloat-test + +# Migrating code + +[clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v18 provides the +[modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html) +check that is capable of converting occurrences of `printf` and +`fprintf` to `fmt::print` if configured to do so. (By default it +converts to `std::print`.) + +# Notable projects using this library + +- [0 A.D.](https://play0ad.com/): a free, open-source, cross-platform + real-time strategy game +- [AMPL/MP](https://github.com/ampl/mp): an open-source library for + mathematical programming +- [Apple's FoundationDB](https://github.com/apple/foundationdb): an open-source, + distributed, transactional key-value store +- [Aseprite](https://github.com/aseprite/aseprite): animated sprite + editor & pixel art tool +- [AvioBook](https://www.aviobook.aero/en): a comprehensive aircraft + operations suite +- [Blizzard Battle.net](https://battle.net/): an online gaming + platform +- [Celestia](https://celestia.space/): real-time 3D visualization of + space +- [Ceph](https://ceph.com/): a scalable distributed storage system +- [ccache](https://ccache.dev/): a compiler cache +- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an + analytical database management system +- [ContextVision](https://www.contextvision.com/): medical imaging software +- [Contour](https://github.com/contour-terminal/contour/): a modern + terminal emulator +- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous + underwater vehicle +- [Drake](https://drake.mit.edu/): a planning, control, and analysis + toolbox for nonlinear dynamical systems (MIT) +- [Envoy](https://github.com/envoyproxy/envoy): C++ L7 proxy and + communication bus (Lyft) +- [FiveM](https://fivem.net/): a modification framework for GTA V +- [fmtlog](https://github.com/MengRao/fmtlog): a performant + fmtlib-style logging library with latency in nanoseconds +- [Folly](https://github.com/facebook/folly): Facebook open-source + library +- [GemRB](https://gemrb.org/): a portable open-source implementation + of Bioware's Infinity Engine +- [Grand Mountain + Adventure](https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/): + a beautiful open-world ski & snowboarding game +- [HarpyWar/pvpgn](https://github.com/pvpgn/pvpgn-server): Player vs + Player Gaming Network with tweaks +- [KBEngine](https://github.com/kbengine/kbengine): an open-source + MMOG server engine +- [Keypirinha](https://keypirinha.com/): a semantic launcher for + Windows +- [Kodi](https://kodi.tv/) (formerly xbmc): home theater software +- [Knuth](https://kth.cash/): high-performance Bitcoin full-node +- [libunicode](https://github.com/contour-terminal/libunicode/): a + modern C++17 Unicode library +- [MariaDB](https://mariadb.org/): relational database management + system +- [Microsoft Verona](https://github.com/microsoft/verona): research + programming language for concurrent ownership +- [MongoDB](https://mongodb.com/): distributed document database +- [MongoDB Smasher](https://github.com/duckie/mongo_smasher): a small + tool to generate randomized datasets +- [OpenSpace](https://openspaceproject.com/): an open-source + astrovisualization framework +- [PenUltima Online (POL)](https://www.polserver.com/): an MMO server, + compatible with most Ultima Online clients +- [PyTorch](https://github.com/pytorch/pytorch): an open-source + machine learning library +- [quasardb](https://www.quasardb.net/): a distributed, + high-performance, associative database +- [Quill](https://github.com/odygrd/quill): asynchronous low-latency + logging library +- [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to + simplify navigation, and execute complex multi-line terminal + command sequences +- [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis + cluster proxy +- [redpanda](https://vectorized.io/redpanda): a 10x faster Kafka® + replacement for mission-critical systems written in C++ +- [rpclib](http://rpclib.net/): a modern C++ msgpack-RPC server and + client library +- [Salesforce Analytics + Cloud](https://www.salesforce.com/analytics-cloud/overview/): + business intelligence software +- [Scylla](https://www.scylladb.com/): a Cassandra-compatible NoSQL + data store that can handle 1 million transactions per second on a + single server +- [Seastar](http://www.seastar-project.org/): an advanced, open-source + C++ framework for high-performance server applications on modern + hardware +- [spdlog](https://github.com/gabime/spdlog): super fast C++ logging + library +- [Stellar](https://www.stellar.org/): financial platform +- [Touch Surgery](https://www.touchsurgery.com/): surgery simulator +- [TrinityCore](https://github.com/TrinityCore/TrinityCore): + open-source MMORPG framework +- [🐙 userver framework](https://userver.tech/): open-source + asynchronous framework with a rich set of abstractions and database + drivers +- [Windows Terminal](https://github.com/microsoft/terminal): the new + Windows terminal + +[More\...](https://github.com/search?q=fmtlib&type=Code) + +If you are aware of other projects using this library, please let me +know by [email](mailto:victor.zverovich@gmail.com) or by submitting an +[issue](https://github.com/fmtlib/fmt/issues). + +# Motivation + +So why yet another formatting library? + +There are plenty of methods for doing this task, from standard ones like +the printf family of function and iostreams to Boost Format and +FastFormat libraries. The reason for creating a new library is that +every existing solution that I found either had serious issues or +didn\'t provide all the features I needed. + +## printf + +The good thing about `printf` is that it is pretty fast and readily +available being a part of the C standard library. The main drawback is +that it doesn\'t support user-defined types. `printf` also has safety +issues although they are somewhat mitigated with [\_\_attribute\_\_ +((format (printf, +\...))](https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html) in +GCC. There is a POSIX extension that adds positional arguments required +for +[i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization) +to `printf` but it is not a part of C99 and may not be available on some +platforms. + +## iostreams + +The main issue with iostreams is best illustrated with an example: + +``` c++ +std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; +``` + +which is a lot of typing compared to printf: + +``` c++ +printf("%.2f\n", 1.23456); +``` + +Matthew Wilson, the author of FastFormat, called this \"chevron hell\". +iostreams don\'t support positional arguments by design. + +The good part is that iostreams support user-defined types and are safe +although error handling is awkward. + +## Boost Format + +This is a very powerful library that supports both `printf`-like format +strings and positional arguments. Its main drawback is performance. +According to various benchmarks, it is much slower than other methods +considered here. Boost Format also has excessive build times and severe +code bloat issues (see [Benchmarks](#benchmarks)). + +## FastFormat + +This is an interesting library that is fast, safe and has positional +arguments. However, it has significant limitations, citing its author: + +> Three features that have no hope of being accommodated within the +> current design are: +> +> - Leading zeros (or any other non-space padding) +> - Octal/hexadecimal encoding +> - Runtime width/alignment specification + +It is also quite big and has a heavy dependency, on STLSoft, which might be +too restrictive for use in some projects. + +## Boost Spirit.Karma + +This is not a formatting library but I decided to include it here for +completeness. As iostreams, it suffers from the problem of mixing +verbatim text with arguments. The library is pretty fast, but slower on +integer formatting than `fmt::format_to` with format string compilation +on Karma\'s own benchmark, see [Converting a hundred million integers to +strings per +second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html). + +# License + +{fmt} is distributed under the MIT +[license](https://github.com/fmtlib/fmt/blob/master/LICENSE). + +# Documentation License + +The [Format String Syntax](https://fmt.dev/latest/syntax/) section +in the documentation is based on the one from Python [string module +documentation](https://docs.python.org/3/library/string.html#module-string). +For this reason, the documentation is distributed under the Python +Software Foundation license available in +[doc/python-license.txt](https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt). +It only applies if you distribute the documentation of {fmt}. + +# Maintainers + +The {fmt} library is maintained by Victor Zverovich +([vitaut](https://github.com/vitaut)) with contributions from many other +people. See +[Contributors](https://github.com/fmtlib/fmt/graphs/contributors) and +[Releases](https://github.com/fmtlib/fmt/releases) for some of the +names. Let us know if your contribution is not listed or mentioned +incorrectly and we\'ll make it right. + +# Security Policy + +To report a security issue, please disclose it at [security +advisory](https://github.com/fmtlib/fmt/security/advisories/new). + +This project is maintained by a team of volunteers on a +reasonable-effort basis. As such, please give us at least *90* days to +work on a fix before public exposure. diff --git a/deps/fmt/README.rst b/deps/fmt/README.rst deleted file mode 100644 index 44bb6868c..000000000 --- a/deps/fmt/README.rst +++ /dev/null @@ -1,545 +0,0 @@ -.. image:: https://user-images.githubusercontent.com/ - 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png - :width: 25% - :alt: {fmt} - -.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg - :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux - -.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg - :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos - -.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg - :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows - -.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg - :alt: fmt is continuously fuzzed at oss-fuzz - :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ - colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\ - Summary&q=proj%3Dfmt&can=1 - -.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg - :alt: Ask questions at StackOverflow with the tag fmt - :target: https://stackoverflow.com/questions/tagged/fmt - -.. image:: https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge - :target: https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt - -**{fmt}** is an open-source formatting library providing a fast and safe -alternative to C stdio and C++ iostreams. - -If you like this project, please consider donating to one of the funds that -help victims of the war in Ukraine: https://www.stopputin.net/. - -`Documentation `__ - -`Cheat Sheets `__ - -Q&A: ask questions on `StackOverflow with the tag fmt -`_. - -Try {fmt} in `Compiler Explorer `_. - -Features --------- - -* Simple `format API `_ with positional arguments - for localization -* Implementation of `C++20 std::format - `__ -* `Format string syntax `_ similar to Python's - `format `_ -* Fast IEEE 754 floating-point formatter with correct rounding, shortness and - round-trip guarantees using the `Dragonbox `_ - algorithm -* Portable Unicode support -* Safe `printf implementation - `_ including the POSIX - extension for positional arguments -* Extensibility: `support for user-defined types - `_ -* High performance: faster than common standard library implementations of - ``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ - and `Converting a hundred million integers to strings per second - `_ -* Small code size both in terms of source code with the minimum configuration - consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``, - and compiled code; see `Compile time and code bloat`_ -* Reliability: the library has an extensive set of `tests - `_ and is `continuously fuzzed - `_ -* Safety: the library is fully type-safe, errors in format strings can be - reported at compile time, automatic memory management prevents buffer overflow - errors -* Ease of use: small self-contained code base, no external dependencies, - permissive MIT `license - `_ -* `Portability `_ with - consistent output across platforms and support for older compilers -* Clean warning-free codebase even on high warning levels such as - ``-Wall -Wextra -pedantic`` -* Locale independence by default -* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro - -See the `documentation `_ for more details. - -Examples --------- - -**Print to stdout** (`run `_) - -.. code:: c++ - - #include - - int main() { - fmt::print("Hello, world!\n"); - } - -**Format a string** (`run `_) - -.. code:: c++ - - std::string s = fmt::format("The answer is {}.", 42); - // s == "The answer is 42." - -**Format a string using positional arguments** (`run `_) - -.. code:: c++ - - std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); - // s == "I'd rather be happy than right." - -**Print chrono durations** (`run `_) - -.. code:: c++ - - #include - - int main() { - using namespace std::literals::chrono_literals; - fmt::print("Default format: {} {}\n", 42s, 100ms); - fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); - } - -Output:: - - Default format: 42s 100ms - strftime-like format: 03:15:30 - -**Print a container** (`run `_) - -.. code:: c++ - - #include - #include - - int main() { - std::vector v = {1, 2, 3}; - fmt::print("{}\n", v); - } - -Output:: - - [1, 2, 3] - -**Check a format string at compile time** - -.. code:: c++ - - std::string s = fmt::format("{:d}", "I am not a number"); - -This gives a compile-time error in C++20 because ``d`` is an invalid format -specifier for a string. - -**Write a file from a single thread** - -.. code:: c++ - - #include - - int main() { - auto out = fmt::output_file("guide.txt"); - out.print("Don't {}", "Panic"); - } - -This can be `5 to 9 times faster than fprintf -`_. - -**Print with colors and text styles** - -.. code:: c++ - - #include - - int main() { - fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, - "Hello, {}!\n", "world"); - fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | - fmt::emphasis::underline, "Hello, {}!\n", "мир"); - fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, - "Hello, {}!\n", "世界"); - } - -Output on a modern terminal: - -.. image:: https://user-images.githubusercontent.com/ - 576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png - -Benchmarks ----------- - -Speed tests -~~~~~~~~~~~ - -================= ============= =========== -Library Method Run Time, s -================= ============= =========== -libc printf 0.91 -libc++ std::ostream 2.49 -{fmt} 9.1 fmt::print 0.74 -Boost Format 1.80 boost::format 6.26 -Folly Format folly::format 1.87 -================= ============= =========== - -{fmt} is the fastest of the benchmarked methods, ~20% faster than ``printf``. - -The above results were generated by building ``tinyformat_test.cpp`` on macOS -12.6.1 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the -best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` -or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for -further details refer to the `source -`_. - -{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on -IEEE754 ``float`` and ``double`` formatting (`dtoa-benchmark `_) -and faster than `double-conversion `_ and -`ryu `_: - -.. image:: https://user-images.githubusercontent.com/576385/ - 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png - :target: https://fmt.dev/unknown_mac64_clang12.0.html - -Compile time and code bloat -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The script `bloat-test.py -`_ -from `format-benchmark `_ -tests compile time and code bloat for nontrivial projects. -It generates 100 translation units and uses ``printf()`` or its alternative -five times in each to simulate a medium-sized project. The resulting -executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), -macOS Sierra, best of three) is shown in the following tables. - -**Optimized build (-O3)** - -============= =============== ==================== ================== -Method Compile Time, s Executable size, KiB Stripped size, KiB -============= =============== ==================== ================== -printf 2.6 29 26 -printf+string 16.4 29 26 -iostreams 31.1 59 55 -{fmt} 19.0 37 34 -Boost Format 91.9 226 203 -Folly Format 115.7 101 88 -============= =============== ==================== ================== - -As you can see, {fmt} has 60% less overhead in terms of resulting binary code -size compared to iostreams and comes pretty close to ``printf``. Boost Format -and Folly Format have the largest overheads. - -``printf+string`` is the same as ``printf`` but with an extra ```` -include to measure the overhead of the latter. - -**Non-optimized build** - -============= =============== ==================== ================== -Method Compile Time, s Executable size, KiB Stripped size, KiB -============= =============== ==================== ================== -printf 2.2 33 30 -printf+string 16.0 33 30 -iostreams 28.3 56 52 -{fmt} 18.2 59 50 -Boost Format 54.1 365 303 -Folly Format 79.9 445 430 -============= =============== ==================== ================== - -``libc``, ``lib(std)c++``, and ``libfmt`` are all linked as shared libraries to -compare formatting function overhead only. Boost Format is a -header-only library so it doesn't provide any linkage options. - -Running the tests -~~~~~~~~~~~~~~~~~ - -Please refer to `Building the library`__ for instructions on how to build -the library and run the unit tests. - -__ https://fmt.dev/latest/usage.html#building-the-library - -Benchmarks reside in a separate repository, -`format-benchmarks `_, -so to run the benchmarks you first need to clone this repository and -generate Makefiles with CMake:: - - $ git clone --recursive https://github.com/fmtlib/format-benchmark.git - $ cd format-benchmark - $ cmake . - -Then you can run the speed test:: - - $ make speed-test - -or the bloat test:: - - $ make bloat-test - -Migrating code --------------- - -`clang-tidy `_ v17 (not yet -released) provides the `modernize-use-std-print -`_ -check that is capable of converting occurrences of ``printf`` and -``fprintf`` to ``fmt::print`` if configured to do so. (By default it -converts to ``std::print``.) - -Projects using this library ---------------------------- - -* `0 A.D. `_: a free, open-source, cross-platform - real-time strategy game - -* `AMPL/MP `_: - an open-source library for mathematical programming - -* `Aseprite `_: - animated sprite editor & pixel art tool - -* `AvioBook `_: a comprehensive aircraft - operations suite - -* `Blizzard Battle.net `_: an online gaming platform - -* `Celestia `_: real-time 3D visualization of space - -* `Ceph `_: a scalable distributed storage system - -* `ccache `_: a compiler cache - -* `ClickHouse `_: an analytical database - management system - -* `Contour `_: a modern terminal emulator - -* `CUAUV `_: Cornell University's autonomous underwater - vehicle - -* `Drake `_: a planning, control, and analysis toolbox - for nonlinear dynamical systems (MIT) - -* `Envoy `_: C++ L7 proxy and communication bus - (Lyft) - -* `FiveM `_: a modification framework for GTA V - -* `fmtlog `_: a performant fmtlib-style - logging library with latency in nanoseconds - -* `Folly `_: Facebook open-source library - -* `GemRB `_: a portable open-source implementation of - Bioware’s Infinity Engine - -* `Grand Mountain Adventure - `_: - a beautiful open-world ski & snowboarding game - -* `HarpyWar/pvpgn `_: - Player vs Player Gaming Network with tweaks - -* `KBEngine `_: an open-source MMOG server - engine - -* `Keypirinha `_: a semantic launcher for Windows - -* `Kodi `_ (formerly xbmc): home theater software - -* `Knuth `_: high-performance Bitcoin full-node - -* `libunicode `_: a modern C++17 Unicode library - -* `MariaDB `_: relational database management system - -* `Microsoft Verona `_: - research programming language for concurrent ownership - -* `MongoDB `_: distributed document database - -* `MongoDB Smasher `_: a small tool to - generate randomized datasets - -* `OpenSpace `_: an open-source - astrovisualization framework - -* `PenUltima Online (POL) `_: - an MMO server, compatible with most Ultima Online clients - -* `PyTorch `_: an open-source machine - learning library - -* `quasardb `_: a distributed, high-performance, - associative database - -* `Quill `_: asynchronous low-latency logging library - -* `QKW `_: generalizing aliasing to simplify - navigation, and executing complex multi-line terminal command sequences - -* `redis-cerberus `_: a Redis cluster - proxy - -* `redpanda `_: a 10x faster Kafka® replacement - for mission-critical systems written in C++ - -* `rpclib `_: a modern C++ msgpack-RPC server and client - library - -* `Salesforce Analytics Cloud - `_: - business intelligence software - -* `Scylla `_: a Cassandra-compatible NoSQL data store - that can handle 1 million transactions per second on a single server - -* `Seastar `_: an advanced, open-source C++ - framework for high-performance server applications on modern hardware - -* `spdlog `_: super fast C++ logging library - -* `Stellar `_: financial platform - -* `Touch Surgery `_: surgery simulator - -* `TrinityCore `_: open-source - MMORPG framework - -* `🐙 userver framework `_: open-source asynchronous - framework with a rich set of abstractions and database drivers - -* `Windows Terminal `_: the new Windows - terminal - -`More... `_ - -If you are aware of other projects using this library, please let me know -by `email `_ or by submitting an -`issue `_. - -Motivation ----------- - -So why yet another formatting library? - -There are plenty of methods for doing this task, from standard ones like -the printf family of function and iostreams to Boost Format and FastFormat -libraries. The reason for creating a new library is that every existing -solution that I found either had serious issues or didn't provide -all the features I needed. - -printf -~~~~~~ - -The good thing about ``printf`` is that it is pretty fast and readily available -being a part of the C standard library. The main drawback is that it -doesn't support user-defined types. ``printf`` also has safety issues although -they are somewhat mitigated with `__attribute__ ((format (printf, ...)) -`_ in GCC. -There is a POSIX extension that adds positional arguments required for -`i18n `_ -to ``printf`` but it is not a part of C99 and may not be available on some -platforms. - -iostreams -~~~~~~~~~ - -The main issue with iostreams is best illustrated with an example: - -.. code:: c++ - - std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; - -which is a lot of typing compared to printf: - -.. code:: c++ - - printf("%.2f\n", 1.23456); - -Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams -don't support positional arguments by design. - -The good part is that iostreams support user-defined types and are safe although -error handling is awkward. - -Boost Format -~~~~~~~~~~~~ - -This is a very powerful library that supports both ``printf``-like format -strings and positional arguments. Its main drawback is performance. According to -various benchmarks, it is much slower than other methods considered here. Boost -Format also has excessive build times and severe code bloat issues (see -`Benchmarks`_). - -FastFormat -~~~~~~~~~~ - -This is an interesting library that is fast, safe, and has positional arguments. -However, it has significant limitations, citing its author: - - Three features that have no hope of being accommodated within the - current design are: - - * Leading zeros (or any other non-space padding) - * Octal/hexadecimal encoding - * Runtime width/alignment specification - -It is also quite big and has a heavy dependency, STLSoft, which might be too -restrictive for using it in some projects. - -Boost Spirit.Karma -~~~~~~~~~~~~~~~~~~ - -This is not a formatting library but I decided to include it here for -completeness. As iostreams, it suffers from the problem of mixing verbatim text -with arguments. The library is pretty fast, but slower on integer formatting -than ``fmt::format_to`` with format string compilation on Karma's own benchmark, -see `Converting a hundred million integers to strings per second -`_. - -License -------- - -{fmt} is distributed under the MIT `license -`_. - -Documentation License ---------------------- - -The `Format String Syntax `_ -section in the documentation is based on the one from Python `string module -documentation `_. -For this reason, the documentation is distributed under the Python Software -Foundation license available in `doc/python-license.txt -`_. -It only applies if you distribute the documentation of {fmt}. - -Maintainers ------------ - -The {fmt} library is maintained by Victor Zverovich (`vitaut -`_) with contributions from many other people. -See `Contributors `_ and -`Releases `_ for some of the names. -Let us know if your contribution is not listed or mentioned incorrectly and -we'll make it right. diff --git a/deps/fmt/include/fmt/args.h b/deps/fmt/include/fmt/args.h index 2d684e7cc..5e5f40f9e 100644 --- a/deps/fmt/include/fmt/args.h +++ b/deps/fmt/include/fmt/args.h @@ -8,34 +8,39 @@ #ifndef FMT_ARGS_H_ #define FMT_ARGS_H_ -#include // std::reference_wrapper -#include // std::unique_ptr -#include +#ifndef FMT_MODULE +# include // std::reference_wrapper +# include // std::unique_ptr +# include +#endif -#include "core.h" +#include "format.h" // std_string_view FMT_BEGIN_NAMESPACE - namespace detail { template struct is_reference_wrapper : std::false_type {}; template struct is_reference_wrapper> : std::true_type {}; -template const T& unwrap(const T& v) { return v; } -template const T& unwrap(const std::reference_wrapper& v) { +template auto unwrap(const T& v) -> const T& { return v; } +template +auto unwrap(const std::reference_wrapper& v) -> const T& { return static_cast(v); } -class dynamic_arg_list { - // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for - // templates it doesn't complain about inability to deduce single translation - // unit for placing vtable. So storage_node_base is made a fake template. - template struct node { - virtual ~node() = default; - std::unique_ptr> next; - }; +// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC +// 2022 (v17.10.0). +// +// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for +// templates it doesn't complain about inability to deduce single translation +// unit for placing vtable. So node is made a fake template. +template struct node { + virtual ~node() = default; + std::unique_ptr> next; +}; +class dynamic_arg_list { template struct typed_node : node<> { T value; @@ -50,7 +55,7 @@ class dynamic_arg_list { std::unique_ptr> head_; public: - template const T& push(const Arg& arg) { + template auto push(const Arg& arg) -> const T& { auto new_node = std::unique_ptr>(new typed_node(arg)); auto& value = new_node->value; new_node->next = std::move(head_); @@ -61,28 +66,18 @@ class dynamic_arg_list { } // namespace detail /** - \rst - A dynamic version of `fmt::format_arg_store`. - It's equipped with a storage to potentially temporary objects which lifetimes - could be shorter than the format arguments object. - - It can be implicitly converted into `~fmt::basic_format_args` for passing - into type-erased formatting functions such as `~fmt::vformat`. - \endrst + * A dynamic list of formatting arguments with storage. + * + * It can be implicitly converted into `fmt::basic_format_args` for passing + * into type-erased formatting functions such as `fmt::vformat`. */ -template -class dynamic_format_arg_store -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ +FMT_EXPORT template class dynamic_format_arg_store { private: using char_type = typename Context::char_type; template struct need_copy { static constexpr detail::type mapped_type = - detail::mapped_type_constant::value; + detail::mapped_type_constant::value; enum { value = !(detail::is_reference_wrapper::value || @@ -95,7 +90,7 @@ class dynamic_format_arg_store }; template - using stored_type = conditional_t< + using stored_t = conditional_t< std::is_convertible>::value && !detail::is_reference_wrapper::value, std::basic_string, T>; @@ -110,80 +105,72 @@ class dynamic_format_arg_store friend class basic_format_args; - unsigned long long get_types() const { - return detail::is_unpacked_bit | data_.size() | - (named_info_.empty() - ? 0ULL - : static_cast(detail::has_named_args_bit)); - } - - const basic_format_arg* data() const { + auto data() const -> const basic_format_arg* { return named_info_.empty() ? data_.data() : data_.data() + 1; } template void emplace_arg(const T& arg) { - data_.emplace_back(detail::make_arg(arg)); + data_.emplace_back(arg); } template void emplace_arg(const detail::named_arg& arg) { - if (named_info_.empty()) { - constexpr const detail::named_arg_info* zero_ptr{nullptr}; - data_.insert(data_.begin(), {zero_ptr, 0}); - } - data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); + if (named_info_.empty()) + data_.insert(data_.begin(), basic_format_arg(nullptr, 0)); + data_.emplace_back(detail::unwrap(arg.value)); auto pop_one = [](std::vector>* data) { data->pop_back(); }; std::unique_ptr>, decltype(pop_one)> guard{&data_, pop_one}; named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); - data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + data_[0] = {named_info_.data(), named_info_.size()}; guard.release(); } public: constexpr dynamic_format_arg_store() = default; + operator basic_format_args() const { + return basic_format_args(data(), static_cast(data_.size()), + !named_info_.empty()); + } + /** - \rst - Adds an argument into the dynamic store for later passing to a formatting - function. - - Note that custom types and string types (but not string views) are copied - into the store dynamically allocating memory if necessary. - - **Example**:: - - fmt::dynamic_format_arg_store store; - store.push_back(42); - store.push_back("abc"); - store.push_back(1.5f); - std::string result = fmt::vformat("{} and {} and {}", store); - \endrst - */ + * Adds an argument into the dynamic store for later passing to a formatting + * function. + * + * Note that custom types and string types (but not string views) are copied + * into the store dynamically allocating memory if necessary. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * store.push_back(42); + * store.push_back("abc"); + * store.push_back(1.5f); + * std::string result = fmt::vformat("{} and {} and {}", store); + */ template void push_back(const T& arg) { if (detail::const_check(need_copy::value)) - emplace_arg(dynamic_args_.push>(arg)); + emplace_arg(dynamic_args_.push>(arg)); else emplace_arg(detail::unwrap(arg)); } /** - \rst - Adds a reference to the argument into the dynamic store for later passing to - a formatting function. - - **Example**:: - - fmt::dynamic_format_arg_store store; - char band[] = "Rolling Stones"; - store.push_back(std::cref(band)); - band[9] = 'c'; // Changing str affects the output. - std::string result = fmt::vformat("{}", store); - // result == "Rolling Scones" - \endrst - */ + * Adds a reference to the argument into the dynamic store for later passing + * to a formatting function. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * char band[] = "Rolling Stones"; + * store.push_back(std::cref(band)); + * band[9] = 'c'; // Changing str affects the output. + * std::string result = fmt::vformat("{}", store); + * // result == "Rolling Scones" + */ template void push_back(std::reference_wrapper arg) { static_assert( need_copy::value, @@ -192,41 +179,40 @@ class dynamic_format_arg_store } /** - Adds named argument into the dynamic store for later passing to a formatting - function. ``std::reference_wrapper`` is supported to avoid copying of the - argument. The name is always copied into the store. - */ + * Adds named argument into the dynamic store for later passing to a + * formatting function. `std::reference_wrapper` is supported to avoid + * copying of the argument. The name is always copied into the store. + */ template void push_back(const detail::named_arg& arg) { const char_type* arg_name = dynamic_args_.push>(arg.name).c_str(); if (detail::const_check(need_copy::value)) { emplace_arg( - fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); } else { emplace_arg(fmt::arg(arg_name, arg.value)); } } - /** Erase all elements from the store */ + /// Erase all elements from the store. void clear() { data_.clear(); named_info_.clear(); - dynamic_args_ = detail::dynamic_arg_list(); + dynamic_args_ = {}; } - /** - \rst - Reserves space to store at least *new_cap* arguments including - *new_cap_named* named arguments. - \endrst - */ + /// Reserves space to store at least `new_cap` arguments including + /// `new_cap_named` named arguments. void reserve(size_t new_cap, size_t new_cap_named) { FMT_ASSERT(new_cap >= new_cap_named, - "Set of arguments includes set of named arguments"); + "set of arguments includes set of named arguments"); data_.reserve(new_cap); named_info_.reserve(new_cap_named); } + + /// Returns the number of elements in the store. + auto size() const noexcept -> size_t { return data_.size(); } }; FMT_END_NAMESPACE diff --git a/deps/fmt/include/fmt/base.h b/deps/fmt/include/fmt/base.h new file mode 100644 index 000000000..0d5786778 --- /dev/null +++ b/deps/fmt/include/fmt/base.h @@ -0,0 +1,3010 @@ +// Formatting library for C++ - the base API for char/UTF-8 +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_BASE_H_ +#define FMT_BASE_H_ + +#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) +# define FMT_MODULE +#endif + +#ifndef FMT_MODULE +# include // CHAR_BIT +# include // FILE +# include // memcmp + +# include // std::enable_if +#endif + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 120100 + +// Detect compiler versions. +#if defined(__clang__) && !defined(__ibmxl__) +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif +#if defined(__ICL) +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif +#if defined(_MSC_VER) +# define FMT_MSC_VERSION _MSC_VER +#else +# define FMT_MSC_VERSION 0 +#endif + +// Detect standard library versions. +#ifdef _GLIBCXX_RELEASE +# define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE +#else +# define FMT_GLIBCXX_RELEASE 0 +#endif +#ifdef _LIBCPP_VERSION +# define FMT_LIBCPP_VERSION _LIBCPP_VERSION +#else +# define FMT_LIBCPP_VERSION 0 +#endif + +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + +// Detect __has_*. +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif +#ifdef __has_include +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Detect C++14 relaxed constexpr. +#ifdef FMT_USE_CONSTEXPR +// Use the provided definition. +#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L +// GCC only allows constexpr member functions in non-literal types since 7.2: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297. +# define FMT_USE_CONSTEXPR 1 +#elif FMT_ICC_VERSION +# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628 +#elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 +# define FMT_USE_CONSTEXPR 1 +#else +# define FMT_USE_CONSTEXPR 0 +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +#else +# define FMT_CONSTEXPR +#endif + +// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. +#ifdef FMT_USE_CONSTEVAL +// Use the provided definition. +#elif !defined(__cpp_lib_is_constant_evaluated) +# define FMT_USE_CONSTEVAL 0 +#elif FMT_CPLUSPLUS < 201709L +# define FMT_USE_CONSTEVAL 0 +#elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10 +# define FMT_USE_CONSTEVAL 0 +#elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000 +# define FMT_USE_CONSTEVAL 0 +#elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L +# define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang < 14. +#elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1929 +# define FMT_USE_CONSTEVAL 0 // consteval is broken in MSVC VS2019 < 16.10. +#elif defined(__cpp_consteval) +# define FMT_USE_CONSTEVAL 1 +#elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101 +# define FMT_USE_CONSTEVAL 1 +#else +# define FMT_USE_CONSTEVAL 0 +#endif +#if FMT_USE_CONSTEVAL +# define FMT_CONSTEVAL consteval +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEVAL +# define FMT_CONSTEXPR20 +#endif + +// Check if exceptions are disabled. +#ifdef FMT_USE_EXCEPTIONS +// Use the provided definition. +#elif defined(__GNUC__) && !defined(__EXCEPTIONS) +# define FMT_USE_EXCEPTIONS 0 +#elif defined(__clang__) && !defined(__cpp_exceptions) +# define FMT_USE_EXCEPTIONS 0 +#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS +# define FMT_USE_EXCEPTIONS 0 +#else +# define FMT_USE_EXCEPTIONS 1 +#endif +#if FMT_USE_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifdef FMT_NO_UNIQUE_ADDRESS +// Use the provided definition. +#elif FMT_CPLUSPLUS < 202002L +// Not supported. +#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) +# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] +// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). +#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION +# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#endif +#ifndef FMT_NO_UNIQUE_ADDRESS +# define FMT_NO_UNIQUE_ADDRESS +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. +#if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__) +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifdef FMT_NODISCARD +// Use the provided definition. +#elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +#else +# define FMT_NODISCARD +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_VISIBILITY(value) __attribute__((visibility(value))) +#else +# define FMT_VISIBILITY(value) +#endif + +// Detect pragmas. +#define FMT_PRAGMA_IMPL(x) _Pragma(#x) +#if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER) +// Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884 +// and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582. +# define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x) +#else +# define FMT_PRAGMA_GCC(x) +#endif +#if FMT_CLANG_VERSION +# define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x) +#else +# define FMT_PRAGMA_CLANG(x) +#endif +#if FMT_MSC_VERSION +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#else +# define FMT_MSC_WARNING(...) +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_PRAGMA_GCC(push_options) +#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) +FMT_PRAGMA_GCC(optimize("Og")) +# define FMT_GCC_OPTIMIZED +#endif +FMT_PRAGMA_CLANG(diagnostic push) +FMT_PRAGMA_GCC(diagnostic push) + +#ifdef FMT_ALWAYS_INLINE +// Use the provided definition. +#elif FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE inline +#endif +// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. +#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED) +# define FMT_INLINE FMT_ALWAYS_INLINE +#else +# define FMT_INLINE inline +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + inline namespace v12 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_EXPORT +# define FMT_EXPORT +# define FMT_BEGIN_EXPORT +# define FMT_END_EXPORT +#endif + +#ifdef _WIN32 +# define FMT_WIN32 1 +#else +# define FMT_WIN32 0 +#endif + +#if !defined(FMT_HEADER_ONLY) && FMT_WIN32 +# if defined(FMT_LIB_EXPORT) +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_API FMT_VISIBILITY("default") +#endif +#ifndef FMT_API +# define FMT_API +#endif + +#ifndef FMT_OPTIMIZE_SIZE +# define FMT_OPTIMIZE_SIZE 0 +#endif + +// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher +// per-call binary size by passing built-in types through the extension API. +#ifndef FMT_BUILTIN_TYPES +# define FMT_BUILTIN_TYPES 1 +#endif + +#define FMT_APPLY_VARIADIC(expr) \ + using unused = int[]; \ + (void)unused { 0, (expr, 0)... } + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template +using make_unsigned_t = typename std::make_unsigned::type; +template +using underlying_t = typename std::underlying_type::type; +template using decay_t = typename std::decay::type; +using nullptr_t = decltype(nullptr); + +#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION +// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context. +template struct void_t_impl { + using type = void; +}; +template using void_t = typename void_t_impl::type; +#else +template using void_t = void; +#endif + +struct monostate { + constexpr monostate() {} +}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +template constexpr auto min_of(T a, T b) -> T { + return a < b ? a : b; +} +template constexpr auto max_of(T a, T b) -> T { + return a > b ? a : b; +} + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +namespace detail { +// Suppresses "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr auto is_constant_evaluated(bool default_value = false) noexcept + -> bool { +// Workaround for incompatibility between clang 14 and libstdc++ consteval-based +// std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247. +#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \ + (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) + ignore_unused(default_value); + return __builtin_is_constant_evaluated(); +#elif defined(__cpp_lib_is_constant_evaluated) + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} + +// Suppresses "conditional expression is constant" warnings. +template FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T { + return val; +} + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#if defined(FMT_ASSERT) +// Use the provided definition. +#elif defined(NDEBUG) +// FMT_ASSERT is not empty to avoid -Wempty-body. +# define FMT_ASSERT(condition, message) \ + fmt::detail::ignore_unused((condition), (message)) +#else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::assert_fail(__FILE__, __LINE__, (message))) +#endif + +#ifdef FMT_USE_INT128 +// Use the provided definition. +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) +# define FMT_USE_INT128 1 +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; +inline auto map(int128_opt x) -> int128_opt { return x; } +inline auto map(uint128_opt x) -> uint128_opt { return x; } +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +enum class int128_opt {}; +enum class uint128_opt {}; +// Reduce template instantiations. +inline auto map(int128_opt) -> monostate { return {}; } +inline auto map(uint128_opt) -> monostate { return {}; } +#endif + +#ifdef FMT_USE_BITINT +// Use the provided definition. +#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__) +# define FMT_USE_BITINT 1 +#else +# define FMT_USE_BITINT 0 +#endif + +#if FMT_USE_BITINT +FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension") +template using bitint = _BitInt(N); +template using ubitint = unsigned _BitInt(N); +#else +template struct bitint {}; +template struct ubitint {}; +#endif // FMT_USE_BITINT + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t { + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); + return static_cast>(value); +} + +template +using unsigned_char = conditional_t; + +// A heuristic to detect std::string and std::[experimental::]string_view. +// It is mainly used to avoid dependency on <[experimental/]string_view>. +template +struct is_std_string_like : std::false_type {}; +template +struct is_std_string_like().find_first_of( + typename T::value_type(), 0))>> + : std::is_convertible().data()), + const typename T::value_type*> {}; + +// Check if the literal encoding is UTF-8. +enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' }; +enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; + +#ifndef FMT_UNICODE +# define FMT_UNICODE 1 +#endif + +static_assert(!FMT_UNICODE || use_utf8, + "Unicode support requires compiling with /utf-8"); + +template constexpr auto narrow(T*) -> char* { return nullptr; } +constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* { + return s; +} + +template +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int { + if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); + for (; n != 0; ++s1, ++s2, --n) { + if (*s1 < *s2) return -1; + if (*s1 > *s2) return 1; + } + return 0; +} + +namespace adl { +using namespace std; + +template +auto invoke_back_inserter() + -> decltype(back_inserter(std::declval())); +} // namespace adl + +template +struct is_back_insert_iterator : std::false_type {}; + +template +struct is_back_insert_iterator< + It, bool_constant()), + It>::value>> : std::true_type {}; + +// Extracts a reference to the container from *insert_iterator. +template +inline FMT_CONSTEXPR20 auto get_container(OutputIt it) -> + typename OutputIt::container_type& { + struct accessor : OutputIt { + FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {} + using OutputIt::container; + }; + return *accessor(it).container; +} +} // namespace detail + +// Parsing-related public API and forward declarations. +FMT_BEGIN_EXPORT + +/** + * An implementation of `std::basic_string_view` for pre-C++17. It provides a + * subset of the API. `fmt::basic_string_view` is used for format strings even + * if `std::basic_string_view` is available to prevent issues when a library is + * compiled with a different `-std` option than the client code (which is not + * recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} + + /// Constructs a string view object from a C string and a size. + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} + + constexpr basic_string_view(nullptr_t) = delete; + + /// Constructs a string view object from a C string. +#if FMT_GCC_VERSION + FMT_ALWAYS_INLINE +#endif + FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { +#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION + if (std::is_same::value && !detail::is_constant_evaluated()) { + size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr. + return; + } +#endif + size_t len = 0; + while (*s++) ++len; + size_ = len; + } + + /// Constructs a string view from a `std::basic_string` or a + /// `std::basic_string_view` object. + template ::value&& std::is_same< + typename S::value_type, Char>::value)> + FMT_CONSTEXPR basic_string_view(const S& s) noexcept + : data_(s.data()), size_(s.size()) {} + + /// Returns a pointer to the string data. + constexpr auto data() const noexcept -> const Char* { return data_; } + + /// Returns the string size. + constexpr auto size() const noexcept -> size_t { return size_; } + + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } + + constexpr auto operator[](size_t pos) const noexcept -> const Char& { + return data_[pos]; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { + data_ += n; + size_ -= n; + } + + FMT_CONSTEXPR auto starts_with(basic_string_view sv) const noexcept + -> bool { + return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0; + } + FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool { + return size_ >= 1 && *data_ == c; + } + FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool { + return starts_with(basic_string_view(s)); + } + + FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { + int result = + detail::compare(data_, other.data_, min_of(size_, other.size_)); + if (result != 0) return result; + return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + } + + FMT_CONSTEXPR friend auto operator==(basic_string_view lhs, + basic_string_view rhs) -> bool { + return lhs.compare(rhs) == 0; + } + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) != 0; + } + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) < 0; + } + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) <= 0; + } + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) > 0; + } + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; + +template class basic_appender; +using appender = basic_appender; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; + +class context; +template class generic_context; +template class parse_context; + +// Longer aliases for C++20 compatibility. +template using basic_format_parse_context = parse_context; +using format_parse_context = parse_context; +template +using basic_format_context = + conditional_t::value, context, + generic_context>; +using format_context = context; + +template +using buffered_context = + conditional_t::value, context, + generic_context, Char>>; + +template class basic_format_arg; +template class basic_format_args; + +// A separate type would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +/// Reports a format error at compile time or, via a `format_error` exception, +/// at runtime. +// This function is intentionally not constexpr to give a compile-time error. +FMT_NORETURN FMT_API void report_error(const char* message); + +enum class presentation_type : unsigned char { + // Common specifiers: + none = 0, + debug = 1, // '?' + string = 2, // 's' (string, bool) + + // Integral, bool and character specifiers: + dec = 3, // 'd' + hex, // 'x' or 'X' + oct, // 'o' + bin, // 'b' or 'B' + chr, // 'c' + + // String and pointer specifiers: + pointer = 3, // 'p' + + // Floating-point specifiers: + exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation) + fixed, // 'f' or 'F' + general, // 'g' or 'G' + hexfloat // 'a' or 'A' +}; + +enum class align { none, left, right, center, numeric }; +enum class sign { none, minus, plus, space }; +enum class arg_id_kind { none, index, name }; + +// Basic format specifiers for built-in and string types. +class basic_specs { + private: + // Data is arranged as follows: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |type |align| w | p | s |u|#|L| f | unused | + // +-----+-----+---+---+---+-+-+-+-----+---------------------------+ + // + // w - dynamic width info + // p - dynamic precision info + // s - sign + // u - uppercase (e.g. 'X' for 'x') + // # - alternate form ('#') + // L - localized + // f - fill size + // + // Bitfields are not used because of compiler bugs such as gcc bug 61414. + enum : unsigned { + type_mask = 0x00007, + align_mask = 0x00038, + width_mask = 0x000C0, + precision_mask = 0x00300, + sign_mask = 0x00C00, + uppercase_mask = 0x01000, + alternate_mask = 0x02000, + localized_mask = 0x04000, + fill_size_mask = 0x38000, + + align_shift = 3, + width_shift = 6, + precision_shift = 8, + sign_shift = 10, + fill_size_shift = 15, + + max_fill_size = 4 + }; + + unsigned data_ = 1 << fill_size_shift; + static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, ""); + + // Character (code unit) type is erased to prevent template bloat. + char fill_data_[max_fill_size] = {' '}; + + FMT_CONSTEXPR void set_fill_size(size_t size) { + data_ = (data_ & ~fill_size_mask) | + (static_cast(size) << fill_size_shift); + } + + public: + constexpr auto type() const -> presentation_type { + return static_cast(data_ & type_mask); + } + FMT_CONSTEXPR void set_type(presentation_type t) { + data_ = (data_ & ~type_mask) | static_cast(t); + } + + constexpr auto align() const -> align { + return static_cast((data_ & align_mask) >> align_shift); + } + FMT_CONSTEXPR void set_align(fmt::align a) { + data_ = (data_ & ~align_mask) | (static_cast(a) << align_shift); + } + + constexpr auto dynamic_width() const -> arg_id_kind { + return static_cast((data_ & width_mask) >> width_shift); + } + FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) { + data_ = (data_ & ~width_mask) | (static_cast(w) << width_shift); + } + + FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind { + return static_cast((data_ & precision_mask) >> + precision_shift); + } + FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) { + data_ = (data_ & ~precision_mask) | + (static_cast(p) << precision_shift); + } + + constexpr auto dynamic() const -> bool { + return (data_ & (width_mask | precision_mask)) != 0; + } + + constexpr auto sign() const -> sign { + return static_cast((data_ & sign_mask) >> sign_shift); + } + FMT_CONSTEXPR void set_sign(fmt::sign s) { + data_ = (data_ & ~sign_mask) | (static_cast(s) << sign_shift); + } + + constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; } + FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; } + + constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; } + FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; } + FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; } + + constexpr auto localized() const -> bool { + return (data_ & localized_mask) != 0; + } + FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; } + + constexpr auto fill_size() const -> size_t { + return (data_ & fill_size_mask) >> fill_size_shift; + } + + template ::value)> + constexpr auto fill() const -> const Char* { + return fill_data_; + } + template ::value)> + constexpr auto fill() const -> const Char* { + return nullptr; + } + + template constexpr auto fill_unit() const -> Char { + using uchar = unsigned char; + return static_cast(static_cast(fill_data_[0]) | + (static_cast(fill_data_[1]) << 8) | + (static_cast(fill_data_[2]) << 16)); + } + + FMT_CONSTEXPR void set_fill(char c) { + fill_data_[0] = c; + set_fill_size(1); + } + + template + FMT_CONSTEXPR void set_fill(basic_string_view s) { + auto size = s.size(); + set_fill_size(size); + if (size == 1) { + unsigned uchar = static_cast>(s[0]); + fill_data_[0] = static_cast(uchar); + fill_data_[1] = static_cast(uchar >> 8); + fill_data_[2] = static_cast(uchar >> 16); + return; + } + FMT_ASSERT(size <= max_fill_size, "invalid fill"); + for (size_t i = 0; i < size; ++i) + fill_data_[i & 3] = static_cast(s[i]); + } + + FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) { + set_fill_size(specs.fill_size()); + for (size_t i = 0; i < max_fill_size; ++i) + fill_data_[i] = specs.fill_data_[i]; + } +}; + +// Format specifiers for built-in and string types. +struct format_specs : basic_specs { + int width; + int precision; + + constexpr format_specs() : width(0), precision(-1) {} +}; + +/** + * Parsing context consisting of a format string range being parsed and an + * argument counter for automatic indexing. + */ +template class parse_context { + private: + basic_string_view fmt_; + int next_arg_id_; + + enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 }; + + FMT_CONSTEXPR void do_check_arg_id(int arg_id); + + public: + using char_type = Char; + using iterator = const Char*; + + constexpr explicit parse_context(basic_string_view fmt, + int next_arg_id = 0) + : fmt_(fmt), next_arg_id_(next_arg_id) {} + + /// Returns an iterator to the beginning of the format string range being + /// parsed. + constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); } + + /// Returns an iterator past the end of the format string range being parsed. + constexpr auto end() const noexcept -> iterator { return fmt_.end(); } + + /// Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator it) { + fmt_.remove_prefix(detail::to_unsigned(it - begin())); + } + + /// Reports an error if using the manual argument indexing; otherwise returns + /// the next argument index and switches to the automatic indexing. + FMT_CONSTEXPR auto next_arg_id() -> int { + if (next_arg_id_ < 0) { + report_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; + } + + /// Reports an error if using the automatic argument indexing; otherwise + /// switches to the manual indexing. + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + report_error("cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); + } + FMT_CONSTEXPR void check_arg_id(basic_string_view) { + next_arg_id_ = -1; + } + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); +}; + +#ifndef FMT_USE_LOCALE +# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) +#endif + +// A type-erased reference to std::locale to avoid the heavy include. +class locale_ref { +#if FMT_USE_LOCALE + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + + template + locale_ref(const Locale& loc) : locale_(&loc) { + // Check if std::isalpha is found via ADL to reduce the chance of misuse. + isalpha('x', loc); + } + + inline explicit operator bool() const noexcept { return locale_ != nullptr; } +#endif // FMT_USE_LOCALE + + public: + template auto get() const -> Locale; +}; + +FMT_END_EXPORT + +namespace detail { + +// Specifies if `T` is a code unit type. +template struct is_code_unit : std::false_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +#ifdef __cpp_char8_t +template <> struct is_code_unit : bool_constant {}; +#endif + +// Constructs fmt::basic_string_view from types implicitly convertible +// to it, deducing Char. Explicitly convertible types such as the ones returned +// from FMT_STRING are intentionally excluded. +template ::value)> +constexpr auto to_string_view(const Char* s) -> basic_string_view { + return s; +} +template ::value)> +constexpr auto to_string_view(const T& s) + -> basic_string_view { + return s; +} +template +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { + return s; +} + +template +struct has_to_string_view : std::false_type {}; +// detail:: is intentional since to_string_view is not an extension point. +template +struct has_to_string_view< + T, void_t()))>> + : std::true_type {}; + +/// String's character (code unit) type. detail:: is intentional to prevent ADL. +template ()))> +using char_t = typename V::value_type; + +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr auto is_integral_type(type t) -> bool { + return t > type::none_type && t <= type::last_integer_type; +} +constexpr auto is_arithmetic_type(type t) -> bool { + return t > type::none_type && t <= type::last_numeric_type; +} + +constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } +constexpr auto in(type t, int set) -> bool { + return ((set >> static_cast(t)) & 1) != 0; +} + +// Bitsets of types. +enum { + sint_set = + set(type::int_type) | set(type::long_long_type) | set(type::int128_type), + uint_set = set(type::uint_type) | set(type::ulong_long_type) | + set(type::uint128_type), + bool_set = set(type::bool_type), + char_set = set(type::char_type), + float_set = set(type::float_type) | set(type::double_type) | + set(type::long_double_type), + string_set = set(type::string_type), + cstring_set = set(type::cstring_type), + pointer_set = set(type::pointer_type) +}; + +struct view {}; + +template +struct is_view : std::false_type {}; +template +struct is_view> : std::is_base_of {}; + +template struct named_arg; +template struct is_named_arg : std::false_type {}; +template struct is_static_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template struct named_arg : view { + const Char* name; + const T& value; + + named_arg(const Char* n, const T& v) : name(n), value(v) {} + static_assert(!is_named_arg::value, "nested named arguments"); +}; + +template constexpr auto count() -> int { return B ? 1 : 0; } +template constexpr auto count() -> int { + return (B1 ? 1 : 0) + count(); +} + +template constexpr auto count_named_args() -> int { + return count::value...>(); +} +template constexpr auto count_static_named_args() -> int { + return count::value...>(); +} + +template struct named_arg_info { + const Char* name; + int id; +}; + +// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13. +template +FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, + int named_arg_index, + basic_string_view arg_name) { + for (int i = 0; i < named_arg_index; ++i) { + if (named_args[i].name == arg_name) report_error("duplicate named arg"); + } +} + +template ::value)> +void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { + ++arg_index; +} +template ::value)> +void init_named_arg(named_arg_info* named_args, int& arg_index, + int& named_arg_index, const T& arg) { + check_for_duplicate(named_args, named_arg_index, arg.name); + named_args[named_arg_index++] = {arg.name, arg_index++}; +} + +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info*, int& arg_index, + int&) { + ++arg_index; +} +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args, + int& arg_index, int& named_arg_index) { + check_for_duplicate(named_args, named_arg_index, T::name); + named_args[named_arg_index++] = {T::name, arg_index++}; +} + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +template +using format_as_result = + remove_cvref_t()))>; +template +using format_as_member_result = + remove_cvref_t::format_as(std::declval()))>; + +template +struct use_format_as : std::false_type {}; +// format_as member is only used to avoid injection into the std namespace. +template +struct use_format_as_member : std::false_type {}; + +// Only map owning types because mapping views can be unsafe. +template +struct use_format_as< + T, bool_constant>::value>> + : std::true_type {}; +template +struct use_format_as_member< + T, bool_constant>::value>> + : std::true_type {}; + +template > +using use_formatter = + bool_constant<(std::is_class::value || std::is_enum::value || + std::is_union::value || std::is_array::value) && + !has_to_string_view::value && !is_named_arg::value && + !use_format_as::value && !use_format_as_member::value>; + +template > +auto has_formatter_impl(T* p, buffered_context* ctx = nullptr) + -> decltype(formatter().format(*p, *ctx), std::true_type()); +template auto has_formatter_impl(...) -> std::false_type; + +// T can be const-qualified to check if it is const-formattable. +template constexpr auto has_formatter() -> bool { + return decltype(has_formatter_impl(static_cast(nullptr)))::value; +} + +// Maps formatting argument types to natively supported types or user-defined +// types with formatters. Returns void on errors to be SFINAE-friendly. +template struct type_mapper { + static auto map(signed char) -> int; + static auto map(unsigned char) -> unsigned; + static auto map(short) -> int; + static auto map(unsigned short) -> unsigned; + static auto map(int) -> int; + static auto map(unsigned) -> unsigned; + static auto map(long) -> long_type; + static auto map(unsigned long) -> ulong_type; + static auto map(long long) -> long long; + static auto map(unsigned long long) -> unsigned long long; + static auto map(int128_opt) -> int128_opt; + static auto map(uint128_opt) -> uint128_opt; + static auto map(bool) -> bool; + + template + static auto map(bitint) -> conditional_t; + template + static auto map(ubitint) + -> conditional_t; + + template ::value)> + static auto map(T) -> conditional_t< + std::is_same::value || std::is_same::value, Char, void>; + + static auto map(float) -> float; + static auto map(double) -> double; + static auto map(long double) -> long double; + + static auto map(Char*) -> const Char*; + static auto map(const Char*) -> const Char*; + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + static auto map(const T&) -> conditional_t::value, + basic_string_view, void>; + + static auto map(void*) -> const void*; + static auto map(const void*) -> const void*; + static auto map(volatile void*) -> const void*; + static auto map(const volatile void*) -> const void*; + static auto map(nullptr_t) -> const void*; + template ::value || + std::is_member_pointer::value)> + static auto map(const T&) -> void; + + template ::value)> + static auto map(const T& x) -> decltype(map(format_as(x))); + template ::value)> + static auto map(const T& x) -> decltype(map(formatter::format_as(x))); + + template ::value)> + static auto map(T&) -> conditional_t(), T&, void>; + + template ::value)> + static auto map(const T& named_arg) -> decltype(map(named_arg.value)); +}; + +// detail:: is used to workaround a bug in MSVC 2017. +template +using mapped_t = decltype(detail::type_mapper::map(std::declval())); + +// A type constant after applying type_mapper. +template +using mapped_type_constant = type_constant, Char>; + +template ::value> +using stored_type_constant = std::integral_constant< + type, Context::builtin_types || TYPE == type::int_type ? TYPE + : type::custom_type>; +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context : public parse_context { + private: + int num_args_; + const type* types_; + using base = parse_context; + + public: + FMT_CONSTEXPR explicit compile_parse_context(basic_string_view fmt, + int num_args, const type* types, + int next_arg_id = 0) + : base(fmt, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) report_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) report_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + ignore_unused(arg_id); + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + report_error("width/precision is not integer"); + } +}; + +// An argument reference. +template union arg_ref { + FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {} + FMT_CONSTEXPR arg_ref(basic_string_view n) : name(n) {} + + int index; + basic_string_view name; +}; + +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow reusing the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template struct dynamic_format_specs : format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +// Converts a character to ASCII. Returns '\0' on conversion failure. +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} + +// Returns the number of code units in a code point or 1 on error. +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto c = static_cast(*begin); + return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1; +} + +// 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 +FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + int digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); + if (num_digits <= digits10) return static_cast(value); + // Check for overflow. + unsigned max = INT_MAX; + return num_digits == digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} + +FMT_CONSTEXPR inline auto parse_align(char c) -> align { + switch (c) { + case '<': return align::left; + case '>': return align::right; + case '^': return align::center; + } + return align::none; +} + +template constexpr auto is_name_start(Char c) -> bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; +} + +template +FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + if (c != '0') + index = parse_nonnegative_int(begin, end, INT_MAX); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + report_error("invalid format string"); + else + handler.on_index(index); + return begin; + } + if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) { + report_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); + handler.on_name({begin, to_unsigned(it - begin)}); + return it; +} + +template struct dynamic_spec_handler { + parse_context& ctx; + arg_ref& ref; + arg_id_kind& kind; + + FMT_CONSTEXPR void on_index(int id) { + ref = id; + kind = arg_id_kind::index; + ctx.check_arg_id(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_name(basic_string_view id) { + ref = id; + kind = arg_id_kind::name; + ctx.check_arg_id(id); + } +}; + +template struct parse_dynamic_spec_result { + const Char* end; + arg_id_kind kind; +}; + +// Parses integer | "{" [arg_id] "}". +template +FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, + parse_context& ctx) + -> parse_dynamic_spec_result { + FMT_ASSERT(begin != end, ""); + auto kind = arg_id_kind::none; + if ('0' <= *begin && *begin <= '9') { + int val = parse_nonnegative_int(begin, end, -1); + if (val == -1) report_error("number is too big"); + value = val; + } else { + if (*begin == '{') { + ++begin; + if (begin != end) { + Char c = *begin; + if (c == '}' || c == ':') { + int id = ctx.next_arg_id(); + ref = id; + kind = arg_id_kind::index; + ctx.check_dynamic_spec(id); + } else { + begin = parse_arg_id(begin, end, + dynamic_spec_handler{ctx, ref, kind}); + } + } + if (begin != end && *begin == '}') return {++begin, kind}; + } + report_error("invalid format string"); + } + return {begin, kind}; +} + +template +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, + format_specs& specs, arg_ref& width_ref, + parse_context& ctx) -> const Char* { + auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + specs.set_dynamic_width(result.kind); + return result.end; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + format_specs& specs, + arg_ref& precision_ref, + parse_context& ctx) -> const Char* { + ++begin; + if (begin == end) { + report_error("invalid precision"); + return begin; + } + auto result = + parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx); + specs.set_dynamic_precision(result.kind); + return result.end; +} + +enum class state { start, align, sign, hash, zero, width, precision, locale }; + +// Parses standard format specifiers. +template +FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, + dynamic_format_specs& specs, + parse_context& ctx, type arg_type) + -> const Char* { + auto c = '\0'; + if (end - begin > 1) { + auto next = to_ascii(begin[1]); + c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; + } else { + if (begin == end) return begin; + c = to_ascii(*begin); + } + + struct { + state current_state = state::start; + FMT_CONSTEXPR void operator()(state s, bool valid = true) { + if (current_state >= s || !valid) + report_error("invalid format specifier"); + current_state = s; + } + } enter_state; + + using pres = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + struct { + const Char*& begin; + format_specs& specs; + type arg_type; + + FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { + if (!in(arg_type, set)) report_error("invalid format specifier"); + specs.set_type(pres_type); + return begin + 1; + } + } parse_presentation_type{begin, specs, arg_type}; + + for (;;) { + switch (c) { + case '<': + case '>': + case '^': + enter_state(state::align); + specs.set_align(parse_align(c)); + ++begin; + break; + case '+': + case ' ': + specs.set_sign(c == ' ' ? sign::space : sign::plus); + FMT_FALLTHROUGH; + case '-': + enter_state(state::sign, in(arg_type, sint_set | float_set)); + ++begin; + break; + case '#': + enter_state(state::hash, is_arithmetic_type(arg_type)); + specs.set_alt(); + ++begin; + break; + case '0': + enter_state(state::zero); + if (!is_arithmetic_type(arg_type)) + report_error("format specifier requires numeric argument"); + if (specs.align() == align::none) { + // Ignore 0 if align is specified for compatibility with std::format. + specs.set_align(align::numeric); + specs.set_fill('0'); + } + ++begin; + break; + // clang-format off + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '{': + // clang-format on + enter_state(state::width); + begin = parse_width(begin, end, specs, specs.width_ref, ctx); + break; + case '.': + enter_state(state::precision, + in(arg_type, float_set | string_set | cstring_set)); + begin = parse_precision(begin, end, specs, specs.precision_ref, ctx); + break; + case 'L': + enter_state(state::locale, is_arithmetic_type(arg_type)); + specs.set_localized(); + ++begin; + break; + case 'd': return parse_presentation_type(pres::dec, integral_set); + case 'X': specs.set_upper(); FMT_FALLTHROUGH; + case 'x': return parse_presentation_type(pres::hex, integral_set); + case 'o': return parse_presentation_type(pres::oct, integral_set); + case 'B': specs.set_upper(); FMT_FALLTHROUGH; + case 'b': return parse_presentation_type(pres::bin, integral_set); + case 'E': specs.set_upper(); FMT_FALLTHROUGH; + case 'e': return parse_presentation_type(pres::exp, float_set); + case 'F': specs.set_upper(); FMT_FALLTHROUGH; + case 'f': return parse_presentation_type(pres::fixed, float_set); + case 'G': specs.set_upper(); FMT_FALLTHROUGH; + case 'g': return parse_presentation_type(pres::general, float_set); + case 'A': specs.set_upper(); FMT_FALLTHROUGH; + case 'a': return parse_presentation_type(pres::hexfloat, float_set); + case 'c': + if (arg_type == type::bool_type) report_error("invalid format specifier"); + return parse_presentation_type(pres::chr, integral_set); + case 's': + return parse_presentation_type(pres::string, + bool_set | string_set | cstring_set); + case 'p': + return parse_presentation_type(pres::pointer, pointer_set | cstring_set); + case '?': + return parse_presentation_type(pres::debug, + char_set | string_set | cstring_set); + case '}': return begin; + default: { + if (*begin == '}') return begin; + // Parse fill and alignment. + auto fill_end = begin + code_point_length(begin); + if (end - fill_end <= 0) { + report_error("invalid format specifier"); + return begin; + } + if (*begin == '{') { + report_error("invalid fill character '{'"); + return begin; + } + auto alignment = parse_align(to_ascii(*fill_end)); + enter_state(state::align, alignment != align::none); + specs.set_fill( + basic_string_view(begin, to_unsigned(fill_end - begin))); + specs.set_align(alignment); + begin = fill_end + 1; + } + } + if (begin == end) return begin; + c = to_ascii(*begin); + } +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, + const Char* end, + Handler&& handler) + -> const Char* { + ++begin; + if (begin == end) { + handler.on_error("invalid format string"); + return end; + } + int arg_id = 0; + switch (*begin) { + case '}': + handler.on_replacement_field(handler.on_arg_id(), begin); + return begin + 1; + case '{': handler.on_text(begin, begin + 1); return begin + 1; + case ':': arg_id = handler.on_arg_id(); break; + default: { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + } adapter = {handler, 0}; + begin = parse_arg_id(begin, end, adapter); + arg_id = adapter.arg_id; + Char c = begin != end ? *begin : Char(); + if (c == '}') { + handler.on_replacement_field(arg_id, begin); + return begin + 1; + } + if (c != ':') { + handler.on_error("missing '}' in format string"); + return end; + } + break; + } + } + begin = handler.on_format_specs(arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; + return begin + 1; +} + +template +FMT_CONSTEXPR void parse_format_string(basic_string_view fmt, + Handler&& handler) { + auto begin = fmt.data(), end = begin + fmt.size(); + auto p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; + } + } + handler.on_text(begin, end); +} + +// Checks char specs and returns true iff the presentation type is char-like. +FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { + auto type = specs.type(); + if (type != presentation_type::none && type != presentation_type::chr && + type != presentation_type::debug) { + return false; + } + if (specs.align() == align::numeric || specs.sign() != sign::none || + specs.alt()) { + report_error("invalid format specifier for char"); + } + return true; +} + +// A base class for compile-time strings. +struct compile_string {}; + +template +FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). +FMT_CONSTEXPR auto invoke_parse(parse_context& ctx) -> const Char* { + using mapped_type = remove_cvref_t>; + constexpr bool formattable = + std::is_constructible>::value; + if (!formattable) return ctx.begin(); // Error is reported in the value ctor. + using formatted_type = conditional_t; + return formatter().parse(ctx); +} + +template struct arg_pack {}; + +template +class format_string_checker { + private: + type types_[max_of(1, NUM_ARGS)]; + named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; + compile_parse_context context_; + + using parse_func = auto (*)(parse_context&) -> const Char*; + parse_func parse_funcs_[max_of(1, NUM_ARGS)]; + + public: + template + FMT_CONSTEXPR explicit format_string_checker(basic_string_view fmt, + arg_pack) + : types_{mapped_type_constant::value...}, + named_args_{}, + context_(fmt, NUM_ARGS, types_), + parse_funcs_{&invoke_parse...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_static_named_arg(named_args_, arg_index, named_arg_index)); + ignore_unused(arg_index, named_arg_index); + } + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + context_.check_arg_id(id); + return id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { + for (int i = 0; i < NUM_NAMED_ARGS; ++i) { + if (named_args_[i].name == id) return named_args_[i].id; + } + if (!DYNAMIC_NAMES) on_error("argument not found"); + return -1; + } + + FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { + on_format_specs(id, begin, begin); // Call parse() on empty specs. + } + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + context_.advance_to(begin); + if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); + + // If id is out of range, it means we do not know the type and cannot parse + // the format at compile time. Instead, skip over content until we finish + // the format spec, accounting for any nested replacements. + for (int bracket_count = 0; + begin != end && (bracket_count > 0 || *begin != '}'); ++begin) { + if (*begin == '{') + ++bracket_count; + else if (*begin == '}') + --bracket_count; + } + return begin; + } + + FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) { + report_error(message); + } +}; + +/// A contiguous memory buffer with an optional growing ability. It is an +/// internal class and shouldn't be used directly, only via `memory_buffer`. +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + using grow_fun = void (*)(buffer& buf, size_t capacity); + grow_fun grow_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_MSC_WARNING(suppress : 26495) + FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept + : size_(sz), capacity_(sz), grow_(grow) {} + + constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0, + size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {} + + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; + + /// Sets the buffer data and capacity. + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } + + auto begin() const noexcept -> const T* { return ptr_; } + auto end() const noexcept -> const T* { return ptr_ + size_; } + + /// Returns the size of this buffer. + constexpr auto size() const noexcept -> size_t { return size_; } + + /// Returns the capacity of this buffer. + constexpr auto capacity() const noexcept -> size_t { return capacity_; } + + /// Returns a pointer to the buffer data (not null-terminated). + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } + + /// Clears this buffer. + FMT_CONSTEXPR void clear() { size_ = 0; } + + // Tries resizing the buffer to contain `count` elements. If T is a POD type + // the new elements may not be initialized. + FMT_CONSTEXPR void try_resize(size_t count) { + try_reserve(count); + size_ = min_of(count, capacity_); + } + + // Tries increasing the buffer capacity to `new_capacity`. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + FMT_CONSTEXPR void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow_(*this, new_capacity); + } + + FMT_CONSTEXPR void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /// Appends data to the end of the buffer. + template +// Workaround for MSVC2019 to fix error C2893: Failed to specialize function +// template 'void fmt::v11::detail::buffer::append(const U *,const U *)'. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940 + FMT_CONSTEXPR20 +#endif + void + append(const U* begin, const U* end) { + while (begin != end) { + auto size = size_; + auto free_cap = capacity_ - size; + auto count = to_unsigned(end - begin); + if (free_cap < count) { + grow_(*this, size + count); + size = size_; + free_cap = capacity_ - size; + count = count < free_cap ? count : free_cap; + } + // A loop is faster than memcpy on small sizes. + T* out = ptr_ + size; + for (size_t i = 0; i < count; ++i) out[i] = begin[i]; + size_ += count; + begin += count; + } + } + + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { + return ptr_[index]; + } +}; + +struct buffer_traits { + constexpr explicit buffer_traits(size_t) {} + constexpr auto count() const -> size_t { return 0; } + constexpr auto limit(size_t size) const -> size_t { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + constexpr auto count() const -> size_t { return count_; } + FMT_CONSTEXPR auto limit(size_t size) -> size_t { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return min_of(size, n); + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buffer_size) static_cast(buf).flush(); + } + + void flush() { + auto size = this->size(); + this->clear(); + const T* begin = data_; + const T* end = begin + this->limit(size); + while (begin != end) *out_++ = *begin++; + } + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), buffer(grow, data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : Traits(other), + buffer(grow, data_, 0, buffer_size), + out_(other.out_) {} + ~iterator_buffer() { + // Don't crash if flush fails during unwinding. + FMT_TRY { flush(); } + FMT_CATCH(...) {} + } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template +class iterator_buffer : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buf.capacity()) + static_cast(buf).flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(grow, out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : fixed_buffer_traits(other), + buffer(static_cast(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + +template class iterator_buffer : public buffer { + public: + explicit iterator_buffer(T* out, size_t = 0) + : buffer([](buffer&, size_t) {}, out, 0, ~size_t()) {} + + auto out() -> T* { return &*this->end(); } +}; + +template +class container_buffer : public buffer { + private: + using value_type = typename Container::value_type; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t capacity) { + auto& self = static_cast(buf); + self.container.resize(capacity); + self.set(&self.container[0], capacity); + } + + public: + Container& container; + + explicit container_buffer(Container& c) + : buffer(grow, c.size()), container(c) {} +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer< + OutputIt, + enable_if_t::value && + is_contiguous::value, + typename OutputIt::container_type::value_type>> + : public container_buffer { + private: + using base = container_buffer; + + public: + explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {} + explicit iterator_buffer(OutputIt out, size_t = 0) + : base(get_container(out)) {} + + auto out() -> OutputIt { return OutputIt(this->container); } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() != buffer_size) return; + static_cast(buf).count_ += buf.size(); + buf.clear(); + } + + public: + FMT_CONSTEXPR counting_buffer() : buffer(grow, data_, 0, buffer_size) {} + + constexpr auto count() const noexcept -> size_t { + return count_ + this->size(); + } +}; + +template +struct is_back_insert_iterator> : std::true_type {}; + +template +struct has_back_insert_iterator_container_append : std::false_type {}; +template +struct has_back_insert_iterator_container_append< + OutputIt, InputIt, + void_t()) + .append(std::declval(), + std::declval()))>> : std::true_type {}; + +template +struct has_back_insert_iterator_container_insert_at_end : std::false_type {}; + +template +struct has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt, + void_t()) + .insert(get_container(std::declval()).end(), + std::declval(), + std::declval()))>> : std::true_type {}; + +// An optimized version of std::copy with the output value type (T). +template ::value&& + has_back_insert_iterator_container_append< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + get_container(out).append(begin, end); + return out; +} + +template ::value && + !has_back_insert_iterator_container_append< + OutputIt, InputIt>::value && + has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + auto& c = get_container(out); + c.insert(c.end(), begin, end); + return out; +} + +template ::value && + (has_back_insert_iterator_container_append< + OutputIt, InputIt>::value || + has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt>::value)))> +FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template +FMT_CONSTEXPR auto copy(basic_string_view s, OutputIt out) -> OutputIt { + return copy(s.begin(), s.end(), out); +} + +template +struct is_buffer_appender : std::false_type {}; +template +struct is_buffer_appender< + It, bool_constant< + is_back_insert_iterator::value && + std::is_base_of, + typename It::container_type>::value>> + : std::true_type {}; + +// Maps an output iterator to a buffer. +template ::value)> +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); +} +template ::value)> +auto get_buffer(OutputIt out) -> buffer& { + return get_container(out); +} + +template +auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { + return buf.out(); +} +template +auto get_iterator(buffer&, OutputIt out) -> OutputIt { + return out; +} + +// This type is intentionally undefined, only used for errors. +template struct type_is_unformattable_for; + +template struct string_value { + const Char* data; + size_t size; + auto str() const -> basic_string_view { return {data, size}; } +}; + +template struct custom_value { + using char_type = typename Context::char_type; + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +struct custom_tag {}; + +#if !FMT_BUILTIN_TYPES +# define FMT_BUILTIN , monostate +#else +# define FMT_BUILTIN +#endif + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + monostate no_value; + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_opt int128_value; + uint128_opt uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(signed char x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(signed short x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(int x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {} + FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {} + FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN) + : value(ulong_type(x)) {} + constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {} + constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN) + : ulong_long_value(x) {} + FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {} + FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {} + constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {} + + template + constexpr FMT_INLINE value(bitint x FMT_BUILTIN) : long_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + template + constexpr FMT_INLINE value(ubitint x FMT_BUILTIN) : ulong_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + + template ::value)> + constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + } + + constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {} + constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {} + FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {} + + FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + FMT_CONSTEXPR value(const T& x FMT_BUILTIN) { + static_assert(std::is_same::value, + "mixing character types is disallowed"); + auto sv = to_string_view(x); + string.data = sv.data(); + string.size = sv.size(); + } + FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(const volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(nullptr_t) : pointer(nullptr) {} + + template ::value || + std::is_member_pointer::value)> + value(const T&) { + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` printed as bool by iostreams. + static_assert(sizeof(T) == 0, + "formatting of non-void pointers is disallowed"); + } + + template ::value)> + value(const T& x) : value(format_as(x)) {} + template ::value)> + value(const T& x) : value(formatter::format_as(x)) {} + + template ::value)> + value(const T& named_arg) : value(named_arg.value) {} + + template ::value || !FMT_BUILTIN_TYPES)> + FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {} + + FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + private: + template ())> + FMT_CONSTEXPR value(T& x, custom_tag) { + using value_type = remove_const_t; + // T may overload operator& e.g. std::vector::reference in libc++. + if (!is_constant_evaluated()) { + custom.value = + const_cast(&reinterpret_cast(x)); + } else { + custom.value = nullptr; +#if defined(__cpp_if_constexpr) + if constexpr (std::is_same*>::value) + custom.value = const_cast(&x); +#endif + } + custom.format = format_custom; + } + + template ())> + FMT_CONSTEXPR value(const T&, custom_tag) { + // Cannot format an argument; to make type T formattable provide a + // formatter specialization: https://fmt.dev/latest/api.html#udt. + type_is_unformattable_for _; + } + + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom(void* arg, parse_context& parse_ctx, + Context& ctx) { + auto f = formatter(); + parse_ctx.advance_to(f.parse(parse_ctx)); + using qualified_type = + conditional_t(), const T, T>; + // format must be const for compatibility with std::format and compilation. + const auto& cf = f; + ctx.advance_to(cf.format(*static_cast(arg), ctx)); + } +}; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; + +template +struct is_output_iterator : std::false_type {}; + +template <> struct is_output_iterator : std::true_type {}; + +template +struct is_output_iterator< + It, T, + enable_if_t&>()++), + T>::value>> : std::true_type {}; + +template constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template +constexpr auto encode_types() -> unsigned long long { + return static_cast(stored_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +constexpr auto make_descriptor() -> unsigned long long { + return NUM_ARGS <= max_packed_args ? encode_types() + : is_unpacked_bit | NUM_ARGS; +} + +template +using arg_t = conditional_t, + basic_format_arg>; + +template +struct named_arg_store { + // args_[0].named_args points to named_args to avoid bloating format_args. + arg_t args[1u + NUM_ARGS]; + named_arg_info + named_args[static_cast(NUM_NAMED_ARGS)]; + + template + FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) + : args{{named_args, NUM_NAMED_ARGS}, values...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_named_arg(named_args, arg_index, named_arg_index, values)); + } + + named_arg_store(named_arg_store&& rhs) { + args[0] = {named_args, NUM_NAMED_ARGS}; + for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i) + args[i] = rhs.args[i]; + for (size_t i = 0; i < NUM_NAMED_ARGS; ++i) + named_args[i] = rhs.named_args[i]; + } + + named_arg_store(const named_arg_store& rhs) = delete; + auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete; + auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete; + operator const arg_t*() const { return args + 1; } +}; + +// An array of references to arguments. It can be implicitly converted to +// `basic_format_args` for passing into type-erased formatting functions +// such as `vformat`. It is a plain struct to reduce binary size in debug mode. +template +struct format_arg_store { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + using type = + conditional_t[max_of(1, NUM_ARGS)], + named_arg_store>; + type args; +}; + +// TYPE can be different from type_constant, e.g. for __float128. +template struct native_formatter { + private: + dynamic_format_specs specs_; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); + if (const_check(TYPE == type::char_type)) check_char_specs(specs_); + return end; + } + + template + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.set_type(set ? presentation_type::debug : presentation_type::none); + } + + FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline") + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +template +struct locking + : bool_constant::value == type::custom_type> {}; +template +struct locking>::nonlocking>> + : std::false_type {}; + +template FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value; +} +template +FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value || is_locking(); +} + +FMT_API void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc = {}); + +#if FMT_WIN32 +FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); +#else // format_args is passed by reference since it is defined later. +inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} +#endif +} // namespace detail + +// The main public API. + +template +FMT_CONSTEXPR void parse_context::do_check_arg_id(int arg_id) { + // Argument id is only checked at compile time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && use_constexpr_cast) { + auto ctx = static_cast*>(this); + if (arg_id >= ctx->num_args()) report_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void parse_context::check_dynamic_spec(int arg_id) { + using detail::compile_parse_context; + if (detail::is_constant_evaluated() && use_constexpr_cast) + static_cast*>(this)->check_dynamic_spec(arg_id); +} + +FMT_BEGIN_EXPORT + +// An output iterator that appends to a buffer. It is used instead of +// back_insert_iterator to reduce symbol sizes and avoid dependency. +template class basic_appender { + protected: + detail::buffer* container; + + public: + using container_type = detail::buffer; + + FMT_CONSTEXPR basic_appender(detail::buffer& buf) : container(&buf) {} + + FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& { + container->push_back(c); + return *this; + } + FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; } +}; + +// A formatting argument. Context is a template parameter for the compiled API +// where output can be unbuffered. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + friend class basic_format_args; + + using char_type = typename Context::char_type; + + public: + class handle { + private: + detail::custom_value custom_; + + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(parse_context& parse_ctx, Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + template + basic_format_arg(T&& val) + : value_(val), type_(detail::stored_type_constant::value) {} + + constexpr explicit operator bool() const noexcept { + return type_ != detail::type::none_type; + } + auto type() const -> detail::type { return type_; } + + /** + * Visits an argument dispatching to the appropriate visit method based on + * the argument type. For example, if the argument type is `double` then + * `vis(value)` will be called with the value of type `double`. + */ + template + FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) { + using detail::map; + switch (type_) { + case detail::type::none_type: break; + case detail::type::int_type: return vis(value_.int_value); + case detail::type::uint_type: return vis(value_.uint_value); + case detail::type::long_long_type: return vis(value_.long_long_value); + case detail::type::ulong_long_type: return vis(value_.ulong_long_value); + case detail::type::int128_type: return vis(map(value_.int128_value)); + case detail::type::uint128_type: return vis(map(value_.uint128_value)); + case detail::type::bool_type: return vis(value_.bool_value); + case detail::type::char_type: return vis(value_.char_value); + case detail::type::float_type: return vis(value_.float_value); + case detail::type::double_type: return vis(value_.double_value); + case detail::type::long_double_type: return vis(value_.long_double_value); + case detail::type::cstring_type: return vis(value_.string.data); + case detail::type::string_type: return vis(value_.string.str()); + case detail::type::pointer_type: return vis(value_.pointer); + case detail::type::custom_type: return vis(handle(value_.custom)); + } + return vis(monostate()); + } + + auto format_custom(const char_type* parse_begin, + parse_context& parse_ctx, Context& ctx) + -> bool { + if (type_ != detail::type::custom_type) return false; + parse_ctx.advance_to(parse_begin); + value_.custom.format(value_.custom.value, parse_ctx, ctx); + return true; + } +}; + +/** + * A view of a collection of formatting arguments. To avoid lifetime issues it + * should only be used as a parameter type in type-erased functions such as + * `vformat`: + * + * void vlog(fmt::string_view fmt, fmt::format_args args); // OK + * fmt::format_args args = fmt::make_format_args(); // Dangling reference + */ +template class basic_format_args { + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const detail::value* values_; + const basic_format_arg* args_; + }; + + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + constexpr auto has_named_args() const -> bool { + return (desc_ & detail::has_named_args_bit) != 0; + } + + FMT_CONSTEXPR auto type(int index) const -> detail::type { + int shift = index * detail::packed_arg_bits; + unsigned mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + template + using store = + detail::format_arg_store; + + public: + using format_arg = basic_format_arg; + + constexpr basic_format_args() : desc_(0), args_(nullptr) {} + + /// Constructs a `basic_format_args` object from `format_arg_store`. + template + constexpr FMT_ALWAYS_INLINE basic_format_args( + const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + values_(s.args) {} + + template detail::max_packed_args)> + constexpr basic_format_args(const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + args_(s.args) {} + + /// Constructs a `basic_format_args` object from a dynamic list of arguments. + constexpr basic_format_args(const format_arg* args, int count, + bool has_named = false) + : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) | + (has_named ? +detail::has_named_args_bit : 0)), + args_(args) {} + + /// Returns the argument with the specified id. + FMT_CONSTEXPR auto get(int id) const -> format_arg { + auto arg = format_arg(); + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (static_cast(id) >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ != detail::type::none_type) arg.value_ = values_[id]; + return arg; + } + + template + auto get(basic_string_view name) const -> format_arg { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template + FMT_CONSTEXPR auto get_id(basic_string_view name) const -> int { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + auto max_size() const -> int { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +// A formatting context. +class context { + private: + appender out_; + format_args args_; + FMT_NO_UNIQUE_ADDRESS locale_ref loc_; + + public: + using char_type = char; ///< The character type for the output. + using iterator = appender; + using format_arg = basic_format_arg; + enum { builtin_types = FMT_BUILTIN_TYPES }; + + /// Constructs a `context` object. References to the arguments are stored + /// in the object so make sure they have appropriate lifetimes. + FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {}) + : out_(out), args_(args), loc_(loc) {} + context(context&&) = default; + context(const context&) = delete; + void operator=(const context&) = delete; + + FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); } + inline auto arg(string_view name) const -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(string_view name) const -> int { + return args_.get_id(name); + } + auto args() const -> const format_args& { return args_; } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() const -> iterator { return out_; } + + // Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator) {} + + FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; } +}; + +template struct runtime_format_string { + basic_string_view str; +}; + +/** + * Creates a runtime format string. + * + * **Example**: + * + * // Check format string at runtime instead of compile-time. + * fmt::print(fmt::runtime("{:d}"), "I am not a number"); + */ +inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } + +/// A compile-time format string. Use `format_string` in the public API to +/// prevent type deduction. +template struct fstring { + private: + static constexpr int num_static_named_args = + detail::count_static_named_args(); + + using checker = detail::format_string_checker< + char, static_cast(sizeof...(T)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; + + public: + string_view str; + using t = fstring; + + // Reports a compile-time error if S is not a valid format string for T. + template + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { + using namespace detail; + static_assert(count<(is_view>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) { + auto sv = string_view(str); + if (FMT_USE_CONSTEVAL) + detail::parse_format_string(sv, checker(sv, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value&& + std::is_same::value)> + FMT_ALWAYS_INLINE fstring(const S&) : str(S()) { + FMT_CONSTEXPR auto sv = string_view(S()); + FMT_CONSTEXPR int unused = + (parse_format_string(sv, checker(sv, arg_pack())), 0); + detail::ignore_unused(unused); + } + fstring(runtime_format_string<> fmt) : str(fmt.str) {} + + // Returning by reference generates better code in debug mode. + FMT_ALWAYS_INLINE operator const string_view&() const { return str; } + auto get() const -> string_view { return str; } +}; + +template using format_string = typename fstring::t; + +template +using is_formattable = bool_constant::value, int*, T>, Char>, + void>::value>; +#ifdef __cpp_concepts +template +concept formattable = is_formattable, Char>::value; +#endif + +// A formatter specialization for natively supported types. +template +struct formatter::value != + detail::type::custom_type>> + : detail::native_formatter::value> { +}; + +/** + * Constructs an object that stores references to arguments and can be + * implicitly converted to `format_args`. `Context` can be omitted in which case + * it defaults to `context`. See `arg` for lifetime considerations. + */ +// Take arguments by lvalue references to avoid some lifetime issues, e.g. +// auto args = make_format_args(std::string()); +template (), + unsigned long long DESC = detail::make_descriptor()> +constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args) + -> detail::format_arg_store { + // Suppress warnings for pathological types convertible to detail::value. + FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion") + return {{args...}}; +} + +template +using vargs = + detail::format_arg_store(), + detail::make_descriptor()>; + +/** + * Returns a named argument to be used in a formatting function. + * It should only be used in a call to a formatting function. + * + * **Example**: + * + * fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); + */ +template +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { + return {name, arg}; +} + +/// Formats a string and writes the output to `out`. +template , + char>::value)> +auto vformat_to(OutputIt&& out, string_view fmt, format_args args) + -> remove_cvref_t { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf, out); +} + +/** + * Formats `args` according to specifications in `fmt`, writes the result to + * the output iterator `out` and returns the iterator past the end of the output + * range. `format_to` does not append a terminating null character. + * + * **Example**: + * + * auto out = std::vector(); + * fmt::format_to(std::back_inserter(out), "{}", 42); + */ +template , + char>::value)> +FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) + -> remove_cvref_t { + return vformat_to(out, fmt.str, vargs{{args...}}); +} + +template struct format_to_n_result { + /// Iterator past the end of the output range. + OutputIt out; + /// Total (not truncated) output size. + size_t size; +}; + +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); + return {buf.out(), buf.count()}; +} + +/** + * Formats `args` according to specifications in `fmt`, writes up to `n` + * characters of the result to the output iterator `out` and returns the total + * (not truncated) output size and the iterator past the end of the output + * range. `format_to_n` does not append a terminating null character. + */ +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + T&&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt.str, vargs{{args...}}); +} + +struct format_to_result { + /// Pointer to just after the last successful write in the array. + char* out; + /// Specifies if the output was truncated. + bool truncated; + + FMT_CONSTEXPR operator char*() const { + // Report truncation to prevent silent data loss. + if (truncated) report_error("output is truncated"); + return out; + } +}; + +template +auto vformat_to(char (&out)[N], string_view fmt, format_args args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt, args); + return {result.out, result.size > N}; +} + +template +FMT_INLINE auto format_to(char (&out)[N], format_string fmt, T&&... args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt.str, vargs{{args...}}); + return {result.out, result.size > N}; +} + +/// Returns the number of chars in the output of `format(fmt, args...)`. +template +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, {}); + return buf.count(); +} + +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(FILE* f, string_view fmt, format_args args); +FMT_API void vprintln(FILE* f, string_view fmt, format_args args); +FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::print("The answer is {}.", 42); + */ +template +FMT_INLINE void print(format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(stdout, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(stdout, fmt.str, va) + : vprint(fmt.str, va); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the + * output to the file `f`. + * + * **Example**: + * + * fmt::print(stderr, "Don't {}!", "panic"); + */ +template +FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(f, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(f, fmt.str, va) + : vprint(f, fmt.str, va); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to the file `f` followed by a newline. +template +FMT_INLINE void println(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + return detail::const_check(detail::use_utf8) + ? vprintln(f, fmt.str, va) + : detail::vprint_mojibake(f, fmt.str, va, true); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to `stdout` followed by a newline. +template +FMT_INLINE void println(format_string fmt, T&&... args) { + return fmt::println(stdout, fmt, static_cast(args)...); +} + +FMT_PRAGMA_GCC(diagnostic pop) +FMT_PRAGMA_CLANG(diagnostic pop) +FMT_PRAGMA_GCC(pop_options) +FMT_END_EXPORT +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif +#endif // FMT_BASE_H_ diff --git a/deps/fmt/include/fmt/chrono.h b/deps/fmt/include/fmt/chrono.h index ff3e1445b..9fbeeed67 100644 --- a/deps/fmt/include/fmt/chrono.h +++ b/deps/fmt/include/fmt/chrono.h @@ -8,52 +8,22 @@ #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ -#include -#include -#include // std::isfinite -#include // std::memcpy -#include -#include -#include -#include -#include +#ifndef FMT_MODULE +# include +# include +# include // std::isfinite +# include // std::memcpy +# include +# include +# include +# include +# include +#endif #include "format.h" FMT_BEGIN_NAMESPACE -// Check if std::chrono::local_t is available. -#ifndef FMT_USE_LOCAL_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_LOCAL_TIME 0 -# endif -#endif - -// Check if std::chrono::utc_timestamp is available. -#ifndef FMT_USE_UTC_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_UTC_TIME 0 -# endif -#endif - -// Enable tzset. -#ifndef FMT_USE_TZSET -// UWP doesn't provide _tzset. -# if FMT_HAS_INCLUDE("winapifamily.h") -# include -# endif -# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ - (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) -# define FMT_USE_TZSET 1 -# else -# define FMT_USE_TZSET 0 -# endif -#endif - // Enable safe chrono durations, unless explicitly disabled. #ifndef FMT_SAFE_DURATION_CAST # define FMT_SAFE_DURATION_CAST 1 @@ -68,11 +38,13 @@ FMT_BEGIN_NAMESPACE // Copyright Paul Dreik 2019 namespace safe_duration_cast { +// DEPRECATED! template ::value && std::numeric_limits::is_signed == std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; @@ -93,15 +65,14 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { return static_cast(from); } -/** - * converts From to To, without loss. If the dynamic value of from - * can't be converted to To without loss, ec is set. - */ +/// Converts From to To, without loss. If the dynamic value of from +/// can't be converted to To without loss, ec is set. template ::value && std::numeric_limits::is_signed != std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; @@ -133,7 +104,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { template ::value)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; return from; } // function @@ -154,7 +126,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { // clang-format on template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; using T = std::numeric_limits; static_assert(std::is_floating_point::value, "From must be floating"); @@ -176,85 +148,20 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; static_assert(std::is_floating_point::value, "From must be floating"); return from; } -/** - * safe duration cast between integral durations - */ -template ::value), - FMT_ENABLE_IF(std::is_integral::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { - using From = std::chrono::duration; - ec = 0; - // the basic idea is that we need to convert from count() in the from type - // to count() in the To type, by multiplying it with this: - struct Factor - : std::ratio_divide {}; - - static_assert(Factor::num > 0, "num must be positive"); - static_assert(Factor::den > 0, "den must be positive"); - - // the conversion is like this: multiply from.count() with Factor::num - // /Factor::den and convert it to To::rep, all this without - // overflow/underflow. let's start by finding a suitable type that can hold - // both To, From and Factor::num - using IntermediateRep = - typename std::common_type::type; - - // safe conversion to IntermediateRep - IntermediateRep count = - lossless_integral_conversion(from.count(), ec); - if (ec) return {}; - // multiply with Factor::num without overflow or underflow - if (detail::const_check(Factor::num != 1)) { - const auto max1 = detail::max_value() / Factor::num; - if (count > max1) { - ec = 1; - return {}; - } - const auto min1 = - (std::numeric_limits::min)() / Factor::num; - if (detail::const_check(!std::is_unsigned::value) && - count < min1) { - ec = 1; - return {}; - } - count *= Factor::num; - } - - if (detail::const_check(Factor::den != 1)) count /= Factor::den; - auto tocount = lossless_integral_conversion(count, ec); - return ec ? To() : To(tocount); -} - -/** - * safe duration_cast between floating point durations - */ +/// Safe duration_cast between floating point durations template ::value), FMT_ENABLE_IF(std::is_floating_point::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { +auto safe_duration_cast(std::chrono::duration from, + int& ec) -> To { using From = std::chrono::duration; ec = 0; - if (std::isnan(from.count())) { - // nan in, gives nan out. easy. - return To{std::numeric_limits::quiet_NaN()}; - } - // maybe we should also check if from is denormal, and decide what to do about - // it. - - // +-inf should be preserved. - if (std::isinf(from.count())) { - return To{from.count()}; - } // the basic idea is that we need to convert from count() in the from type // to count() in the To type, by multiplying it with this: @@ -315,45 +222,114 @@ To safe_duration_cast(std::chrono::duration from, } // namespace safe_duration_cast #endif +namespace detail { + +// Check if std::chrono::utc_time is available. +#ifdef FMT_USE_UTC_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_UTC_TIME 0 +#endif +#if FMT_USE_UTC_TIME +using utc_clock = std::chrono::utc_clock; +#else +struct utc_clock { + template void to_sys(T); +}; +#endif + +// Check if std::chrono::local_time is available. +#ifdef FMT_USE_LOCAL_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_LOCAL_TIME 0 +#endif +#if FMT_USE_LOCAL_TIME +using local_t = std::chrono::local_t; +#else +struct local_t {}; +#endif + +} // namespace detail + +template +using sys_time = std::chrono::time_point; + +template +using utc_time = std::chrono::time_point; + +template +using local_time = std::chrono::time_point; + +namespace detail { + // Prevents expansion of a preceding token as a function-style macro. // Usage: f FMT_NOMACRO() #define FMT_NOMACRO -namespace detail { template struct null {}; -inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } -inline null<> localtime_s(...) { return null<>(); } -inline null<> gmtime_r(...) { return null<>(); } -inline null<> gmtime_s(...) { return null<>(); } +inline auto gmtime_r(...) -> null<> { return null<>(); } +inline auto gmtime_s(...) -> null<> { return null<>(); } -inline const std::locale& get_classic_locale() { +// It is defined here and not in ostream.h because the latter has expensive +// includes. +template class formatbuf : public StreamBuf { + private: + using char_type = typename StreamBuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename StreamBuf::int_type; + using traits_type = typename StreamBuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + +inline auto get_classic_locale() -> const std::locale& { static const auto& locale = std::locale::classic(); return locale; } template struct codecvt_result { - static constexpr const size_t max_size = 32; + static constexpr size_t max_size = 32; CodeUnit buf[max_size]; CodeUnit* end; }; -template -constexpr const size_t codecvt_result::max_size; template -void write_codecvt(codecvt_result& out, string_view in_buf, +void write_codecvt(codecvt_result& out, string_view in, const std::locale& loc) { -#if FMT_CLANG_VERSION -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated" + FMT_PRAGMA_CLANG(diagnostic push) + FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated") auto& f = std::use_facet>(loc); -# pragma clang diagnostic pop -#else - auto& f = std::use_facet>(loc); -#endif + FMT_PRAGMA_CLANG(diagnostic pop) auto mb = std::mbstate_t(); const char* from_next = nullptr; - auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, - std::begin(out.buf), std::end(out.buf), out.end); + auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf), + std::end(out.buf), out.end); if (result != std::codecvt_base::ok) FMT_THROW(format_error("failed to format time")); } @@ -361,11 +337,12 @@ void write_codecvt(codecvt_result& out, string_view in_buf, template auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) -> OutputIt { - if (detail::is_utf8() && loc != get_classic_locale()) { + if (const_check(detail::use_utf8) && loc != get_classic_locale()) { // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // gcc-4. -#if FMT_MSC_VERSION != 0 || \ - (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) +#if FMT_MSC_VERSION != 0 || \ + (defined(__GLIBCXX__) && \ + (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // and newer. using code_unit = wchar_t; @@ -381,9 +358,9 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) to_utf8>(); if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) FMT_THROW(format_error("failed to format time")); - return copy_str(u.c_str(), u.c_str() + u.size(), out); + return copy(u.c_str(), u.c_str() + u.size(), out); } - return copy_str(in.data(), in.data() + in.size(), out); + return copy(in.data(), in.data() + in.size(), out); } template OutputIt { codecvt_result unit; write_codecvt(unit, sv, loc); - return copy_str(unit.buf, unit.end, out); + return copy(unit.buf, unit.end, out); } template & buf, const std::tm& time, auto&& format_buf = formatbuf>(buf); auto&& os = std::basic_ostream(&format_buf); os.imbue(loc); - using iterator = std::ostreambuf_iterator; - const auto& facet = std::use_facet>(loc); + const auto& facet = std::use_facet>(loc); auto end = facet.put(os, os, Char(' '), &time, format, modifier); if (end.failed()) FMT_THROW(format_error("failed to format time")); } @@ -432,87 +408,118 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc, return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } +template +using is_similar_arithmetic_type = + bool_constant<(std::is_integral::value && std::is_integral::value) || + (std::is_floating_point::value && + std::is_floating_point::value)>; + +FMT_NORETURN inline void throw_duration_error() { + FMT_THROW(format_error("cannot format duration")); +} + +// Cast one integral duration to another with an overflow check. +template ::value&& + std::is_integral::value)> +auto duration_cast(std::chrono::duration from) -> To { +#if !FMT_SAFE_DURATION_CAST + return std::chrono::duration_cast(from); +#else + // The conversion factor: to.count() == factor * from.count(). + using factor = std::ratio_divide; + + using common_rep = typename std::common_type::type; + common_rep count = from.count(); // This conversion is lossless. + + // Multiply from.count() by factor and check for overflow. + if (const_check(factor::num != 1)) { + if (count > max_value() / factor::num) throw_duration_error(); + const auto min = (std::numeric_limits::min)() / factor::num; + if (const_check(!std::is_unsigned::value) && count < min) + throw_duration_error(); + count *= factor::num; + } + if (const_check(factor::den != 1)) count /= factor::den; + int ec = 0; + auto to = + To(safe_duration_cast::lossless_integral_conversion( + count, ec)); + if (ec) throw_duration_error(); + return to; +#endif +} + +template ::value&& + std::is_floating_point::value)> +auto duration_cast(std::chrono::duration from) -> To { +#if FMT_SAFE_DURATION_CAST + // Preserve infinity and NaN. + if (!isfinite(from.count())) return static_cast(from.count()); + // Throwing version of safe_duration_cast is only available for + // integer to integer or float to float casts. + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) throw_duration_error(); + return to; +#else + // Standard duration cast, may overflow. + return std::chrono::duration_cast(from); +#endif +} + +template ::value)> +auto duration_cast(std::chrono::duration from) -> To { + // Mixed integer <-> float cast is not supported by safe duration_cast. + return std::chrono::duration_cast(from); +} + +template +auto to_time_t(sys_time time_point) -> std::time_t { + // Cannot use std::chrono::system_clock::to_time_t since this would first + // require a cast to std::chrono::system_clock::time_point, which could + // overflow. + return detail::duration_cast>( + time_point.time_since_epoch()) + .count(); +} + } // namespace detail FMT_BEGIN_EXPORT /** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in local time. Unlike ``std::localtime``, this function is - thread-safe on most platforms. + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this + * function is thread-safe on most platforms. */ -inline std::tm localtime(std::time_t time) { +inline auto gmtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; - dispatcher(std::time_t t) : time_(t) {} + inline dispatcher(std::time_t t) : time_(t) {} - bool run() { - using namespace fmt::detail; - return handle(localtime_r(&time_, &tm_)); - } - - bool handle(std::tm* tm) { return tm != nullptr; } - - bool handle(detail::null<>) { - using namespace fmt::detail; - return fallback(localtime_s(&tm_, &time_)); - } - - bool fallback(int res) { return res == 0; } - -#if !FMT_MSC_VERSION - bool fallback(detail::null<>) { - using namespace fmt::detail; - std::tm* tm = std::localtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } -#endif - }; - dispatcher lt(time); - // Too big time values may be unsupported. - if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); - return lt.tm_; -} - -#if FMT_USE_LOCAL_TIME -template -inline auto localtime(std::chrono::local_time time) -> std::tm { - return localtime(std::chrono::system_clock::to_time_t( - std::chrono::current_zone()->to_sys(time))); -} -#endif - -/** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this - function is thread-safe on most platforms. - */ -inline std::tm gmtime(std::time_t time) { - struct dispatcher { - std::time_t time_; - std::tm tm_; - - dispatcher(std::time_t t) : time_(t) {} - - bool run() { + inline auto run() -> bool { using namespace fmt::detail; return handle(gmtime_r(&time_, &tm_)); } - bool handle(std::tm* tm) { return tm != nullptr; } + inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - bool handle(detail::null<>) { + inline auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(gmtime_s(&tm_, &time_)); } - bool fallback(int res) { return res == 0; } + inline auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - bool fallback(detail::null<>) { + inline auto fallback(detail::null<>) -> bool { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; return tm != nullptr; @@ -525,9 +532,9 @@ inline std::tm gmtime(std::time_t time) { return gt.tm_; } -inline std::tm gmtime( - std::chrono::time_point time_point) { - return gmtime(std::chrono::system_clock::to_time_t(time_point)); +template +inline auto gmtime(sys_time time_point) -> std::tm { + return gmtime(detail::to_time_t(time_point)); } namespace detail { @@ -556,7 +563,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, // Add ASCII '0' to each digit byte and insert separators. digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); - constexpr const size_t len = 8; + constexpr size_t len = 8; if (const_check(is_big_endian())) { char tmp[len]; std::memcpy(tmp, &digits, len); @@ -566,12 +573,14 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, } } -template FMT_CONSTEXPR inline const char* get_units() { +template +FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "as"; if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; if (std::is_same::value) return "ns"; - if (std::is_same::value) return "µs"; + if (std::is_same::value) + return detail::use_utf8 ? "µs" : "us"; if (std::is_same::value) return "ms"; if (std::is_same::value) return "cs"; if (std::is_same::value) return "ds"; @@ -584,8 +593,9 @@ template FMT_CONSTEXPR inline const char* get_units() { if (std::is_same::value) return "Ts"; if (std::is_same::value) return "Ps"; if (std::is_same::value) return "Es"; - if (std::is_same>::value) return "m"; + if (std::is_same>::value) return "min"; if (std::is_same>::value) return "h"; + if (std::is_same>::value) return "d"; return nullptr; } @@ -597,12 +607,10 @@ enum class numeric_system { // Glibc extensions for formatting numeric values. enum class pad_type { - unspecified, + // Pad a numeric result string with zeros (the default). + zero, // Do not pad a numeric result string. none, - // Pad a numeric result string with zeros even if the conversion specifier - // character uses space-padding by default. - zero, // Pad a numeric result string with spaces. space, }; @@ -610,7 +618,7 @@ enum class pad_type { template auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { if (pad == pad_type::none) return out; - return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); + return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); } template @@ -621,14 +629,13 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt { // Parses a put_time-like format string and invokes handler actions. template -FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - const Char* end, - Handler&& handler) { +FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { if (begin == end || *begin == '}') return begin; if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; - pad_type pad = pad_type::unspecified; while (ptr != end) { + pad_type pad = pad_type::zero; auto c = *ptr; if (c == '}') break; if (c != '%') { @@ -648,17 +655,11 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, pad = pad_type::none; ++ptr; break; - case '0': - pad = pad_type::zero; - ++ptr; - break; } if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case '%': - handler.on_text(ptr - 1, ptr); - break; + case '%': handler.on_text(ptr - 1, ptr); break; case 'n': { const Char newline[] = {'\n'}; handler.on_text(newline, newline + 1); @@ -670,145 +671,66 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, break; } // Year: - case 'Y': - handler.on_year(numeric_system::standard); - break; - case 'y': - handler.on_short_year(numeric_system::standard); - break; - case 'C': - handler.on_century(numeric_system::standard); - break; - case 'G': - handler.on_iso_week_based_year(); - break; - case 'g': - handler.on_iso_week_based_short_year(); - break; + case 'Y': handler.on_year(numeric_system::standard, pad); break; + case 'y': handler.on_short_year(numeric_system::standard); break; + case 'C': handler.on_century(numeric_system::standard); break; + case 'G': handler.on_iso_week_based_year(); break; + case 'g': handler.on_iso_week_based_short_year(); break; // Day of the week: - case 'a': - handler.on_abbr_weekday(); - break; - case 'A': - handler.on_full_weekday(); - break; - case 'w': - handler.on_dec0_weekday(numeric_system::standard); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::standard); - break; + case 'a': handler.on_abbr_weekday(); break; + case 'A': handler.on_full_weekday(); break; + case 'w': handler.on_dec0_weekday(numeric_system::standard); break; + case 'u': handler.on_dec1_weekday(numeric_system::standard); break; // Month: case 'b': - case 'h': - handler.on_abbr_month(); - break; - case 'B': - handler.on_full_month(); - break; - case 'm': - handler.on_dec_month(numeric_system::standard); - break; + case 'h': handler.on_abbr_month(); break; + case 'B': handler.on_full_month(); break; + case 'm': handler.on_dec_month(numeric_system::standard, pad); break; // Day of the year/month: case 'U': - handler.on_dec0_week_of_year(numeric_system::standard); + handler.on_dec0_week_of_year(numeric_system::standard, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::standard); - break; - case 'V': - handler.on_iso_week_of_year(numeric_system::standard); - break; - case 'j': - handler.on_day_of_year(); - break; - case 'd': - handler.on_day_of_month(numeric_system::standard); + handler.on_dec1_week_of_year(numeric_system::standard, pad); break; + case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break; + case 'j': handler.on_day_of_year(pad); break; + case 'd': handler.on_day_of_month(numeric_system::standard, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::standard); + handler.on_day_of_month(numeric_system::standard, pad_type::space); break; // Hour, minute, second: - case 'H': - handler.on_24_hour(numeric_system::standard, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::standard, pad); - break; - case 'M': - handler.on_minute(numeric_system::standard, pad); - break; - case 'S': - handler.on_second(numeric_system::standard, pad); - break; + case 'H': handler.on_24_hour(numeric_system::standard, pad); break; + case 'I': handler.on_12_hour(numeric_system::standard, pad); break; + case 'M': handler.on_minute(numeric_system::standard, pad); break; + case 'S': handler.on_second(numeric_system::standard, pad); break; // Other: - case 'c': - handler.on_datetime(numeric_system::standard); - break; - case 'x': - handler.on_loc_date(numeric_system::standard); - break; - case 'X': - handler.on_loc_time(numeric_system::standard); - break; - case 'D': - handler.on_us_date(); - break; - case 'F': - handler.on_iso_date(); - break; - case 'r': - handler.on_12_hour_time(); - break; - case 'R': - handler.on_24_hour_time(); - break; - case 'T': - handler.on_iso_time(); - break; - case 'p': - handler.on_am_pm(); - break; - case 'Q': - handler.on_duration_value(); - break; - case 'q': - handler.on_duration_unit(); - break; - case 'z': - handler.on_utc_offset(numeric_system::standard); - break; - case 'Z': - handler.on_tz_name(); - break; + case 'c': handler.on_datetime(numeric_system::standard); break; + case 'x': handler.on_loc_date(numeric_system::standard); break; + case 'X': handler.on_loc_time(numeric_system::standard); break; + case 'D': handler.on_us_date(); break; + case 'F': handler.on_iso_date(); break; + case 'r': handler.on_12_hour_time(); break; + case 'R': handler.on_24_hour_time(); break; + case 'T': handler.on_iso_time(); break; + case 'p': handler.on_am_pm(); break; + case 'Q': handler.on_duration_value(); break; + case 'q': handler.on_duration_unit(); break; + case 'z': handler.on_utc_offset(numeric_system::standard); break; + case 'Z': handler.on_tz_name(); break; // Alternative representation: case 'E': { if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case 'Y': - handler.on_year(numeric_system::alternative); - break; - case 'y': - handler.on_offset_year(); - break; - case 'C': - handler.on_century(numeric_system::alternative); - break; - case 'c': - handler.on_datetime(numeric_system::alternative); - break; - case 'x': - handler.on_loc_date(numeric_system::alternative); - break; - case 'X': - handler.on_loc_time(numeric_system::alternative); - break; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); + case 'Y': handler.on_year(numeric_system::alternative, pad); break; + case 'y': handler.on_offset_year(); break; + case 'C': handler.on_century(numeric_system::alternative); break; + case 'c': handler.on_datetime(numeric_system::alternative); break; + case 'x': handler.on_loc_date(numeric_system::alternative); break; + case 'X': handler.on_loc_time(numeric_system::alternative); break; + case 'z': handler.on_utc_offset(numeric_system::alternative); break; + default: FMT_THROW(format_error("invalid format")); } break; } @@ -816,54 +738,34 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case 'y': - handler.on_short_year(numeric_system::alternative); - break; - case 'm': - handler.on_dec_month(numeric_system::alternative); - break; + case 'y': handler.on_short_year(numeric_system::alternative); break; + case 'm': handler.on_dec_month(numeric_system::alternative, pad); break; case 'U': - handler.on_dec0_week_of_year(numeric_system::alternative); + handler.on_dec0_week_of_year(numeric_system::alternative, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::alternative); + handler.on_dec1_week_of_year(numeric_system::alternative, pad); break; case 'V': - handler.on_iso_week_of_year(numeric_system::alternative); + handler.on_iso_week_of_year(numeric_system::alternative, pad); break; case 'd': - handler.on_day_of_month(numeric_system::alternative); + handler.on_day_of_month(numeric_system::alternative, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::alternative); + handler.on_day_of_month(numeric_system::alternative, pad_type::space); break; - case 'w': - handler.on_dec0_weekday(numeric_system::alternative); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::alternative); - break; - case 'H': - handler.on_24_hour(numeric_system::alternative, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::alternative, pad); - break; - case 'M': - handler.on_minute(numeric_system::alternative, pad); - break; - case 'S': - handler.on_second(numeric_system::alternative, pad); - break; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); + case 'w': handler.on_dec0_weekday(numeric_system::alternative); break; + case 'u': handler.on_dec1_weekday(numeric_system::alternative); break; + case 'H': handler.on_24_hour(numeric_system::alternative, pad); break; + case 'I': handler.on_12_hour(numeric_system::alternative, pad); break; + case 'M': handler.on_minute(numeric_system::alternative, pad); break; + case 'S': handler.on_second(numeric_system::alternative, pad); break; + case 'z': handler.on_utc_offset(numeric_system::alternative); break; + default: FMT_THROW(format_error("invalid format")); } break; - default: - FMT_THROW(format_error("invalid format")); + default: FMT_THROW(format_error("invalid format")); } begin = ptr; } @@ -875,7 +777,7 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void unsupported() { static_cast(this)->unsupported(); } - FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_offset_year() { unsupported(); } FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } @@ -887,13 +789,20 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_abbr_month() { unsupported(); } FMT_CONSTEXPR void on_full_month() { unsupported(); } - FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_day_of_year() { unsupported(); } - FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { + unsupported(); + } FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } @@ -913,12 +822,21 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_tz_name() { unsupported(); } }; -struct tm_format_checker : null_chrono_spec_handler { - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } +class tm_format_checker : public null_chrono_spec_handler { + private: + bool has_timezone_ = false; + + public: + constexpr explicit tm_format_checker(bool has_timezone) + : has_timezone_(has_timezone) {} + + FMT_NORETURN inline void unsupported() { + FMT_THROW(format_error("no format")); + } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_short_year(numeric_system) {} FMT_CONSTEXPR void on_offset_year() {} FMT_CONSTEXPR void on_century(numeric_system) {} @@ -930,13 +848,12 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} FMT_CONSTEXPR void on_abbr_month() {} FMT_CONSTEXPR void on_full_month() {} - FMT_CONSTEXPR void on_dec_month(numeric_system) {} - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_day_of_year() {} - FMT_CONSTEXPR void on_day_of_month(numeric_system) {} - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} @@ -950,29 +867,33 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_utc_offset(numeric_system) {} - FMT_CONSTEXPR void on_tz_name() {} + FMT_CONSTEXPR void on_utc_offset(numeric_system) { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } + FMT_CONSTEXPR void on_tz_name() { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } }; -inline const char* tm_wday_full_name(int wday) { +inline auto tm_wday_full_name(int wday) -> const char* { static constexpr const char* full_name_list[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; } -inline const char* tm_wday_short_name(int wday) { +inline auto tm_wday_short_name(int wday) -> const char* { static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; } -inline const char* tm_mon_full_name(int mon) { +inline auto tm_mon_full_name(int mon) -> const char* { static constexpr const char* full_name_list[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; } -inline const char* tm_mon_short_name(int mon) { +inline auto tm_mon_short_name(int mon) -> const char* { static constexpr const char* short_name_list[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", @@ -981,44 +902,47 @@ inline const char* tm_mon_short_name(int mon) { } template -struct has_member_data_tm_gmtoff : std::false_type {}; +struct has_tm_gmtoff : std::false_type {}; template -struct has_member_data_tm_gmtoff> - : std::true_type {}; +struct has_tm_gmtoff> : std::true_type {}; -template -struct has_member_data_tm_zone : std::false_type {}; +template struct has_tm_zone : std::false_type {}; template -struct has_member_data_tm_zone> - : std::true_type {}; +struct has_tm_zone> : std::true_type {}; -#if FMT_USE_TZSET -inline void tzset_once() { - static bool init = []() -> bool { - _tzset(); - return true; - }(); - ignore_unused(init); +template ::value)> +auto set_tm_zone(T& time, char* tz) -> bool { + time.tm_zone = tz; + return true; +} +template ::value)> +auto set_tm_zone(T&, char*) -> bool { + return false; +} + +inline auto utc() -> char* { + static char tz[] = "UTC"; + return tz; } -#endif // Converts value to Int and checks that it's in the range [0, upper). template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { - FMT_ASSERT(std::is_unsigned::value || - (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), - "invalid value"); - (void)upper; +inline auto to_nonnegative_int(T value, Int upper) -> Int { + if (!std::is_unsigned::value && + (value < 0 || to_unsigned(value) > to_unsigned(upper))) { + FMT_THROW(format_error("chrono value is out of range")); + } return static_cast(value); } template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { - if (value < 0 || value > static_cast(upper)) +inline auto to_nonnegative_int(T value, Int upper) -> Int { + auto int_value = static_cast(value); + if (int_value < 0 || value > static_cast(upper)) FMT_THROW(format_error("invalid value")); - return static_cast(value); + return int_value; } -constexpr long long pow10(std::uint32_t n) { +constexpr auto pow10(std::uint32_t n) -> long long { return n == 0 ? 1 : 10 * pow10(n - 1); } @@ -1050,17 +974,16 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { using subsecond_precision = std::chrono::duration< typename std::common_type::type, - std::ratio<1, detail::pow10(num_fractional_digits)>>; + std::ratio<1, pow10(num_fractional_digits)>>; - const auto fractional = - d - std::chrono::duration_cast(d); + const auto fractional = d - detail::duration_cast(d); const auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() - : std::chrono::duration_cast(fractional).count(); + : detail::duration_cast(fractional).count(); auto n = static_cast>(subseconds); - const int num_digits = detail::count_digits(n); + const int num_digits = count_digits(n); int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); if (precision < 0) { @@ -1068,28 +991,31 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { if (std::ratio_less::value) { *out++ = '.'; - out = std::fill_n(out, leading_zeroes, '0'); - out = format_decimal(out, n, num_digits).end; + out = detail::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, n, num_digits); } - } else { + } else if (precision > 0) { *out++ = '.'; - leading_zeroes = (std::min)(leading_zeroes, precision); - out = std::fill_n(out, leading_zeroes, '0'); + leading_zeroes = min_of(leading_zeroes, precision); int remaining = precision - leading_zeroes; - if (remaining != 0 && remaining < num_digits) { - n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); - out = format_decimal(out, n, remaining).end; + out = detail::fill_n(out, leading_zeroes, '0'); + if (remaining < num_digits) { + int num_truncated_digits = num_digits - remaining; + n /= to_unsigned(pow10(to_unsigned(num_truncated_digits))); + if (n != 0) out = format_decimal(out, n, remaining); return; } - out = format_decimal(out, n, num_digits).end; - remaining -= num_digits; - out = std::fill_n(out, remaining, '0'); + if (n != 0) { + out = format_decimal(out, n, num_digits); + remaining -= num_digits; + } + out = detail::fill_n(out, remaining, '0'); } } // Format subseconds which are given as a floating point type with an // appropriate number of digits. We cannot pass the Duration here, as we -// explicitly need to pass the Rep value in the chrono_formatter. +// explicitly need to pass the Rep value in the duration_formatter. template void write_floating_seconds(memory_buffer& buf, Duration duration, int num_fractional_digits = -1) { @@ -1109,11 +1035,11 @@ void write_floating_seconds(memory_buffer& buf, Duration duration, num_fractional_digits = 6; } - format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), - std::fmod(val * static_cast(Duration::period::num) / - static_cast(Duration::period::den), - static_cast(60)), - num_fractional_digits); + fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), + std::fmod(val * static_cast(Duration::period::num) / + static_cast(Duration::period::den), + static_cast(60)), + num_fractional_digits); } template int { - const auto h = tm_hour(); - const auto z = h < 12 ? h : h - 12; + auto h = tm_hour(); + auto z = h < 12 ? h : h - 12; return z == 0 ? 12 : z; } @@ -1174,14 +1100,13 @@ class tm_writer { return static_cast(l); } - // Algorithm: - // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. auto iso_year_weeks(long long curr_year) const noexcept -> int { - const auto prev_year = curr_year - 1; - const auto curr_p = + auto prev_year = curr_year - 1; + auto curr_p = (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % days_per_week; - const auto prev_p = + auto prev_p = (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % days_per_week; return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); @@ -1191,15 +1116,15 @@ class tm_writer { days_per_week; } auto tm_iso_week_year() const noexcept -> long long { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return year - 1; if (w > iso_year_weeks(year)) return year + 1; return year; } auto tm_iso_week_of_year() const noexcept -> int { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return iso_year_weeks(year - 1); if (w > iso_year_weeks(year)) return 1; return w; @@ -1225,29 +1150,27 @@ class tm_writer { } } - void write_year_extended(long long year) { + void write_year_extended(long long year, pad_type pad) { // At least 4 characters. int width = 4; - if (year < 0) { - *out_++ = '-'; + bool negative = year < 0; + if (negative) { year = 0 - year; --width; } uint32_or_64_or_128_t n = to_unsigned(year); const int num_digits = count_digits(n); - if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); - out_ = format_decimal(out_, n, num_digits).end; + if (negative && pad == pad_type::zero) *out_++ = '-'; + if (width > num_digits) + out_ = detail::write_padding(out_, pad, width - num_digits); + if (negative && pad != pad_type::zero) *out_++ = '-'; + out_ = format_decimal(out_, n, num_digits); } - void write_year(long long year) { - if (year >= 0 && year < 10000) { - write2(static_cast(year / 100)); - write2(static_cast(year % 100)); - } else { - write_year_extended(year); - } + void write_year(long long year, pad_type pad) { + write_year_extended(year, pad); } - void write_utc_offset(long offset, numeric_system ns) { + void write_utc_offset(long long offset, numeric_system ns) { if (offset < 0) { *out_++ = '-'; offset = -offset; @@ -1259,47 +1182,23 @@ class tm_writer { if (ns != numeric_system::standard) *out_++ = ':'; write2(static_cast(offset % 60)); } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { + + template ::value)> + void format_utc_offset(const T& tm, numeric_system ns) { write_utc_offset(tm.tm_gmtoff, ns); } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { -#if defined(_WIN32) && defined(_UCRT) -# if FMT_USE_TZSET - tzset_once(); -# endif - long offset = 0; - _get_timezone(&offset); - if (tm.tm_isdst) { - long dstbias = 0; - _get_dstbias(&dstbias); - offset += dstbias; - } - write_utc_offset(-offset, ns); -#else - if (ns == numeric_system::standard) return format_localized('z'); - - // Extract timezone offset from timezone conversion functions. - std::tm gtm = tm; - std::time_t gt = std::mktime(>m); - std::tm ltm = gmtime(gt); - std::time_t lt = std::mktime(<m); - long offset = gt - lt; - write_utc_offset(offset, ns); -#endif + template ::value)> + void format_utc_offset(const T&, numeric_system ns) { + write_utc_offset(0, ns); } - template ::value)> - void format_tz_name_impl(const T& tm) { - if (is_classic_) - out_ = write_tm_str(out_, tm.tm_zone, loc_); - else - format_localized('Z'); + template ::value)> + void format_tz_name(const T& tm) { + out_ = write_tm_str(out_, tm.tm_zone, loc_); } - template ::value)> - void format_tz_name_impl(const T&) { - format_localized('Z'); + template ::value)> + void format_tz_name(const T&) { + out_ = std::copy_n(utc(), 3, out_); } void format_localized(char format, char modifier = 0) { @@ -1315,10 +1214,10 @@ class tm_writer { subsecs_(subsecs), tm_(tm) {} - OutputIt out() const { return out_; } + auto out() const -> OutputIt { return out_; } FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { - out_ = copy_str(begin, end, out_); + out_ = copy(begin, end, out_); } void on_abbr_weekday() { @@ -1365,11 +1264,11 @@ class tm_writer { *out_++ = ' '; on_abbr_month(); *out_++ = ' '; - on_day_of_month_space(numeric_system::standard); + on_day_of_month(numeric_system::standard, pad_type::space); *out_++ = ' '; on_iso_time(); *out_++ = ' '; - on_year(numeric_system::standard); + on_year(numeric_system::standard, pad_type::space); } else { format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); } @@ -1391,31 +1290,31 @@ class tm_writer { write_digit2_separated(buf, to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), to_unsigned(split_year_lower(tm_year())), '/'); - out_ = copy_str(std::begin(buf), std::end(buf), out_); + out_ = copy(std::begin(buf), std::end(buf), out_); } void on_iso_date() { auto year = tm_year(); char buf[10]; size_t offset = 0; if (year >= 0 && year < 10000) { - copy2(buf, digits2(static_cast(year / 100))); + write2digits(buf, static_cast(year / 100)); } else { offset = 4; - write_year_extended(year); + write_year_extended(year, pad_type::zero); year = 0; } write_digit2_separated(buf + 2, static_cast(year % 100), to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), '-'); - out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); + out_ = copy(std::begin(buf) + offset, std::end(buf), out_); } - void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } - void on_tz_name() { format_tz_name_impl(tm_); } + void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); } + void on_tz_name() { format_tz_name(tm_); } - void on_year(numeric_system ns) { + void on_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write_year(tm_year()); + return write_year(tm_year(), pad); format_localized('Y', 'E'); } void on_short_year(numeric_system ns) { @@ -1446,57 +1345,57 @@ class tm_writer { } } - void on_dec_month(numeric_system ns) { + void on_dec_month(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_mon() + 1); + return write2(tm_mon() + 1, pad); format_localized('m', 'O'); } - void on_dec0_week_of_year(numeric_system ns) { + void on_dec0_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, + pad); format_localized('U', 'O'); } - void on_dec1_week_of_year(numeric_system ns) { + void on_dec1_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) { auto wday = tm_wday(); write2((tm_yday() + days_per_week - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / - days_per_week); + days_per_week, + pad); } else { format_localized('W', 'O'); } } - void on_iso_week_of_year(numeric_system ns) { + void on_iso_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_iso_week_of_year()); + return write2(tm_iso_week_of_year(), pad); format_localized('V', 'O'); } - void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_year() { + write_year(tm_iso_week_year(), pad_type::zero); + } void on_iso_week_based_short_year() { write2(split_year_lower(tm_iso_week_year())); } - void on_day_of_year() { + void on_day_of_year(pad_type pad) { auto yday = tm_yday() + 1; - write1(yday / 100); - write2(yday % 100); + auto digit1 = yday / 100; + if (digit1 != 0) + write1(digit1); + else + out_ = detail::write_padding(out_, pad); + write2(yday % 100, pad); } - void on_day_of_month(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + + void on_day_of_month(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mday(), pad); format_localized('d', 'O'); } - void on_day_of_month_space(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) { - auto mday = to_unsigned(tm_mday()) % 100; - const char* d2 = digits2(mday); - *out_++ = mday < 10 ? ' ' : d2[0]; - *out_++ = d2[1]; - } else { - format_localized('e', 'O'); - } - } void on_24_hour(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) @@ -1523,7 +1422,7 @@ class tm_writer { write_floating_seconds(buf, *subsecs_); if (buf.size() > 1) { // Remove the leading "0", write something like ".123". - out_ = std::copy(buf.begin() + 1, buf.end(), out_); + out_ = copy(buf.begin() + 1, buf.end(), out_); } } else { write_fractional_seconds(out_, *subsecs_); @@ -1540,7 +1439,7 @@ class tm_writer { char buf[8]; write_digit2_separated(buf, to_unsigned(tm_hour12()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); - out_ = copy_str(std::begin(buf), std::end(buf), out_); + out_ = copy(std::begin(buf), std::end(buf), out_); *out_++ = ' '; on_am_pm(); } else { @@ -1555,7 +1454,7 @@ class tm_writer { void on_iso_time() { on_24_hour_time(); *out_++ = ':'; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -1575,10 +1474,11 @@ class tm_writer { struct chrono_format_checker : null_chrono_spec_handler { bool has_precision_integral = false; - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } + FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} @@ -1588,25 +1488,24 @@ struct chrono_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_duration_value() const { - if (has_precision_integral) { + if (has_precision_integral) FMT_THROW(format_error("precision not allowed for this argument type")); - } } FMT_CONSTEXPR void on_duration_unit() {} }; template ::value&& has_isfinite::value)> -inline bool isfinite(T) { +inline auto isfinite(T) -> bool { return true; } template ::value)> -inline T mod(T x, int y) { +inline auto mod(T x, int y) -> T { return x % static_cast(y); } template ::value)> -inline T mod(T x, int y) { +inline auto mod(T x, int y) -> T { return std::fmod(x, static_cast(y)); } @@ -1621,71 +1520,58 @@ template struct make_unsigned_or_unchanged { using type = typename std::make_unsigned::type; }; -#if FMT_SAFE_DURATION_CAST -// throwing version of safe_duration_cast -template -To fmt_safe_duration_cast(std::chrono::duration from) { - int ec; - To to = safe_duration_cast::safe_duration_cast(from, ec); - if (ec) FMT_THROW(format_error("cannot format duration")); - return to; -} -#endif - template ::value)> -inline std::chrono::duration get_milliseconds( - std::chrono::duration d) { - // this may overflow and/or the result may not fit in the - // target type. +inline auto get_milliseconds(std::chrono::duration d) + -> std::chrono::duration { + // This may overflow and/or the result may not fit in the target type. #if FMT_SAFE_DURATION_CAST - using CommonSecondsType = + using common_seconds_type = typename std::common_type::type; - const auto d_as_common = fmt_safe_duration_cast(d); - const auto d_as_whole_seconds = - fmt_safe_duration_cast(d_as_common); - // this conversion should be nonproblematic - const auto diff = d_as_common - d_as_whole_seconds; - const auto ms = - fmt_safe_duration_cast>(diff); + auto d_as_common = detail::duration_cast(d); + auto d_as_whole_seconds = + detail::duration_cast(d_as_common); + // This conversion should be nonproblematic. + auto diff = d_as_common - d_as_whole_seconds; + auto ms = detail::duration_cast>(diff); return ms; #else - auto s = std::chrono::duration_cast(d); - return std::chrono::duration_cast(d - s); + auto s = detail::duration_cast(d); + return detail::duration_cast(d - s); #endif } template ::value)> -OutputIt format_duration_value(OutputIt out, Rep val, int) { +auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { return write(out, val); } template ::value)> -OutputIt format_duration_value(OutputIt out, Rep val, int precision) { - auto specs = format_specs(); +auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { + auto specs = format_specs(); specs.precision = precision; - specs.type = precision >= 0 ? presentation_type::fixed_lower - : presentation_type::general_lower; + specs.set_type(precision >= 0 ? presentation_type::fixed + : presentation_type::general); return write(out, val, specs); } template -OutputIt copy_unit(string_view unit, OutputIt out, Char) { - return std::copy(unit.begin(), unit.end(), out); +auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { + return copy(unit.begin(), unit.end(), out); } template -OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { +auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { // This works when wchar_t is UTF-32 because units only contain characters // that have the same representation in UTF-16 and UTF-32. utf8_to_utf16 u(unit); - return std::copy(u.c_str(), u.c_str() + u.size(), out); + return copy(u.c_str(), u.c_str() + u.size(), out); } template -OutputIt format_duration_unit(OutputIt out) { +auto format_duration_unit(OutputIt out) -> OutputIt { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); *out++ = '['; @@ -1707,44 +1593,45 @@ class get_locale { bool has_locale_ = false; public: - get_locale(bool localized, locale_ref loc) : has_locale_(localized) { - if (localized) - ::new (&locale_) std::locale(loc.template get()); + inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + if (!localized) return; + ignore_unused(loc); + ::new (&locale_) std::locale( +#if FMT_USE_LOCALE + loc.template get() +#endif + ); } - ~get_locale() { + inline ~get_locale() { if (has_locale_) locale_.~locale(); } - operator const std::locale&() const { + inline operator const std::locale&() const { return has_locale_ ? locale_ : get_classic_locale(); } }; -template -struct chrono_formatter { - FormatContext& context; - OutputIt out; - int precision; - bool localized = false; +template +struct duration_formatter { + using iterator = basic_appender; + iterator out; // rep is unsigned to avoid overflow. using rep = conditional_t::value && sizeof(Rep) < sizeof(int), unsigned, typename make_unsigned_or_unchanged::type>; rep val; + int precision; + locale_ref locale; + bool localized = false; using seconds = std::chrono::duration; seconds s; using milliseconds = std::chrono::duration; bool negative; - using char_type = typename FormatContext::char_type; - using tm_writer_type = tm_writer; + using tm_writer_type = tm_writer; - chrono_formatter(FormatContext& ctx, OutputIt o, - std::chrono::duration d) - : context(ctx), - out(o), - val(static_cast(d.count())), - negative(false) { + duration_formatter(iterator o, std::chrono::duration d, + locale_ref loc) + : out(o), val(static_cast(d.count())), locale(loc), negative(false) { if (d.count() < 0) { val = 0 - val; negative = true; @@ -1752,45 +1639,41 @@ struct chrono_formatter { // this may overflow and/or the result may not fit in the // target type. -#if FMT_SAFE_DURATION_CAST // might need checked conversion (rep!=Rep) - auto tmpval = std::chrono::duration(val); - s = fmt_safe_duration_cast(tmpval); -#else - s = std::chrono::duration_cast( - std::chrono::duration(val)); -#endif + s = detail::duration_cast(std::chrono::duration(val)); } // returns true if nan or inf, writes to out. - bool handle_nan_inf() { - if (isfinite(val)) { - return false; - } + auto handle_nan_inf() -> bool { + if (isfinite(val)) return false; if (isnan(val)) { write_nan(); return true; } // must be +-inf - if (val > 0) { - write_pinf(); - } else { - write_ninf(); - } + if (val > 0) + std::copy_n("inf", 3, out); + else + std::copy_n("-inf", 4, out); return true; } - Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + auto days() const -> Rep { return static_cast(s.count() / 86400); } + auto hour() const -> Rep { + return static_cast(mod((s.count() / 3600), 24)); + } - Rep hour12() const { + auto hour12() const -> Rep { Rep hour = static_cast(mod((s.count() / 3600), 12)); return hour <= 0 ? 12 : hour; } - Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } - Rep second() const { return static_cast(mod(s.count(), 60)); } + auto minute() const -> Rep { + return static_cast(mod((s.count() / 60), 60)); + } + auto second() const -> Rep { return static_cast(mod(s.count(), 60)); } - std::tm time() const { + auto time() const -> std::tm { auto time = std::tm(); time.tm_hour = to_nonnegative_int(hour(), 24); time.tm_min = to_nonnegative_int(minute(), 60); @@ -1799,13 +1682,12 @@ struct chrono_formatter { } void write_sign() { - if (negative) { - *out++ = '-'; - negative = false; - } + if (!negative) return; + *out++ = '-'; + negative = false; } - void write(Rep value, int width, pad_type pad = pad_type::unspecified) { + void write(Rep value, int width, pad_type pad = pad_type::zero) { write_sign(); if (isnan(value)) return write_nan(); uint32_or_64_or_128_t n = @@ -1814,24 +1696,22 @@ struct chrono_formatter { if (width > num_digits) { out = detail::write_padding(out, pad, width - num_digits); } - out = format_decimal(out, n, num_digits).end; + out = format_decimal(out, n, num_digits); } void write_nan() { std::copy_n("nan", 3, out); } - void write_pinf() { std::copy_n("inf", 3, out); } - void write_ninf() { std::copy_n("-inf", 4, out); } template void format_tm(const tm& time, Callback cb, Args... args) { if (isnan(val)) return write_nan(); - get_locale loc(localized, context.locale()); + get_locale loc(localized, locale); auto w = tm_writer_type(loc, out, time); (w.*cb)(args...); out = w.out(); } - void on_text(const char_type* begin, const char_type* end) { - std::copy(begin, end, out); + void on_text(const Char* begin, const Char* end) { + copy(begin, end, out); } // These are not implemented because durations don't have date information. @@ -1848,19 +1728,22 @@ struct chrono_formatter { void on_iso_date() {} void on_utc_offset(numeric_system) {} void on_tz_name() {} - void on_year(numeric_system) {} + void on_year(numeric_system, pad_type) {} void on_short_year(numeric_system) {} void on_offset_year() {} void on_century(numeric_system) {} void on_iso_week_based_year() {} void on_iso_week_based_short_year() {} - void on_dec_month(numeric_system) {} - void on_dec0_week_of_year(numeric_system) {} - void on_dec1_week_of_year(numeric_system) {} - void on_iso_week_of_year(numeric_system) {} - void on_day_of_year() {} - void on_day_of_month(numeric_system) {} - void on_day_of_month_space(numeric_system) {} + void on_dec_month(numeric_system, pad_type) {} + void on_dec0_week_of_year(numeric_system, pad_type) {} + void on_dec1_week_of_year(numeric_system, pad_type) {} + void on_iso_week_of_year(numeric_system, pad_type) {} + void on_day_of_month(numeric_system, pad_type) {} + + void on_day_of_year(pad_type) { + if (handle_nan_inf()) return; + write(days(), 0); + } void on_24_hour(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; @@ -1898,13 +1781,12 @@ struct chrono_formatter { write_floating_seconds(buf, std::chrono::duration(val), precision); if (negative) *out++ = '-'; - if (buf.size() < 2 || buf[1] == '.') { + if (buf.size() < 2 || buf[1] == '.') out = detail::write_padding(out, pad); - } - out = std::copy(buf.begin(), buf.end(), out); + out = copy(buf.begin(), buf.end(), out); } else { write(second(), 2, pad); - write_fractional_seconds( + write_fractional_seconds( out, std::chrono::duration(val), precision); } return; @@ -1935,7 +1817,7 @@ struct chrono_formatter { on_24_hour_time(); *out++ = ':'; if (handle_nan_inf()) return; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -1946,94 +1828,245 @@ struct chrono_formatter { void on_duration_value() { if (handle_nan_inf()) return; write_sign(); - out = format_duration_value(out, val, precision); + out = format_duration_value(out, val, precision); } - void on_duration_unit() { - out = format_duration_unit(out); - } + void on_duration_unit() { out = format_duration_unit(out); } }; } // namespace detail #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 using weekday = std::chrono::weekday; +using day = std::chrono::day; +using month = std::chrono::month; +using year = std::chrono::year; +using year_month_day = std::chrono::year_month_day; #else // A fallback version of weekday. class weekday { private: - unsigned char value; + unsigned char value_; public: weekday() = default; - explicit constexpr weekday(unsigned wd) noexcept - : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr unsigned c_encoding() const noexcept { return value; } + constexpr explicit weekday(unsigned wd) noexcept + : value_(static_cast(wd != 7 ? wd : 0)) {} + constexpr auto c_encoding() const noexcept -> unsigned { return value_; } }; -class year_month_day {}; -#endif - -// A rudimentary weekday formatter. -template struct formatter { +class day { private: - bool localized = false; + unsigned char value_; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto begin = ctx.begin(), end = ctx.end(); - if (begin != end && *begin == 'L') { - ++begin; - localized = true; + day() = default; + constexpr explicit day(unsigned d) noexcept + : value_(static_cast(d)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class month { + private: + unsigned char value_; + + public: + month() = default; + constexpr explicit month(unsigned m) noexcept + : value_(static_cast(m)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class year { + private: + int value_; + + public: + year() = default; + constexpr explicit year(int y) noexcept : value_(y) {} + constexpr explicit operator int() const noexcept { return value_; } +}; + +class year_month_day { + private: + fmt::year year_; + fmt::month month_; + fmt::day day_; + + public: + year_month_day() = default; + constexpr year_month_day(const year& y, const month& m, const day& d) noexcept + : year_(y), month_(m), day_(d) {} + constexpr auto year() const noexcept -> fmt::year { return year_; } + constexpr auto month() const noexcept -> fmt::month { return month_; } + constexpr auto day() const noexcept -> fmt::day { return day_; } +}; +#endif // __cpp_lib_chrono >= 201907 + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + this->set_localized(); } - return begin; + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_wday = static_cast(wd.c_encoding()); - detail::get_locale loc(localized, ctx.locale()); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(this->localized(), ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_weekday(); return w.out(); } }; +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mday = static_cast(static_cast(d)); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + this->set_localized(); + } + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mon = static_cast(static_cast(m)) - 1; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(this->localized(), ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_month(); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(y) - 1900; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_year(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year_month_day val, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(val.year()) - 1900; + time.tm_mon = static_cast(static_cast(val.month())) - 1; + time.tm_mday = static_cast(static_cast(val.day())); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(true, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_iso_date(); + return w.out(); + } +}; + template struct formatter, Char> { private: - format_specs specs_; + format_specs specs_; detail::arg_ref width_ref_; detail::arg_ref precision_ref_; - bool localized_ = false; - basic_string_view format_str_; + basic_string_view fmt_; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it == end || *it == '}') return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; + } auto checker = detail::chrono_format_checker(); if (*it == '.') { checker.has_precision_integral = !std::is_floating_point::value; - it = detail::parse_precision(it, end, specs_.precision, precision_ref_, - ctx); + it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); } if (it != end && *it == 'L') { - localized_ = true; + specs_.set_localized(); ++it; } end = detail::parse_chrono_format(it, end, checker); - format_str_ = {it, detail::to_unsigned(end - it)}; + fmt_ = {it, detail::to_unsigned(end - it)}; return end; } @@ -2043,24 +2076,23 @@ struct formatter, Char> { auto specs = specs_; auto precision = specs.precision; specs.precision = -1; - auto begin = format_str_.begin(), end = format_str_.end(); + auto begin = fmt_.begin(), end = fmt_.end(); // As a possible future optimization, we could avoid extra copying if width // is not specified. auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); - detail::handle_dynamic_spec(precision, - precision_ref_, ctx); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), precision, + precision_ref_, ctx); if (begin == end || *begin == '}') { out = detail::format_duration_value(out, d.count(), precision); detail::format_duration_unit(out); } else { - using chrono_formatter = - detail::chrono_formatter; - auto f = chrono_formatter(ctx, out, d); + auto f = + detail::duration_formatter(out, d, ctx.locale()); f.precision = precision; - f.localized = localized_; + f.localized = specs_.localized(); detail::parse_chrono_format(begin, end, f); } return detail::write( @@ -2068,137 +2100,143 @@ struct formatter, Char> { } }; -template -struct formatter, - Char> : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } - - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - using period = typename Duration::period; - if (detail::const_check( - period::num != 1 || period::den != 1 || - std::is_floating_point::value)) { - const auto epoch = val.time_since_epoch(); - auto subsecs = std::chrono::duration_cast( - epoch - std::chrono::duration_cast(epoch)); - - if (subsecs.count() < 0) { - auto second = - std::chrono::duration_cast(std::chrono::seconds(1)); - if (epoch.count() < ((Duration::min)() + second).count()) - FMT_THROW(format_error("duration is too small")); - subsecs += second; - val -= second; - } - - return formatter::do_format( - gmtime(std::chrono::time_point_cast(val)), ctx, - &subsecs); - } - - return formatter::format( - gmtime(std::chrono::time_point_cast(val)), ctx); - } -}; - -#if FMT_USE_LOCAL_TIME -template -struct formatter, Char> - : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } - - template - auto format(std::chrono::local_time val, FormatContext& ctx) const - -> decltype(ctx.out()) { - using period = typename Duration::period; - if (period::num != 1 || period::den != 1 || - std::is_floating_point::value) { - const auto epoch = val.time_since_epoch(); - const auto subsecs = std::chrono::duration_cast( - epoch - std::chrono::duration_cast(epoch)); - - return formatter::do_format( - localtime(std::chrono::time_point_cast(val)), - ctx, &subsecs); - } - - return formatter::format( - localtime(std::chrono::time_point_cast(val)), - ctx); - } -}; -#endif - -#if FMT_USE_UTC_TIME -template -struct formatter, - Char> - : formatter, - Char> { - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - return formatter< - std::chrono::time_point, - Char>::format(std::chrono::utc_clock::to_sys(val), ctx); - } -}; -#endif - template struct formatter { private: - format_specs specs_; + format_specs specs_; detail::arg_ref width_ref_; + basic_string_view fmt_ = + detail::string_literal(); protected: - basic_string_view format_str_; + auto localized() const -> bool { return specs_.localized(); } + FMT_CONSTEXPR void set_localized() { specs_.set_localized(); } - template - auto do_format(const std::tm& tm, FormatContext& ctx, - const Duration* subsecs) const -> decltype(ctx.out()) { - auto specs = specs_; - auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); - - auto loc_ref = ctx.locale(); - detail::get_locale loc(static_cast(loc_ref), loc_ref); - auto w = - detail::tm_writer(loc, out, tm, subsecs); - detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w); - return detail::write( - ctx.out(), basic_string_view(buf.data(), buf.size()), specs); - } - - public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto do_parse(parse_context& ctx, bool has_timezone) + -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it == end || *it == '}') return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; + } - end = detail::parse_chrono_format(it, end, detail::tm_format_checker()); - // Replace the default format_str only if the new spec is not empty. - if (end != it) format_str_ = {it, detail::to_unsigned(end - it)}; + if (*it == 'L') { + specs_.set_localized(); + ++it; + } + + end = detail::parse_chrono_format(it, end, + detail::tm_format_checker(has_timezone)); + // Replace the default format string only if the new spec is not empty. + if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; return end; } + template + auto do_format(const std::tm& tm, FormatContext& ctx, + const Duration* subsecs) const -> decltype(ctx.out()) { + auto specs = specs_; + auto buf = basic_memory_buffer(); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + + auto loc_ref = specs.localized() ? ctx.locale() : locale_ref(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = detail::tm_writer, Char, Duration>( + loc, out, tm, subsecs); + detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return do_parse(ctx, detail::has_tm_gmtoff::value); + } + template auto format(const std::tm& tm, FormatContext& ctx) const -> decltype(ctx.out()) { - return do_format(tm, ctx, nullptr); + return do_format(tm, ctx, nullptr); + } +}; + +// DEPRECATED! Reversed order of template parameters. +template +struct formatter, Char> : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, true); + } + + template + auto format(sys_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + std::tm tm = gmtime(val); + using period = typename Duration::period; + if (detail::const_check( + period::num == 1 && period::den == 1 && + !std::is_floating_point::value)) { + detail::set_tm_zone(tm, detail::utc()); + return formatter::format(tm, ctx); + } + Duration epoch = val.time_since_epoch(); + Duration subsecs = detail::duration_cast( + epoch - detail::duration_cast(epoch)); + if (subsecs.count() < 0) { + auto second = detail::duration_cast(std::chrono::seconds(1)); + if (tm.tm_sec != 0) { + --tm.tm_sec; + } else { + tm = gmtime(val - second); + detail::set_tm_zone(tm, detail::utc()); + } + subsecs += second; + } + return formatter::do_format(tm, ctx, &subsecs); + } +}; + +template +struct formatter, Char> + : formatter, Char> { + template + auto format(utc_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter, Char>::format( + detail::utc_clock::to_sys(val), ctx); + } +}; + +template +struct formatter, Char> + : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, false); + } + + template + auto format(local_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto time_since_epoch = val.time_since_epoch(); + auto seconds_since_epoch = + detail::duration_cast(time_since_epoch); + // Use gmtime to prevent time zone conversion since local_time has an + // unspecified time zone. + std::tm t = gmtime(seconds_since_epoch.count()); + using period = typename Duration::period; + if (period::num == 1 && period::den == 1 && + !std::is_floating_point::value) { + return formatter::format(t, ctx); + } + auto subsecs = + detail::duration_cast(time_since_epoch - seconds_since_epoch); + return formatter::do_format(t, ctx, &subsecs); } }; diff --git a/deps/fmt/include/fmt/color.h b/deps/fmt/include/fmt/color.h index 8697e1ca0..2cbc53ca3 100644 --- a/deps/fmt/include/fmt/color.h +++ b/deps/fmt/include/fmt/color.h @@ -190,11 +190,11 @@ enum class emphasis : uint8_t { // rgb is a struct for red, green and blue colors. // Using the name "rgb" makes some editors show the color in a tooltip. struct rgb { - FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} - FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} - FMT_CONSTEXPR rgb(uint32_t hex) + constexpr rgb() : r(0), g(0), b(0) {} + constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + constexpr rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} - FMT_CONSTEXPR rgb(color hex) + constexpr rgb(color hex) : r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), b(uint32_t(hex) & 0xFF) {} @@ -205,161 +205,198 @@ struct rgb { namespace detail { -// color is a struct of either a rgb color or a terminal color. +// A bit-packed variant of an RGB color, a terminal color, or unset color. +// see text_style for the bit-packing scheme. struct color_type { - FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} - FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { - value.rgb_color = static_cast(rgb_color); + constexpr color_type() noexcept = default; + constexpr color_type(color rgb_color) noexcept + : value_(static_cast(rgb_color) | (1 << 24)) {} + constexpr color_type(rgb rgb_color) noexcept + : color_type(static_cast( + (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b)) {} + constexpr color_type(terminal_color term_color) noexcept + : value_(static_cast(term_color) | (3 << 24)) {} + + constexpr auto is_terminal_color() const noexcept -> bool { + return (value_ & (1 << 25)) != 0; } - FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { - value.rgb_color = (static_cast(rgb_color.r) << 16) | - (static_cast(rgb_color.g) << 8) | rgb_color.b; + + constexpr auto value() const noexcept -> uint32_t { + return value_ & 0xFFFFFF; } - FMT_CONSTEXPR color_type(terminal_color term_color) noexcept - : is_rgb(), value{} { - value.term_color = static_cast(term_color); - } - bool is_rgb; - union color_union { - uint8_t term_color; - uint32_t rgb_color; - } value; + + constexpr color_type(uint32_t value) noexcept : value_(value) {} + + uint32_t value_ = 0; }; } // namespace detail -/** A text style consisting of foreground and background colors and emphasis. */ +/// A text style consisting of foreground and background colors and emphasis. class text_style { + // The information is packed as follows: + // ┌──┐ + // │ 0│─┐ + // │..│ ├── foreground color value + // │23│─┘ + // ├──┤ + // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's + // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused) + // ├──┤ + // │26│──── overflow bit, always zero (see below) + // ├──┤ + // │27│─┐ + // │..│ │ + // │50│ │ + // ├──┤ │ + // │51│ ├── background color (same format as the foreground color) + // │52│ │ + // ├──┤ │ + // │53│─┘ + // ├──┤ + // │54│─┐ + // │..│ ├── emphases + // │61│─┘ + // ├──┤ + // │62│─┬── unused + // │63│─┘ + // └──┘ + // The overflow bits are there to make operator|= efficient. + // When ORing, we must throw if, for either the foreground or background, + // one style specifies a terminal color and the other specifies any color + // (terminal or RGB); in other words, if one discriminator is 11 and the + // other is 11 or 01. + // + // We do that check by adding the styles. Consider what adding does to each + // possible pair of discriminators: + // 00 + 00 = 000 + // 01 + 00 = 001 + // 11 + 00 = 011 + // 01 + 01 = 010 + // 11 + 01 = 100 (!!) + // 11 + 11 = 110 (!!) + // In the last two cases, the ones we want to catch, the third bit——the + // overflow bit——is set. Bingo. + // + // We must take into account the possible carry bit from the bits + // before the discriminator. The only potentially problematic case is + // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry + // bit is impossible in that case, because 00 (unset color) means the + // 24 bits that precede the discriminator are all zero. + // + // This test can be applied to both colors simultaneously. + public: FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept - : set_foreground_color(), set_background_color(), ems(em) {} + : style_(static_cast(em) << 54) {} - FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { - if (!set_foreground_color) { - set_foreground_color = rhs.set_foreground_color; - foreground_color = rhs.foreground_color; - } else if (rhs.set_foreground_color) { - if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) - FMT_THROW(format_error("can't OR a terminal color")); - foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; - } - - if (!set_background_color) { - set_background_color = rhs.set_background_color; - background_color = rhs.background_color; - } else if (rhs.set_background_color) { - if (!background_color.is_rgb || !rhs.background_color.is_rgb) - FMT_THROW(format_error("can't OR a terminal color")); - background_color.value.rgb_color |= rhs.background_color.value.rgb_color; - } - - ems = static_cast(static_cast(ems) | - static_cast(rhs.ems)); + FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& { + if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0) + report_error("can't OR a terminal color"); + style_ |= rhs.style_; return *this; } - friend FMT_CONSTEXPR text_style operator|(text_style lhs, - const text_style& rhs) { + friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs) + -> text_style { return lhs |= rhs; } - FMT_CONSTEXPR bool has_foreground() const noexcept { - return set_foreground_color; + FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool { + return style_ == rhs.style_; } - FMT_CONSTEXPR bool has_background() const noexcept { - return set_background_color; + + FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool { + return !(*this == rhs); } - FMT_CONSTEXPR bool has_emphasis() const noexcept { - return static_cast(ems) != 0; + + FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { + return (style_ & (1 << 24)) != 0; } - FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { + FMT_CONSTEXPR auto has_background() const noexcept -> bool { + return (style_ & (1ULL << 51)) != 0; + } + FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { + return (style_ >> 54) != 0; + } + FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { FMT_ASSERT(has_foreground(), "no foreground specified for this style"); - return foreground_color; + return style_ & 0x3FFFFFF; } - FMT_CONSTEXPR detail::color_type get_background() const noexcept { + FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { FMT_ASSERT(has_background(), "no background specified for this style"); - return background_color; + return (style_ >> 27) & 0x3FFFFFF; } - FMT_CONSTEXPR emphasis get_emphasis() const noexcept { + FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); - return ems; + return static_cast(style_ >> 54); } private: - FMT_CONSTEXPR text_style(bool is_foreground, - detail::color_type text_color) noexcept - : set_foreground_color(), set_background_color(), ems() { - if (is_foreground) { - foreground_color = text_color; - set_foreground_color = true; - } else { - background_color = text_color; - set_background_color = true; - } - } + FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {} - friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; + friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept + -> text_style; - friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; + friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept + -> text_style; - detail::color_type foreground_color; - detail::color_type background_color; - bool set_foreground_color; - bool set_background_color; - emphasis ems; + uint64_t style_ = 0; }; -/** Creates a text style from the foreground (text) color. */ -FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { - return text_style(true, foreground); +/// Creates a text style from the foreground (text) color. +FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept + -> text_style { + return foreground.value_; } -/** Creates a text style from the background color. */ -FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { - return text_style(false, background); +/// Creates a text style from the background color. +FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept + -> text_style { + return static_cast(background.value_) << 27; } -FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { +FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept + -> text_style { return text_style(lhs) | rhs; } namespace detail { template struct ansi_color_escape { - FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, + FMT_CONSTEXPR ansi_color_escape(color_type text_color, const char* esc) noexcept { // If we have a terminal color, we need to output another escape code // sequence. - if (!text_color.is_rgb) { + if (text_color.is_terminal_color()) { bool is_background = esc == string_view("\x1b[48;2;"); - uint32_t value = text_color.value.term_color; + uint32_t value = text_color.value(); // Background ASCII codes are the same as the foreground ones but with // 10 more. if (is_background) value += 10u; - size_t index = 0; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); if (value >= 100u) { - buffer[index++] = static_cast('1'); + buffer[size++] = static_cast('1'); value %= 100u; } - buffer[index++] = static_cast('0' + value / 10u); - buffer[index++] = static_cast('0' + value % 10u); + buffer[size++] = static_cast('0' + value / 10u); + buffer[size++] = static_cast('0' + value % 10u); - buffer[index++] = static_cast('m'); - buffer[index++] = static_cast('\0'); + buffer[size++] = static_cast('m'); return; } for (int i = 0; i < 7; i++) { buffer[i] = static_cast(esc[i]); } - rgb color(text_color.value.rgb_color); + rgb color(text_color.value()); to_esc(color.r, buffer + 7, ';'); to_esc(color.g, buffer + 11, ';'); to_esc(color.b, buffer + 15, 'm'); - buffer[19] = static_cast(0); + size = 19; } FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { uint8_t em_codes[num_emphases] = {}; @@ -372,26 +409,28 @@ template struct ansi_color_escape { if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; - size_t index = 0; + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); + for (size_t i = 0; i < num_emphases; ++i) { if (!em_codes[i]) continue; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); - buffer[index++] = static_cast('0' + em_codes[i]); - buffer[index++] = static_cast('m'); + buffer[size++] = static_cast('0' + em_codes[i]); + buffer[size++] = static_cast(';'); } - buffer[index++] = static_cast(0); + + buffer[size - 1] = static_cast('m'); } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } - FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } - FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { - return buffer + std::char_traits::length(buffer); + FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } + FMT_CONSTEXPR auto end() const noexcept -> const Char* { + return buffer + size; } private: static constexpr size_t num_emphases = 8; - Char buffer[7u + 3u * num_emphases + 1u]; + Char buffer[7u + 4u * num_emphases] = {}; + size_t size = 0; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, char delimiter) noexcept { @@ -400,25 +439,27 @@ template struct ansi_color_escape { out[2] = static_cast('0' + c % 10); out[3] = static_cast(delimiter); } - static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { + static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept + -> bool { return static_cast(em) & static_cast(mask); } }; template -FMT_CONSTEXPR ansi_color_escape make_foreground_color( - detail::color_type foreground) noexcept { +FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept + -> ansi_color_escape { return ansi_color_escape(foreground, "\x1b[38;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_background_color( - detail::color_type background) noexcept { +FMT_CONSTEXPR auto make_background_color(color_type background) noexcept + -> ansi_color_escape { return ansi_color_escape(background, "\x1b[48;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { +FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept + -> ansi_color_escape { return ansi_color_escape(em); } @@ -427,149 +468,116 @@ template inline void reset_color(buffer& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } -template struct styled_arg { +template struct styled_arg : view { const T& value; text_style style; + styled_arg(const T& v, text_style s) : value(v), style(s) {} }; template -void vformat_to(buffer& buf, const text_style& ts, - basic_string_view format_str, - basic_format_args>> args) { - bool has_style = false; +void vformat_to(buffer& buf, text_style ts, basic_string_view fmt, + basic_format_args> args) { if (ts.has_emphasis()) { - has_style = true; - auto emphasis = detail::make_emphasis(ts.get_emphasis()); + auto emphasis = make_emphasis(ts.get_emphasis()); buf.append(emphasis.begin(), emphasis.end()); } if (ts.has_foreground()) { - has_style = true; - auto foreground = detail::make_foreground_color(ts.get_foreground()); + auto foreground = make_foreground_color(ts.get_foreground()); buf.append(foreground.begin(), foreground.end()); } if (ts.has_background()) { - has_style = true; - auto background = detail::make_background_color(ts.get_background()); + auto background = make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } - detail::vformat_to(buf, format_str, args, {}); - if (has_style) detail::reset_color(buf); + vformat_to(buf, fmt, args); + if (ts != text_style()) reset_color(buf); } - } // namespace detail -inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, - format_args args) { - // Legacy wide streams are not supported. +inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) { auto buf = memory_buffer(); detail::vformat_to(buf, ts, fmt, args); - if (detail::is_utf8()) { - detail::print(f, string_view(buf.begin(), buf.size())); - return; - } - buf.push_back('\0'); - int result = std::fputs(buf.data(), f); - if (result < 0) - FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); + print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); } /** - \rst - Formats a string and prints it to the specified file stream using ANSI - escape sequences to specify text formatting. - - **Example**:: - - fmt::print(fmt::emphasis::bold | fg(fmt::color::red), - "Elapsed time: {0:.2f} seconds", 1.23); - \endrst + * Formats a string and prints it to the specified file stream using ANSI + * escape sequences to specify text formatting. + * + * **Example**: + * + * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + * "Elapsed time: {0:.2f} seconds", 1.23); */ -template ::value)> -void print(std::FILE* f, const text_style& ts, const S& format_str, - const Args&... args) { - vprint(f, ts, format_str, - fmt::make_format_args>>(args...)); +template +void print(FILE* f, text_style ts, format_string fmt, T&&... args) { + vprint(f, ts, fmt.str, vargs{{args...}}); } /** - \rst - Formats a string and prints it to stdout using ANSI escape sequences to - specify text formatting. - - **Example**:: - - fmt::print(fmt::emphasis::bold | fg(fmt::color::red), - "Elapsed time: {0:.2f} seconds", 1.23); - \endrst + * Formats a string and prints it to stdout using ANSI escape sequences to + * specify text formatting. + * + * **Example**: + * + * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + * "Elapsed time: {0:.2f} seconds", 1.23); */ -template ::value)> -void print(const text_style& ts, const S& format_str, const Args&... args) { - return print(stdout, ts, format_str, args...); +template +void print(text_style ts, format_string fmt, T&&... args) { + return print(stdout, ts, fmt, std::forward(args)...); } -template > -inline std::basic_string vformat( - const text_style& ts, const S& format_str, - basic_format_args>> args) { - basic_memory_buffer buf; - detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); +inline auto vformat(text_style ts, string_view fmt, format_args args) + -> std::string { + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, args); return fmt::to_string(buf); } /** - \rst - Formats arguments and returns the result as a string using ANSI - escape sequences to specify text formatting. - - **Example**:: - - #include - std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), - "The answer is {}", 42); - \endrst -*/ -template > -inline std::basic_string format(const text_style& ts, const S& format_str, - const Args&... args) { - return fmt::vformat(ts, detail::to_string_view(format_str), - fmt::make_format_args>(args...)); + * Formats arguments and returns the result as a string using ANSI escape + * sequences to specify text formatting. + * + * **Example**: + * + * ``` + * #include + * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + * "The answer is {}", 42); + * ``` + */ +template +inline auto format(text_style ts, format_string fmt, T&&... args) + -> std::string { + return fmt::vformat(ts, fmt.str, vargs{{args...}}); } -/** - Formats a string with the given text_style and writes the output to ``out``. - */ -template ::value)> -OutputIt vformat_to( - OutputIt out, const text_style& ts, basic_string_view format_str, - basic_format_args>> args) { - auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, ts, format_str, args); +/// Formats a string with the given text_style and writes the output to `out`. +template ::value)> +auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args) + -> OutputIt { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, ts, fmt, args); return detail::get_iterator(buf, out); } /** - \rst - Formats arguments with the given text_style, writes the result to the output - iterator ``out`` and returns the iterator past the end of the output range. - - **Example**:: - - std::vector out; - fmt::format_to(std::back_inserter(out), - fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); - \endrst -*/ -template >::value&& - detail::is_string::value> -inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, - Args&&... args) -> - typename std::enable_if::type { - return vformat_to(out, ts, detail::to_string_view(format_str), - fmt::make_format_args>>(args...)); + * Formats arguments with the given text style, writes the result to the output + * iterator `out` and returns the iterator past the end of the output range. + * + * **Example**: + * + * std::vector out; + * fmt::format_to(std::back_inserter(out), + * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + */ +template ::value)> +inline auto format_to(OutputIt out, text_style ts, format_string fmt, + T&&... args) -> OutputIt { + return vformat_to(out, ts, fmt.str, vargs{{args...}}); } template @@ -578,47 +586,44 @@ struct formatter, Char> : formatter { auto format(const detail::styled_arg& arg, FormatContext& ctx) const -> decltype(ctx.out()) { const auto& ts = arg.style; - const auto& value = arg.value; auto out = ctx.out(); bool has_style = false; if (ts.has_emphasis()) { has_style = true; auto emphasis = detail::make_emphasis(ts.get_emphasis()); - out = std::copy(emphasis.begin(), emphasis.end(), out); + out = detail::copy(emphasis.begin(), emphasis.end(), out); } if (ts.has_foreground()) { has_style = true; auto foreground = detail::make_foreground_color(ts.get_foreground()); - out = std::copy(foreground.begin(), foreground.end(), out); + out = detail::copy(foreground.begin(), foreground.end(), out); } if (ts.has_background()) { has_style = true; auto background = detail::make_background_color(ts.get_background()); - out = std::copy(background.begin(), background.end(), out); + out = detail::copy(background.begin(), background.end(), out); } - out = formatter::format(value, ctx); + out = formatter::format(arg.value, ctx); if (has_style) { auto reset_color = string_view("\x1b[0m"); - out = std::copy(reset_color.begin(), reset_color.end(), out); + out = detail::copy(reset_color.begin(), reset_color.end(), out); } return out; } }; /** - \rst - Returns an argument that will be formatted using ANSI escape sequences, - to be used in a formatting function. - - **Example**:: - - fmt::print("Elapsed time: {0:.2f} seconds", - fmt::styled(1.23, fmt::fg(fmt::color::green) | - fmt::bg(fmt::color::blue))); - \endrst + * Returns an argument that will be formatted using ANSI escape sequences, + * to be used in a formatting function. + * + * **Example**: + * + * fmt::print("Elapsed time: {0:.2f} seconds", + * fmt::styled(1.23, fmt::fg(fmt::color::green) | + * fmt::bg(fmt::color::blue))); */ template FMT_CONSTEXPR auto styled(const T& value, text_style ts) diff --git a/deps/fmt/include/fmt/compile.h b/deps/fmt/include/fmt/compile.h index a4c7e4956..64eb7a20d 100644 --- a/deps/fmt/include/fmt/compile.h +++ b/deps/fmt/include/fmt/compile.h @@ -8,16 +8,14 @@ #ifndef FMT_COMPILE_H_ #define FMT_COMPILE_H_ +#ifndef FMT_MODULE +# include // std::back_inserter +#endif + #include "format.h" FMT_BEGIN_NAMESPACE -namespace detail { - -template -FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, - counting_iterator it) { - return it + (end - begin); -} +FMT_BEGIN_EXPORT // A compile-time string which is compiled into fast formatting code. class compiled_string {}; @@ -26,48 +24,58 @@ template struct is_compiled_string : std::is_base_of {}; /** - \rst - Converts a string literal *s* into a format string that will be parsed at - compile time and converted into efficient formatting code. Requires C++17 - ``constexpr if`` compiler support. - - **Example**:: - - // Converts 42 into std::string using the most efficient method and no - // runtime format string processing. - std::string s = fmt::format(FMT_COMPILE("{}"), 42); - \endrst + * Converts a string literal `s` into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires C++17 + * `constexpr if` compiler support. + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * std::string s = fmt::format(FMT_COMPILE("{}"), 42); */ #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -# define FMT_COMPILE(s) \ - FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) +# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) #else # define FMT_COMPILE(s) FMT_STRING(s) #endif +/** + * Converts a string literal into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires support + * for class types in constant template parameters (a C++20 feature). + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * using namespace fmt::literals; + * std::string s = fmt::format("{}"_cf, 42); + */ #if FMT_USE_NONTYPE_TEMPLATE_ARGS -template Str> -struct udl_compiled_string : compiled_string { - using char_type = Char; - explicit constexpr operator basic_string_view() const { - return {Str.data, N - 1}; - } -}; +inline namespace literals { +template constexpr auto operator""_cf() { + return FMT_COMPILE(Str.data); +} +} // namespace literals #endif +FMT_END_EXPORT + +namespace detail { + template -const T& first(const T& value, const Tail&...) { +constexpr auto first(const T& value, const Tail&...) -> const T& { return value; } #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template struct type_list {}; +template struct type_list {}; // Returns a reference to the argument at index N from [first, rest...]. template -constexpr const auto& get([[maybe_unused]] const T& first, - [[maybe_unused]] const Args&... rest) { +constexpr auto get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) -> const auto& { static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); if constexpr (N == 0) return first; @@ -75,9 +83,32 @@ constexpr const auto& get([[maybe_unused]] const T& first, return detail::get(rest...); } +# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (is_static_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return -1; +} +# endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +# if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +# endif + (void)name; + return -1; +} + template -constexpr int get_arg_index_by_name(basic_string_view name, - type_list) { +constexpr auto get_arg_index_by_name(basic_string_view name, + type_list) -> int { return get_arg_index_by_name(name); } @@ -97,8 +128,8 @@ template struct text { basic_string_view data; using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&...) const { + template + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { return write(out, data); } }; @@ -107,8 +138,8 @@ template struct is_compiled_format> : std::true_type {}; template -constexpr text make_text(basic_string_view s, size_t pos, - size_t size) { +constexpr auto make_text(basic_string_view s, size_t pos, size_t size) + -> text { return {{&s[pos], size}}; } @@ -116,8 +147,8 @@ template struct code_unit { Char value; using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&...) const { + template + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { *out++ = value; return out; } @@ -125,7 +156,7 @@ template struct code_unit { // This ensures that the argument type is convertible to `const T&`. template -constexpr const T& get_arg_checked(const Args&... args) { +constexpr auto get_arg_checked(const Args&... args) -> const T& { const auto& arg = detail::get(args...); if constexpr (detail::is_named_arg>()) { return arg.value; @@ -138,17 +169,18 @@ template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N. -template struct field { +template struct field { using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { - const T& arg = get_arg_checked(args...); - if constexpr (std::is_convertible_v>) { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { + const V& arg = get_arg_checked(args...); + if constexpr (std::is_convertible>::value) { auto s = basic_string_view(arg); - return copy_str(s.begin(), s.end(), out); + return copy(s.begin(), s.end(), out); + } else { + return write(out, arg); } - return write(out, arg); } }; @@ -161,10 +193,10 @@ template struct runtime_named_field { basic_string_view name; template - constexpr static bool try_format_argument( + constexpr static auto try_format_argument( OutputIt& out, // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 - [[maybe_unused]] basic_string_view arg_name, const T& arg) { + [[maybe_unused]] basic_string_view arg_name, const T& arg) -> bool { if constexpr (is_named_arg::type>::value) { if (arg_name == arg.name) { out = write(out, arg.value); @@ -174,8 +206,8 @@ template struct runtime_named_field { return false; } - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { bool found = (try_format_argument(out, name, args) || ...); if (!found) { FMT_THROW(format_error("argument with specified name is not found")); @@ -188,17 +220,17 @@ template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N and has format specifiers. -template struct spec_field { +template struct spec_field { using char_type = Char; - formatter fmt; + formatter fmt; - template - constexpr FMT_INLINE OutputIt format(OutputIt out, - const Args&... args) const { + template + constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const + -> OutputIt { const auto& vargs = fmt::make_format_args>(args...); basic_format_context ctx(out, vargs); - return fmt.format(get_arg_checked(args...), ctx); + return fmt.format(get_arg_checked(args...), ctx); } }; @@ -210,8 +242,8 @@ template struct concat { R rhs; using char_type = typename L::char_type; - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { out = lhs.format(out, args...); return rhs.format(out, args...); } @@ -221,14 +253,14 @@ template struct is_compiled_format> : std::true_type {}; template -constexpr concat make_concat(L lhs, R rhs) { +constexpr auto make_concat(L lhs, R rhs) -> concat { return {lhs, rhs}; } struct unknown_format {}; template -constexpr size_t parse_text(basic_string_view str, size_t pos) { +constexpr auto parse_text(basic_string_view str, size_t pos) -> size_t { for (size_t size = str.size(); pos != size; ++pos) { if (str[pos] == '{' || str[pos] == '}') break; } @@ -236,13 +268,12 @@ constexpr size_t parse_text(basic_string_view str, size_t pos) { } template -constexpr auto compile_format_string(S format_str); +constexpr auto compile_format_string(S fmt); template -constexpr auto parse_tail(T head, S format_str) { - if constexpr (POS != - basic_string_view(format_str).size()) { - constexpr auto tail = compile_format_string(format_str); +constexpr auto parse_tail(T head, S fmt) { + if constexpr (POS != basic_string_view(fmt).size()) { + constexpr auto tail = compile_format_string(fmt); if constexpr (std::is_same, unknown_format>()) return tail; @@ -262,8 +293,8 @@ template struct parse_specs_result { enum { manual_indexing_id = -1 }; template -constexpr parse_specs_result parse_specs(basic_string_view str, - size_t pos, int next_arg_id) { +constexpr auto parse_specs(basic_string_view str, size_t pos, + int next_arg_id) -> parse_specs_result { str.remove_prefix(pos); auto ctx = compile_parse_context(str, max_value(), nullptr, next_arg_id); @@ -274,32 +305,36 @@ constexpr parse_specs_result parse_specs(basic_string_view str, } template struct arg_id_handler { + arg_id_kind kind; arg_ref arg_id; - constexpr int on_auto() { + constexpr auto on_auto() -> int { FMT_ASSERT(false, "handler cannot be used with automatic indexing"); return 0; } - constexpr int on_index(int id) { + constexpr auto on_index(int id) -> int { + kind = arg_id_kind::index; arg_id = arg_ref(id); return 0; } - constexpr int on_name(basic_string_view id) { + constexpr auto on_name(basic_string_view id) -> int { + kind = arg_id_kind::name; arg_id = arg_ref(id); return 0; } }; template struct parse_arg_id_result { + arg_id_kind kind; arg_ref arg_id; const Char* arg_id_end; }; template constexpr auto parse_arg_id(const Char* begin, const Char* end) { - auto handler = arg_id_handler{arg_ref{}}; + auto handler = arg_id_handler{arg_id_kind::none, arg_ref{}}; auto arg_id_end = parse_arg_id(begin, end, handler); - return parse_arg_id_result{handler.arg_id, arg_id_end}; + return parse_arg_id_result{handler.kind, handler.arg_id, arg_id_end}; } template struct field_type { @@ -313,14 +348,13 @@ struct field_type::value>> { template -constexpr auto parse_replacement_field_then_tail(S format_str) { +constexpr auto parse_replacement_field_then_tail(S fmt) { using char_type = typename S::char_type; - constexpr auto str = basic_string_view(format_str); + constexpr auto str = basic_string_view(fmt); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); if constexpr (c == '}') { return parse_tail( - field::type, ARG_INDEX>(), - format_str); + field::type, ARG_INDEX>(), fmt); } else if constexpr (c != ':') { FMT_THROW(format_error("expected ':'")); } else { @@ -333,7 +367,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { return parse_tail( spec_field::type, ARG_INDEX>{ result.fmt}, - format_str); + fmt); } } } @@ -341,22 +375,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { // Compiles a non-empty format string and returns the compiled representation // or unknown_format() on unrecognized input. template -constexpr auto compile_format_string(S format_str) { +constexpr auto compile_format_string(S fmt) { using char_type = typename S::char_type; - constexpr auto str = basic_string_view(format_str); + constexpr auto str = basic_string_view(fmt); if constexpr (str[POS] == '{') { if constexpr (POS + 1 == str.size()) FMT_THROW(format_error("unmatched '{' in format string")); if constexpr (str[POS + 1] == '{') { - return parse_tail(make_text(str, POS, 1), format_str); + return parse_tail(make_text(str, POS, 1), fmt); } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { static_assert(ID != manual_indexing_id, "cannot switch from manual to automatic argument indexing"); constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id; return parse_replacement_field_then_tail, Args, - POS + 1, ID, next_id>( - format_str); + POS + 1, ID, next_id>(fmt); } else { constexpr auto arg_id_result = parse_arg_id(str.data() + POS + 1, str.data() + str.size()); @@ -364,28 +397,27 @@ constexpr auto compile_format_string(S format_str) { constexpr char_type c = arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); static_assert(c == '}' || c == ':', "missing '}' in format string"); - if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { + if constexpr (arg_id_result.kind == arg_id_kind::index) { static_assert( ID == manual_indexing_id || ID == 0, "cannot switch from automatic to manual argument indexing"); - constexpr auto arg_index = arg_id_result.arg_id.val.index; + constexpr auto arg_index = arg_id_result.arg_id.index; return parse_replacement_field_then_tail, Args, arg_id_end_pos, arg_index, manual_indexing_id>( - format_str); - } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { + fmt); + } else if constexpr (arg_id_result.kind == arg_id_kind::name) { constexpr auto arg_index = - get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); + get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); if constexpr (arg_index >= 0) { constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id; return parse_replacement_field_then_tail< decltype(get_type::value), Args, arg_id_end_pos, - arg_index, next_id>(format_str); + arg_index, next_id>(fmt); } else if constexpr (c == '}') { return parse_tail( - runtime_named_field{arg_id_result.arg_id.val.name}, - format_str); + runtime_named_field{arg_id_result.arg_id.name}, fmt); } else if constexpr (c == ':') { return unknown_format(); // no type info for specs parsing } @@ -394,29 +426,26 @@ constexpr auto compile_format_string(S format_str) { } else if constexpr (str[POS] == '}') { if constexpr (POS + 1 == str.size()) FMT_THROW(format_error("unmatched '}' in format string")); - return parse_tail(make_text(str, POS, 1), format_str); + return parse_tail(make_text(str, POS, 1), fmt); } else { constexpr auto end = parse_text(str, POS + 1); if constexpr (end - POS > 1) { - return parse_tail(make_text(str, POS, end - POS), - format_str); + return parse_tail(make_text(str, POS, end - POS), fmt); } else { - return parse_tail(code_unit{str[POS]}, - format_str); + return parse_tail(code_unit{str[POS]}, fmt); } } } template ::value)> -constexpr auto compile(S format_str) { - constexpr auto str = basic_string_view(format_str); + FMT_ENABLE_IF(is_compiled_string::value)> +constexpr auto compile(S fmt) { + constexpr auto str = basic_string_view(fmt); if constexpr (str.size() == 0) { return detail::make_text(str, 0, 0); } else { constexpr auto result = - detail::compile_format_string, 0, 0>( - format_str); + detail::compile_format_string, 0, 0>(fmt); return result; } } @@ -427,27 +456,28 @@ FMT_BEGIN_EXPORT #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template ::value)> -FMT_INLINE std::basic_string format(const CompiledFormat& cf, - const Args&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf, + const T&... args) + -> std::basic_string { auto s = std::basic_string(); cf.format(std::back_inserter(s), args...); return s; } -template ::value)> -constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { +constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf, + const T&... args) -> OutputIt { return cf.format(out, args...); } -template ::value)> -FMT_INLINE std::basic_string format(const S&, - Args&&... args) { +template ::value)> +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args) + -> std::basic_string { if constexpr (std::is_same::value) { constexpr auto str = basic_string_view(S()); if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { @@ -460,73 +490,97 @@ FMT_INLINE std::basic_string format(const S&, } } } - constexpr auto compiled = detail::compile(S()); + constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format( static_cast>(S()), - std::forward(args)...); + std::forward(args)...); } else { - return fmt::format(compiled, std::forward(args)...); + return fmt::format(compiled, std::forward(args)...); } } -template ::value)> -FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { - constexpr auto compiled = detail::compile(S()); +template ::value)> +FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt { + constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format_to( out, static_cast>(S()), - std::forward(args)...); + std::forward(args)...); } else { - return fmt::format_to(out, compiled, std::forward(args)...); + return fmt::format_to(out, compiled, std::forward(args)...); } } #endif -template ::value)> -format_to_n_result format_to_n(OutputIt out, size_t n, - const S& format_str, Args&&... args) { +template ::value)> +auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) + -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); - format_to(std::back_inserter(buf), format_str, std::forward(args)...); + fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); return {buf.out(), buf.count()}; } -template ::value)> -FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, - const Args&... args) { - return fmt::format_to(detail::counting_iterator(), format_str, args...) - .count(); +template ::value)> +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + fmt::format_to(appender(buf), fmt, std::forward(args)...); + return buf.count(); } -template ::value)> -void print(std::FILE* f, const S& format_str, const Args&... args) { - memory_buffer buffer; - fmt::format_to(std::back_inserter(buffer), format_str, args...); - detail::print(f, {buffer.data(), buffer.size()}); +template ::value)> +void print(std::FILE* f, const S& fmt, T&&... args) { + auto buf = memory_buffer(); + fmt::format_to(appender(buf), fmt, std::forward(args)...); + detail::print(f, {buf.data(), buf.size()}); } -template ::value)> -void print(const S& format_str, const Args&... args) { - print(stdout, format_str, args...); +template ::value)> +void print(const S& fmt, T&&... args) { + print(stdout, fmt, std::forward(args)...); } -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -inline namespace literals { -template constexpr auto operator""_cf() { - using char_t = remove_cvref_t; - return detail::udl_compiled_string(); -} -} // namespace literals -#endif +template class static_format_result { + private: + char data[N]; + + public: + template ::value)> + explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) { + *fmt::format_to(data, fmt, std::forward(args)...) = '\0'; + } + + auto str() const -> fmt::string_view { return {data, N - 1}; } + auto c_str() const -> const char* { return data; } +}; + +/** + * Formats arguments according to the format string `fmt_str` and produces + * a string of the exact required size at compile time. Both the format string + * and the arguments must be compile-time expressions. + * + * The resulting string can be accessed as a C string via `c_str()` or as + * a `fmt::string_view` via `str()`. + * + * **Example**: + * + * // Produces the static string "42" at compile time. + * static constexpr auto result = FMT_STATIC_FORMAT("{}", 42); + * const char* s = result.c_str(); + */ +#define FMT_STATIC_FORMAT(fmt_str, ...) \ + fmt::static_format_result< \ + fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \ + FMT_COMPILE(fmt_str), __VA_ARGS__) FMT_END_EXPORT FMT_END_NAMESPACE diff --git a/deps/fmt/include/fmt/core.h b/deps/fmt/include/fmt/core.h index f9e3b7d6d..8ca735f0c 100644 --- a/deps/fmt/include/fmt/core.h +++ b/deps/fmt/include/fmt/core.h @@ -1,2922 +1,5 @@ -// Formatting library for C++ - the core API for char/UTF-8 -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. +// This file is only provided for compatibility and may be removed in future +// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h +// otherwise. -#ifndef FMT_CORE_H_ -#define FMT_CORE_H_ - -#include // std::byte -#include // std::FILE -#include // std::strlen -#include -#include -#include // std::addressof -#include -#include - -// The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 100101 - -#if defined(__clang__) && !defined(__ibmxl__) -# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -#else -# define FMT_CLANG_VERSION 0 -#endif - -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ - !defined(__NVCOMPILER) -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#else -# define FMT_GCC_VERSION 0 -#endif - -#ifndef FMT_GCC_PRAGMA -// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. -# if FMT_GCC_VERSION >= 504 -# define FMT_GCC_PRAGMA(arg) _Pragma(arg) -# else -# define FMT_GCC_PRAGMA(arg) -# endif -#endif - -#ifdef __ICL -# define FMT_ICC_VERSION __ICL -#elif defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER -#else -# define FMT_ICC_VERSION 0 -#endif - -#ifdef _MSC_VER -# define FMT_MSC_VERSION _MSC_VER -# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) -#else -# define FMT_MSC_VERSION 0 -# define FMT_MSC_WARNING(...) -#endif - -#ifdef _MSVC_LANG -# define FMT_CPLUSPLUS _MSVC_LANG -#else -# define FMT_CPLUSPLUS __cplusplus -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900 -# define FMT_HAS_INCLUDE(x) __has_include(x) -#else -# define FMT_HAS_INCLUDE(x) 0 -#endif - -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ - (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - -#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ - (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - -// Check if relaxed C++14 constexpr is supported. -// GCC doesn't allow throw in constexpr until version 6 (bug 67371). -#ifndef FMT_USE_CONSTEXPR -# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ - (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ - !FMT_ICC_VERSION && (!defined(__NVCC__) || FMT_CPLUSPLUS >= 202002L) -# define FMT_USE_CONSTEXPR 1 -# else -# define FMT_USE_CONSTEXPR 0 -# endif -#endif -#if FMT_USE_CONSTEXPR -# define FMT_CONSTEXPR constexpr -#else -# define FMT_CONSTEXPR -#endif - -#if ((FMT_CPLUSPLUS >= 202002L) && \ - (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ - (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) -# define FMT_CONSTEXPR20 constexpr -#else -# define FMT_CONSTEXPR20 -#endif - -// Check if constexpr std::char_traits<>::{compare,length} are supported. -#if defined(__GLIBCXX__) -# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -# endif -#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ - _LIBCPP_VERSION >= 4000 -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#endif -#ifndef FMT_CONSTEXPR_CHAR_TRAITS -# define FMT_CONSTEXPR_CHAR_TRAITS -#endif - -// Check if exceptions are disabled. -#ifndef FMT_EXCEPTIONS -# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) -# define FMT_EXCEPTIONS 0 -# else -# define FMT_EXCEPTIONS 1 -# endif -#endif - -// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ - !defined(__NVCC__) -# define FMT_NORETURN [[noreturn]] -#else -# define FMT_NORETURN -#endif - -#ifndef FMT_NODISCARD -# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) -# define FMT_NODISCARD [[nodiscard]] -# else -# define FMT_NODISCARD -# endif -#endif - -#ifndef FMT_INLINE -# if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_INLINE inline __attribute__((always_inline)) -# else -# define FMT_INLINE inline -# endif -#endif - -#ifdef _MSC_VER -# define FMT_UNCHECKED_ITERATOR(It) \ - using _Unchecked_type = It // Mark iterator as checked. -#else -# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It -#endif - -#ifndef FMT_BEGIN_NAMESPACE -# define FMT_BEGIN_NAMESPACE \ - namespace fmt { \ - inline namespace v10 { -# define FMT_END_NAMESPACE \ - } \ - } -#endif - -#ifndef FMT_EXPORT -# define FMT_EXPORT -# define FMT_BEGIN_EXPORT -# define FMT_END_EXPORT -#endif - -#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# ifdef FMT_LIB_EXPORT -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif -#else -# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) -# if defined(__GNUC__) || defined(__clang__) -# define FMT_API __attribute__((visibility("default"))) -# endif -# endif -#endif -#ifndef FMT_API -# define FMT_API -#endif - -// libc++ supports string_view in pre-c++17. -#if FMT_HAS_INCLUDE() && \ - (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) -# include -# define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L -# include -# define FMT_USE_EXPERIMENTAL_STRING_VIEW -#endif - -#ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VERSION -#endif - -#ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - (!defined(__apple_build_version__) || \ - __apple_build_version__ >= 14000029L) && \ - FMT_CPLUSPLUS >= 202002L) || \ - (defined(__cpp_consteval) && \ - (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) -// consteval is broken in MSVC before VS2022 and Apple clang before 14. -# define FMT_CONSTEVAL consteval -# define FMT_HAS_CONSTEVAL -# else -# define FMT_CONSTEVAL -# endif -#endif - -#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ - __cpp_nontype_template_args >= 201911L) && \ - !defined(__NVCOMPILER) && !defined(__LCC__) -# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 -# else -# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 -# endif -#endif - -// Enable minimal optimizations for more compact code in debug mode. -FMT_GCC_PRAGMA("GCC push_options") -#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ - !defined(__CUDACC__) -FMT_GCC_PRAGMA("GCC optimize(\"Og\")") -#endif - -FMT_BEGIN_NAMESPACE - -// Implementations of enable_if_t and other metafunctions for older systems. -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; -template using bool_constant = std::integral_constant; -template -using remove_reference_t = typename std::remove_reference::type; -template -using remove_const_t = typename std::remove_const::type; -template -using remove_cvref_t = typename std::remove_cv>::type; -template struct type_identity { using type = T; }; -template using type_identity_t = typename type_identity::type; -template -using underlying_t = typename std::underlying_type::type; - -// Checks whether T is a container with contiguous storage. -template struct is_contiguous : std::false_type {}; -template -struct is_contiguous> : std::true_type {}; - -struct monostate { - constexpr monostate() {} -}; - -// An enable_if helper to be used in template parameters which results in much -// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed -// to workaround a bug in MSVC 2019 (see #1140 and #1186). -#ifdef FMT_DOC -# define FMT_ENABLE_IF(...) -#else -# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 -#endif - -// This is defined in core.h instead of format.h to avoid injecting in std. -// It is a template to avoid undesirable implicit conversions to std::byte. -#ifdef __cpp_lib_byte -template ::value)> -inline auto format_as(T b) -> unsigned char { - return static_cast(b); -} -#endif - -namespace detail { -// Suppresses "unused variable" warnings with the method described in -// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. -// (void)var does not work on many Intel compilers. -template FMT_CONSTEXPR void ignore_unused(const T&...) {} - -constexpr FMT_INLINE auto is_constant_evaluated( - bool default_value = false) noexcept -> bool { -// Workaround for incompatibility between libstdc++ consteval-based -// std::is_constant_evaluated() implementation and clang-14. -// https://github.com/fmtlib/fmt/issues/3247 -#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 12 && \ - (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) - ignore_unused(default_value); - return __builtin_is_constant_evaluated(); -#elif defined(__cpp_lib_is_constant_evaluated) - ignore_unused(default_value); - return std::is_constant_evaluated(); -#else - return default_value; -#endif -} - -// Suppresses "conditional expression is constant" warnings. -template constexpr FMT_INLINE auto const_check(T value) -> T { - return value; -} - -FMT_NORETURN FMT_API void assert_fail(const char* file, int line, - const char* message); - -#ifndef FMT_ASSERT -# ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Wempty-body. -# define FMT_ASSERT(condition, message) \ - fmt::detail::ignore_unused((condition), (message)) -# else -# define FMT_ASSERT(condition, message) \ - ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ - ? (void)0 \ - : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) -# endif -#endif - -#if defined(FMT_USE_STRING_VIEW) -template using std_string_view = std::basic_string_view; -#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) -template -using std_string_view = std::experimental::basic_string_view; -#else -template struct std_string_view {}; -#endif - -#ifdef FMT_USE_INT128 -// Do nothing. -#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ - !(FMT_CLANG_VERSION && FMT_MSC_VERSION) -# define FMT_USE_INT128 1 -using int128_opt = __int128_t; // An optional native 128-bit integer. -using uint128_opt = __uint128_t; -template inline auto convert_for_visit(T value) -> T { - return value; -} -#else -# define FMT_USE_INT128 0 -#endif -#if !FMT_USE_INT128 -enum class int128_opt {}; -enum class uint128_opt {}; -// Reduce template instantiations. -template auto convert_for_visit(T) -> monostate { return {}; } -#endif - -// Casts a nonnegative integer to unsigned. -template -FMT_CONSTEXPR auto to_unsigned(Int value) -> - typename std::make_unsigned::type { - FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); - return static_cast::type>(value); -} - -FMT_CONSTEXPR inline auto is_utf8() -> bool { - FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; - - // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). - using uchar = unsigned char; - return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && - uchar(section[1]) == 0xA7); -} -} // namespace detail - -/** - An implementation of ``std::basic_string_view`` for pre-C++17. It provides a - subset of the API. ``fmt::basic_string_view`` is used for format strings even - if ``std::string_view`` is available to prevent issues when a library is - compiled with a different ``-std`` option than the client code (which is not - recommended). - */ -FMT_EXPORT -template class basic_string_view { - private: - const Char* data_; - size_t size_; - - public: - using value_type = Char; - using iterator = const Char*; - - constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} - - /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char* s, size_t count) noexcept - : data_(s), size_(count) {} - - /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ - FMT_CONSTEXPR_CHAR_TRAITS - FMT_INLINE - basic_string_view(const Char* s) - : data_(s), - size_(detail::const_check(std::is_same::value && - !detail::is_constant_evaluated(true)) - ? std::strlen(reinterpret_cast(s)) - : std::char_traits::length(s)) {} - - /** Constructs a string reference from a ``std::basic_string`` object. */ - template - FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) noexcept - : data_(s.data()), size_(s.size()) {} - - template >::value)> - FMT_CONSTEXPR basic_string_view(S s) noexcept - : data_(s.data()), size_(s.size()) {} - - /** Returns a pointer to the string data. */ - constexpr auto data() const noexcept -> const Char* { return data_; } - - /** Returns the string size. */ - constexpr auto size() const noexcept -> size_t { return size_; } - - constexpr auto begin() const noexcept -> iterator { return data_; } - constexpr auto end() const noexcept -> iterator { return data_ + size_; } - - constexpr auto operator[](size_t pos) const noexcept -> const Char& { - return data_[pos]; - } - - FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { - data_ += n; - size_ -= n; - } - - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with( - basic_string_view sv) const noexcept { - return size_ >= sv.size_ && - std::char_traits::compare(data_, sv.data_, sv.size_) == 0; - } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept { - return size_ >= 1 && std::char_traits::eq(*data_, c); - } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const { - return starts_with(basic_string_view(s)); - } - - // Lexicographically compare this string reference to other. - FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { - size_t str_size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, str_size); - if (result == 0) - result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); - return result; - } - - FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, - basic_string_view rhs) - -> bool { - return lhs.compare(rhs) == 0; - } - friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) != 0; - } - friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) < 0; - } - friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) <= 0; - } - friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) > 0; - } - friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) >= 0; - } -}; - -FMT_EXPORT -using string_view = basic_string_view; - -/** Specifies if ``T`` is a character type. Can be specialized by users. */ -FMT_EXPORT -template struct is_char : std::false_type {}; -template <> struct is_char : std::true_type {}; - -namespace detail { - -// A base class for compile-time strings. -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - -template ::value)> -FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { - return s; -} -template -inline auto to_string_view(const std::basic_string& s) - -> basic_string_view { - return s; -} -template -constexpr auto to_string_view(basic_string_view s) - -> basic_string_view { - return s; -} -template >::value)> -inline auto to_string_view(std_string_view s) -> basic_string_view { - return s; -} -template ::value)> -constexpr auto to_string_view(const S& s) - -> basic_string_view { - return basic_string_view(s); -} -void to_string_view(...); - -// Specifies whether S is a string type convertible to fmt::basic_string_view. -// It should be a constexpr function but MSVC 2017 fails to compile it in -// enable_if and MSVC 2015 fails to compile it as an alias template. -// ADL is intentionally disabled as to_string_view is not an extension point. -template -struct is_string - : std::is_class()))> {}; - -template struct char_t_impl {}; -template struct char_t_impl::value>> { - using result = decltype(to_string_view(std::declval())); - using type = typename result::value_type; -}; - -enum class type { - none_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant \ - : std::integral_constant {} - -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_opt, int128_type); -FMT_TYPE_CONSTANT(uint128_opt, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -constexpr bool is_integral_type(type t) { - return t > type::none_type && t <= type::last_integer_type; -} -constexpr bool is_arithmetic_type(type t) { - return t > type::none_type && t <= type::last_numeric_type; -} - -constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } -constexpr auto in(type t, int set) -> bool { - return ((set >> static_cast(t)) & 1) != 0; -} - -// Bitsets of types. -enum { - sint_set = - set(type::int_type) | set(type::long_long_type) | set(type::int128_type), - uint_set = set(type::uint_type) | set(type::ulong_long_type) | - set(type::uint128_type), - bool_set = set(type::bool_type), - char_set = set(type::char_type), - float_set = set(type::float_type) | set(type::double_type) | - set(type::long_double_type), - string_set = set(type::string_type), - cstring_set = set(type::cstring_type), - pointer_set = set(type::pointer_type) -}; - -FMT_NORETURN FMT_API void throw_format_error(const char* message); - -struct error_handler { - constexpr error_handler() = default; - - // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN void on_error(const char* message) { - throw_format_error(message); - } -}; -} // namespace detail - -/** Throws ``format_error`` with a given message. */ -using detail::throw_format_error; - -/** String's character type. */ -template using char_t = typename detail::char_t_impl::type; - -/** - \rst - Parsing context consisting of a format string range being parsed and an - argument counter for automatic indexing. - You can use the ``format_parse_context`` type alias for ``char`` instead. - \endrst - */ -FMT_EXPORT -template class basic_format_parse_context { - private: - basic_string_view format_str_; - int next_arg_id_; - - FMT_CONSTEXPR void do_check_arg_id(int id); - - public: - using char_type = Char; - using iterator = const Char*; - - explicit constexpr basic_format_parse_context( - basic_string_view format_str, int next_arg_id = 0) - : format_str_(format_str), next_arg_id_(next_arg_id) {} - - /** - Returns an iterator to the beginning of the format string range being - parsed. - */ - constexpr auto begin() const noexcept -> iterator { - return format_str_.begin(); - } - - /** - Returns an iterator past the end of the format string range being parsed. - */ - constexpr auto end() const noexcept -> iterator { return format_str_.end(); } - - /** Advances the begin iterator to ``it``. */ - FMT_CONSTEXPR void advance_to(iterator it) { - format_str_.remove_prefix(detail::to_unsigned(it - begin())); - } - - /** - Reports an error if using the manual argument indexing; otherwise returns - the next argument index and switches to the automatic indexing. - */ - FMT_CONSTEXPR auto next_arg_id() -> int { - if (next_arg_id_ < 0) { - detail::throw_format_error( - "cannot switch from manual to automatic argument indexing"); - return 0; - } - int id = next_arg_id_++; - do_check_arg_id(id); - return id; - } - - /** - Reports an error if using the automatic argument indexing; otherwise - switches to the manual indexing. - */ - FMT_CONSTEXPR void check_arg_id(int id) { - if (next_arg_id_ > 0) { - detail::throw_format_error( - "cannot switch from automatic to manual argument indexing"); - return; - } - next_arg_id_ = -1; - do_check_arg_id(id); - } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} - FMT_CONSTEXPR void check_dynamic_spec(int arg_id); -}; - -FMT_EXPORT -using format_parse_context = basic_format_parse_context; - -namespace detail { -// A parse context with extra data used only in compile-time checks. -template -class compile_parse_context : public basic_format_parse_context { - private: - int num_args_; - const type* types_; - using base = basic_format_parse_context; - - public: - explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, int num_args, const type* types, - int next_arg_id = 0) - : base(format_str, next_arg_id), num_args_(num_args), types_(types) {} - - constexpr auto num_args() const -> int { return num_args_; } - constexpr auto arg_type(int id) const -> type { return types_[id]; } - - FMT_CONSTEXPR auto next_arg_id() -> int { - int id = base::next_arg_id(); - if (id >= num_args_) throw_format_error("argument not found"); - return id; - } - - FMT_CONSTEXPR void check_arg_id(int id) { - base::check_arg_id(id); - if (id >= num_args_) throw_format_error("argument not found"); - } - using base::check_arg_id; - - FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { - detail::ignore_unused(arg_id); -#if !defined(__LCC__) - if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) - throw_format_error("width/precision is not integer"); -#endif - } -}; - -// Extracts a reference to the container from back_insert_iterator. -template -inline auto get_container(std::back_insert_iterator it) - -> Container& { - using base = std::back_insert_iterator; - struct accessor : base { - accessor(base b) : base(b) {} - using base::container; - }; - return *accessor(it).container; -} - -template -FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) - -> OutputIt { - while (begin != end) *out++ = static_cast(*begin++); - return out; -} - -template , U>::value&& is_char::value)> -FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { - if (is_constant_evaluated()) return copy_str(begin, end, out); - auto size = to_unsigned(end - begin); - if (size > 0) memcpy(out, begin, size * sizeof(U)); - return out + size; -} - -/** - \rst - A contiguous memory buffer with an optional growing ability. It is an internal - class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. - \endrst - */ -template class buffer { - private: - T* ptr_; - size_t size_; - size_t capacity_; - - protected: - // Don't initialize ptr_ since it is not accessed to save a few cycles. - FMT_MSC_WARNING(suppress : 26495) - buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} - - FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept - : ptr_(p), size_(sz), capacity_(cap) {} - - FMT_CONSTEXPR20 ~buffer() = default; - buffer(buffer&&) = default; - - /** Sets the buffer data and capacity. */ - FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { - ptr_ = buf_data; - capacity_ = buf_capacity; - } - - /** Increases the buffer capacity to hold at least *capacity* elements. */ - virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; - - public: - using value_type = T; - using const_reference = const T&; - - buffer(const buffer&) = delete; - void operator=(const buffer&) = delete; - - FMT_INLINE auto begin() noexcept -> T* { return ptr_; } - FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; } - - FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; } - FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; } - - /** Returns the size of this buffer. */ - constexpr auto size() const noexcept -> size_t { return size_; } - - /** Returns the capacity of this buffer. */ - constexpr auto capacity() const noexcept -> size_t { return capacity_; } - - /** Returns a pointer to the buffer data (not null-terminated). */ - FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } - FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } - - /** Clears this buffer. */ - void clear() { size_ = 0; } - - // Tries resizing the buffer to contain *count* elements. If T is a POD type - // the new elements may not be initialized. - FMT_CONSTEXPR20 void try_resize(size_t count) { - try_reserve(count); - size_ = count <= capacity_ ? count : capacity_; - } - - // Tries increasing the buffer capacity to *new_capacity*. It can increase the - // capacity by a smaller amount than requested but guarantees there is space - // for at least one additional element either by increasing the capacity or by - // flushing the buffer if it is full. - FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { - if (new_capacity > capacity_) grow(new_capacity); - } - - FMT_CONSTEXPR20 void push_back(const T& value) { - try_reserve(size_ + 1); - ptr_[size_++] = value; - } - - /** Appends data to the end of the buffer. */ - template void append(const U* begin, const U* end); - - template FMT_CONSTEXPR auto operator[](Idx index) -> T& { - return ptr_[index]; - } - template - FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { - return ptr_[index]; - } -}; - -struct buffer_traits { - explicit buffer_traits(size_t) {} - auto count() const -> size_t { return 0; } - auto limit(size_t size) -> size_t { return size; } -}; - -class fixed_buffer_traits { - private: - size_t count_ = 0; - size_t limit_; - - public: - explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} - auto count() const -> size_t { return count_; } - auto limit(size_t size) -> size_t { - size_t n = limit_ > count_ ? limit_ - count_ : 0; - count_ += size; - return size < n ? size : n; - } -}; - -// A buffer that writes to an output iterator when flushed. -template -class iterator_buffer final : public Traits, public buffer { - private: - OutputIt out_; - enum { buffer_size = 256 }; - T data_[buffer_size]; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == buffer_size) flush(); - } - - void flush() { - auto size = this->size(); - this->clear(); - out_ = copy_str(data_, data_ + this->limit(size), out_); - } - - public: - explicit iterator_buffer(OutputIt out, size_t n = buffer_size) - : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} - iterator_buffer(iterator_buffer&& other) - : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} - ~iterator_buffer() { flush(); } - - auto out() -> OutputIt { - flush(); - return out_; - } - auto count() const -> size_t { return Traits::count() + this->size(); } -}; - -template -class iterator_buffer final - : public fixed_buffer_traits, - public buffer { - private: - T* out_; - enum { buffer_size = 256 }; - T data_[buffer_size]; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == this->capacity()) flush(); - } - - void flush() { - size_t n = this->limit(this->size()); - if (this->data() == out_) { - out_ += n; - this->set(data_, buffer_size); - } - this->clear(); - } - - public: - explicit iterator_buffer(T* out, size_t n = buffer_size) - : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} - iterator_buffer(iterator_buffer&& other) - : fixed_buffer_traits(other), - buffer(std::move(other)), - out_(other.out_) { - if (this->data() != out_) { - this->set(data_, buffer_size); - this->clear(); - } - } - ~iterator_buffer() { flush(); } - - auto out() -> T* { - flush(); - return out_; - } - auto count() const -> size_t { - return fixed_buffer_traits::count() + this->size(); - } -}; - -template class iterator_buffer final : public buffer { - protected: - FMT_CONSTEXPR20 void grow(size_t) override {} - - public: - explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} - - auto out() -> T* { return &*this->end(); } -}; - -// A buffer that writes to a container with the contiguous storage. -template -class iterator_buffer, - enable_if_t::value, - typename Container::value_type>> - final : public buffer { - private: - Container& container_; - - protected: - FMT_CONSTEXPR20 void grow(size_t capacity) override { - container_.resize(capacity); - this->set(&container_[0], capacity); - } - - public: - explicit iterator_buffer(Container& c) - : buffer(c.size()), container_(c) {} - explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) - : iterator_buffer(get_container(out)) {} - - auto out() -> std::back_insert_iterator { - return std::back_inserter(container_); - } -}; - -// A buffer that counts the number of code units written discarding the output. -template class counting_buffer final : public buffer { - private: - enum { buffer_size = 256 }; - T data_[buffer_size]; - size_t count_ = 0; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() != buffer_size) return; - count_ += this->size(); - this->clear(); - } - - public: - counting_buffer() : buffer(data_, 0, buffer_size) {} - - auto count() -> size_t { return count_ + this->size(); } -}; -} // namespace detail - -template -FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { - // Argument id is only checked at compile-time during parsing because - // formatting has its own validation. - if (detail::is_constant_evaluated() && - (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context; - if (id >= static_cast(this)->num_args()) - detail::throw_format_error("argument not found"); - } -} - -template -FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( - int arg_id) { - if (detail::is_constant_evaluated() && - (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context; - static_cast(this)->check_dynamic_spec(arg_id); - } -} - -FMT_EXPORT template class basic_format_arg; -FMT_EXPORT template class basic_format_args; -FMT_EXPORT template class dynamic_format_arg_store; - -// A formatter for objects of type T. -FMT_EXPORT -template -struct formatter { - // A deleted default constructor indicates a disabled formatter. - formatter() = delete; -}; - -// Specifies if T has an enabled formatter specialization. A type can be -// formattable even if it doesn't have a formatter e.g. via a conversion. -template -using has_formatter = - std::is_constructible>; - -// An output iterator that appends to a buffer. -// It is used to reduce symbol sizes for the common case. -class appender : public std::back_insert_iterator> { - using base = std::back_insert_iterator>; - - public: - using std::back_insert_iterator>::back_insert_iterator; - appender(base it) noexcept : base(it) {} - FMT_UNCHECKED_ITERATOR(appender); - - auto operator++() noexcept -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } -}; - -namespace detail { - -template -constexpr auto has_const_formatter_impl(T*) - -> decltype(typename Context::template formatter_type().format( - std::declval(), std::declval()), - true) { - return true; -} -template -constexpr auto has_const_formatter_impl(...) -> bool { - return false; -} -template -constexpr auto has_const_formatter() -> bool { - return has_const_formatter_impl(static_cast(nullptr)); -} - -template -using buffer_appender = conditional_t::value, appender, - std::back_insert_iterator>>; - -// Maps an output iterator to a buffer. -template -auto get_buffer(OutputIt out) -> iterator_buffer { - return iterator_buffer(out); -} -template , Buf>::value)> -auto get_buffer(std::back_insert_iterator out) -> buffer& { - return get_container(out); -} - -template -FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { - return buf.out(); -} -template -auto get_iterator(buffer&, OutputIt out) -> OutputIt { - return out; -} - -struct view {}; - -template struct named_arg : view { - const Char* name; - const T& value; - named_arg(const Char* n, const T& v) : name(n), value(v) {} -}; - -template struct named_arg_info { - const Char* name; - int id; -}; - -template -struct arg_data { - // args_[0].named_args points to named_args_ to avoid bloating format_args. - // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. - T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; - named_arg_info named_args_[NUM_NAMED_ARGS]; - - template - arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} - arg_data(const arg_data& other) = delete; - auto args() const -> const T* { return args_ + 1; } - auto named_args() -> named_arg_info* { return named_args_; } -}; - -template -struct arg_data { - // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. - T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; - - template - FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} - FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } - FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { - return nullptr; - } -}; - -template -inline void init_named_args(named_arg_info*, int, int) {} - -template struct is_named_arg : std::false_type {}; -template struct is_statically_named_arg : std::false_type {}; - -template -struct is_named_arg> : std::true_type {}; - -template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T&, const Tail&... args) { - init_named_args(named_args, arg_count + 1, named_arg_count, args...); -} - -template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T& arg, const Tail&... args) { - named_args[named_arg_count++] = {arg.name, arg_count}; - init_named_args(named_args, arg_count + 1, named_arg_count, args...); -} - -template -FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, - const Args&...) {} - -template constexpr auto count() -> size_t { return B ? 1 : 0; } -template constexpr auto count() -> size_t { - return (B1 ? 1 : 0) + count(); -} - -template constexpr auto count_named_args() -> size_t { - return count::value...>(); -} - -template -constexpr auto count_statically_named_args() -> size_t { - return count::value...>(); -} - -struct unformattable {}; -struct unformattable_char : unformattable {}; -struct unformattable_pointer : unformattable {}; - -template struct string_value { - const Char* data; - size_t size; -}; - -template struct named_arg_value { - const named_arg_info* data; - size_t size; -}; - -template struct custom_value { - using parse_context = typename Context::parse_context_type; - void* value; - void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); -}; - -// A formatting argument value. -template class value { - public: - using char_type = typename Context::char_type; - - union { - monostate no_value; - int int_value; - unsigned uint_value; - long long long_long_value; - unsigned long long ulong_long_value; - int128_opt int128_value; - uint128_opt uint128_value; - bool bool_value; - char_type char_value; - float float_value; - double double_value; - long double long_double_value; - const void* pointer; - string_value string; - custom_value custom; - named_arg_value named_args; - }; - - constexpr FMT_INLINE value() : no_value() {} - constexpr FMT_INLINE value(int val) : int_value(val) {} - constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} - constexpr FMT_INLINE value(long long val) : long_long_value(val) {} - constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} - FMT_INLINE value(int128_opt val) : int128_value(val) {} - FMT_INLINE value(uint128_opt val) : uint128_value(val) {} - constexpr FMT_INLINE value(float val) : float_value(val) {} - constexpr FMT_INLINE value(double val) : double_value(val) {} - FMT_INLINE value(long double val) : long_double_value(val) {} - constexpr FMT_INLINE value(bool val) : bool_value(val) {} - constexpr FMT_INLINE value(char_type val) : char_value(val) {} - FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { - string.data = val; - if (is_constant_evaluated()) string.size = {}; - } - FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { - string.data = val.data(); - string.size = val.size(); - } - FMT_INLINE value(const void* val) : pointer(val) {} - FMT_INLINE value(const named_arg_info* args, size_t size) - : named_args{args, size} {} - - template FMT_CONSTEXPR20 FMT_INLINE value(T& val) { - using value_type = remove_const_t; - custom.value = const_cast(std::addressof(val)); - // Get the formatter type through the context to allow different contexts - // have different extension points, e.g. `formatter` for `format` and - // `printf_formatter` for `printf`. - custom.format = format_custom_arg< - value_type, typename Context::template formatter_type>; - } - value(unformattable); - value(unformattable_char); - value(unformattable_pointer); - - private: - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg(void* arg, - typename Context::parse_context_type& parse_ctx, - Context& ctx) { - auto f = Formatter(); - parse_ctx.advance_to(f.parse(parse_ctx)); - using qualified_type = - conditional_t(), const T, T>; - ctx.advance_to(f.format(*static_cast(arg), ctx)); - } -}; - -// To minimize the number of types we need to deal with, long is translated -// either to int or to long long depending on its size. -enum { long_short = sizeof(long) == sizeof(int) }; -using long_type = conditional_t; -using ulong_type = conditional_t; - -template struct format_as_result { - template ::value || std::is_class::value)> - static auto map(U*) -> decltype(format_as(std::declval())); - static auto map(...) -> void; - - using type = decltype(map(static_cast(nullptr))); -}; -template using format_as_t = typename format_as_result::type; - -template -struct has_format_as - : bool_constant, void>::value> {}; - -// Maps formatting arguments to core types. -// arg_mapper reports errors by returning unformattable instead of using -// static_assert because it's used in the is_formattable trait. -template struct arg_mapper { - using char_type = typename Context::char_type; - - FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) - -> unsigned long long { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } - - template ::value || - std::is_same::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { - return val; - } - template ::value || -#ifdef __cpp_char8_t - std::is_same::value || -#endif - std::is_same::value || - std::is_same::value) && - !std::is_same::value, - int> = 0> - FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { - return {}; - } - - FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { - return val; - } - - FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { - return val; - } - template ::value && !std::is_pointer::value && - std::is_same>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { - return to_string_view(val); - } - template ::value && !std::is_pointer::value && - !std::is_same>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { - return {}; - } - - FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { - return val; - } - - // Use SFINAE instead of a const T* parameter to avoid a conflict with the - // array overload. - template < - typename T, - FMT_ENABLE_IF( - std::is_pointer::value || std::is_member_pointer::value || - std::is_function::type>::value || - (std::is_array::value && - !std::is_convertible::value))> - FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { - return {}; - } - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { - return values; - } - - // Only map owning types because mapping views can be unsafe. - template , - FMT_ENABLE_IF(std::is_arithmetic::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) { - return map(format_as(val)); - } - - template > - struct formattable : bool_constant() || - (has_formatter::value && - !std::is_const::value)> {}; - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& { - return val; - } - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable { - return {}; - } - - template , - FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || - std::is_union::value) && - !is_string::value && !is_char::value && - !is_named_arg::value && - !std::is_arithmetic>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) { - return do_map(val); - } - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) - -> decltype(this->map(named_arg.value)) { - return map(named_arg.value); - } - - auto map(...) -> unformattable { return {}; } -}; - -// A type constant after applying arg_mapper. -template -using mapped_type_constant = - type_constant().map(std::declval())), - typename Context::char_type>; - -enum { packed_arg_bits = 4 }; -// Maximum number of arguments with packed types. -enum { max_packed_args = 62 / packed_arg_bits }; -enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; -enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; - -template -auto copy_str(InputIt begin, InputIt end, appender out) -> appender { - get_container(out).append(begin, end); - return out; -} -template -auto copy_str(InputIt begin, InputIt end, - std::back_insert_iterator out) - -> std::back_insert_iterator { - get_container(out).append(begin, end); - return out; -} - -template -FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { - return detail::copy_str(rng.begin(), rng.end(), out); -} - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -// A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { using type = void; }; -template using void_t = typename void_t_impl::type; -#else -template using void_t = void; -#endif - -template -struct is_output_iterator : std::false_type {}; - -template -struct is_output_iterator< - It, T, - void_t::iterator_category, - decltype(*std::declval() = std::declval())>> - : std::true_type {}; - -template struct is_back_insert_iterator : std::false_type {}; -template -struct is_back_insert_iterator> - : std::true_type {}; - -// A type-erased reference to an std::locale to avoid a heavy include. -class locale_ref { - private: - const void* locale_; // A type-erased pointer to std::locale. - - public: - constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} - template explicit locale_ref(const Locale& loc); - - explicit operator bool() const noexcept { return locale_ != nullptr; } - - template auto get() const -> Locale; -}; - -template constexpr auto encode_types() -> unsigned long long { - return 0; -} - -template -constexpr auto encode_types() -> unsigned long long { - return static_cast(mapped_type_constant::value) | - (encode_types() << packed_arg_bits); -} - -#if defined(__cpp_if_constexpr) -// This type is intentionally undefined, only used for errors -template struct type_is_unformattable_for; -#endif - -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value { - using arg_type = remove_cvref_t().map(val))>; - - constexpr bool formattable_char = - !std::is_same::value; - static_assert(formattable_char, "Mixing character types is disallowed."); - - // Formatting of arbitrary pointers is disallowed. If you want to format a - // pointer cast it to `void*` or `const void*`. In particular, this forbids - // formatting of `[const] volatile char*` printed as bool by iostreams. - constexpr bool formattable_pointer = - !std::is_same::value; - static_assert(formattable_pointer, - "Formatting of non-void pointers is disallowed."); - - constexpr bool formattable = !std::is_same::value; -#if defined(__cpp_if_constexpr) - if constexpr (!formattable) { - type_is_unformattable_for _; - } -#endif - static_assert( - formattable, - "Cannot format an argument. To make type T formattable provide a " - "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return {arg_mapper().map(val)}; -} - -template -FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg { - auto arg = basic_format_arg(); - arg.type_ = mapped_type_constant::value; - arg.value_ = make_arg(val); - return arg; -} - -template -FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg { - return make_arg(val); -} -} // namespace detail -FMT_BEGIN_EXPORT - -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in basic_memory_buffer. -template class basic_format_arg { - private: - detail::value value_; - detail::type type_; - - template - friend FMT_CONSTEXPR auto detail::make_arg(T& value) - -> basic_format_arg; - - template - friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, - const basic_format_arg& arg) - -> decltype(vis(0)); - - friend class basic_format_args; - friend class dynamic_format_arg_store; - - using char_type = typename Context::char_type; - - template - friend struct detail::arg_data; - - basic_format_arg(const detail::named_arg_info* args, size_t size) - : value_(args, size) {} - - public: - class handle { - public: - explicit handle(detail::custom_value custom) : custom_(custom) {} - - void format(typename Context::parse_context_type& parse_ctx, - Context& ctx) const { - custom_.format(custom_.value, parse_ctx, ctx); - } - - private: - detail::custom_value custom_; - }; - - constexpr basic_format_arg() : type_(detail::type::none_type) {} - - constexpr explicit operator bool() const noexcept { - return type_ != detail::type::none_type; - } - - auto type() const -> detail::type { return type_; } - - auto is_integral() const -> bool { return detail::is_integral_type(type_); } - auto is_arithmetic() const -> bool { - return detail::is_arithmetic_type(type_); - } -}; - -/** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - ``vis(value)`` will be called with the value of type ``double``. - \endrst - */ -// DEPRECATED! -template -FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( - Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { - switch (arg.type_) { - case detail::type::none_type: - break; - case detail::type::int_type: - return vis(arg.value_.int_value); - case detail::type::uint_type: - return vis(arg.value_.uint_value); - case detail::type::long_long_type: - return vis(arg.value_.long_long_value); - case detail::type::ulong_long_type: - return vis(arg.value_.ulong_long_value); - case detail::type::int128_type: - return vis(detail::convert_for_visit(arg.value_.int128_value)); - case detail::type::uint128_type: - return vis(detail::convert_for_visit(arg.value_.uint128_value)); - case detail::type::bool_type: - return vis(arg.value_.bool_value); - case detail::type::char_type: - return vis(arg.value_.char_value); - case detail::type::float_type: - return vis(arg.value_.float_value); - case detail::type::double_type: - return vis(arg.value_.double_value); - case detail::type::long_double_type: - return vis(arg.value_.long_double_value); - case detail::type::cstring_type: - return vis(arg.value_.string.data); - case detail::type::string_type: - using sv = basic_string_view; - return vis(sv(arg.value_.string.data, arg.value_.string.size)); - case detail::type::pointer_type: - return vis(arg.value_.pointer); - case detail::type::custom_type: - return vis(typename basic_format_arg::handle(arg.value_.custom)); - } - return vis(monostate()); -} - -// Formatting context. -template class basic_format_context { - private: - OutputIt out_; - basic_format_args args_; - detail::locale_ref loc_; - - public: - using iterator = OutputIt; - using format_arg = basic_format_arg; - using format_args = basic_format_args; - using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; - - /** The character type for the output. */ - using char_type = Char; - - basic_format_context(basic_format_context&&) = default; - basic_format_context(const basic_format_context&) = delete; - void operator=(const basic_format_context&) = delete; - /** - Constructs a ``basic_format_context`` object. References to the arguments - are stored in the object so make sure they have appropriate lifetimes. - */ - constexpr basic_format_context(OutputIt out, format_args ctx_args, - detail::locale_ref loc = {}) - : out_(out), args_(ctx_args), loc_(loc) {} - - constexpr auto arg(int id) const -> format_arg { return args_.get(id); } - FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { - return args_.get(name); - } - FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { - return args_.get_id(name); - } - auto args() const -> const format_args& { return args_; } - - FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } - void on_error(const char* message) { error_handler().on_error(message); } - - // Returns an iterator to the beginning of the output range. - FMT_CONSTEXPR auto out() -> iterator { return out_; } - - // Advances the begin iterator to ``it``. - void advance_to(iterator it) { - if (!detail::is_back_insert_iterator()) out_ = it; - } - - FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } -}; - -template -using buffer_context = - basic_format_context, Char>; -using format_context = buffer_context; - -template -using is_formattable = bool_constant>() - .map(std::declval()))>::value>; - -/** - \rst - An array of references to arguments. It can be implicitly converted into - `~fmt::basic_format_args` for passing into type-erased formatting functions - such as `~fmt::vformat`. - \endrst - */ -template -class format_arg_store -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ - private: - static const size_t num_args = sizeof...(Args); - static constexpr size_t num_named_args = detail::count_named_args(); - static const bool is_packed = num_args <= detail::max_packed_args; - - using value_type = conditional_t, - basic_format_arg>; - - detail::arg_data - data_; - - friend class basic_format_args; - - static constexpr unsigned long long desc = - (is_packed ? detail::encode_types() - : detail::is_unpacked_bit | num_args) | - (num_named_args != 0 - ? static_cast(detail::has_named_args_bit) - : 0); - - public: - template - FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args) - : -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - basic_format_args(*this), -#endif - data_{detail::make_arg(args)...} { - if (detail::const_check(num_named_args != 0)) - detail::init_named_args(data_.named_args(), 0, 0, args...); - } -}; - -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references to - arguments and can be implicitly converted to `~fmt::format_args`. `Context` - can be omitted in which case it defaults to `~fmt::format_context`. - See `~fmt::arg` for lifetime considerations. - \endrst - */ -// Arguments are taken by lvalue references to avoid some lifetime issues. -template -constexpr auto make_format_args(T&... args) - -> format_arg_store...> { - return {args...}; -} - -/** - \rst - Returns a named argument to be used in a formatting function. - It should only be used in a call to a formatting function or - `dynamic_format_arg_store::push_back`. - - **Example**:: - - fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); - \endrst - */ -template -inline auto arg(const Char* name, const T& arg) -> detail::named_arg { - static_assert(!detail::is_named_arg(), "nested named arguments"); - return {name, arg}; -} -FMT_END_EXPORT - -/** - \rst - A view of a collection of formatting arguments. To avoid lifetime issues it - should only be used as a parameter type in type-erased functions such as - ``vformat``:: - - void vlog(string_view format_str, format_args args); // OK - format_args args = make_format_args(); // Error: dangling reference - \endrst - */ -template class basic_format_args { - public: - using size_type = int; - using format_arg = basic_format_arg; - - private: - // A descriptor that contains information about formatting arguments. - // If the number of arguments is less or equal to max_packed_args then - // argument types are passed in the descriptor. This reduces binary code size - // per formatting function call. - unsigned long long desc_; - union { - // If is_packed() returns true then argument values are stored in values_; - // otherwise they are stored in args_. This is done to improve cache - // locality and reduce compiled code size since storing larger objects - // may require more code (at least on x86-64) even if the same amount of - // data is actually copied to stack. It saves ~10% on the bloat test. - const detail::value* values_; - const format_arg* args_; - }; - - constexpr auto is_packed() const -> bool { - return (desc_ & detail::is_unpacked_bit) == 0; - } - auto has_named_args() const -> bool { - return (desc_ & detail::has_named_args_bit) != 0; - } - - FMT_CONSTEXPR auto type(int index) const -> detail::type { - int shift = index * detail::packed_arg_bits; - unsigned int mask = (1 << detail::packed_arg_bits) - 1; - return static_cast((desc_ >> shift) & mask); - } - - constexpr FMT_INLINE basic_format_args(unsigned long long desc, - const detail::value* values) - : desc_(desc), values_(values) {} - constexpr basic_format_args(unsigned long long desc, const format_arg* args) - : desc_(desc), args_(args) {} - - public: - constexpr basic_format_args() : desc_(0), args_(nullptr) {} - - /** - \rst - Constructs a `basic_format_args` object from `~fmt::format_arg_store`. - \endrst - */ - template - constexpr FMT_INLINE basic_format_args( - const format_arg_store& store) - : basic_format_args(format_arg_store::desc, - store.data_.args()) {} - - /** - \rst - Constructs a `basic_format_args` object from - `~fmt::dynamic_format_arg_store`. - \endrst - */ - constexpr FMT_INLINE basic_format_args( - const dynamic_format_arg_store& store) - : basic_format_args(store.get_types(), store.data()) {} - - /** - \rst - Constructs a `basic_format_args` object from a dynamic set of arguments. - \endrst - */ - constexpr basic_format_args(const format_arg* args, int count) - : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), - args) {} - - /** Returns the argument with the specified id. */ - FMT_CONSTEXPR auto get(int id) const -> format_arg { - format_arg arg; - if (!is_packed()) { - if (id < max_size()) arg = args_[id]; - return arg; - } - if (id >= detail::max_packed_args) return arg; - arg.type_ = type(id); - if (arg.type_ == detail::type::none_type) return arg; - arg.value_ = values_[id]; - return arg; - } - - template - auto get(basic_string_view name) const -> format_arg { - int id = get_id(name); - return id >= 0 ? get(id) : format_arg(); - } - - template - auto get_id(basic_string_view name) const -> int { - if (!has_named_args()) return -1; - const auto& named_args = - (is_packed() ? values_[-1] : args_[-1].value_).named_args; - for (size_t i = 0; i < named_args.size; ++i) { - if (named_args.data[i].name == name) return named_args.data[i].id; - } - return -1; - } - - auto max_size() const -> int { - unsigned long long max_packed = detail::max_packed_args; - return static_cast(is_packed() ? max_packed - : desc_ & ~detail::is_unpacked_bit); - } -}; - -/** An alias to ``basic_format_args``. */ -// A separate type would result in shorter symbols but break ABI compatibility -// between clang and gcc on ARM (#1919). -FMT_EXPORT using format_args = basic_format_args; - -// We cannot use enum classes as bit fields because of a gcc bug, so we put them -// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). -// Additionally, if an underlying type is specified, older gcc incorrectly warns -// that the type is too small. Both bugs are fixed in gcc 9.3. -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 -# define FMT_ENUM_UNDERLYING_TYPE(type) -#else -# define FMT_ENUM_UNDERLYING_TYPE(type) : type -#endif -namespace align { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, - numeric}; -} -using align_t = align::type; -namespace sign { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; -} -using sign_t = sign::type; - -namespace detail { - -// Workaround an array initialization issue in gcc 4.8. -template struct fill_t { - private: - enum { max_size = 4 }; - Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; - unsigned char size_ = 1; - - public: - FMT_CONSTEXPR void operator=(basic_string_view s) { - auto size = s.size(); - FMT_ASSERT(size <= max_size, "invalid fill"); - for (size_t i = 0; i < size; ++i) data_[i] = s[i]; - size_ = static_cast(size); - } - - constexpr auto size() const -> size_t { return size_; } - constexpr auto data() const -> const Char* { return data_; } - - FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } - FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { - return data_[index]; - } -}; -} // namespace detail - -enum class presentation_type : unsigned char { - none, - dec, // 'd' - oct, // 'o' - hex_lower, // 'x' - hex_upper, // 'X' - bin_lower, // 'b' - bin_upper, // 'B' - hexfloat_lower, // 'a' - hexfloat_upper, // 'A' - exp_lower, // 'e' - exp_upper, // 'E' - fixed_lower, // 'f' - fixed_upper, // 'F' - general_lower, // 'g' - general_upper, // 'G' - chr, // 'c' - string, // 's' - pointer, // 'p' - debug // '?' -}; - -// Format specifiers for built-in and string types. -template struct format_specs { - int width; - int precision; - presentation_type type; - align_t align : 4; - sign_t sign : 3; - bool alt : 1; // Alternate form ('#'). - bool localized : 1; - detail::fill_t fill; - - constexpr format_specs() - : width(0), - precision(-1), - type(presentation_type::none), - align(align::none), - sign(sign::none), - alt(false), - localized(false) {} -}; - -namespace detail { - -enum class arg_id_kind { none, index, name }; - -// An argument reference. -template 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 name) - : kind(arg_id_kind::name), val(name) {} - - FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { - kind = arg_id_kind::index; - val.index = idx; - return *this; - } - - arg_id_kind kind; - union value { - FMT_CONSTEXPR value(int idx = 0) : index(idx) {} - FMT_CONSTEXPR value(basic_string_view n) : name(n) {} - - int index; - basic_string_view name; - } val; -}; - -// Format specifiers with width and precision resolved at formatting rather -// than parsing time to allow reusing the same parsed specifiers with -// different sets of arguments (precompilation of format strings). -template -struct dynamic_format_specs : format_specs { - arg_ref width_ref; - arg_ref precision_ref; -}; - -// Converts a character to ASCII. Returns '\0' on conversion failure. -template ::value)> -constexpr auto to_ascii(Char c) -> char { - return c <= 0xff ? static_cast(c) : '\0'; -} -template ::value)> -constexpr auto to_ascii(Char c) -> char { - return c <= 0xff ? static_cast(c) : '\0'; -} - -// Returns the number of code units in a code point or 1 on error. -template -FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { - if (const_check(sizeof(Char) != 1)) return 1; - auto c = static_cast(*begin); - return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1; -} - -// Return the result via the out param to workaround gcc bug 77539. -template -FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { - for (out = first; out != last; ++out) { - if (*out == value) return true; - } - return false; -} - -template <> -inline auto find(const char* first, const char* last, char value, - const char*& out) -> bool { - out = static_cast( - std::memchr(first, value, to_unsigned(last - first))); - return out != nullptr; -} - -// 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 -FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, - int error_value) noexcept -> int { - FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - unsigned value = 0, prev = 0; - auto p = begin; - do { - prev = value; - value = value * 10 + unsigned(*p - '0'); - ++p; - } while (p != end && '0' <= *p && *p <= '9'); - auto num_digits = p - begin; - begin = p; - if (num_digits <= std::numeric_limits::digits10) - return static_cast(value); - // Check for overflow. - const unsigned max = to_unsigned((std::numeric_limits::max)()); - return num_digits == std::numeric_limits::digits10 + 1 && - prev * 10ull + unsigned(p[-1] - '0') <= max - ? static_cast(value) - : error_value; -} - -FMT_CONSTEXPR inline auto parse_align(char c) -> align_t { - switch (c) { - case '<': - return align::left; - case '>': - return align::right; - case '^': - return align::center; - } - return align::none; -} - -template constexpr auto is_name_start(Char c) -> bool { - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; -} - -template -FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - Char c = *begin; - if (c >= '0' && c <= '9') { - int index = 0; - constexpr int max = (std::numeric_limits::max)(); - if (c != '0') - index = parse_nonnegative_int(begin, end, max); - else - ++begin; - if (begin == end || (*begin != '}' && *begin != ':')) - throw_format_error("invalid format string"); - else - handler.on_index(index); - return begin; - } - if (!is_name_start(c)) { - throw_format_error("invalid format string"); - return begin; - } - auto it = begin; - do { - ++it; - } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); - handler.on_name({begin, to_unsigned(it - begin)}); - return it; -} - -template -FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - FMT_ASSERT(begin != end, ""); - Char c = *begin; - if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); - handler.on_auto(); - return begin; -} - -template struct dynamic_spec_id_handler { - basic_format_parse_context& ctx; - arg_ref& ref; - - FMT_CONSTEXPR void on_auto() { - int id = ctx.next_arg_id(); - ref = arg_ref(id); - ctx.check_dynamic_spec(id); - } - FMT_CONSTEXPR void on_index(int id) { - ref = arg_ref(id); - ctx.check_arg_id(id); - ctx.check_dynamic_spec(id); - } - FMT_CONSTEXPR void on_name(basic_string_view id) { - ref = arg_ref(id); - ctx.check_arg_id(id); - } -}; - -// Parses [integer | "{" [arg_id] "}"]. -template -FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, - int& value, arg_ref& ref, - basic_format_parse_context& ctx) - -> const Char* { - FMT_ASSERT(begin != end, ""); - if ('0' <= *begin && *begin <= '9') { - int val = parse_nonnegative_int(begin, end, -1); - if (val != -1) - value = val; - else - throw_format_error("number is too big"); - } else if (*begin == '{') { - ++begin; - auto handler = dynamic_spec_id_handler{ctx, ref}; - if (begin != end) begin = parse_arg_id(begin, end, handler); - if (begin != end && *begin == '}') return ++begin; - throw_format_error("invalid format string"); - } - return begin; -} - -template -FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, - int& value, arg_ref& ref, - basic_format_parse_context& ctx) - -> const Char* { - ++begin; - if (begin == end || *begin == '}') { - throw_format_error("invalid precision"); - return begin; - } - return parse_dynamic_spec(begin, end, value, ref, ctx); -} - -enum class state { start, align, sign, hash, zero, width, precision, locale }; - -// Parses standard format specifiers. -template -FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( - const Char* begin, const Char* end, dynamic_format_specs& specs, - basic_format_parse_context& ctx, type arg_type) -> const Char* { - auto c = '\0'; - if (end - begin > 1) { - auto next = to_ascii(begin[1]); - c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; - } else { - if (begin == end) return begin; - c = to_ascii(*begin); - } - - struct { - state current_state = state::start; - FMT_CONSTEXPR void operator()(state s, bool valid = true) { - if (current_state >= s || !valid) - throw_format_error("invalid format specifier"); - current_state = s; - } - } enter_state; - - using pres = presentation_type; - constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; - struct { - const Char*& begin; - dynamic_format_specs& specs; - type arg_type; - - FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* { - if (!in(arg_type, set)) throw_format_error("invalid format specifier"); - specs.type = type; - return begin + 1; - } - } parse_presentation_type{begin, specs, arg_type}; - - for (;;) { - switch (c) { - case '<': - case '>': - case '^': - enter_state(state::align); - specs.align = parse_align(c); - ++begin; - break; - case '+': - case '-': - case ' ': - enter_state(state::sign, in(arg_type, sint_set | float_set)); - switch (c) { - case '+': - specs.sign = sign::plus; - break; - case '-': - specs.sign = sign::minus; - break; - case ' ': - specs.sign = sign::space; - break; - } - ++begin; - break; - case '#': - enter_state(state::hash, is_arithmetic_type(arg_type)); - specs.alt = true; - ++begin; - break; - case '0': - enter_state(state::zero); - if (!is_arithmetic_type(arg_type)) - throw_format_error("format specifier requires numeric argument"); - if (specs.align == align::none) { - // Ignore 0 if align is specified for compatibility with std::format. - specs.align = align::numeric; - specs.fill[0] = Char('0'); - } - ++begin; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '{': - enter_state(state::width); - begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); - break; - case '.': - enter_state(state::precision, - in(arg_type, float_set | string_set | cstring_set)); - begin = parse_precision(begin, end, specs.precision, specs.precision_ref, - ctx); - break; - case 'L': - enter_state(state::locale, is_arithmetic_type(arg_type)); - specs.localized = true; - ++begin; - break; - case 'd': - return parse_presentation_type(pres::dec, integral_set); - case 'o': - return parse_presentation_type(pres::oct, integral_set); - case 'x': - return parse_presentation_type(pres::hex_lower, integral_set); - case 'X': - return parse_presentation_type(pres::hex_upper, integral_set); - case 'b': - return parse_presentation_type(pres::bin_lower, integral_set); - case 'B': - return parse_presentation_type(pres::bin_upper, integral_set); - case 'a': - return parse_presentation_type(pres::hexfloat_lower, float_set); - case 'A': - return parse_presentation_type(pres::hexfloat_upper, float_set); - case 'e': - return parse_presentation_type(pres::exp_lower, float_set); - case 'E': - return parse_presentation_type(pres::exp_upper, float_set); - case 'f': - return parse_presentation_type(pres::fixed_lower, float_set); - case 'F': - return parse_presentation_type(pres::fixed_upper, float_set); - case 'g': - return parse_presentation_type(pres::general_lower, float_set); - case 'G': - return parse_presentation_type(pres::general_upper, float_set); - case 'c': - return parse_presentation_type(pres::chr, integral_set); - case 's': - return parse_presentation_type(pres::string, - bool_set | string_set | cstring_set); - case 'p': - return parse_presentation_type(pres::pointer, pointer_set | cstring_set); - case '?': - return parse_presentation_type(pres::debug, - char_set | string_set | cstring_set); - case '}': - return begin; - default: { - if (*begin == '}') return begin; - // Parse fill and alignment. - auto fill_end = begin + code_point_length(begin); - if (end - fill_end <= 0) { - throw_format_error("invalid format specifier"); - return begin; - } - if (*begin == '{') { - throw_format_error("invalid fill character '{'"); - return begin; - } - auto align = parse_align(to_ascii(*fill_end)); - enter_state(state::align, align != align::none); - specs.fill = {begin, to_unsigned(fill_end - begin)}; - specs.align = align; - begin = fill_end + 1; - } - } - if (begin == end) return begin; - c = to_ascii(*begin); - } -} - -template -FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - struct id_adapter { - Handler& handler; - int arg_id; - - FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); } - FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } - FMT_CONSTEXPR void on_name(basic_string_view id) { - arg_id = handler.on_arg_id(id); - } - }; - - ++begin; - if (begin == end) return handler.on_error("invalid format string"), end; - if (*begin == '}') { - handler.on_replacement_field(handler.on_arg_id(), begin); - } else if (*begin == '{') { - handler.on_text(begin, begin + 1); - } else { - auto adapter = id_adapter{handler, 0}; - begin = parse_arg_id(begin, end, adapter); - Char c = begin != end ? *begin : Char(); - if (c == '}') { - handler.on_replacement_field(adapter.arg_id, begin); - } else if (c == ':') { - begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); - if (begin == end || *begin != '}') - return handler.on_error("unknown format specifier"), end; - } else { - return handler.on_error("missing '}' in format string"), end; - } - } - return begin + 1; -} - -template -FMT_CONSTEXPR FMT_INLINE void parse_format_string( - basic_string_view format_str, Handler&& handler) { - auto begin = format_str.data(); - auto end = begin + format_str.size(); - if (end - begin < 32) { - // Use a simple loop instead of memchr for small strings. - const Char* p = begin; - while (p != end) { - auto c = *p++; - if (c == '{') { - handler.on_text(begin, p - 1); - begin = p = parse_replacement_field(p - 1, end, handler); - } else if (c == '}') { - if (p == end || *p != '}') - return handler.on_error("unmatched '}' in format string"); - handler.on_text(begin, p); - begin = ++p; - } - } - handler.on_text(begin, end); - return; - } - struct writer { - FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { - if (from == to) return; - for (;;) { - const Char* p = nullptr; - if (!find(from, to, Char('}'), p)) - return handler_.on_text(from, to); - ++p; - if (p == to || *p != '}') - return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(from, p); - from = p + 1; - } - } - Handler& handler_; - } write = {handler}; - 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(begin + 1, end, Char('{'), p)) - return write(begin, end); - write(begin, p); - begin = parse_replacement_field(p, end, handler); - } -} - -template ::value> struct strip_named_arg { - using type = T; -}; -template struct strip_named_arg { - using type = remove_cvref_t; -}; - -template -FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) - -> decltype(ctx.begin()) { - using char_type = typename ParseContext::char_type; - using context = buffer_context; - using mapped_type = conditional_t< - mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), - typename strip_named_arg::type>; -#if defined(__cpp_if_constexpr) - if constexpr (std::is_default_constructible_v< - formatter>) { - return formatter().parse(ctx); - } else { - type_is_unformattable_for _; - return ctx.begin(); - } -#else - return formatter().parse(ctx); -#endif -} - -// Checks char specs and returns true iff the presentation type is char-like. -template -FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { - if (specs.type != presentation_type::none && - specs.type != presentation_type::chr && - specs.type != presentation_type::debug) { - return false; - } - if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) - throw_format_error("invalid format specifier for char"); - return true; -} - -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -template -constexpr auto get_arg_index_by_name(basic_string_view name) -> int { - if constexpr (is_statically_named_arg()) { - if (name == T::name) return N; - } - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name(name); - (void)name; // Workaround an MSVC bug about "unused" parameter. - return -1; -} -#endif - -template -FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name<0, Args...>(name); -#endif - (void)name; - return -1; -} - -template class format_string_checker { - private: - using parse_context_type = compile_parse_context; - static constexpr int num_args = sizeof...(Args); - - // Format specifier parsing function. - // In the future basic_format_parse_context will replace compile_parse_context - // here and will use is_constant_evaluated and downcasting to access the data - // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. - using parse_func = const Char* (*)(parse_context_type&); - - type types_[num_args > 0 ? static_cast(num_args) : 1]; - parse_context_type context_; - parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; - - public: - explicit FMT_CONSTEXPR format_string_checker(basic_string_view fmt) - : types_{mapped_type_constant>::value...}, - context_(fmt, num_args, types_), - parse_funcs_{&parse_format_specs...} {} - - FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - - FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } - FMT_CONSTEXPR auto on_arg_id(int id) -> int { - return context_.check_arg_id(id), id; - } - FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - auto index = get_arg_index_by_name(id); - if (index < 0) on_error("named argument is not found"); - return index; -#else - (void)id; - on_error("compile-time checks for named arguments require C++20 support"); - return 0; -#endif - } - - FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { - on_format_specs(id, begin, begin); // Call parse() on empty specs. - } - - FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) - -> const Char* { - context_.advance_to(begin); - // id >= 0 check is a workaround for gcc 10 bug (#2065). - return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; - } - - FMT_CONSTEXPR void on_error(const char* message) { - throw_format_error(message); - } -}; - -// Reports a compile-time error if S is not a valid format string. -template ::value)> -FMT_INLINE void check_format_string(const S&) { -#ifdef FMT_ENFORCE_COMPILE_STRING - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); -#endif -} -template ::value)> -void check_format_string(S format_str) { - using char_t = typename S::char_type; - FMT_CONSTEXPR auto s = basic_string_view(format_str); - using checker = format_string_checker...>; - FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); - ignore_unused(error); -} - -template struct vformat_args { - using type = basic_format_args< - basic_format_context>, Char>>; -}; -template <> struct vformat_args { using type = format_args; }; - -// Use vformat_args and avoid type_identity to keep symbols short. -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc = {}); - -FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); -#ifndef _WIN32 -inline void vprint_mojibake(std::FILE*, string_view, format_args) {} -#endif -} // namespace detail - -FMT_BEGIN_EXPORT - -// A formatter specialization for natively supported types. -template -struct formatter::value != - detail::type::custom_type>> { - private: - detail::dynamic_format_specs specs_; - - public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { - auto type = detail::type_constant::value; - auto end = - detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type); - if (type == detail::type::char_type) detail::check_char_specs(specs_); - return end; - } - - template ::value, - FMT_ENABLE_IF(U == detail::type::string_type || - U == detail::type::cstring_type || - U == detail::type::char_type)> - FMT_CONSTEXPR void set_debug_format(bool set = true) { - specs_.type = set ? presentation_type::debug : presentation_type::none; - } - - template - FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const - -> decltype(ctx.out()); -}; - -template struct runtime_format_string { - basic_string_view str; -}; - -/** A compile-time format string. */ -template class basic_format_string { - private: - basic_string_view str_; - - public: - template >::value)> - FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { - static_assert( - detail::count< - (std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); -#ifdef FMT_HAS_CONSTEVAL - if constexpr (detail::count_named_args() == - detail::count_statically_named_args()) { - using checker = - detail::format_string_checker...>; - detail::parse_format_string(str_, checker(s)); - } -#else - detail::check_format_string(s); -#endif - } - basic_format_string(runtime_format_string fmt) : str_(fmt.str) {} - - FMT_INLINE operator basic_string_view() const { return str_; } - FMT_INLINE auto get() const -> basic_string_view { return str_; } -}; - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -// Workaround broken conversion on older gcc. -template using format_string = string_view; -inline auto runtime(string_view s) -> string_view { return s; } -#else -template -using format_string = basic_format_string...>; -/** - \rst - Creates a runtime format string. - - **Example**:: - - // Check format string at runtime instead of compile-time. - fmt::print(fmt::runtime("{:d}"), "I am not a number"); - \endrst - */ -inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } -#endif - -FMT_API auto vformat(string_view fmt, format_args args) -> std::string; - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and returns the result - as a string. - - **Example**:: - - #include - std::string message = fmt::format("The answer is {}.", 42); - \endrst -*/ -template -FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) - -> std::string { - return vformat(fmt, fmt::make_format_args(args...)); -} - -/** Formats a string and writes the output to ``out``. */ -template ::value)> -auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { - auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, fmt, args, {}); - return detail::get_iterator(buf, out); -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt``, writes the result to - the output iterator ``out`` and returns the iterator past the end of the output - range. `format_to` does not append a terminating null character. - - **Example**:: - - auto out = std::vector(); - fmt::format_to(std::back_inserter(out), "{}", 42); - \endrst - */ -template ::value)> -FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) - -> OutputIt { - return vformat_to(out, fmt, fmt::make_format_args(args...)); -} - -template struct format_to_n_result { - /** Iterator past the end of the output range. */ - OutputIt out; - /** Total (not truncated) output size. */ - size_t size; -}; - -template ::value)> -auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) - -> format_to_n_result { - using traits = detail::fixed_buffer_traits; - auto buf = detail::iterator_buffer(out, n); - detail::vformat_to(buf, fmt, args, {}); - return {buf.out(), buf.count()}; -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` - characters of the result to the output iterator ``out`` and returns the total - (not truncated) output size and the iterator past the end of the output range. - `format_to_n` does not append a terminating null character. - \endrst - */ -template ::value)> -FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, - T&&... args) -> format_to_n_result { - return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); -} - -/** Returns the number of chars in the output of ``format(fmt, args...)``. */ -template -FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, - T&&... args) -> size_t { - auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt, fmt::make_format_args(args...), {}); - return buf.count(); -} - -FMT_API void vprint(string_view fmt, format_args args); -FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout``. - - **Example**:: - - fmt::print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -template -FMT_INLINE void print(format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - return detail::is_utf8() ? vprint(fmt, vargs) - : detail::vprint_mojibake(stdout, fmt, vargs); -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f``. - - **Example**:: - - fmt::print(stderr, "Don't {}!", "panic"); - \endrst - */ -template -FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - return detail::is_utf8() ? vprint(f, fmt, vargs) - : detail::vprint_mojibake(f, fmt, vargs); -} - -/** - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f`` followed by a newline. - */ -template -FMT_INLINE void println(std::FILE* f, format_string fmt, T&&... args) { - return fmt::print(f, "{}\n", fmt::format(fmt, std::forward(args)...)); -} - -/** - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout`` followed by a newline. - */ -template -FMT_INLINE void println(format_string fmt, T&&... args) { - return fmt::println(stdout, fmt, std::forward(args)...); -} - -FMT_END_EXPORT -FMT_GCC_PRAGMA("GCC pop_options") -FMT_END_NAMESPACE - -#ifdef FMT_HEADER_ONLY -# include "format.h" -#endif -#endif // FMT_CORE_H_ +#include "format.h" diff --git a/deps/fmt/include/fmt/format-inl.h b/deps/fmt/include/fmt/format-inl.h index dac2d437a..945cb912a 100644 --- a/deps/fmt/include/fmt/format-inl.h +++ b/deps/fmt/include/fmt/format-inl.h @@ -8,37 +8,67 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#include -#include // errno -#include -#include -#include - -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -# include +#ifndef FMT_MODULE +# include +# include // errno +# include +# include +# include #endif -#ifdef _WIN32 +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) # include // _isatty #endif #include "format.h" -FMT_BEGIN_NAMESPACE -namespace detail { +#if FMT_USE_LOCALE && !defined(FMT_MODULE) +# include +#endif +#ifndef FMT_FUNC +# define FMT_FUNC +#endif + +FMT_BEGIN_NAMESPACE + +#ifndef FMT_CUSTOM_ASSERT_FAIL FMT_FUNC void assert_fail(const char* file, int line, const char* message) { // Use unchecked std::fprintf to avoid triggering another assertion when - // writing to stderr fails + // writing to stderr fails. std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); - // Chosen instead of std::abort to satisfy Clang in CUDA mode during device - // code pass. - std::terminate(); + abort(); +} +#endif + +#if FMT_USE_LOCALE +namespace detail { +using std::locale; +using std::numpunct; +using std::use_facet; +} // namespace detail +#else +namespace detail { +struct locale {}; +template struct numpunct { + auto grouping() const -> std::string { return "\03"; } + auto thousands_sep() const -> Char { return ','; } + auto decimal_point() const -> Char { return '.'; } +}; +template Facet use_facet(locale) { return {}; } +} // namespace detail +#endif // FMT_USE_LOCALE + +template auto locale_ref::get() const -> Locale { + using namespace detail; + static_assert(std::is_same::value, ""); +#if FMT_USE_LOCALE + if (locale_) return *static_cast(locale_); +#endif + return locale(); } -FMT_FUNC void throw_format_error(const char* message) { - FMT_THROW(format_error(message)); -} +namespace detail { FMT_FUNC void format_error_code(detail::buffer& out, int error_code, string_view message) noexcept { @@ -56,112 +86,101 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, ++error_code_size; } error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); - auto it = buffer_appender(out); + auto it = appender(out); if (message.size() <= inline_buffer_size - error_code_size) - format_to(it, FMT_STRING("{}{}"), message, SEP); - format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); + fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); + fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); FMT_ASSERT(out.size() <= inline_buffer_size, ""); } -FMT_FUNC void report_error(format_func func, int error_code, - const char* message) noexcept { +FMT_FUNC void do_report_error(format_func func, int error_code, + const char* message) noexcept { memory_buffer full_message; func(full_message, error_code, message); - // Don't use fwrite_fully because the latter may throw. + // Don't use fwrite_all because the latter may throw. if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) std::fputc('\n', stderr); } // A wrapper around fwrite that throws on error. -inline void fwrite_fully(const void* ptr, size_t size, size_t count, - FILE* stream) { - size_t written = std::fwrite(ptr, size, count, stream); +inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { + size_t written = std::fwrite(ptr, 1, count, stream); if (written < count) FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -template -locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { - static_assert(std::is_same::value, ""); -} - -template Locale locale_ref::get() const { - static_assert(std::is_same::value, ""); - return locale_ ? *static_cast(locale_) : std::locale(); -} - template FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { - auto& facet = std::use_facet>(loc.get()); + auto&& facet = use_facet>(loc.get()); auto grouping = facet.grouping(); auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); return {std::move(grouping), thousands_sep}; } -template FMT_FUNC Char decimal_point_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .decimal_point(); -} -#else template -FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { - return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; +FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { + return use_facet>(loc.get()).decimal_point(); } -template FMT_FUNC Char decimal_point_impl(locale_ref) { - return '.'; -} -#endif +#if FMT_USE_LOCALE FMT_FUNC auto write_loc(appender out, loc_value value, - const format_specs<>& specs, locale_ref loc) -> bool { -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + const format_specs& specs, locale_ref loc) -> bool { auto locale = loc.get(); // We cannot use the num_put facet because it may produce output in // a wrong encoding. using facet = format_facet; if (std::has_facet(locale)) - return std::use_facet(locale).put(out, value, specs); + return use_facet(locale).put(out, value, specs); return facet(locale).put(out, value, specs); -#endif - return false; } +#endif } // namespace detail +FMT_FUNC void report_error(const char* message) { +#if FMT_MSC_VERSION || defined(__NVCC__) + // 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) return; +#endif + FMT_THROW(format_error(message)); +} + template typename Locale::id format_facet::id; -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR template format_facet::format_facet(Locale& loc) { - auto& numpunct = std::use_facet>(loc); - grouping_ = numpunct.grouping(); - if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); + auto& np = detail::use_facet>(loc); + grouping_ = np.grouping(); + if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep()); } +#if FMT_USE_LOCALE template <> FMT_API FMT_FUNC auto format_facet::do_put( - appender out, loc_value val, const format_specs<>& specs) const -> bool { + appender out, loc_value val, const format_specs& specs) const -> bool { return val.visit( detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); } #endif -FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, - format_args args) { +FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args) + -> std::system_error { auto ec = std::error_code(error_code, std::generic_category()); return std::system_error(ec, vformat(fmt, args)); } namespace detail { -template inline bool operator==(basic_fp x, basic_fp y) { +template +inline auto operator==(basic_fp x, basic_fp y) -> bool { return x.f == y.f && x.e == y.e; } // Compilers should be able to optimize this into the ror instruction. -FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { +FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { r &= 31; return (n >> r) | (n << (32 - r)); } -FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { +FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { r &= 63; return (n >> r) | (n << (64 - r)); } @@ -170,14 +189,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { namespace dragonbox { // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { +inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t { return umul128_upper64(static_cast(x) << 32, y); } // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_fallback umul192_lower128(uint64_t x, - uint128_fallback y) noexcept { +inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { uint64_t high = x * y.high(); uint128_fallback high_low = umul128(x, y.low()); return {high + high_low.high(), high_low.low()}; @@ -185,17 +204,17 @@ inline uint128_fallback umul192_lower128(uint64_t x, // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { +inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t { return x * y; } // Various fast log computations. -inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { +inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); return (e * 631305 - 261663) >> 21; } -FMT_INLINE_VARIABLE constexpr struct { +FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct { uint32_t divisor; int shift_amount; } div_small_pow10_infos[] = {{10, 16}, {100, 16}}; @@ -204,7 +223,7 @@ FMT_INLINE_VARIABLE constexpr struct { // divisible by pow(10, N). // Precondition: n <= pow(10, N + 1). template -bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { +auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool { // The numbers below are chosen such that: // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, // 2. nm mod 2^k < m if and only if n is divisible by d, @@ -229,7 +248,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { // Computes floor(n / pow(10, N)) for small n and N. // Precondition: n <= pow(10, N + 1). -template uint32_t small_division_by_pow10(uint32_t n) noexcept { +template auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t { constexpr auto info = div_small_pow10_infos[N - 1]; FMT_ASSERT(n <= info.divisor * 10, "n is too large"); constexpr uint32_t magic_number = @@ -238,12 +257,12 @@ template uint32_t small_division_by_pow10(uint32_t n) noexcept { } // Computes floor(n / 10^(kappa + 1)) (float) -inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { +inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t { // 1374389535 = ceil(2^37/100) return static_cast((static_cast(n) * 1374389535) >> 37); } // Computes floor(n / 10^(kappa + 1)) (double) -inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { +inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t { // 2361183241434822607 = ceil(2^(64+7)/1000) return umul128_upper64(n, 2361183241434822607ull) >> 7; } @@ -255,10 +274,10 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; - static uint64_t get_cached_power(int k) noexcept { + static auto get_cached_power(int k) noexcept -> uint64_t { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - static constexpr const uint64_t pow10_significands[] = { + static constexpr uint64_t pow10_significands[] = { 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, @@ -297,20 +316,23 @@ template <> struct cache_accessor { bool is_integer; }; - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { auto r = umul96_upper64(u, cache); return {static_cast(r >> 32), static_cast(r) == 0}; } - static uint32_t compute_delta(const cache_entry_type& cache, - int beta) noexcept { + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept + -> uint32_t { return static_cast(cache >> (64 - 1 - beta)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); @@ -319,22 +341,22 @@ template <> struct cache_accessor { static_cast(r >> (32 - beta)) == 0}; } - static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache - (cache >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta)); } - static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache + (cache >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta)); } - static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (static_cast( cache >> (64 - num_significand_bits() - 2 - beta)) + 1) / @@ -346,11 +368,11 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint128_fallback; - static uint128_fallback get_cached_power(int k) noexcept { + static auto get_cached_power(int k) noexcept -> uint128_fallback { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - static constexpr const uint128_fallback pow10_significands[] = { + static constexpr uint128_fallback pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0x9faacf3df73609b1, 0x77b191618c54e9ad}, @@ -985,8 +1007,7 @@ template <> struct cache_accessor { {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, - { 0xdb68c2ca82ed2a05, - 0xa67398db9f6820e2 } + {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}, #else {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, @@ -1018,7 +1039,7 @@ template <> struct cache_accessor { #if FMT_USE_FULL_CACHE_DRAGONBOX return pow10_significands[k - float_info::min_k]; #else - static constexpr const uint64_t powers_of_5_64[] = { + static constexpr uint64_t powers_of_5_64[] = { 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, @@ -1071,19 +1092,22 @@ template <> struct cache_accessor { bool is_integer; }; - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { auto r = umul192_upper128(u, cache); return {r.high(), r.low() == 0}; } - static uint32_t compute_delta(cache_entry_type const& cache, - int beta) noexcept { + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept + -> uint32_t { return static_cast(cache.high() >> (64 - 1 - beta)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); @@ -1092,47 +1116,47 @@ template <> struct cache_accessor { ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; } - static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() - (cache.high() >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta); } - static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() + (cache.high() >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta); } - static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } }; -FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { +FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback { return cache_accessor::get_cached_power(k); } // Various integer checks template -bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { +auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_upper_threshold = 3; return exponent >= case_shorter_interval_left_endpoint_lower_threshold && exponent <= case_shorter_interval_left_endpoint_upper_threshold; } -// Remove trailing zeros from n and return the number of zeros removed (float) -FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { +// Remove trailing zeros from n and return the number of zeros removed (float). +FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int { FMT_ASSERT(n != 0, ""); // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. constexpr uint32_t mod_inv_5 = 0xcccccccd; - constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 + constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 while (true) { auto q = rotr(n * mod_inv_25, 2); @@ -1148,27 +1172,24 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { return s; } -// Removes trailing zeros and returns the number of zeros removed (double) -FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { +// Removes trailing zeros and returns the number of zeros removed (double). +FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int { FMT_ASSERT(n != 0, ""); - // This magic number is ceil(2^90 / 10^8). - constexpr uint64_t magic_number = 12379400392853802749ull; - auto nm = umul128(n, magic_number); - // Is n is divisible by 10^8? - if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + constexpr uint32_t ten_pow_8 = 100000000u; + if ((n % ten_pow_8) == 0) { // If yes, work with the quotient... - auto n32 = static_cast(nm.high() >> (90 - 64)); + auto n32 = static_cast(n / ten_pow_8); // ... and use the 32 bit variant of the function - int s = remove_trailing_zeros(n32, 8); + int num_zeros = remove_trailing_zeros(n32, 8); n = n32; - return s; + return num_zeros; } // If n is not divisible by 10^8, work with n itself. constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd; - constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5 + constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5 int s = 0; while (true) { @@ -1188,7 +1209,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { // The main algorithm for shorter interval case template -FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { +FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); @@ -1234,7 +1255,7 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { return ret_value; } -template decimal_fp to_decimal(T x) noexcept { +template auto to_decimal(T x) noexcept -> decimal_fp { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -1373,15 +1394,15 @@ template <> struct formatter { for (auto i = n.bigits_.size(); i > 0; --i) { auto value = n.bigits_[i - 1u]; if (first) { - out = format_to(out, FMT_STRING("{:x}"), value); + out = fmt::format_to(out, FMT_STRING("{:x}"), value); first = false; continue; } - out = format_to(out, FMT_STRING("{:08x}"), value); + out = fmt::format_to(out, FMT_STRING("{:08x}"), value); } if (n.exp_ > 0) - out = format_to(out, FMT_STRING("p{}"), - n.exp_ * detail::bigint::bigit_bits); + out = fmt::format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); return out; } }; @@ -1405,7 +1426,7 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept { FMT_TRY { auto ec = std::error_code(error_code, std::generic_category()); - write(std::back_inserter(out), std::system_error(ec, message).what()); + detail::write(appender(out), std::system_error(ec, message).what()); return; } FMT_CATCH(...) {} @@ -1414,10 +1435,10 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, FMT_FUNC void report_system_error(int error_code, const char* message) noexcept { - report_error(format_system_error, error_code, message); + do_report_error(format_system_error, error_code, message); } -FMT_FUNC std::string vformat(string_view fmt, format_args args) { +FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { // Don't optimize the "{}" case to keep the binary size small and because it // can be better optimized in fmt::format anyway. auto buffer = memory_buffer(); @@ -1426,42 +1447,307 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { } namespace detail { -#ifndef _WIN32 -FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } + +FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc) { + auto out = appender(buf); + if (fmt.size() == 2 && equal2(fmt.data(), "{}")) + return args.get(0).visit(default_arg_formatter{out}); + parse_format_string(fmt, + format_handler<>{parse_context<>(fmt), {out, args, loc}}); +} + +template struct span { + T* data; + size_t size; +}; + +template auto flockfile(F* f) -> decltype(_lock_file(f)) { + _lock_file(f); +} +template auto funlockfile(F* f) -> decltype(_unlock_file(f)) { + _unlock_file(f); +} + +#ifndef getc_unlocked +template auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { + return _fgetc_nolock(f); +} +#endif + +template +struct has_flockfile : std::false_type {}; + +template +struct has_flockfile()))>> + : std::true_type {}; + +// A FILE wrapper. F is FILE defined as a template parameter to make system API +// detection work. +template class file_base { + public: + F* file_; + + public: + file_base(F* file) : file_(file) {} + operator F*() const { return file_; } + + // Reads a code unit from the stream. + auto get() -> int { + int result = getc_unlocked(file_); + if (result == EOF && ferror(file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); + return result; + } + + // Puts the code unit back into the stream buffer. + void unget(char c) { + if (ungetc(c, file_) == EOF) + FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); + } + + void flush() { fflush(this->file_); } +}; + +// A FILE wrapper for glibc. +template class glibc_file : public file_base { + private: + enum { + line_buffered = 0x200, // _IO_LINE_BUF + unbuffered = 2 // _IO_UNBUFFERED + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return; + // Force buffer initialization by placing and removing a char in a buffer. + putc_unlocked(0, this->file_); + --this->file_->_IO_write_ptr; + } + + // Returns the file's read buffer. + auto get_read_buffer() const -> span { + auto ptr = this->file_->_IO_read_ptr; + return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)}; + } + + // Returns the file's write buffer. + auto get_write_buffer() const -> span { + auto ptr = this->file_->_IO_write_ptr; + return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)}; + } + + void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } + + auto needs_flush() const -> bool { + if ((this->file_->_flags & line_buffered) == 0) return false; + char* end = this->file_->_IO_write_end; + auto size = max_of(this->file_->_IO_write_ptr - end, 0); + return memchr(end, '\n', static_cast(size)); + } + + void flush() { fflush_unlocked(this->file_); } +}; + +// A FILE wrapper for Apple's libc. +template class apple_file : public file_base { + private: + enum { + line_buffered = 1, // __SNBF + unbuffered = 2 // __SLBF + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_p) return; + // Force buffer initialization by placing and removing a char in a buffer. + if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_); + --this->file_->_p; + ++this->file_->_w; + } + + auto get_read_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_r)}; + } + + auto get_write_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_bf._base + this->file_->_bf._size - + this->file_->_p)}; + } + + void advance_write_buffer(size_t size) { + this->file_->_p += size; + this->file_->_w -= size; + } + + auto needs_flush() const -> bool { + if ((this->file_->_flags & line_buffered) == 0) return false; + return memchr(this->file_->_p + this->file_->_w, '\n', + to_unsigned(-this->file_->_w)); + } +}; + +// A fallback FILE wrapper. +template class fallback_file : public file_base { + private: + char next_; // The next unconsumed character in the buffer. + bool has_next_ = false; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { return false; } + auto needs_flush() const -> bool { return false; } + void init_buffer() {} + + auto get_read_buffer() const -> span { + return {&next_, has_next_ ? 1u : 0u}; + } + + auto get_write_buffer() const -> span { return {nullptr, 0}; } + + void advance_write_buffer(size_t) {} + + auto get() -> int { + has_next_ = false; + return file_base::get(); + } + + void unget(char c) { + file_base::unget(c); + next_ = c; + has_next_ = true; + } +}; + +#ifndef FMT_USE_FALLBACK_FILE +# define FMT_USE_FALLBACK_FILE 0 +#endif + +template +auto get_file(F* f, int) -> apple_file { + return f; +} +template +inline auto get_file(F* f, int) -> glibc_file { + return f; +} + +inline auto get_file(FILE* f, ...) -> fallback_file { return f; } + +using file_ref = decltype(get_file(static_cast(nullptr), 0)); + +template +class file_print_buffer : public buffer { + public: + explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} +}; + +template +class file_print_buffer::value>> + : public buffer { + private: + file_ref file_; + + static void grow(buffer& base, size_t) { + auto& self = static_cast(base); + self.file_.advance_write_buffer(self.size()); + if (self.file_.get_write_buffer().size == 0) self.file_.flush(); + auto buf = self.file_.get_write_buffer(); + FMT_ASSERT(buf.size > 0, ""); + self.set(buf.data, buf.size); + self.clear(); + } + + public: + explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { + flockfile(f); + file_.init_buffer(); + auto buf = file_.get_write_buffer(); + set(buf.data, buf.size); + } + ~file_print_buffer() { + file_.advance_write_buffer(size()); + bool flush = file_.needs_flush(); + F* f = file_; // Make funlockfile depend on the template parameter F + funlockfile(f); // for the system API detection to work. + if (flush) fflush(file_); + } +}; + +#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE) +FMT_FUNC auto write_console(int, string_view) -> bool { return false; } #else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); -FMT_FUNC bool write_console(std::FILE* f, string_view text) { - auto fd = _fileno(f); - if (!_isatty(fd)) return false; +FMT_FUNC bool write_console(int fd, string_view text) { auto u16 = utf8_to_utf16(text); - auto written = dword(); return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), - static_cast(u16.size()), &written, nullptr) != 0; + static_cast(u16.size()), nullptr, nullptr) != 0; } +#endif +#ifdef _WIN32 // Print assuming legacy (non-Unicode) encoding. -FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { +FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args, + bool newline) { auto buffer = memory_buffer(); - detail::vformat_to(buffer, fmt, - basic_format_args>(args)); - fwrite_fully(buffer.data(), 1, buffer.size(), f); + detail::vformat_to(buffer, fmt, args); + if (newline) buffer.push_back('\n'); + fwrite_all(buffer.data(), buffer.size(), f); } #endif FMT_FUNC void print(std::FILE* f, string_view text) { - if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f); +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) + int fd = _fileno(f); + if (_isatty(fd)) { + std::fflush(f); + if (write_console(fd, text)) return; + } +#endif + fwrite_all(text.data(), text.size(), f); } } // namespace detail -FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { +FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); detail::print(f, {buffer.data(), buffer.size()}); } +FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) + return vprint_buffered(f, fmt, args); + auto&& buffer = detail::file_print_buffer<>(f); + return detail::vformat_to(buffer, fmt, args); +} + +FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + buffer.push_back('\n'); + detail::print(f, {buffer.data(), buffer.size()}); +} + FMT_FUNC void vprint(string_view fmt, format_args args) { vprint(stdout, fmt, args); } diff --git a/deps/fmt/include/fmt/format.h b/deps/fmt/include/fmt/format.h index 87a34b972..4a6530072 100644 --- a/deps/fmt/include/fmt/format.h +++ b/deps/fmt/include/fmt/format.h @@ -33,20 +33,64 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include // std::signbit -#include // uint32_t -#include // std::memcpy -#include // std::initializer_list -#include // std::numeric_limits -#include // std::uninitialized_copy -#include // std::runtime_error -#include // std::system_error - -#ifdef __cpp_lib_bit_cast -# include // std::bitcast +#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define FMT_REMOVE_TRANSITIVE_INCLUDES #endif -#include "core.h" +#include "base.h" + +// libc++ supports string_view in pre-c++17. +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# define FMT_USE_STRING_VIEW +#endif + +#ifndef FMT_MODULE +# include // malloc, free + +# include // std::signbit +# include // std::byte +# include // uint32_t +# include // std::memcpy +# include // std::numeric_limits +# include // std::bad_alloc +# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI) +// Workaround for pre gcc 5 libstdc++. +# include // std::allocator_traits +# endif +# include // std::runtime_error +# include // std::string +# include // std::system_error + +// Check FMT_CPLUSPLUS to avoid a warning in MSVC. +# if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L +# include // std::bit_cast +# endif + +# if defined(FMT_USE_STRING_VIEW) +# include +# endif + +# if FMT_MSC_VERSION +# include // _BitScanReverse[64], _umul128 +# endif +#endif // FMT_MODULE + +#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS) +// Use the provided definition. +#elif defined(__NVCOMPILER) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif defined(__cpp_nontype_template_args) && \ + __cpp_nontype_template_args >= 201911L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#endif #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L # define FMT_INLINE_VARIABLE inline @@ -54,55 +98,22 @@ # define FMT_INLINE_VARIABLE #endif -#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) -# define FMT_FALLTHROUGH [[fallthrough]] -#elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -#elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] +// Check if RTTI is disabled. +#ifdef FMT_USE_RTTI +// Use the provided definition. +#elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ + defined(__INTEL_RTTI__) || defined(__RTTI) +// __RTTI is for EDG compilers. _CPPRTTI is for MSVC. +# define FMT_USE_RTTI 1 #else -# define FMT_FALLTHROUGH +# define FMT_USE_RTTI 0 #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VERSION -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif -#endif - -#ifndef FMT_NO_UNIQUE_ADDRESS -# if FMT_CPLUSPLUS >= 202002L -# if FMT_HAS_CPP_ATTRIBUTE(no_unique_address) -# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] -// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485) -# elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION -# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] -# endif -# endif -#endif -#ifndef FMT_NO_UNIQUE_ADDRESS -# define FMT_NO_UNIQUE_ADDRESS -#endif - -#if FMT_GCC_VERSION || defined(__clang__) -# define FMT_VISIBILITY(value) __attribute__((visibility(value))) +// Visibility when compiled as a shared library/object. +#if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_SO_VISIBILITY(value) FMT_VISIBILITY(value) #else -# define FMT_VISIBILITY(value) -#endif - -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 +# define FMT_SO_VISIBILITY(value) #endif #if FMT_GCC_VERSION || FMT_CLANG_VERSION @@ -111,54 +122,65 @@ # define FMT_NOINLINE #endif -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# if FMT_MSC_VERSION || defined(__NVCC__) -FMT_BEGIN_NAMESPACE -namespace detail { -template 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 detail -FMT_END_NAMESPACE -# define FMT_THROW(x) detail::do_throw(x) +#ifdef FMT_DEPRECATED +// Use the provided definition. +#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) +# define FMT_DEPRECATED [[deprecated]] +#else +# define FMT_DEPRECATED /* deprecated */ +#endif + +// Detect constexpr std::string. +#if !FMT_USE_CONSTEVAL +# define FMT_USE_CONSTEXPR_STRING 0 +#elif defined(__cpp_lib_constexpr_string) && \ + __cpp_lib_constexpr_string >= 201907L +# if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE +// clang + libstdc++ are able to work only starting with gcc13.3 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294 +# if FMT_GLIBCXX_RELEASE < 13 +# define FMT_USE_CONSTEXPR_STRING 0 +# elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521 +# define FMT_USE_CONSTEXPR_STRING 0 # else -# define FMT_THROW(x) throw x +# define FMT_USE_CONSTEXPR_STRING 1 # endif # else -# define FMT_THROW(x) \ - ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) +# define FMT_USE_CONSTEXPR_STRING 1 # 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) +# define FMT_USE_CONSTEXPR_STRING 0 +#endif +#if FMT_USE_CONSTEXPR_STRING +# define FMT_CONSTEXPR_STRING constexpr +#else +# define FMT_CONSTEXPR_STRING #endif -#ifndef FMT_MAYBE_UNUSED -# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) -# define FMT_MAYBE_UNUSED [[maybe_unused]] -# else -# define FMT_MAYBE_UNUSED -# endif +// GCC 4.9 doesn't support qualified names in specializations. +namespace std { +template struct iterator_traits> { + using iterator_category = output_iterator_tag; + using value_type = T; + using difference_type = + decltype(static_cast(nullptr) - static_cast(nullptr)); + using pointer = void; + using reference = void; +}; +} // namespace std + +#ifdef FMT_THROW +// Use the provided definition. +#elif FMT_USE_EXCEPTIONS +# define FMT_THROW(x) throw x +#else +# define FMT_THROW(x) ::fmt::assert_fail(__FILE__, __LINE__, (x).what()) #endif -#ifndef FMT_USE_USER_DEFINED_LITERALS -// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. -# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ - FMT_MSC_VERSION >= 1900) && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) -# define FMT_USE_USER_DEFINED_LITERALS 1 -# else -# define FMT_USE_USER_DEFINED_LITERALS 0 -# endif +#ifdef __clang_analyzer__ +# define FMT_CLANG_ANALYZER 1 +#else +# define FMT_CLANG_ANALYZER 0 #endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of @@ -169,7 +191,15 @@ FMT_END_NAMESPACE # define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif -// __builtin_clz is broken in clang with Microsoft CodeGen: +FMT_BEGIN_NAMESPACE + +template +struct is_contiguous> + : std::true_type {}; + +namespace detail { + +// __builtin_clz is broken in clang with Microsoft codegen: // https://github.com/fmtlib/fmt/issues/519. #if !FMT_MSC_VERSION # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION @@ -180,53 +210,30 @@ FMT_END_NAMESPACE # endif #endif -// __builtin_ctz is broken in Intel Compiler Classic on Windows: -// https://github.com/fmtlib/fmt/issues/2510. -#ifndef __ICL -# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ - defined(__NVCOMPILER) -# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) -# endif -# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ - FMT_ICC_VERSION || defined(__NVCOMPILER) -# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) -# endif -#endif - -#if FMT_MSC_VERSION -# include // _BitScanReverse[64], _BitScanForward[64], _umul128 -#endif - -// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// Some compilers masquerade as both MSVC and GCC but 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_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ - !defined(FMT_BUILTIN_CTZLL) -FMT_BEGIN_NAMESPACE -namespace detail { +#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. -# if !defined(__clang__) -# pragma intrinsic(_BitScanForward) +# ifndef __clang__ # pragma intrinsic(_BitScanReverse) -# if defined(_WIN64) -# pragma intrinsic(_BitScanForward64) +# ifdef _WIN64 # pragma intrinsic(_BitScanReverse64) # endif # endif inline auto clz(uint32_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. 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. - FMT_MSC_WARNING(suppress : 6102) return 31 ^ static_cast(r); } # define FMT_BUILTIN_CLZ(n) detail::clz(n) inline auto clzll(uint64_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); @@ -237,56 +244,10 @@ inline auto clzll(uint64_t x) -> int { // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return 63 ^ static_cast(r); } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) - -inline auto ctz(uint32_t x) -> int { - unsigned long r = 0; - _BitScanForward(&r, x); - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. - return static_cast(r); -} -# define FMT_BUILTIN_CTZ(n) detail::ctz(n) - -inline auto ctzll(uint64_t x) -> int { - unsigned long r = 0; - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -# ifdef _WIN64 - _BitScanForward64(&r, x); -# else - // Scan the low 32 bits. - if (_BitScanForward(&r, static_cast(x))) return static_cast(r); - // Scan the high 32 bits. - _BitScanForward(&r, static_cast(x >> 32)); - r += 32; -# endif - return static_cast(r); -} -# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) -} // namespace detail -FMT_END_NAMESPACE -#endif - -FMT_BEGIN_NAMESPACE - -template struct disjunction : std::false_type {}; -template struct disjunction

: P {}; -template -struct disjunction - : conditional_t> {}; - -template struct conjunction : std::true_type {}; -template struct conjunction

: P {}; -template -struct conjunction - : conditional_t, P1> {}; - -namespace detail { +#endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { ignore_unused(condition); @@ -295,49 +256,25 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { #endif } -template struct string_literal { - static constexpr CharT value[sizeof...(C)] = {C...}; - constexpr operator basic_string_view() const { +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#else +template struct std_string_view { + operator basic_string_view() const; +}; +#endif + +template struct string_literal { + static constexpr Char value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { return {value, sizeof...(C)}; } }; - #if FMT_CPLUSPLUS < 201703L -template -constexpr CharT string_literal::value[sizeof...(C)]; +template +constexpr Char string_literal::value[sizeof...(C)]; #endif -template class formatbuf : public Streambuf { - private: - using char_type = typename Streambuf::char_type; - using streamsize = decltype(std::declval().sputn(nullptr, 0)); - using int_type = typename Streambuf::int_type; - using traits_type = typename Streambuf::traits_type; - - buffer& buffer_; - - public: - explicit formatbuf(buffer& buf) : buffer_(buf) {} - - protected: - // The put area is always empty. This makes the implementation simpler and has - // the advantage that the streambuf and the buffer are always in sync and - // sputc never writes into uninitialized memory. A disadvantage is that each - // call to sputc always results in a (virtual) call to overflow. There is no - // disadvantage here for sputn since this always results in a call to xsputn. - - auto overflow(int_type ch) -> int_type override { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - buffer_.push_back(static_cast(ch)); - return ch; - } - - auto xsputn(const char_type* s, streamsize count) -> streamsize override { - buffer_.append(s, s + count); - return count; - } -}; - // Implementation of std::bit_cast for pre-C++20. template FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { @@ -373,8 +310,8 @@ class uint128_fallback { constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} - constexpr uint64_t high() const noexcept { return hi_; } - constexpr uint64_t low() const noexcept { return lo_; } + constexpr auto high() const noexcept -> uint64_t { return hi_; } + constexpr auto low() const noexcept -> uint64_t { return lo_; } template ::value)> constexpr explicit operator T() const { @@ -407,13 +344,14 @@ class uint128_fallback { -> uint128_fallback { return {~n.hi_, ~n.lo_}; } - friend auto operator+(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> uint128_fallback { + friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { auto result = uint128_fallback(lhs); result += rhs; return result; } - friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) + friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs) -> uint128_fallback { FMT_ASSERT(lhs.hi_ == 0, ""); uint64_t hi = (lhs.lo_ >> 32) * rhs; @@ -421,7 +359,7 @@ class uint128_fallback { uint64_t new_lo = (hi << 32) + lo; return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; } - friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) + friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs) -> uint128_fallback { return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; } @@ -450,7 +388,7 @@ class uint128_fallback { hi_ &= n.hi_; } - FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& { if (is_constant_evaluated()) { lo_ += n; hi_ += (lo_ < n ? 1 : 0); @@ -494,23 +432,24 @@ template constexpr auto num_bits() -> int { } // std::numeric_limits::digits may return 0 for 128-bit ints. template <> constexpr auto num_bits() -> int { return 128; } -template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } // A heterogeneous bit_cast used for converting 96-bit long double to uint128_t // and 128-bit pointers to uint128_fallback. template sizeof(From))> inline auto bit_cast(const From& from) -> To { - constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned short)); struct data_t { - unsigned value[static_cast(size)]; + unsigned short value[static_cast(size)]; } data = bit_cast(from); auto result = To(); if (const_check(is_big_endian())) { for (int i = 0; i < size; ++i) - result = (result << num_bits()) | data.value[i]; + result = (result << num_bits()) | data.value[i]; } else { for (int i = size - 1; i >= 0; --i) - result = (result << num_bits()) | data.value[i]; + result = (result << num_bits()) | data.value[i]; } return result; } @@ -546,38 +485,25 @@ FMT_INLINE void assume(bool condition) { #endif } -// An approximation of iterator_t for pre-C++20 systems. -template -using iterator_t = decltype(std::begin(std::declval())); -template using sentinel_t = decltype(std::end(std::declval())); - -// A workaround for std::string not having mutable data() until C++17. -template -inline auto get_data(std::basic_string& s) -> Char* { - return &s[0]; -} -template -inline auto get_data(Container& c) -> typename Container::value_type* { - return c.data(); -} - // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to it. -template ::value)> +template ::value&& + is_contiguous::value)> #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline auto -reserve(std::back_insert_iterator it, size_t n) -> - typename Container::value_type* { - Container& c = get_container(it); +FMT_CONSTEXPR20 inline auto +reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { + auto& c = get_container(it); size_t size = c.size(); c.resize(size + n); - return get_data(c) + size; + return &c[size]; } template -inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { +FMT_CONSTEXPR20 inline auto reserve(basic_appender it, size_t n) + -> basic_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; @@ -596,18 +522,27 @@ template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template auto to_pointer(buffer_appender it, size_t n) -> T* { +template FMT_CONSTEXPR auto to_pointer(T*& ptr, size_t n) -> T* { + T* begin = ptr; + ptr += n; + return begin; +} +template +FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; buf.try_resize(size + n); return buf.data() + size; } -template ::value)> -inline auto base_iterator(std::back_insert_iterator it, - typename Container::value_type*) - -> std::back_insert_iterator { +template ::value&& + is_contiguous::value)> +inline auto base_iterator(OutputIt it, + typename OutputIt::container_type::value_type*) + -> OutputIt { return it; } @@ -626,23 +561,17 @@ FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) } template FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { - if (is_constant_evaluated()) { - return fill_n(out, count, value); - } + if (is_constant_evaluated()) return fill_n(out, count, value); + static_assert(sizeof(T) == 1, + "sizeof(T) must be 1 to use char for initialization"); std::memset(out, value, to_unsigned(count)); return out + count; } -#ifdef __cpp_char8_t -using char8_type = char8_t; -#else -enum char8_type : unsigned char {}; -#endif - template -FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, - OutputIt out) -> OutputIt { - return copy_str(begin, end, out); +FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy(begin, end, out); } // A public domain branchless UTF-8 decoder by Christopher Wellons: @@ -664,10 +593,10 @@ FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, */ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) -> const char* { - constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; - constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; - constexpr const int shiftc[] = {0, 18, 12, 6, 0}; - constexpr const int shifte[] = {0, 6, 4, 2, 0}; + constexpr int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr int shiftc[] = {0, 18, 12, 6, 0}; + constexpr int shifte[] = {0, 6, 4, 2, 0}; int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" [static_cast(*s) >> 3]; @@ -713,6 +642,7 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); return result ? (error ? buf_ptr + 1 : end) : nullptr; }; + auto p = s.data(); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. if (s.size() >= block_size) { @@ -721,34 +651,25 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { if (!p) return; } } - if (auto num_chars_left = s.data() + s.size() - p) { - char buf[2 * block_size - 1] = {}; - copy_str(p, p + num_chars_left, buf); - const char* buf_ptr = buf; - do { - auto end = decode(buf_ptr, p); - if (!end) return; - p += end - buf_ptr; - buf_ptr = end; - } while (buf_ptr - buf < num_chars_left); - } + auto num_chars_left = to_unsigned(s.data() + s.size() - p); + if (num_chars_left == 0) return; + + // Suppress bogus -Wstringop-overflow. + if (FMT_GCC_VERSION) num_chars_left &= 3; + char buf[2 * block_size - 1] = {}; + copy(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr < buf + num_chars_left); } -template -inline auto compute_width(basic_string_view s) -> size_t { - return s.size(); -} - -// Computes approximate display width of a UTF-8 string. -FMT_CONSTEXPR inline size_t compute_width(string_view s) { - size_t num_code_points = 0; - // It is not a lambda for compatibility with C++14. - struct count_code_points { - size_t* count; - FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { - *count += detail::to_unsigned( - 1 + - (cp >= 0x1100 && +FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t { + return to_unsigned( + 1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET @@ -766,39 +687,6 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: (cp >= 0x1f900 && cp <= 0x1f9ff)))); - return true; - } - }; - // We could avoid branches by using utf8_decode directly. - for_each_codepoint(s, count_code_points{&num_code_points}); - return num_code_points; -} - -inline auto compute_width(basic_string_view s) -> size_t { - return compute_width( - string_view(reinterpret_cast(s.data()), s.size())); -} - -template -inline auto code_point_index(basic_string_view s, size_t n) -> size_t { - size_t size = s.size(); - return n < size ? n : size; -} - -// Calculates the index of the nth code point in a UTF-8 string. -inline auto code_point_index(string_view s, size_t n) -> size_t { - 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 > n) return i; - } - return s.size(); -} - -inline auto code_point_index(basic_string_view s, size_t n) - -> size_t { - return code_point_index( - string_view(reinterpret_cast(s.data()), s.size()), n); } template struct is_integral : std::is_integral {}; @@ -816,49 +704,35 @@ using is_integer = !std::is_same::value && !std::is_same::value>; -#ifndef FMT_USE_FLOAT -# define FMT_USE_FLOAT 1 +#if defined(FMT_USE_FLOAT128) +// Use the provided definition. +#elif FMT_CLANG_VERSION >= 309 && FMT_HAS_INCLUDE() +# define FMT_USE_FLOAT128 1 +#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \ + !defined(__STRICT_ANSI__) +# define FMT_USE_FLOAT128 1 +#else +# define FMT_USE_FLOAT128 0 #endif -#ifndef FMT_USE_DOUBLE -# define FMT_USE_DOUBLE 1 -#endif -#ifndef FMT_USE_LONG_DOUBLE -# define FMT_USE_LONG_DOUBLE 1 -#endif - -#ifndef FMT_USE_FLOAT128 -# ifdef __clang__ -// Clang emulates GCC, so it has to appear early. -# if FMT_HAS_INCLUDE() -# define FMT_USE_FLOAT128 1 -# endif -# elif defined(__GNUC__) -// GNU C++: -# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) -# define FMT_USE_FLOAT128 1 -# endif -# endif -# ifndef FMT_USE_FLOAT128 -# define FMT_USE_FLOAT128 0 -# endif -#endif - #if FMT_USE_FLOAT128 using float128 = __float128; #else -using float128 = void; +struct float128 {}; #endif + template using is_float128 = std::is_same; -template -using is_floating_point = - bool_constant::value || is_float128::value>; +template struct is_floating_point : std::is_floating_point {}; +template <> struct is_floating_point : std::true_type {}; -template ::value> +template ::value> struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; +template +using fast_float_t = conditional_t; + template using is_double_double = bool_constant::digits == 106>; @@ -866,24 +740,37 @@ using is_double_double = bool_constant::digits == 106>; # define FMT_USE_FULL_CACHE_DRAGONBOX 0 #endif -template -template -void buffer::append(const U* begin, const U* end) { - while (begin != end) { - auto count = to_unsigned(end - begin); - try_reserve(size_ + count); - auto free_cap = capacity_ - size_; - if (free_cap < count) count = free_cap; - std::uninitialized_copy_n(begin, count, ptr_ + size_); - size_ += count; - begin += count; - } -} +// An allocator that uses malloc/free to allow removing dependency on the C++ +// standard libary runtime. std::decay is used for back_inserter to be found by +// ADL when applied to memory_buffer. +template struct allocator : private std::decay { + using value_type = T; + + auto allocate(size_t n) -> T* { + FMT_ASSERT(n <= max_value() / sizeof(T), ""); + T* p = static_cast(malloc(n * sizeof(T))); + if (!p) FMT_THROW(std::bad_alloc()); + return p; + } + + void deallocate(T* p, size_t) { free(p); } + + constexpr friend auto operator==(allocator, allocator) noexcept -> bool { + return true; // All instances of this allocator are equivalent. + } + constexpr friend auto operator!=(allocator, allocator) noexcept -> bool { + return false; + } +}; + +template +FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) + -> decltype(f.set_debug_format(set)) { + f.set_debug_format(set); +} +template +FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} -template -struct is_locale : std::false_type {}; -template -struct is_locale> : std::true_type {}; } // namespace detail FMT_BEGIN_EXPORT @@ -893,29 +780,21 @@ FMT_BEGIN_EXPORT 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 the ``memory_buffer`` type alias for ``char`` instead. - - **Example**:: - - auto out = fmt::memory_buffer(); - format_to(std::back_inserter(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 + * A dynamically growing memory buffer for trivially copyable/constructible + * types with the first `SIZE` elements stored in the object itself. Most + * commonly used via the `memory_buffer` alias for `char`. + * + * **Example**: + * + * auto out = fmt::memory_buffer(); + * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); + * + * This will append "The answer is 42." to `out`. The buffer content can be + * converted to `std::string` with `to_string(out)`. */ template > -class basic_memory_buffer final : public detail::buffer { + typename Allocator = detail::allocator> +class basic_memory_buffer : public detail::buffer { private: T store_[SIZE]; @@ -928,51 +807,72 @@ class basic_memory_buffer final : public detail::buffer { if (data != store_) alloc_.deallocate(data, this->capacity()); } - protected: - FMT_CONSTEXPR20 void grow(size_t size) override { + static FMT_CONSTEXPR20 void grow(detail::buffer& buf, size_t size) { detail::abort_fuzzing_if(size > 5000); - const size_t max_size = std::allocator_traits::max_size(alloc_); - size_t old_capacity = this->capacity(); + auto& self = static_cast(buf); + const size_t max_size = + std::allocator_traits::max_size(self.alloc_); + size_t old_capacity = buf.capacity(); size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; else if (new_capacity > max_size) - new_capacity = size > max_size ? size : max_size; - T* old_data = this->data(); - T* new_data = - std::allocator_traits::allocate(alloc_, new_capacity); + new_capacity = max_of(size, max_size); + T* old_data = buf.data(); + T* new_data = self.alloc_.allocate(new_capacity); // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481). - detail::assume(this->size() <= new_capacity); + detail::assume(buf.size() <= new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy_n(old_data, this->size(), new_data); - this->set(new_data, new_capacity); + memcpy(new_data, old_data, buf.size() * sizeof(T)); + self.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_) alloc_.deallocate(old_data, old_capacity); + if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity); } public: using value_type = T; using const_reference = const T&; - FMT_CONSTEXPR20 explicit basic_memory_buffer( + FMT_CONSTEXPR explicit basic_memory_buffer( const Allocator& alloc = Allocator()) - : alloc_(alloc) { + : detail::buffer(grow), alloc_(alloc) { this->set(store_, SIZE); if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); } FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } private: + template :: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + alloc_ = std::move(other.alloc_); + return true; + } + // If the allocator does not propagate then copy the data from other. + template :: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + T* data = other.data(); + if (alloc_ == other.alloc_ || data == other.store_) return true; + size_t size = other.size(); + // Perform copy operation, allocators are different. + this->resize(size); + detail::copy(data, data + size, this->data()); + return false; + } + // Move data from other to this buffer. FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { - alloc_ = std::move(other.alloc_); T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); + if (!move_alloc(other)) return; if (data == other.store_) { this->set(store_, capacity); - detail::copy_str(other.store_, other.store_ + size, store_); + detail::copy(other.store_, other.store_ + size, store_); } else { this->set(data, capacity); // Set pointer to the inline array so that delete is not called @@ -984,21 +884,14 @@ class basic_memory_buffer final : public detail::buffer { } public: - /** - \rst - Constructs a :class:`fmt::basic_memory_buffer` object moving the content - of the other object to it. - \endrst - */ - FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + /// Constructs a `basic_memory_buffer` object moving the content of the other + /// object to it. + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept + : detail::buffer(grow) { move(other); } - /** - \rst - Moves the content of the other ``basic_memory_buffer`` object to this one. - \endrst - */ + /// Moves the content of the other `basic_memory_buffer` object to this one. auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); @@ -1009,120 +902,108 @@ class basic_memory_buffer final : public detail::buffer { // Returns a copy of the allocator associated with this buffer. auto get_allocator() const -> Allocator { return alloc_; } - /** - Resizes the buffer to contain *count* elements. If T is a POD type new - elements may not be initialized. - */ - FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } + /// Resizes the buffer to contain `count` elements. If T is a POD type new + /// elements may not be initialized. + FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); } - /** Increases the buffer capacity to *new_capacity*. */ + /// Increases the buffer capacity to `new_capacity`. void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } - // Directly append data into the buffer using detail::buffer::append; template - void append(const ContiguousRange& range) { + FMT_CONSTEXPR20 void append(const ContiguousRange& range) { append(range.data(), range.data() + range.size()); } }; using memory_buffer = basic_memory_buffer; +template +FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) + -> std::string { + auto size = buf.size(); + detail::assume(size < std::string().max_size()); + return {buf.data(), size}; +} + +// A writer to a buffered stream. It doesn't own the underlying stream. +class writer { + private: + detail::buffer* buf_; + + // We cannot create a file buffer in advance because any write to a FILE may + // invalidate it. + FILE* file_; + + public: + inline writer(FILE* f) : buf_(nullptr), file_(f) {} + inline writer(detail::buffer& buf) : buf_(&buf) {} + + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. + template void print(format_string fmt, T&&... args) { + if (buf_) + fmt::format_to(appender(*buf_), fmt, std::forward(args)...); + else + fmt::print(file_, fmt, std::forward(args)...); + } +}; + +class string_buffer { + private: + std::string str_; + detail::container_buffer buf_; + + public: + inline string_buffer() : buf_(str_) {} + + inline operator writer() { return buf_; } + inline auto str() -> std::string& { return str_; } +}; + template struct is_contiguous> : std::true_type { }; -FMT_END_EXPORT -namespace detail { -FMT_API bool write_console(std::FILE* f, string_view text); -FMT_API void print(std::FILE*, string_view); -} // namespace detail - -FMT_BEGIN_EXPORT - // Suppress a misleading warning in older versions of clang. -#if FMT_CLANG_VERSION -# pragma clang diagnostic ignored "-Wweak-vtables" -#endif +FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") -/** An error reported from a formatting function. */ -class FMT_VISIBILITY("default") format_error : public std::runtime_error { +/// An error reported from a formatting function. +class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { public: using std::runtime_error::runtime_error; }; -namespace detail_exported { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS +class loc_value; + +FMT_END_EXPORT +namespace detail { +FMT_API auto write_console(int fd, string_view text) -> bool; +FMT_API void print(FILE*, string_view); +} // namespace detail + +namespace detail { template struct fixed_string { - constexpr fixed_string(const Char (&str)[N]) { - detail::copy_str(static_cast(str), - str + N, data); + FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) { + detail::copy(static_cast(s), s + N, + data); } Char data[N] = {}; }; -#endif // Converts a compile-time string to basic_string_view. -template +FMT_EXPORT template constexpr auto compile_string_to_view(const Char (&s)[N]) -> basic_string_view { // Remove trailing NUL character if needed. Won't be present if this is used // with a raw character array (i.e. not defined as a string). return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; } -template -constexpr auto compile_string_to_view(detail::std_string_view s) +FMT_EXPORT template +constexpr auto compile_string_to_view(basic_string_view s) -> basic_string_view { - return {s.data(), s.size()}; + return s; } -} // namespace detail_exported - -class loc_value { - private: - basic_format_arg value_; - - public: - template ::value)> - loc_value(T value) : value_(detail::make_arg(value)) {} - - template ::value)> - loc_value(T) {} - - template auto visit(Visitor&& vis) -> decltype(vis(0)) { - return visit_format_arg(vis, value_); - } -}; - -// A locale facet that formats values in UTF-8. -// It is parameterized on the locale to avoid the heavy include. -template class format_facet : public Locale::facet { - private: - std::string separator_; - std::string grouping_; - std::string decimal_point_; - - protected: - virtual auto do_put(appender out, loc_value val, - const format_specs<>& specs) const -> bool; - - public: - static FMT_API typename Locale::id id; - - explicit format_facet(Locale& loc); - explicit format_facet(string_view sep = "", - std::initializer_list g = {3}, - std::string decimal_point = ".") - : separator_(sep.data(), sep.size()), - grouping_(g.begin(), g.end()), - decimal_point_(decimal_point) {} - - auto put(appender out, loc_value val, const format_specs<>& specs) const - -> bool { - return do_put(out, val, specs); - } -}; - -namespace detail { // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. @@ -1135,14 +1016,6 @@ constexpr auto is_negative(T) -> bool { return false; } -template -FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { - if (std::is_same()) return FMT_USE_FLOAT; - if (std::is_same()) return FMT_USE_DOUBLE; - if (std::is_same()) return FMT_USE_LONG_DOUBLE; - return true; -} - // Smallest of uint32_t, uint64_t, uint128_t that is large enough to // represent all values of an integral type T. template @@ -1153,27 +1026,28 @@ using uint32_or_64_or_128_t = template using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; -#define FMT_POWERS_OF_10(factor) \ - factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ - (factor)*1000000, (factor)*10000000, (factor)*100000000, \ - (factor)*1000000000 +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \ + (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \ + (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. -constexpr const char* digits2(size_t value) { - // GCC generates slightly better code when value is pointer-size. - return &"0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"[value * 2]; +// GCC generates slightly better code when value is pointer-size. +inline auto digits2(size_t value) -> const char* { + // Align data since unaligned access may be slower when crossing a + // hardware-specific boundary. + alignas(2) static const char data[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + return &data[value * 2]; } -// Sign is a template parameter to workaround a bug in gcc 4.8. -template constexpr Char sign(Sign s) { -#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 - static_assert(std::is_same::value, ""); -#endif - return static_cast("\0-+ "[s]); +template constexpr auto getsign(sign s) -> Char { + return static_cast(((' ' << 24) | ('+' << 16) | ('-' << 8)) >> + (static_cast(s) * 8)); } template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { @@ -1210,7 +1084,7 @@ inline auto do_count_digits(uint64_t n) -> int { 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; - static constexpr const uint64_t zero_or_powers_of_10[] = { + static constexpr uint64_t zero_or_powers_of_10[] = { 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL}; return t - (n < zero_or_powers_of_10[t]); @@ -1221,9 +1095,7 @@ inline auto do_count_digits(uint64_t n) -> int { // except for n == 0 in which case count_digits returns 1. FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL - if (!is_constant_evaluated()) { - return do_count_digits(n); - } + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } @@ -1273,9 +1145,7 @@ FMT_INLINE auto do_count_digits(uint32_t n) -> int { // Optional version of count_digits for better performance on 32-bit platforms. FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { #ifdef FMT_BUILTIN_CLZ - if (!is_constant_evaluated()) { - return do_count_digits(n); - } + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } @@ -1312,6 +1182,17 @@ template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } +#ifndef FMT_HEADER_ONLY +FMT_BEGIN_EXPORT +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto decimal_point_impl(locale_ref) -> char; +extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +FMT_END_EXPORT +#endif // FMT_HEADER_ONLY + // Compares two characters for equality. template auto equal2(const Char* lhs, const char* rhs) -> bool { return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); @@ -1320,83 +1201,99 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } -// Copies two characters from src to dst. +// Writes a two-digit value to out. template -FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { - if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { - memcpy(dst, src, 2); +FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) { + if (!is_constant_evaluated() && std::is_same::value && + !FMT_OPTIMIZE_SIZE) { + memcpy(out, digits2(value), 2); return; } - *dst++ = static_cast(*src++); - *dst = static_cast(*src); + *out++ = static_cast('0' + value / 10); + *out = static_cast('0' + value % 10); } -template struct format_decimal_result { - Iterator begin; - Iterator end; -}; - -// Formats a decimal unsigned integer value writing into out pointing to a -// buffer of specified size. The caller must ensure that the buffer is large -// enough. +// Formats a decimal unsigned integer value writing to out pointing to a buffer +// of specified size. The caller must ensure that the buffer is large enough. template -FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) - -> format_decimal_result { +FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size) + -> Char* { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); - out += size; - Char* end = out; + unsigned n = to_unsigned(size); 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. - out -= 2; - copy2(out, digits2(static_cast(value % 100))); + n -= 2; + write2digits(out + n, static_cast(value % 100)); value /= 100; } - if (value < 10) { - *--out = static_cast('0' + value); - return {out, end}; + if (value >= 10) { + n -= 2; + write2digits(out + n, static_cast(value)); + } else { + out[--n] = static_cast('0' + value); } - out -= 2; - copy2(out, digits2(static_cast(value))); - return {out, end}; + return out + n; } -template >::value)> -FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) - -> format_decimal_result { - // Buffer is large enough to hold all digits (digits10 + 1). - Char buffer[digits10() + 1] = {}; - auto end = format_decimal(buffer, value, size).end; - return {out, detail::copy_str_noinline(buffer, end, out)}; +template +FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value, + int num_digits) -> Char* { + do_format_decimal(out, value, num_digits); + return out + num_digits; } -template -FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) -> Char* { - buffer += num_digits; - Char* end = buffer; - do { - const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); - *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) - : digits[digit]); - } while ((value >>= BASE_BITS) != 0); - return end; -} - -template -FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits, - bool upper = false) -> It { +template >::value)> +FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) + -> OutputIt { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { - format_uint(ptr, value, num_digits, upper); + do_format_decimal(ptr, value, num_digits); return out; } - // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). - char buffer[num_bits() / BASE_BITS + 1]; - format_uint(buffer, value, num_digits, upper); - return detail::copy_str_noinline(buffer, buffer + num_digits, out); + // Buffer is large enough to hold all digits (digits10 + 1). + char buffer[digits10() + 1]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + do_format_decimal(buffer, value, num_digits); + return copy_noinline(buffer, buffer + num_digits, out); +} + +template +FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, + int size, bool upper = false) -> Char* { + out += size; + do { + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = static_cast(value & ((1u << base_bits) - 1)); + *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= base_bits) != 0); + return out; +} + +// Formats an unsigned integer in the power of two base (binary, octal, hex). +template +FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value, + int num_digits, bool upper = false) -> Char* { + do_format_base2e(base_bits, out, value, num_digits, upper); + return out + num_digits; +} + +template ::value)> +FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value, + int num_digits, bool upper = false) + -> OutputIt { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + format_base2e(base_bits, ptr, value, num_digits, upper); + return out; + } + // Make buffer large enough for any base. + char buffer[num_bits()]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + format_base2e(base_bits, buffer, value, num_digits, upper); + return detail::copy_noinline(buffer, buffer + num_digits, out); } // A converter from UTF-8 to UTF-16. @@ -1406,10 +1303,12 @@ class utf8_to_utf16 { public: FMT_API explicit utf8_to_utf16(string_view s); - operator basic_string_view() const { return {&buffer_[0], size()}; } - auto size() const -> size_t { return buffer_.size() - 1; } - auto c_str() const -> const wchar_t* { return &buffer_[0]; } - auto str() const -> std::wstring { return {&buffer_[0], size()}; } + inline operator basic_string_view() const { + return {&buffer_[0], size()}; + } + inline auto size() const -> size_t { return buffer_.size() - 1; } + inline auto c_str() const -> const wchar_t* { return &buffer_[0]; } + inline auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; enum class to_utf8_error_policy { abort, replace }; @@ -1424,28 +1323,30 @@ template class to_utf8 { explicit to_utf8(basic_string_view s, to_utf8_error_policy policy = to_utf8_error_policy::abort) { static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, - "Expect utf16 or utf32"); - if (!convert(s, policy)) + "expected utf16 or utf32"); + if (!convert(s, policy)) { FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" : "invalid utf32")); + } } operator string_view() const { return string_view(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char* c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const char* { return &buffer_[0]; } + auto str() const -> std::string { return std::string(&buffer_[0], size()); } // Performs conversion returning a bool instead of throwing exception on // conversion error. This method may still throw in case of memory allocation // error. - bool convert(basic_string_view s, - to_utf8_error_policy policy = to_utf8_error_policy::abort) { + auto convert(basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { if (!convert(buffer_, s, policy)) return false; buffer_.push_back(0); return true; } - static bool convert( - Buffer& buf, basic_string_view s, - to_utf8_error_policy policy = to_utf8_error_policy::abort) { + static auto convert(Buffer& buf, basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { for (auto p = s.begin(); p != s.end(); ++p) { uint32_t c = static_cast(*p); if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { @@ -1455,10 +1356,11 @@ template class to_utf8 { if (policy == to_utf8_error_policy::abort) return false; buf.append(string_view("\xEF\xBF\xBD")); --p; - } else { - c = (c << 10) + static_cast(*p) - 0x35fdc00; + continue; } - } else if (c < 0x80) { + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { buf.push_back(static_cast(c)); } else if (c < 0x800) { buf.push_back(static_cast(0xc0 | (c >> 6))); @@ -1481,7 +1383,7 @@ template class to_utf8 { }; // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { +FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return {static_cast(p >> 64), static_cast(p)}; @@ -1512,19 +1414,19 @@ inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { namespace dragonbox { // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. -inline int floor_log10_pow2(int e) noexcept { +inline auto floor_log10_pow2(int e) noexcept -> int { FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); return (e * 315653) >> 20; } -inline int floor_log2_pow10(int e) noexcept { +inline auto floor_log2_pow10(int e) noexcept -> int { FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); return (e * 1741647) >> 19; } // Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return static_cast(p >> 64); @@ -1537,14 +1439,14 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_fallback umul192_upper128(uint64_t x, - uint128_fallback y) noexcept { +inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { uint128_fallback r = umul128(x, y.high()); r += umul128_upper64(x, y.low()); return r; } -FMT_API uint128_fallback get_cached_power(int k) noexcept; +FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback; // Type-specific information that Dragonbox uses. template struct float_info; @@ -1598,14 +1500,14 @@ template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox // Returns true iff Float has the implicit bit which is not stored. -template constexpr bool has_implicit_bit() { +template constexpr auto has_implicit_bit() -> bool { // An 80-bit FP number has a 64-bit significand an no implicit bit. return std::numeric_limits::digits != 64; } // Returns the number of significand bits stored in Float. The implicit bit is // not counted since it is not stored. -template constexpr int num_significand_bits() { +template constexpr auto num_significand_bits() -> int { // std::numeric_limits may not support __float128. return is_float128() ? 112 : (std::numeric_limits::digits - @@ -1625,26 +1527,38 @@ template constexpr auto exponent_bias() -> int { : std::numeric_limits::max_exponent - 1; } +FMT_CONSTEXPR inline auto compute_exp_size(int exp) -> int { + auto prefix_size = 2; // sign + 'e' + auto abs_exp = exp >= 0 ? exp : -exp; + if (abs_exp < 100) return prefix_size + 2; + return prefix_size + (abs_exp >= 1000 ? 4 : 3); +} + // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template -FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { +template +FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { - *it++ = static_cast('-'); + *out++ = static_cast('-'); exp = -exp; } else { - *it++ = static_cast('+'); + *out++ = static_cast('+'); } - if (exp >= 100) { - const char* top = digits2(to_unsigned(exp / 100)); - if (exp >= 1000) *it++ = static_cast(top[0]); - *it++ = static_cast(top[1]); - exp %= 100; + auto uexp = static_cast(exp); + if (is_constant_evaluated()) { + if (uexp < 10) *out++ = '0'; + return format_decimal(out, uexp, count_digits(uexp)); } - const char* d = digits2(to_unsigned(exp)); - *it++ = static_cast(d[0]); - *it++ = static_cast(d[1]); - return it; + if (uexp >= 100u) { + const char* top = digits2(uexp / 100); + if (uexp >= 1000u) *out++ = static_cast(top[0]); + *out++ = static_cast(top[1]); + uexp %= 100; + } + const char* d = digits2(uexp); + *out++ = static_cast(d[0]); + *out++ = static_cast(d[1]); + return out; } // A floating-point number f * pow(2, e) where F is an unsigned type. @@ -1652,7 +1566,7 @@ template struct basic_fp { F f; int e; - static constexpr const int num_significand_bits = + static constexpr int num_significand_bits = static_cast(sizeof(F) * num_bits()); constexpr basic_fp() : f(0), e(0) {} @@ -1698,7 +1612,7 @@ using fp = basic_fp; // Normalizes the value converted from double and multiplied by (1 << SHIFT). template -FMT_CONSTEXPR basic_fp normalize(basic_fp value) { +FMT_CONSTEXPR auto normalize(basic_fp value) -> basic_fp { // Handle subnormals. const auto implicit_bit = F(1) << num_significand_bits(); const auto shifted_implicit_bit = implicit_bit << SHIFT; @@ -1715,7 +1629,7 @@ FMT_CONSTEXPR basic_fp normalize(basic_fp value) { } // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t { #if FMT_USE_INT128 auto product = static_cast<__uint128_t>(lhs) * rhs; auto f = static_cast(product >> 64); @@ -1732,33 +1646,10 @@ FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { #endif } -FMT_CONSTEXPR inline fp operator*(fp x, fp y) { +FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp { return {multiply(x.f, y.f), x.e + y.e + 64}; } -template struct basic_data { - // For checking rounding thresholds. - // The kth entry is chosen to be the smallest integer such that the - // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. - static constexpr uint32_t fractional_part_rounding_thresholds[8] = { - 2576980378U, // ceil(2^31 + 2^32/10^1) - 2190433321U, // ceil(2^31 + 2^32/10^2) - 2151778616U, // ceil(2^31 + 2^32/10^3) - 2147913145U, // ceil(2^31 + 2^32/10^4) - 2147526598U, // ceil(2^31 + 2^32/10^5) - 2147487943U, // ceil(2^31 + 2^32/10^6) - 2147484078U, // ceil(2^31 + 2^32/10^7) - 2147483691U // ceil(2^31 + 2^32/10^8) - }; -}; -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct data : basic_data<> {}; - -#if FMT_CPLUSPLUS < 201703L -template -constexpr uint32_t basic_data::fractional_part_rounding_thresholds[]; -#endif - template () == num_bits()> using convert_float_result = conditional_t::value || doublish, double, T>; @@ -1768,67 +1659,78 @@ constexpr auto convert_float(T value) -> convert_float_result { return static_cast>(value); } -template -FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, - const fill_t& fill) -> OutputIt { - auto fill_size = fill.size(); - if (fill_size == 1) return detail::fill_n(it, n, fill[0]); - auto data = fill.data(); - for (size_t i = 0; i < n; ++i) - it = copy_str(data, data + fill_size, it); +template +auto select(T true_value, F) -> T { + return true_value; +} +template +auto select(T, F false_value) -> F { + return false_value; +} + +template +FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, + const basic_specs& specs) -> OutputIt { + auto fill_size = specs.fill_size(); + if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit()); + if (const Char* data = specs.fill()) { + for (size_t i = 0; i < n; ++i) it = copy(data, data + fill_size, it); + } return it; } // Writes the output of f, padded according to format specifications in specs. // size: output size in code units. // width: output display width in (terminal) column positions. -template -FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, +FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, size_t size, size_t width, F&& f) -> OutputIt { - static_assert(align == align::left || align == align::right, ""); + static_assert(default_align == align::left || default_align == align::right, + ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; // Shifts are encoded as string literals because static constexpr is not // supported in constexpr functions. - auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; - size_t left_padding = padding >> shifts[specs.align]; + auto* shifts = + default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; + size_t left_padding = padding >> shifts[static_cast(specs.align())]; size_t right_padding = padding - left_padding; - auto it = reserve(out, size + padding * specs.fill.size()); - if (left_padding != 0) it = fill(it, left_padding, specs.fill); + auto it = reserve(out, size + padding * specs.fill_size()); + if (left_padding != 0) it = fill(it, left_padding, specs); it = f(it); - if (right_padding != 0) it = fill(it, right_padding, specs.fill); + if (right_padding != 0) it = fill(it, right_padding, specs); return base_iterator(out, it); } -template -constexpr auto write_padded(OutputIt out, const format_specs& specs, +constexpr auto write_padded(OutputIt out, const format_specs& specs, size_t size, F&& f) -> OutputIt { - return write_padded(out, specs, size, size, f); + return write_padded(out, specs, size, size, f); } -template +template FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, - const format_specs& specs) -> OutputIt { - return write_padded( + const format_specs& specs = {}) -> OutputIt { + return write_padded( out, specs, bytes.size(), [bytes](reserve_iterator it) { const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); + return copy(data, data + bytes.size(), it); }); } template -auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) +auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) -> OutputIt { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); auto write = [=](reserve_iterator it) { *it++ = static_cast('0'); *it++ = static_cast('x'); - return format_uint<4, Char>(it, value, num_digits); + return format_base2e(4, it, value, num_digits); }; - return specs ? write_padded(out, *specs, size, write) + return specs ? write_padded(out, *specs, size, write) : base_iterator(out, write(reserve(out, size))); } @@ -1836,8 +1738,9 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) FMT_API auto is_printable(uint32_t cp) -> bool; inline auto needs_escape(uint32_t cp) -> bool { - return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || - !is_printable(cp); + if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; + if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false; + return !is_printable(cp); } template struct find_escape_result { @@ -1846,17 +1749,11 @@ template struct find_escape_result { uint32_t cp; }; -template -using make_unsigned_char = - typename conditional_t::value, - std::make_unsigned, - type_identity>::type; - template auto find_escape(const Char* begin, const Char* end) -> find_escape_result { for (; begin != end; ++begin) { - uint32_t cp = static_cast>(*begin); + uint32_t cp = static_cast>(*begin); if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; if (needs_escape(cp)) return {begin, begin + 1, cp}; } @@ -1865,7 +1762,7 @@ auto find_escape(const Char* begin, const Char* end) inline auto find_escape(const char* begin, const char* end) -> find_escape_result { - if (!is_utf8()) return find_escape(begin, end); + if (const_check(!use_utf8)) return find_escape(begin, end); auto result = find_escape_result{end, nullptr, 0}; for_each_codepoint(string_view(begin, to_unsigned(end - begin)), [&](uint32_t cp, string_view sv) { @@ -1878,40 +1775,14 @@ inline auto find_escape(const char* begin, const char* end) return result; } -#define FMT_STRING_IMPL(s, base, explicit) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ - using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ - operator fmt::basic_string_view() const { \ - return fmt::detail_exported::compile_string_to_view(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 = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst - */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) - template auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { *out++ = static_cast('\\'); *out++ = static_cast(prefix); Char buf[width]; fill_n(buf, width, static_cast('0')); - format_uint<4>(buf, cp, width); - return copy_str(buf, buf + width, out); + format_base2e(4, buf, cp, width); + return copy(buf, buf + width, out); } template @@ -1931,23 +1802,15 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) *out++ = static_cast('\\'); c = static_cast('t'); break; - case '"': - FMT_FALLTHROUGH; - case '\'': - FMT_FALLTHROUGH; - case '\\': - *out++ = static_cast('\\'); - break; + case '"': FMT_FALLTHROUGH; + case '\'': FMT_FALLTHROUGH; + case '\\': *out++ = static_cast('\\'); break; default: - if (escape.cp < 0x100) { - return write_codepoint<2, Char>(out, 'x', escape.cp); - } - if (escape.cp < 0x10000) { + if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp); + if (escape.cp < 0x10000) return write_codepoint<4, Char>(out, 'u', escape.cp); - } - if (escape.cp < 0x110000) { + if (escape.cp < 0x110000) return write_codepoint<8, Char>(out, 'U', escape.cp); - } for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { out = write_codepoint<2, Char>(out, 'x', @@ -1966,7 +1829,7 @@ auto write_escaped_string(OutputIt out, basic_string_view str) auto begin = str.begin(), end = str.end(); do { auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); + out = copy(begin, escape.begin, out); begin = escape.end; if (!begin) break; out = write_escaped_cp(out, escape); @@ -1977,11 +1840,13 @@ auto write_escaped_string(OutputIt out, basic_string_view str) template auto write_escaped_char(OutputIt out, Char v) -> OutputIt { + Char v_array[1] = {v}; *out++ = static_cast('\''); if ((needs_escape(static_cast(v)) && v != static_cast('"')) || v == static_cast('\'')) { - out = write_escaped_cp( - out, find_escape_result{&v, &v + 1, static_cast(v)}); + out = write_escaped_cp(out, + find_escape_result{v_array, v_array + 1, + static_cast(v)}); } else { *out++ = v; } @@ -1991,75 +1856,14 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt { template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, - const format_specs& specs) -> OutputIt { - bool is_debug = specs.type == presentation_type::debug; - return write_padded(out, specs, 1, [=](reserve_iterator it) { + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + return write_padded(out, specs, 1, [=](reserve_iterator it) { if (is_debug) return write_escaped_char(it, value); *it++ = value; return it; }); } -template -FMT_CONSTEXPR auto write(OutputIt out, Char value, - const format_specs& specs, locale_ref loc = {}) - -> OutputIt { - // char is formatted as unsigned char for consistency across platforms. - using unsigned_type = - conditional_t::value, unsigned char, unsigned>; - return check_char_specs(specs) - ? write_char(out, value, specs) - : write(out, static_cast(value), specs, loc); -} - -// Data for write_int that doesn't depend on output iterator type. It is used to -// avoid template code bloat. -template struct write_int_data { - size_t size; - size_t padding; - - FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, - const format_specs& specs) - : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { - if (specs.align == align::numeric) { - auto width = to_unsigned(specs.width); - if (width > size) { - padding = width - size; - size = width; - } - } else if (specs.precision > num_digits) { - size = (prefix >> 24) + to_unsigned(specs.precision); - padding = to_unsigned(specs.precision - num_digits); - } - } -}; - -// Writes an integer in the format -// -// where are written by write_digits(it). -// prefix contains chars in three lower bytes and the size in the fourth byte. -template -FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, - unsigned prefix, - const format_specs& specs, - W write_digits) -> OutputIt { - // Slightly faster check for specs.width == 0 && specs.precision == -1. - if ((specs.width | (specs.precision + 1)) == 0) { - auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); - if (prefix != 0) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - } - return base_iterator(out, write_digits(it)); - } - auto data = write_int_data(num_digits, prefix, specs); - return write_padded( - out, specs, data.size, [=](reserve_iterator it) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - it = detail::fill_n(it, data.padding, static_cast('0')); - return write_digits(it); - }); -} template class digit_grouping { private: @@ -2070,10 +1874,10 @@ template class digit_grouping { std::string::const_iterator group; int pos; }; - next_state initial_state() const { return {grouping_.begin(), 0}; } + auto initial_state() const -> next_state { return {grouping_.begin(), 0}; } // Returns the next digit group separator position. - int next(next_state& state) const { + auto next(next_state& state) const -> int { if (thousands_sep_.empty()) return max_value(); if (state.group == grouping_.end()) return state.pos += grouping_.back(); if (*state.group <= 0 || *state.group == max_value()) @@ -2092,18 +1896,18 @@ template class digit_grouping { digit_grouping(std::string grouping, std::basic_string sep) : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} - bool has_separator() const { return !thousands_sep_.empty(); } + auto has_separator() const -> bool { return !thousands_sep_.empty(); } - int count_separators(int num_digits) const { + auto count_separators(int num_digits) const -> int { int count = 0; auto state = initial_state(); while (num_digits > next(state)) ++count; return count; } - // Applies grouping to digits and write the output to out. + // Applies grouping to digits and writes the output to out. template - Out apply(Out out, basic_string_view digits) const { + auto apply(Out out, basic_string_view digits) const -> Out { auto num_digits = static_cast(digits.size()); auto separators = basic_memory_buffer(); separators.push_back(0); @@ -2115,9 +1919,8 @@ template class digit_grouping { for (int i = 0, sep_index = static_cast(separators.size() - 1); i < num_digits; ++i) { if (num_digits - i == separators[sep_index]) { - out = - copy_str(thousands_sep_.data(), - thousands_sep_.data() + thousands_sep_.size(), out); + out = copy(thousands_sep_.data(), + thousands_sep_.data() + thousands_sep_.size(), out); --sep_index; } *out++ = static_cast(digits[to_unsigned(i)]); @@ -2126,48 +1929,80 @@ template class digit_grouping { } }; -// Writes a decimal integer with digit grouping. -template -auto write_int(OutputIt out, UInt value, unsigned prefix, - const format_specs& specs, - const digit_grouping& grouping) -> OutputIt { - static_assert(std::is_same, UInt>::value, ""); - int num_digits = count_digits(value); - char digits[40]; - format_decimal(digits, value, num_digits); - unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + - grouping.count_separators(num_digits)); - return write_padded( - out, specs, size, size, [&](reserve_iterator it) { - if (prefix != 0) { - char sign = static_cast(prefix); - *it++ = static_cast(sign); - } - return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); - }); -} - -// Writes a localized value. -FMT_API auto write_loc(appender out, loc_value value, - const format_specs<>& specs, locale_ref loc) -> bool; -template -inline auto write_loc(OutputIt, loc_value, const format_specs&, - locale_ref) -> bool { - return false; -} - FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { prefix |= prefix != 0 ? value << 8 : value; prefix += (1u + (value > 0xff ? 1 : 0)) << 24; } +// Writes a decimal integer with digit grouping. +template +auto write_int(OutputIt out, UInt value, unsigned prefix, + const format_specs& specs, const digit_grouping& grouping) + -> OutputIt { + static_assert(std::is_same, UInt>::value, ""); + int num_digits = 0; + auto buffer = memory_buffer(); + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; + case presentation_type::none: + case presentation_type::dec: + num_digits = count_digits(value); + format_decimal(appender(buffer), value, num_digits); + break; + case presentation_type::hex: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); + num_digits = count_digits<4>(value); + format_base2e(4, appender(buffer), value, num_digits, specs.upper()); + break; + case presentation_type::oct: + num_digits = count_digits<3>(value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt() && specs.precision <= num_digits && value != 0) + prefix_append(prefix, '0'); + format_base2e(3, appender(buffer), value, num_digits); + break; + case presentation_type::bin: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + num_digits = count_digits<1>(value); + format_base2e(1, appender(buffer), value, num_digits); + break; + case presentation_type::chr: + return write_char(out, static_cast(value), specs); + } + + unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + + to_unsigned(grouping.count_separators(num_digits)); + return write_padded( + out, specs, size, size, [&](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return grouping.apply(it, string_view(buffer.data(), buffer.size())); + }); +} + +#if FMT_USE_LOCALE +// Writes a localized value. +FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, + locale_ref loc) -> bool; +auto write_loc(basic_appender out, loc_value value, + const format_specs& specs, locale_ref loc) -> bool; +#endif +template +inline auto write_loc(OutputIt, const loc_value&, const format_specs&, + locale_ref) -> bool { + return false; +} + template struct write_int_arg { UInt abs_value; unsigned prefix; }; template -FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) +FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) -> write_int_arg> { auto prefix = 0u; auto abs_value = static_cast>(value); @@ -2175,23 +2010,22 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) prefix = 0x01000000 | '-'; abs_value = 0 - abs_value; } else { - constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; - prefix = prefixes[sign]; + constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; + prefix = prefixes[static_cast(s)]; } return {abs_value, prefix}; } template struct loc_writer { - buffer_appender out; - const format_specs& specs; + basic_appender out; + const format_specs& specs; std::basic_string sep; std::string grouping; std::basic_string decimal_point; template ::value)> auto operator()(T value) -> bool { - auto arg = make_write_int_arg(value, specs.sign); + auto arg = make_write_int_arg(value, specs.sign()); write_int(out, static_cast>(arg.abs_value), arg.prefix, specs, digit_grouping(grouping, sep)); return true; @@ -2203,166 +2037,255 @@ template struct loc_writer { } }; +// Size and padding computation separate from write_int to avoid template bloat. +struct size_padding { + unsigned size; + unsigned padding; + + FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix, + const format_specs& specs) + : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { + if (specs.align() == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = (prefix >> 24) + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, - const format_specs& specs, - locale_ref) -> OutputIt { + const format_specs& specs) -> OutputIt { static_assert(std::is_same>::value, ""); + + constexpr size_t buffer_size = num_bits(); + char buffer[buffer_size]; + if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); + const char* begin = nullptr; + const char* end = buffer + buffer_size; + auto abs_value = arg.abs_value; auto prefix = arg.prefix; - switch (specs.type) { + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: - case presentation_type::dec: { - auto num_digits = count_digits(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_decimal(it, abs_value, num_digits).end; - }); - } - case presentation_type::hex_lower: - case presentation_type::hex_upper: { - bool upper = specs.type == presentation_type::hex_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); - int num_digits = count_digits<4>(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, upper); - }); - } - case presentation_type::bin_lower: - case presentation_type::bin_upper: { - bool upper = specs.type == presentation_type::bin_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); - int num_digits = count_digits<1>(abs_value); - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } + case presentation_type::dec: + begin = do_format_decimal(buffer, abs_value, buffer_size); + break; + case presentation_type::hex: + begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper()); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); + break; case presentation_type::oct: { - int num_digits = count_digits<3>(abs_value); + begin = do_format_base2e(3, buffer, abs_value, buffer_size); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. - if (specs.alt && specs.precision <= num_digits && abs_value != 0) + auto num_digits = end - begin; + if (specs.alt() && specs.precision <= num_digits && abs_value != 0) prefix_append(prefix, '0'); - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); + break; } + case presentation_type::bin: + begin = do_format_base2e(1, buffer, abs_value, buffer_size); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + break; case presentation_type::chr: - return write_char(out, static_cast(abs_value), specs); - default: - throw_format_error("invalid format specifier"); + return write_char(out, static_cast(abs_value), specs); } - return out; + + // Write an integer in the format + // + // prefix contains chars in three lower bytes and the size in the fourth byte. + int num_digits = static_cast(end - begin); + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return base_iterator(out, copy(begin, end, it)); + } + auto sp = size_padding(num_digits, prefix, specs); + unsigned padding = sp.padding; + return write_padded( + out, specs, sp.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, padding, static_cast('0')); + return copy(begin, end, it); + }); } + template -FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( - OutputIt out, write_int_arg arg, const format_specs& specs, - locale_ref loc) -> OutputIt { - return write_int(out, arg, specs, loc); +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, + write_int_arg arg, + const format_specs& specs) + -> OutputIt { + return write_int(out, arg, specs); } -template ::value && !std::is_same::value && - std::is_same>::value)> -FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const format_specs& specs, - locale_ref loc) -> OutputIt { - if (specs.localized && write_loc(out, value, specs, loc)) return out; - return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, - loc); + !std::is_same::value)> +FMT_CONSTEXPR FMT_INLINE auto write(basic_appender out, T value, + const format_specs& specs, locale_ref loc) + -> basic_appender { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int_noinline(out, make_write_int_arg(value, specs.sign()), + specs); } + // An inlined version of write used in format string compilation. template ::value && !std::is_same::value && - !std::is_same>::value)> + !std::is_same::value && + !std::is_same>::value)> FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const format_specs& specs, - locale_ref loc) -> OutputIt { - if (specs.localized && write_loc(out, value, specs, loc)) return out; - return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); + const format_specs& specs, locale_ref loc) + -> OutputIt { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int(out, make_write_int_arg(value, specs.sign()), specs); } -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - size_t count_; +template +FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, + locale_ref loc = {}) -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t::value, unsigned char, unsigned>; + return check_char_specs(specs) + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); +} - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - FMT_UNCHECKED_ITERATOR(counting_iterator); +template ::value)> +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + if (specs.precision < 0 && specs.width == 0) { + auto&& it = reserve(out, s.size()); + return is_debug ? write_escaped_string(it, s) : copy(s, it); + } - struct value_type { - template FMT_CONSTEXPR void operator=(const T&) {} + size_t display_width_limit = + specs.precision < 0 ? SIZE_MAX : to_unsigned(specs.precision); + size_t display_width = + !is_debug || specs.precision == 0 ? 0 : 1; // Account for opening '"'. + size_t size = !is_debug || specs.precision == 0 ? 0 : 1; + for_each_codepoint(s, [&](uint32_t cp, string_view sv) { + if (is_debug && needs_escape(cp)) { + counting_buffer buf; + write_escaped_cp(basic_appender(buf), + find_escape_result{sv.begin(), sv.end(), cp}); + // We're reinterpreting bytes as display width. That's okay + // because write_escaped_cp() only writes ASCII characters. + size_t cp_width = buf.count(); + if (display_width + cp_width <= display_width_limit) { + display_width += cp_width; + size += cp_width; + // If this is the end of the string, account for closing '"'. + if (display_width < display_width_limit && sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + size += display_width_limit - display_width; + display_width = display_width_limit; + return false; + } + + size_t cp_width = display_width_of(cp); + if (cp_width + display_width <= display_width_limit) { + display_width += cp_width; + size += sv.size(); + // If this is the end of the string, account for closing '"'. + if (is_debug && display_width < display_width_limit && + sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + return false; + }); + + struct bounded_output_iterator { + reserve_iterator underlying_iterator; + size_t bound; + + FMT_CONSTEXPR auto operator*() -> bounded_output_iterator& { return *this; } + FMT_CONSTEXPR auto operator++() -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator++(int) -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator=(char c) -> bounded_output_iterator& { + if (bound > 0) { + *underlying_iterator++ = c; + --bound; + } + return *this; + } }; - FMT_CONSTEXPR counting_iterator() : count_(0) {} + return write_padded( + out, specs, size, display_width, [=](reserve_iterator it) { + return is_debug + ? write_escaped_string(bounded_output_iterator{it, size}, s) + .underlying_iterator + : copy(s.data(), s.data() + size, it); + }); +} - FMT_CONSTEXPR size_t count() const { return count_; } - - FMT_CONSTEXPR counting_iterator& operator++() { - ++count_; - return *this; - } - FMT_CONSTEXPR counting_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } - - FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, - difference_type n) { - it.count_ += static_cast(n); - return it; - } - - FMT_CONSTEXPR value_type operator*() const { return {}; } -}; - -template +template ::value)> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, - const format_specs& specs) -> OutputIt { + const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) - size = code_point_index(s, to_unsigned(specs.precision)); - bool is_debug = specs.type == presentation_type::debug; - size_t width = 0; - if (specs.width != 0) { - if (is_debug) - width = write_escaped_string(counting_iterator{}, s).count(); - else - width = compute_width(basic_string_view(data, size)); + size = to_unsigned(specs.precision); + + bool is_debug = specs.type() == presentation_type::debug; + if (is_debug) { + auto buf = counting_buffer(); + write_escaped_string(basic_appender(buf), s); + size = buf.count(); } - return write_padded(out, specs, size, width, - [=](reserve_iterator it) { - if (is_debug) return write_escaped_string(it, s); - return copy_str(data, data + size, it); - }); + + return write_padded( + out, specs, size, [=](reserve_iterator it) { + return is_debug ? write_escaped_string(it, s) + : copy(data, data + size, it); + }); } + template -FMT_CONSTEXPR auto write(OutputIt out, - basic_string_view> s, - const format_specs& specs, locale_ref) - -> OutputIt { - return write(out, s, specs); +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs, locale_ref) -> OutputIt { + return write(out, s, specs); } + template -FMT_CONSTEXPR auto write(OutputIt out, const Char* s, - const format_specs& specs, locale_ref) - -> OutputIt { - return specs.type != presentation_type::pointer - ? write(out, basic_string_view(s), specs, {}) - : write_ptr(out, bit_cast(s), &specs); +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, + locale_ref) -> OutputIt { + if (specs.type() == presentation_type::pointer) + return write_ptr(out, bit_cast(s), &specs); + if (!s) report_error("string pointer is null"); + return write(out, basic_string_view(s), specs, {}); } template OutputIt { if (negative) abs_value = ~abs_value + 1; int num_digits = count_digits(abs_value); auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { + if (auto ptr = to_pointer(out, size)) { if (negative) *ptr++ = static_cast('-'); format_decimal(ptr, abs_value, num_digits); return out; } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); + if (negative) *out++ = static_cast('-'); + return format_decimal(out, abs_value, num_digits); } -// DEPRECATED! template FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, - format_specs& specs) -> const Char* { + format_specs& specs) -> const Char* { FMT_ASSERT(begin != end, ""); - auto align = align::none; + auto alignment = align::none; auto p = begin + code_point_length(begin); if (end - p <= 0) p = begin; for (;;) { switch (to_ascii(*p)) { - case '<': - align = align::left; - break; - case '>': - align = align::right; - break; - case '^': - align = align::center; - break; + case '<': alignment = align::left; break; + case '>': alignment = align::right; break; + case '^': alignment = align::center; break; } - if (align != align::none) { + if (alignment != align::none) { if (p != begin) { auto c = *begin; if (c == '}') return begin; if (c == '{') { - throw_format_error("invalid fill character '{'"); + report_error("invalid fill character '{'"); return begin; } - specs.fill = {begin, to_unsigned(p - begin)}; + specs.set_fill(basic_string_view(begin, to_unsigned(p - begin))); begin = p + 1; } else { ++begin; @@ -2426,89 +2340,27 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, } p = begin; } - specs.align = align; + specs.set_align(alignment); return begin; } -// 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 binary32 : 1; - bool showpoint : 1; -}; - -template -FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, - ErrorHandler&& eh = {}) - -> float_specs { - auto result = float_specs(); - result.showpoint = specs.alt; - result.locale = specs.localized; - switch (specs.type) { - case presentation_type::none: - result.format = float_format::general; - break; - case presentation_type::general_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::general_lower: - result.format = float_format::general; - break; - case presentation_type::exp_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::exp_lower: - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::fixed_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::fixed_lower: - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::hexfloat_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::hexfloat_lower: - result.format = float_format::hex; - break; - default: - eh.on_error("invalid format specifier"); - break; - } - return result; -} - template FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, - format_specs specs, - const float_specs& fspecs) -> OutputIt { + format_specs specs, sign s) -> OutputIt { auto str = - isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); + isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf"); constexpr size_t str_size = 3; - auto sign = fspecs.sign; - auto size = str_size + (sign ? 1 : 0); + auto size = str_size + (s != sign::none ? 1 : 0); // Replace '0'-padding with space for non-finite values. const bool is_zero_fill = - specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); - if (is_zero_fill) specs.fill[0] = static_cast(' '); - return write_padded(out, specs, size, [=](reserve_iterator it) { - if (sign) *it++ = detail::sign(sign); - return copy_str(str, str + str_size, it); - }); + specs.fill_size() == 1 && specs.fill_unit() == '0'; + if (is_zero_fill) specs.set_fill(' '); + return write_padded(out, specs, size, + [=](reserve_iterator it) { + if (s != sign::none) + *it++ = detail::getsign(s); + return copy(str, str + str_size, it); + }); } // A decimal floating-point number significand * pow(10, exp). @@ -2529,12 +2381,12 @@ inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { template constexpr auto write_significand(OutputIt out, const char* significand, int significand_size) -> OutputIt { - return copy_str(significand, significand + significand_size, out); + return copy(significand, significand + significand_size, out); } template inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt { - return format_decimal(out, significand, significand_size).end; + return format_decimal(out, significand, significand_size); } template FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, @@ -2554,14 +2406,13 @@ template ::value)> inline auto write_significand(Char* out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> Char* { - if (!decimal_point) - return format_decimal(out, significand, significand_size).end; + if (!decimal_point) return format_decimal(out, significand, significand_size); out += significand_size + 1; Char* end = out; int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - copy2(out, digits2(static_cast(significand % 100))); + write2digits(out, static_cast(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2582,19 +2433,19 @@ inline auto write_significand(OutputIt out, UInt significand, Char buffer[digits10() + 2]; auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point); - return detail::copy_str_noinline(buffer, end, out); + return detail::copy_noinline(buffer, end, out); } template FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, int significand_size, int integral_size, Char decimal_point) -> OutputIt { - out = detail::copy_str_noinline(significand, - significand + integral_size, out); + out = detail::copy_noinline(significand, significand + integral_size, + out); if (!decimal_point) return out; *out++ = decimal_point; - return detail::copy_str_noinline(significand + integral_size, - significand + significand_size, out); + return detail::copy_noinline(significand + integral_size, + significand + significand_size, out); } template @@ -2607,150 +2458,163 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, decimal_point); } auto buffer = basic_memory_buffer(); - write_significand(buffer_appender(buffer), significand, - significand_size, integral_size, decimal_point); + write_significand(basic_appender(buffer), significand, significand_size, + integral_size, decimal_point); grouping.apply( out, basic_string_view(buffer.data(), to_unsigned(integral_size))); - return detail::copy_str_noinline(buffer.data() + integral_size, - buffer.end(), out); + return detail::copy_noinline(buffer.data() + integral_size, + buffer.end(), out); } -template > -FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, - float_specs fspecs, locale_ref loc) - -> OutputIt { - auto significand = f.significand; - int significand_size = get_significand_size(f); - const Char zero = static_cast('0'); - auto sign = fspecs.sign; - size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); - using iterator = reserve_iterator; +// Numbers with exponents greater or equal to the returned value will use +// the exponential notation. +template FMT_CONSTEVAL auto exp_upper() -> int { + return std::numeric_limits::digits10 != 0 + ? min_of(16, std::numeric_limits::digits10 + 1) + : 16; +} - Char decimal_point = - fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); - - int output_exp = f.exponent + significand_size - 1; - auto use_exp_format = [=]() { - if (fspecs.format == float_format::exp) return true; - if (fspecs.format != float_format::general) return false; - // Use the fixed notation if the exponent is in [exp_lower, exp_upper), - // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. - const int exp_lower = -4, exp_upper = 16; - return output_exp < exp_lower || - output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); - }; - if (use_exp_format()) { - int num_zeros = 0; - if (fspecs.showpoint) { - num_zeros = fspecs.precision - significand_size; - if (num_zeros < 0) num_zeros = 0; - size += to_unsigned(num_zeros); - } else if (significand_size == 1) { - decimal_point = Char(); - } - auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; - int exp_digits = 2; - if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; - - size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); - char exp_char = fspecs.upper ? 'E' : 'e'; - auto write = [=](iterator it) { - if (sign) *it++ = detail::sign(sign); - // Insert a decimal point after the first digit and add an exponent. - it = write_significand(it, significand, significand_size, 1, - decimal_point); - if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); - *it++ = static_cast(exp_char); - return write_exponent(output_exp, it); - }; - return specs.width > 0 ? write_padded(out, specs, size, write) - : base_iterator(out, write(reserve(out, size))); - } - - int exp = f.exponent + significand_size; - if (f.exponent >= 0) { - // 1234e5 -> 123400000[.0+] - size += to_unsigned(f.exponent); - int num_zeros = fspecs.precision - exp; - abort_fuzzing_if(num_zeros > 5000); - if (fspecs.showpoint) { - ++size; - if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0; - if (num_zeros > 0) size += to_unsigned(num_zeros); - } - auto grouping = Grouping(loc, fspecs.locale); - size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); - it = write_significand(it, significand, significand_size, - f.exponent, grouping); - if (!fspecs.showpoint) return it; - *it++ = decimal_point; - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); - } else if (exp > 0) { - // 1234e-2 -> 12.34[0+] - int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; - size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); - auto grouping = Grouping(loc, fspecs.locale); - size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); - it = write_significand(it, significand, significand_size, exp, - decimal_point, grouping); - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); - } - // 1234e-6 -> 0.001234 - int num_zeros = -exp; - if (significand_size == 0 && fspecs.precision >= 0 && - fspecs.precision < num_zeros) { - num_zeros = fspecs.precision; - } - bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; - size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); - *it++ = zero; - if (!pointy) return it; - *it++ = decimal_point; - it = detail::fill_n(it, num_zeros, zero); - return write_significand(it, significand, significand_size); - }); +// Use the fixed notation if the exponent is in [-4, exp_upper), +// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. +constexpr auto use_fixed(int exp, int exp_upper) -> bool { + return exp >= -4 && exp < exp_upper; } template class fallback_digit_grouping { public: constexpr fallback_digit_grouping(locale_ref, bool) {} - constexpr bool has_separator() const { return false; } + constexpr auto has_separator() const -> bool { return false; } - constexpr int count_separators(int) const { return 0; } + constexpr auto count_separators(int) const -> int { return 0; } template - constexpr Out apply(Out out, basic_string_view) const { + constexpr auto apply(Out out, basic_string_view) const -> Out { return out; } }; -template +template +FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f, + int significand_size, Char decimal_point, + const format_specs& specs, sign s, + locale_ref loc = {}) -> OutputIt { + using iterator = reserve_iterator; + + int exp = f.exponent + significand_size; + long long size = significand_size + (s != sign::none ? 1 : 0); + if (f.exponent >= 0) { + // 1234e5 -> 123400000[.0+] + size += f.exponent; + int num_zeros = specs.precision - exp; + abort_fuzzing_if(num_zeros > 5000); + if (specs.alt()) { + ++size; + if (num_zeros <= 0 && specs.type() != presentation_type::fixed) + num_zeros = 0; + if (num_zeros > 0) size += num_zeros; + } + auto grouping = Grouping(loc, specs.localized()); + size += grouping.count_separators(exp); + return write_padded( + out, specs, static_cast(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, f.significand, significand_size, + f.exponent, grouping); + if (!specs.alt()) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); + } + if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = specs.alt() ? specs.precision - significand_size : 0; + size += 1 + max_of(num_zeros, 0); + auto grouping = Grouping(loc, specs.localized()); + size += grouping.count_separators(exp); + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, f.significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (significand_size == 0 && specs.precision >= 0 && + specs.precision < num_zeros) { + num_zeros = specs.precision; + } + bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); + size += 1 + (pointy ? 1 : 0) + num_zeros; + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + *it++ = Char('0'); + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, Char('0')); + return write_significand(it, f.significand, significand_size); + }); +} + +template +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { + Char point = specs.localized() ? detail::decimal_point(loc) : Char('.'); + int significand_size = get_significand_size(f); + int exp = f.exponent + significand_size - 1; + if (specs.type() == presentation_type::fixed || + (specs.type() != presentation_type::exp && + use_fixed(exp, specs.precision > 0 ? specs.precision : exp_upper))) { + return write_fixed(out, f, significand_size, point, specs, + s, loc); + } + + // Write value in the exponential format. + int num_zeros = 0; + long long size = significand_size + (s != sign::none ? 1 : 0); + if (specs.alt()) { + num_zeros = max_of(specs.precision - significand_size, 0); + size += num_zeros; + } else if (significand_size == 1) { + point = Char(); + } + size += (point ? 1 : 0) + compute_exp_size(exp); + char exp_char = specs.upper() ? 'E' : 'e'; + auto write = [=](reserve_iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, f.significand, significand_size, 1, point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, Char('0')); + *it++ = Char(exp_char); + return write_exponent(exp, it); + }; + auto usize = to_unsigned(size); + return specs.width > 0 + ? write_padded(out, specs, usize, write) + : base_iterator(out, write(reserve(out, usize))); +} + +template FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, - float_specs fspecs, locale_ref loc) - -> OutputIt { + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { - return do_write_float>(out, f, specs, fspecs, - loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } else { - return do_write_float(out, f, specs, fspecs, loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } } -template constexpr bool isnan(T value) { - return !(value >= value); // std::isnan doesn't support __float128. +template constexpr auto isnan(T value) -> bool { + return value != value; // std::isnan doesn't support __float128. } template @@ -2760,23 +2624,23 @@ template struct has_isfinite> : std::true_type {}; -template ::value&& - has_isfinite::value)> -FMT_CONSTEXPR20 bool isfinite(T value) { +template ::value&& has_isfinite::value)> +FMT_CONSTEXPR20 auto isfinite(T value) -> bool { constexpr T inf = T(std::numeric_limits::infinity()); if (is_constant_evaluated()) return !detail::isnan(value) && value < inf && value > -inf; return std::isfinite(value); } template ::value)> -FMT_CONSTEXPR bool isfinite(T value) { +FMT_CONSTEXPR auto isfinite(T value) -> bool { T inf = T(std::numeric_limits::infinity()); // std::isfinite doesn't support __float128. return !detail::isnan(value) && value < inf && value > -inf; } template ::value)> -FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { +FMT_INLINE FMT_CONSTEXPR auto signbit(T value) -> bool { if (is_constant_evaluated()) { #ifdef __cpp_if_constexpr if constexpr (std::numeric_limits::is_iec559) { @@ -2798,52 +2662,48 @@ inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { class bigint { private: - // A bigint is stored as an array of bigits (big digits), with bigit at index - // 0 being the least significant one. - using bigit = uint32_t; + // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_. + using bigit = uint32_t; // A big digit. using double_bigit = uint64_t; + enum { bigit_bits = num_bits() }; enum { bigits_capacity = 32 }; basic_memory_buffer bigits_; int exp_; - FMT_CONSTEXPR20 bigit operator[](int index) const { - return bigits_[to_unsigned(index)]; - } - FMT_CONSTEXPR20 bigit& operator[](int index) { - return bigits_[to_unsigned(index)]; - } - - static constexpr const int bigit_bits = num_bits(); - friend struct formatter; - FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); + FMT_CONSTEXPR auto get_bigit(int i) const -> bigit { + return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0; + } + + FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = double_bigit(bigits_[index]) - other - borrow; + bigits_[index] = static_cast(result); borrow = static_cast(result >> (bigit_bits * 2 - 1)); } - FMT_CONSTEXPR20 void remove_leading_zeros() { + FMT_CONSTEXPR void remove_leading_zeros() { int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; bigits_.resize(to_unsigned(num_bigits + 1)); } // Computes *this -= other assuming aligned bigints and *this >= other. - FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { + FMT_CONSTEXPR void subtract_aligned(const bigint& other) { FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); FMT_ASSERT(compare(*this, other) >= 0, ""); bigit borrow = 0; int i = other.exp_ - exp_; for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) subtract_bigits(i, other.bigits_[j], borrow); - while (borrow > 0) subtract_bigits(i, 0, borrow); + if (borrow != 0) subtract_bigits(i, 0, borrow); + FMT_ASSERT(borrow == 0, ""); remove_leading_zeros(); } - FMT_CONSTEXPR20 void multiply(uint32_t value) { - const double_bigit wide_value = value; + FMT_CONSTEXPR void multiply(uint32_t value) { bigit carry = 0; + const double_bigit wide_value = value; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { double_bigit result = bigits_[i] * wide_value + carry; bigits_[i] = static_cast(result); @@ -2854,7 +2714,7 @@ class bigint { template ::value || std::is_same::value)> - FMT_CONSTEXPR20 void multiply(UInt value) { + FMT_CONSTEXPR void multiply(UInt value) { using half_uint = conditional_t::value, uint64_t, uint32_t>; const int shift = num_bits() - bigit_bits; @@ -2875,7 +2735,7 @@ class bigint { template ::value || std::is_same::value)> - FMT_CONSTEXPR20 void assign(UInt n) { + FMT_CONSTEXPR void assign(UInt n) { size_t num_bigits = 0; do { bigits_[num_bigits++] = static_cast(n); @@ -2886,30 +2746,30 @@ class bigint { } public: - FMT_CONSTEXPR20 bigint() : exp_(0) {} + FMT_CONSTEXPR bigint() : exp_(0) {} explicit bigint(uint64_t n) { assign(n); } bigint(const bigint&) = delete; void operator=(const bigint&) = delete; - FMT_CONSTEXPR20 void assign(const bigint& other) { + FMT_CONSTEXPR void assign(const bigint& other) { auto size = other.bigits_.size(); bigits_.resize(size); auto data = other.bigits_.data(); - copy_str(data, data + size, bigits_.data()); + copy(data, data + size, bigits_.data()); exp_ = other.exp_; } - template FMT_CONSTEXPR20 void operator=(Int n) { + template FMT_CONSTEXPR void operator=(Int n) { FMT_ASSERT(n > 0, ""); assign(uint64_or_128_t(n)); } - FMT_CONSTEXPR20 int num_bigits() const { + FMT_CONSTEXPR auto num_bigits() const -> int { return static_cast(bigits_.size()) + exp_; } - FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& { FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; @@ -2924,46 +2784,39 @@ class bigint { return *this; } - template FMT_CONSTEXPR20 bigint& operator*=(Int value) { + template FMT_CONSTEXPR auto operator*=(Int value) -> bigint& { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; } - friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { - int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; - int i = static_cast(lhs.bigits_.size()) - 1; - int j = static_cast(rhs.bigits_.size()) - 1; + friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int { + int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits(); + if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1; + int i = static_cast(b1.bigits_.size()) - 1; + int j = static_cast(b2.bigits_.size()) - 1; int end = i - j; if (end < 0) end = 0; for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j]; + if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1; } if (i != j) return i > j ? 1 : -1; return 0; } // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { - auto minimum = [](int a, int b) { return a < b ? a : b; }; - auto maximum = [](int a, int b) { return a > b ? a : b; }; - int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); + friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) -> int { + int max_lhs_bigits = max_of(lhs1.num_bigits(), lhs2.num_bigits()); int num_rhs_bigits = rhs.num_bigits(); if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; if (max_lhs_bigits > num_rhs_bigits) return 1; - auto get_bigit = [](const bigint& n, int i) -> bigit { - return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; - }; double_bigit borrow = 0; - int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_); + int min_exp = min_of(min_of(lhs1.exp_, lhs2.exp_), rhs.exp_); for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { - double_bigit sum = - static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); - bigit rhs_bigit = get_bigit(rhs, i); + double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i); + bigit rhs_bigit = rhs.get_bigit(i); if (sum > rhs_bigit + borrow) return 1; borrow = rhs_bigit + borrow - sum; if (borrow > 1) return -1; @@ -2976,10 +2829,8 @@ class bigint { FMT_CONSTEXPR20 void assign_pow10(int exp) { FMT_ASSERT(exp >= 0, ""); if (exp == 0) return *this = 1; - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; + int bitmask = 1 << (num_bits() - + countl_zero(static_cast(exp)) - 1); // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by // repeated squaring and multiplication. *this = 5; @@ -3003,17 +2854,17 @@ class bigint { // cross-product terms n[i] * n[j] such that i + j == bigit_index. for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { // Most terms are multiplied twice which can be optimized in the future. - sum += static_cast(n[i]) * n[j]; + sum += double_bigit(n[i]) * n[j]; } - (*this)[bigit_index] = static_cast(sum); + bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); // Compute the carry. } // Do the same for the top half. for (int bigit_index = num_bigits; bigit_index < num_result_bigits; ++bigit_index) { for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) - sum += static_cast(n[i++]) * n[j--]; - (*this)[bigit_index] = static_cast(sum); + sum += double_bigit(n[i++]) * n[j--]; + bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); } remove_leading_zeros(); @@ -3022,20 +2873,20 @@ class bigint { // If this bigint has a bigger exponent than other, adds trailing zero to make // exponents equal. This simplifies some operations such as subtraction. - FMT_CONSTEXPR20 void align(const bigint& other) { + FMT_CONSTEXPR void align(const bigint& other) { int exp_difference = exp_ - other.exp_; if (exp_difference <= 0) return; int num_bigits = static_cast(bigits_.size()); bigits_.resize(to_unsigned(num_bigits + exp_difference)); for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) bigits_[j] = bigits_[i]; - std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + fill_n(bigits_.data(), to_unsigned(exp_difference), 0U); exp_ -= exp_difference; } // Divides this bignum by divisor, assigning the remainder to this and // returning the quotient. - FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); @@ -3154,8 +3005,11 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, // Generate the given number of digits. exp10 -= num_digits - 1; if (num_digits <= 0) { - denominator *= 10; - auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + auto digit = '0'; + if (num_digits == 0) { + denominator *= 10; + digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + } buf.push_back(digit); return; } @@ -3178,8 +3032,10 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, } if (buf[0] == overflow) { buf[0] = '1'; - if ((flags & dragon::fixed) != 0) buf.push_back('0'); - else ++exp10; + if ((flags & dragon::fixed) != 0) + buf.push_back('0'); + else + ++exp10; } return; } @@ -3190,8 +3046,8 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, // Formats a floating-point number using the hexfloat format. template ::value)> -FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - float_specs specs, buffer& buf) { +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { // float is passed as double to reduce the number of instantiations and to // simplify implementation. static_assert(!std::is_same::value, ""); @@ -3201,26 +3057,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, // Assume Float is in the format [sign][exponent][significand]. using carrier_uint = typename info::carrier_uint; - constexpr auto num_float_significand_bits = - detail::num_significand_bits(); + const auto num_float_significand_bits = detail::num_significand_bits(); basic_fp f(value); f.e += num_float_significand_bits; if (!has_implicit_bit()) --f.e; - constexpr auto num_fraction_bits = + const auto num_fraction_bits = num_float_significand_bits + (has_implicit_bit() ? 1 : 0); - constexpr auto num_xdigits = (num_fraction_bits + 3) / 4; + const auto num_xdigits = (num_fraction_bits + 3) / 4; - constexpr auto leading_shift = ((num_xdigits - 1) * 4); + const auto leading_shift = ((num_xdigits - 1) * 4); const auto leading_mask = carrier_uint(0xF) << leading_shift; const auto leading_xdigit = static_cast((f.f & leading_mask) >> leading_shift); if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); int print_xdigits = num_xdigits - 1; - if (precision >= 0 && print_xdigits > precision) { - const int shift = ((print_xdigits - precision - 1) * 4); + if (specs.precision >= 0 && print_xdigits > specs.precision) { + const int shift = ((print_xdigits - specs.precision - 1) * 4); const auto mask = carrier_uint(0xF) << shift; const auto v = static_cast((f.f & mask) >> shift); @@ -3239,25 +3094,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, } } - print_xdigits = precision; + print_xdigits = specs.precision; } char xdigits[num_bits() / 4]; detail::fill_n(xdigits, sizeof(xdigits), '0'); - format_uint<4>(xdigits, f.f, num_xdigits, specs.upper); + format_base2e(4, xdigits, f.f, num_xdigits, specs.upper()); // Remove zero tail while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; buf.push_back('0'); - buf.push_back(specs.upper ? 'X' : 'x'); + buf.push_back(specs.upper() ? 'X' : 'x'); buf.push_back(xdigits[0]); - if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision) + if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision) buf.push_back('.'); buf.append(xdigits + 1, xdigits + 1 + print_xdigits); - for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0'); + for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0'); - buf.push_back(specs.upper ? 'P' : 'p'); + buf.push_back(specs.upper() ? 'P' : 'p'); uint32_t abs_e; if (f.e < 0) { @@ -3271,21 +3126,32 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, } template ::value)> -FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - float_specs specs, buffer& buf) { - format_hexfloat(static_cast(value), precision, specs, buf); +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { + format_hexfloat(static_cast(value), specs, buf); +} + +constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { + // For checking rounding thresholds. + // The kth entry is chosen to be the smallest integer such that the + // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. + // It is equal to ceil(2^31 + 2^32/10^(k + 1)). + // These are stored in a string literal because we cannot have static arrays + // in constexpr functions and non-static ones are poorly optimized. + return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7" + U"\x800001ae\x8000002b"[index]; } template -FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, +FMT_CONSTEXPR20 auto format_float(Float value, int precision, + const format_specs& specs, bool binary32, buffer& buf) -> int { // float is passed as double to reduce the number of instantiations. static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); auto converted_value = convert_float(value); - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. + const bool fixed = specs.type() == presentation_type::fixed; + if (value == 0) { if (precision <= 0 || !fixed) { buf.push_back('0'); return 0; @@ -3310,16 +3176,6 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, exp = static_cast(e); if (e > exp) ++exp; // Compute ceil. dragon_flags = dragon::fixup; - } else if (precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; } else { // Extract significand bits and exponent bits. using info = dragonbox::float_info; @@ -3418,7 +3274,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, uint64_t prod; uint32_t digits; bool should_round_up; - int number_of_digits_to_print = precision > 9 ? 9 : precision; + int number_of_digits_to_print = min_of(precision, 9); // Print a 9-digits subsegment, either the first or the second. auto print_subsegment = [&](uint32_t subsegment, char* buffer) { @@ -3446,7 +3302,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // for details. prod = ((subsegment * static_cast(450359963)) >> 20) + 1; digits = static_cast(prod >> 32); - copy2(buffer, digits2(digits)); + write2digits(buffer, digits); number_of_digits_printed += 2; } @@ -3454,7 +3310,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, while (number_of_digits_printed < number_of_digits_to_print) { prod = static_cast(prod) * static_cast(100); digits = static_cast(prod >> 32); - copy2(buffer + number_of_digits_printed, digits2(digits)); + write2digits(buffer + number_of_digits_printed, digits); number_of_digits_printed += 2; } }; @@ -3480,12 +3336,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // fractional part is strictly larger than 1/2. if (precision < 9) { uint32_t fractional_part = static_cast(prod); - should_round_up = fractional_part >= - data::fractional_part_rounding_thresholds - [8 - number_of_digits_to_print] || - ((fractional_part >> 31) & - ((digits & 1) | (second_third_subsegments != 0) | - has_more_segments)) != 0; + should_round_up = + fractional_part >= fractional_part_rounding_thresholds( + 8 - number_of_digits_to_print) || + ((fractional_part >> 31) & + ((digits & 1) | (second_third_subsegments != 0) | + has_more_segments)) != 0; } // Rounding at the subsegment boundary. // In this case, the fractional part is at least 1/2 if and only if @@ -3520,12 +3376,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // of 19 digits, so in this case the third segment should be // consisting of a genuine digit from the input. uint32_t fractional_part = static_cast(prod); - should_round_up = fractional_part >= - data::fractional_part_rounding_thresholds - [8 - number_of_digits_to_print] || - ((fractional_part >> 31) & - ((digits & 1) | (third_subsegment != 0) | - has_more_segments)) != 0; + should_round_up = + fractional_part >= fractional_part_rounding_thresholds( + 8 - number_of_digits_to_print) || + ((fractional_part >> 31) & + ((digits & 1) | (third_subsegment != 0) | + has_more_segments)) != 0; } // Rounding at the subsegment boundary. else { @@ -3563,9 +3419,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, } if (use_dragon) { auto f = basic_fp(); - bool is_predecessor_closer = specs.binary32 - ? f.assign(static_cast(value)) - : f.assign(converted_value); + bool is_predecessor_closer = binary32 ? f.assign(static_cast(value)) + : f.assign(converted_value); if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; if (fixed) dragon_flags |= dragon::fixed; // Limit precision to the maximum possible number of significant digits in @@ -3574,7 +3429,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, if (precision > max_double_digits) precision = max_double_digits; format_dragon(f, dragon_flags, precision, buf, exp); } - if (!fixed && !specs.showpoint) { + if (!fixed && !specs.alt()) { // Remove trailing zeros. auto num_digits = buf.size(); while (num_digits > 0 && buf[num_digits - 1] == '0') { @@ -3585,97 +3440,139 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, } return exp; } -template -FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, - format_specs specs, locale_ref loc) - -> OutputIt { - float_specs fspecs = parse_float_type_spec(specs); - fspecs.sign = specs.sign; - if (detail::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 (!detail::isfinite(value)) - return write_nonfinite(out, detail::isnan(value), specs, fspecs); - - if (specs.align == align::numeric && fspecs.sign) { - auto it = reserve(out, 1); - *it++ = detail::sign(fspecs.sign); - out = base_iterator(out, it); - fspecs.sign = sign::none; - if (specs.width != 0) --specs.width; - } - - memory_buffer buffer; - if (fspecs.format == float_format::hex) { - if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - format_hexfloat(convert_float(value), specs.precision, fspecs, buffer); - return write_bytes(out, {buffer.data(), buffer.size()}, - specs); - } - int precision = specs.precision >= 0 || specs.type == presentation_type::none - ? specs.precision - : 6; - if (fspecs.format == float_format::exp) { - if (precision == max_value()) - throw_format_error("number is too big"); - else - ++precision; - } else if (fspecs.format != float_format::fixed && precision == 0) { - precision = 1; - } - if (const_check(std::is_same())) fspecs.binary32 = true; - int exp = format_float(convert_float(value), precision, fspecs, buffer); - fspecs.precision = precision; - auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, f, specs, fspecs, loc); -} template ::value)> -FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, locale_ref loc = {}) -> OutputIt { - if (const_check(!is_supported_floating_point(value))) return out; - return specs.localized && write_loc(out, value, specs, loc) - ? out - : write_float(out, value, specs, loc); + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + + // Use signbit because value < 0 is false for NaN. + sign s = detail::signbit(value) ? sign::minus : specs.sign(); + + if (!detail::isfinite(value)) + return write_nonfinite(out, detail::isnan(value), specs, s); + + if (specs.align() == align::numeric && s != sign::none) { + *out++ = detail::getsign(s); + s = sign::none; + if (specs.width != 0) --specs.width; + } + + const int exp_upper = detail::exp_upper(); + int precision = specs.precision; + if (precision < 0) { + if (specs.type() != presentation_type::none) { + precision = 6; + } else if (is_fast_float::value && !is_constant_evaluated()) { + // Use Dragonbox for the shortest format. + auto dec = dragonbox::to_decimal(static_cast>(value)); + return write_float(out, dec, specs, s, exp_upper, loc); + } + } + + memory_buffer buffer; + if (specs.type() == presentation_type::hexfloat) { + if (s != sign::none) buffer.push_back(detail::getsign(s)); + format_hexfloat(convert_float(value), specs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); + } + + if (specs.type() == presentation_type::exp) { + if (precision == max_value()) + report_error("number is too big"); + else + ++precision; + if (specs.precision != 0) specs.set_alt(); + } else if (specs.type() == presentation_type::fixed) { + if (specs.precision != 0) specs.set_alt(); + } else if (precision == 0) { + precision = 1; + } + int exp = format_float(convert_float(value), precision, specs, + std::is_same(), buffer); + + specs.precision = precision; + auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, f, specs, s, exp_upper, loc); } template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { - if (is_constant_evaluated()) return write(out, value, format_specs()); - if (const_check(!is_supported_floating_point(value))) return out; + if (is_constant_evaluated()) return write(out, value, format_specs()); - auto fspecs = float_specs(); - if (detail::signbit(value)) { - fspecs.sign = sign::minus; - value = -value; + auto s = detail::signbit(value) ? sign::minus : sign::none; + auto mask = exponent_mask>(); + if ((bit_cast(value) & mask) == mask) + return write_nonfinite(out, std::isnan(value), {}, s); + + auto dec = dragonbox::to_decimal(static_cast>(value)); + auto significand = dec.significand; + int significand_size = count_digits(significand); + int exponent = dec.exponent + significand_size - 1; + if (use_fixed(exponent, detail::exp_upper())) { + return write_fixed>( + out, dec, significand_size, Char('.'), {}, s); } - constexpr auto specs = format_specs(); - using floaty = conditional_t::value, double, T>; - using floaty_uint = typename dragonbox::float_info::carrier_uint; - floaty_uint mask = exponent_mask(); - if ((bit_cast(value) & mask) == mask) - return write_nonfinite(out, std::isnan(value), specs, fspecs); - - auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, fspecs, {}); + // Write value in the exponential format. + const char* prefix = "e+"; + int abs_exponent = exponent; + if (exponent < 0) { + abs_exponent = -exponent; + prefix = "e-"; + } + auto has_decimal_point = significand_size != 1; + size_t size = std::is_pointer::value + ? 0u + : to_unsigned((s != sign::none ? 1 : 0) + significand_size + + (has_decimal_point ? 1 : 0) + + (abs_exponent >= 100 ? 5 : 4)); + if (auto ptr = to_pointer(out, size)) { + if (s != sign::none) *ptr++ = Char('-'); + if (has_decimal_point) { + auto begin = ptr; + ptr = format_decimal(ptr, significand, significand_size + 1); + *begin = begin[1]; + begin[1] = '.'; + } else { + *ptr++ = static_cast('0' + significand); + } + if (std::is_same::value) { + memcpy(ptr, prefix, 2); + ptr += 2; + } else { + *ptr++ = prefix[0]; + *ptr++ = prefix[1]; + } + if (abs_exponent >= 100) { + *ptr++ = static_cast('0' + abs_exponent / 100); + abs_exponent %= 100; + } + write2digits(ptr, static_cast(abs_exponent)); + return select::value>(ptr + 2, out); + } + auto it = reserve(out, size); + if (s != sign::none) *it++ = Char('-'); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + has_decimal_point ? Char('.') : Char()); + *it++ = Char('e'); + it = write_exponent(exponent, it); + return base_iterator(out, it); } template ::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { - return write(out, value, format_specs()); + return write(out, value, {}); } template -auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) +auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) -> OutputIt { FMT_ASSERT(false, ""); return out; @@ -3684,13 +3581,11 @@ auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) -> OutputIt { - auto it = reserve(out, value.size()); - it = copy_str_noinline(value.begin(), value.end(), it); - return base_iterator(out, it); + return copy_noinline(value.begin(), value.end(), out); } template ::value)> + FMT_ENABLE_IF(has_to_string_view::value)> constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } @@ -3698,10 +3593,8 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, - bool check = - std::is_enum::value && !std::is_same::value && - mapped_type_constant>::value != - type::custom_type, + bool check = std::is_enum::value && !std::is_same::value && + mapped_type_constant::value != type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { return write(out, static_cast>(value)); @@ -3709,13 +3602,12 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { template ::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value, - const format_specs& specs = {}, locale_ref = {}) - -> OutputIt { - return specs.type != presentation_type::none && - specs.type != presentation_type::string - ? write(out, value ? 1 : 0, specs, {}) - : write_bytes(out, value ? "true" : "false", specs); +FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + return specs.type() != presentation_type::none && + specs.type() != presentation_type::string + ? write(out, value ? 1 : 0, specs, {}) + : write_bytes(out, value ? "true" : "false", specs); } template @@ -3726,193 +3618,136 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { } template -FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) - -> OutputIt { +FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { if (value) return write(out, basic_string_view(value)); - throw_format_error("string pointer is null"); + report_error("string pointer is null"); return out; } template ::value)> -auto write(OutputIt out, const T* value, const format_specs& specs = {}, +auto write(OutputIt out, const T* value, const format_specs& specs = {}, locale_ref = {}) -> OutputIt { return write_ptr(out, bit_cast(value), &specs); } -// A write overload that handles implicit conversions. template > -FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< - std::is_class::value && !is_string::value && - !is_floating_point::value && !std::is_same::value && - !std::is_same().map( - value))>>::value, - OutputIt> { - return write(out, arg_mapper().map(value)); + FMT_ENABLE_IF(mapped_type_constant::value == + type::custom_type && + !std::is_fundamental::value)> +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt { + auto f = formatter(); + auto parse_ctx = parse_context({}); + f.parse(parse_ctx); + auto ctx = basic_format_context(out, {}, {}); + return f.format(value, ctx); } -template > -FMT_CONSTEXPR auto write(OutputIt out, const T& value) - -> enable_if_t::value == type::custom_type, - OutputIt> { - auto ctx = Context(out, {}, {}); - return typename Context::template formatter_type().format(value, ctx); -} +template +using is_builtin = + bool_constant::value || FMT_BUILTIN_TYPES>; // An argument visitor that formats the argument and writes it via the output // iterator. It's a class and not a generic lambda for compatibility with C++11. template struct default_arg_formatter { - using iterator = buffer_appender; - using context = buffer_context; + using context = buffered_context; - iterator out; - basic_format_args args; - locale_ref loc; + basic_appender out; - template auto operator()(T value) -> iterator { - return write(out, value); + void operator()(monostate) { report_error("argument not found"); } + + template ::value)> + void operator()(T value) { + write(out, value); } - auto operator()(typename basic_format_arg::handle h) -> iterator { - basic_format_parse_context parse_ctx({}); - context format_ctx(out, args, loc); + + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); + } + + void operator()(typename basic_format_arg::handle h) { + // Use a null locale since the default format must be unlocalized. + auto parse_ctx = parse_context({}); + auto format_ctx = context(out, {}, {}); h.format(parse_ctx, format_ctx); - return format_ctx.out(); } }; template struct arg_formatter { - using iterator = buffer_appender; - using context = buffer_context; + basic_appender out; + const format_specs& specs; + FMT_NO_UNIQUE_ADDRESS locale_ref locale; - iterator out; - const format_specs& specs; - locale_ref locale; - - template - FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { - return detail::write(out, value, specs, locale); + template ::value)> + FMT_CONSTEXPR FMT_INLINE void operator()(T value) { + detail::write(out, value, specs, locale); } - auto operator()(typename basic_format_arg::handle) -> iterator { + + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); + } + + void operator()(typename basic_format_arg>::handle) { // User-defined types are handled separately because they require access // to the parse context. - return out; } }; -template struct custom_formatter { - basic_format_parse_context& parse_ctx; - buffer_context& ctx; - - void operator()( - typename basic_format_arg>::handle h) const { - h.format(parse_ctx, ctx); - } - template void operator()(T) const {} -}; - -template class width_checker { - public: - explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} - +struct dynamic_spec_getter { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative width"); - return static_cast(value); + return is_negative(value) ? ~0ull : static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("width is not integer"); + report_error("width/precision is not integer"); return 0; } - - private: - ErrorHandler& handler_; }; -template class precision_checker { - public: - explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} - - template ::value)> - FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative precision"); - return static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("precision is not integer"); - return 0; - } - - private: - ErrorHandler& handler_; -}; - -template