139 Commits

Author SHA1 Message Date
EricPlayZ 90c9337a88 push 2026-03-27 21:59:17 +02:00
EricPlayZ 73c9333dd6 refactor: update classDependencies to use dict and enhance dependency handling in JSONGen 2026-03-08 20:26:44 +02:00
EricPlayZ 5564beddb8 added class dependency generator for IDA scripts, so you can simply copy paste the generated header file and it should compile fine (more to test when I have time) 2025-04-04 01:55:30 +03:00
EricPlayZ ef14ed13c5 script changes 2025-03-31 03:45:50 +03:00
EricPlayZ 970f5745aa backup scripts 2025-03-19 03:14:20 +02:00
EricPlayZ 0c5d0c76f9 backup script changes 2025-03-17 02:09:24 +02:00
EricPlayZ d40310fd80 backup script changes for JSONParser 2025-03-16 10:25:30 +02:00
EricPlayZ 0dd46bbabd bak 2025-03-13 01:09:04 +02:00
EricPlayZ 6c309ecffb backup script changes 2025-03-13 01:08:39 +02:00
EricPlayZ 12de78c264 backup of scripts before json parser changes 2025-03-13 00:30:18 +02:00
EricPlayZ edad5f4ead backup scripts 2025-03-09 03:04:02 +02:00
EricPlayZ 9962a58cb7 backup scripts 2025-03-09 01:10:59 +02:00
EricPlayZ 003d18cf72 backup script changes 2025-03-08 17:06:28 +02:00
EricPlayZ 0e7674c15a scripts update and SDK update with lots of new classes - gonna have to do this automatically somehow because it will take the entire weekend to generate each class manually! 2025-03-08 03:39:31 +02:00
EricPlayZ 6f030f6cc6 backup script changes 2025-03-07 01:10:01 +02:00
EricPlayZ 930559e1cb scripts backup 2025-03-06 02:07:40 +02:00
EricPlayZ 5182263775 scripts backup 2025-03-05 03:34:37 +02:00
EricPlayZ 65553c05f9 update scripts 2025-03-04 02:19:36 +02:00
EricPlayZ 8b226df725 update scripts 2025-03-04 00:50:33 +02:00
EricPlayZ 84366681a9 script changes 2025-03-03 04:46:44 +02:00
EricPlayZ 8280593e36 backup scripts 2025-03-02 08:13:24 +02:00
EricPlayZ d0c64ca0e0 backup scripts 2025-03-01 06:40:13 +02:00
EricPlayZ 0983c21489 backup 2025-02-27 03:59:52 +02:00
EricPlayZ fc084f4187 backup script changes 2025-02-27 02:31:06 +02:00
EricPlayZ fe942594d8 backup of scripts 2025-02-26 03:19:35 +02:00
EricPlayZ 2c1c9cc8f6 backup of massive SDK changes incoming! 2025-02-26 03:15:58 +02:00
EricPlayZ ea47fe6746 backup changes 2025-02-25 03:06:29 +02:00
EricPlayZ d81cac478b improve performance by using std::string_view instead of std::string 2025-02-25 02:17:21 +02:00
EricPlayZ e7351020a4 changelog 2025-02-24 03:14:59 +02:00
EricPlayZ e9323fa362 merged variables list rendering logic into 1 class 2025-02-24 03:14:01 +02:00
EricPlayZ 5559600a94 fixed player variables list lag 2025-02-24 01:25:58 +02:00
EricPlayZ 83a7efed93 fixed thread mutex locking issues, fixed ManageVarByBool return address not getting the correct return addr (because of stacked function calls inside ManageVarByBool), fixed some player variables list issues 2025-02-23 23:25:22 +02:00
EricPlayZ 88857a7da8 - Fixed zoom-in not setting the first zoom level accordingly to the current FOV (say if FOV is already below first zoomLevel, or is in a threshold of 10 from first zoomLevel, then the desired zoomLevel should be one level further) 2025-02-23 17:48:01 +02:00
EricPlayZ e88031630d changed VarManagerBase alongside the other var management classes 2025-02-23 06:48:04 +02:00
EricPlayZ 0c7b625a9c working VarManagerBase with VarRef 2025-02-17 01:41:21 +02:00
EricPlayZ d5b5268f00 - working VarManagerBase for player variables too 2025-02-16 21:04:15 +02:00
EricPlayZ 3017ec4d53 cleaned up CVars, added Disable TAA to config 2025-02-13 00:49:45 +02:00
EricPlayZ acbcba0a69 - Added Disable TAA option
- Added Vec3/Vec4 support for CVars
2025-02-11 03:05:29 +02:00
EricPlayZ f3ece5c0e5 backup of new VarManagerBase for variable managing such as PlayerVariables or CVars 2025-02-10 04:02:37 +02:00
EricPlayZ ae711dca84 - Removed low level mouse hook and replaced with DInput8 hooks instead
- Blocked in-game mouse scroll wheel when "First Person Zoom In" is being pressed down
- Added E3 Def support for player orientation
2025-02-02 21:20:56 +02:00
EricPlayZ b50c3c4756 - Added proper Nightrunner/Fury Mode effects when Nightrunner Mode is enabled (Player) 2025-02-02 04:04:58 +02:00
EricPlayZ ee667276cf - Added the ability to save player orientation in the Saved Locations list (Teleport)
- Added the ability to automatically set player orientation when teleporting to a Saved Location (Teleport)
- Added "Overwrite Selected Location" button for Saved Locations list (Teleport)
2025-02-02 02:21:02 +02:00
EricPlayZ 5bedd2c0cd fixed small error (forgot to include playerPos2 2025-01-27 01:43:53 +02:00
EricPlayZ 168195fc28 - Fixed Free Camera orientation resetting back to player orientation after pausing and unpausing the game
- Added Vec4, changed Mtx34 structure to reflect the game's structure
2025-01-27 01:41:58 +02:00
EricPlayZ 7f2d9de327 fixed CameraMtx struct 2025-01-26 19:35:42 +02:00
EricPlayZ 050561e35b - Automatically calculate base FOV at start of the game (so we don't have to rely on setting it based on game version)
- Fixed some time/weather interpolation problems
- Renamed Vector3 to Vec3
- Added Mtx34 struct
2025-01-26 19:34:24 +02:00
EricPlayZ 8f3c694c88 - Fixed player restrictions not working for v1.12.0
- Implemented some checks for v1.12.0
- Improved time/weather interpolation behavior
2025-01-26 04:54:57 +02:00
EricPlayZ cd939c0eab - Added compatibility with v1.12.0 (including E3 Definitive Mod support!)
- Added "Renderer CVars list" (Misc) for changing game rendering variables (only float for now)
- Changed DirectX 11 and DirectX 12 hooks to be more reliable and less likely to crash the game (removed kiero dependency)
- Fixed time and weather changing taking a long time to apply (now it is instant!)
- Fixed ownership of Player Variables managed by EGameTools, removing conflict in managing Player Variables between different modules (e.g. Player and World) and any bugs associated with this issue
- Fixed conflict between ImGui state and Player Variables list, causing the game to crash when changing Player Variables
- Removed Windows.h dependency where possible
- General code cleanup
- Added a way of managing dynamic offsets based on game version
- Added a way to stop hooks from running on startup, requiring manual hooking
2025-01-26 02:52:46 +02:00
EricPlayZ 9e7ee5450c - Fixed some hotkey buttons being disabled at startup until the game loads into the main menu 2025-01-19 04:00:05 +02:00
EricPlayZ d4f68003a5 - Added automatic savegame backups to "%localappdata%\EGameSDK\SavegameBackups" using Steamworks API for Steam; also supports the Epic Games version 2025-01-19 01:58:15 +02:00
EricPlayZ 9f3e55dfa7 - Added "Disable Safezone Restrictions" (Player)
- Added "E3 2019 Truck Chase - Tall Building" location (Teleport)
2025-01-18 06:53:08 +02:00
EricPlayZ d00fd40a5f fixed zoom-in issues, fixed Player Variables performance in Debug mode, cleaned up code 2025-01-12 23:47:45 +02:00
EricPlayZ c3307b24c7 removed degree symbols from camera FOV sliders 2025-01-12 08:09:41 +02:00
EricPlayZ 2e5d807ca8 - Added "Camera Offset" sliders for camera position (Camera)
- Added "Zoom In" with default key set to Q (Camera); this is similar to the zoom-in feature from Cyberpunk 2077
- Added separate FOV sliders for each type of camera (Camera)
- Added "Enable Debugging Console" for developer debugging purposes (Debug)

- Fixed FOV slider not affecting Third Person Camera
- Fixed not all options in the mod menu being disabled whilst the game is starting up
2025-01-12 08:08:02 +02:00
EricPlayZ d269480ef2 Fixed Player Variables not working, fixed ImGui crashes because of state changes before ImGui::Render (added DeferredActions class), cleaned some code, made Player Variables more thread safe by creating a thread safe ForEach function for the lists 2025-01-09 02:40:30 +02:00
EricPlayZ 9e03a97d7f backup of thread safe changes 2025-01-07 07:45:32 +02:00
EricPlayZ fdd82c56f1 cleaned code for Player tab and Player Variables, made Player Variables code thread-safe 2025-01-07 06:54:36 +02:00
EricPlayZ c64cf6fc93 backup with working Player Variables 2025-01-07 04:42:06 +02:00
EricPlayZ 35bf50de2e backup for the working code, i hate c++ and its vague errors 2025-01-07 00:51:27 +02:00
EricPlayZ 8552e28bcd backup to Player Variables changes 2025-01-07 00:28:09 +02:00
EricPlayZ 24a343d3f9 massive changes to Player Variables structure 2025-01-06 09:11:01 +02:00
EricPlayZ 2e335c5a9c - fixed some config initialization problems and changed default console window size 2025-01-05 20:34:04 +02:00
EricPlayZ daca9a6cb1 - changed some Windows.h and ImGui definitions to be by default defined in the entire project 2025-01-05 19:53:13 +02:00
EricPlayZ 55fd8936e2 - fix a default config loading issue with saved teleports list 2025-01-05 19:44:39 +02:00
EricPlayZ 4f8dcf503c - USE DEFAULT CLASS FIELD INITIALIZER INSTEAD OF CUSTOM FOR CONFIG 2025-01-05 19:39:17 +02:00
EricPlayZ 820da8db58 - FIX BOOLEAN LOGIC FOR LOW LEVEL MOUSE HOOK 2025-01-05 05:40:16 +02:00
EricPlayZ 7b596fc23d reminders 2025-01-04 05:21:31 +02:00
EricPlayZ 6cdbf2caf0 small changes 2025-01-04 04:58:42 +02:00
EricPlayZ 5e92a4df21 lots of CPU performance optimization 2025-01-04 04:53:31 +02:00
EricPlayZ 32a0f8b124 backup, made a lot of changes related to vftable scanning 2025-01-03 04:35:13 +02:00
EricPlayZ eb633ec72f - fixed some dll export warnings from EGameSDK
- fixed hooking issue where EGameSDK and EGameTools both were trying to hook the same functions (even though they were already hooked by EGameSDK)
2024-12-31 01:48:02 +02:00
EricPlayZ cd0ae09639 fixed low level mouse hook lag 2024-12-30 20:39:03 +02:00
EricPlayZ 427b60f9ba reorganized dependencies, fixed Old World Money slider not working 2024-12-30 19:32:40 +02:00
EricPlayZ fb4b651094 fix offset functions not working properly after getting hooked cuz of no EGameSDK_API dll export 2024-12-30 18:19:24 +02:00
EricPlayZ 819b439d15 Low Level Mouse Hook changes 2024-12-30 17:39:17 +02:00
EricPlayZ 92a84d71d1 add Ultimate ASI Loader 2024-12-30 16:11:23 +02:00
EricPlayZ 131086a282 gitignore change 2024-12-30 15:30:33 +02:00
EricPlayZ 1673c8f1e5 make Release build work 2024-12-30 02:00:42 +02:00
EricPlayZ 8a3a8ece07 Combined EGameSDK and EGameTools (this repo will soon become EGameSDK by default, which will also include EGameTools) 2024-12-30 01:25:31 +02:00
EricPlayZ 1314807788 EGameSDK changes 2024-12-29 21:04:07 +02:00
EricPlayZ a0baf6a7e1 backup of update before EGameSDK merge 2024-12-28 20:07:17 +02:00
EricPlayZ fa9990d3d4 backup of next update 2024-12-28 04:50:50 +02:00
EricPlayZ ba8842c017 backup for next update 2024-12-27 21:00:26 +02:00
EricPlayZ 3361e47c46 Update README.md 2024-11-07 01:04:22 +02:00
EricPlayZ 5a229137f3 next hotfix 2024-11-07 00:53:35 +02:00
EricPlayZ 405cdcef1f changed ImGui style 2024-11-03 21:51:58 +02:00
EricPlayZ 7993931f88 - Improved MountDataPaks hook error detection (the error related to MountDataPaks should not show up in the console as often anymore) 2024-11-03 19:46:41 +02:00
EricPlayZ c163301344 removed PlayerObjProperties class (it is the same as PlayerDI_PH), better multi-threading safety 2024-11-03 19:27:40 +02:00
EricPlayZ 8b4e3c4b75 merge previous master commits to dev, change config player_variables.scr, change changelog 2024-11-03 18:24:33 +02:00
EricPlayZ 0ee417db70 - Added compatibility with v1.19 Tower Raid: Halloween Run update
- Improved CPU performance at game startup when using the mod
2024-11-03 17:53:49 +02:00
EricPlayZ 3280768125 backup of new update 2024-11-02 02:04:12 +02:00
EricPlayZ 71b67d0e46 prepare for v1.2.0 release 2024-05-13 02:20:55 +03:00
EricPlayZ 2cadab18ae changelog 2024-05-12 05:42:33 +03:00
EricPlayZ 3c31878ef8 change default hotkeys 2024-05-12 05:34:42 +03:00
EricPlayZ 263db930a4 change default hotkeys 2024-05-12 05:34:28 +03:00
EricPlayZ 099fd311e3 fixed some bugs, added default teleports list 2024-05-12 05:29:53 +03:00
EricPlayZ d3f36c047e fixed UI scaling, changed text 2024-05-12 04:14:13 +03:00
EricPlayZ 96fa5c0a0f change text 2024-05-12 03:51:19 +03:00
EricPlayZ 598c88bbd6 huge statement about IALR lmao and some bug fix 2024-05-12 03:45:47 +03:00
EricPlayZ 3d546cc198 fixed FOV stuff again 2024-05-12 02:28:49 +03:00
EricPlayZ 38acd3ebe0 fixed FOV issues 2024-05-12 02:26:29 +03:00
EricPlayZ 4043de504b fixed bugs 2024-05-12 02:00:35 +03:00
EricPlayZ f50c883efc fixed some player variables issues, implemented camera teleportation 2024-05-11 20:27:35 +03:00
EricPlayZ dae6072423 fixed teleport to waypoint issues 2024-05-11 19:31:55 +03:00
EricPlayZ 61922760a3 - Added "Teleport to Waypoint" (Teleport)
- WARNING: if the waypoint is selected to track an object/item on the map, Teleport to Waypoint will not work, if so just set the waypoint nearby instead
  - WARNING: your player height won't change when teleporting, so make sure you catch yourself if you fall under the map because of the teleportation
2024-05-11 18:54:48 +03:00
EricPlayZ ce56c7e71e fixed some GUI stuff 2024-05-11 06:11:56 +03:00
EricPlayZ 7c79bc18e3 fixed some GUI stuff 2024-05-11 06:03:46 +03:00
EricPlayZ bb21fb7446 fixed some issues 2024-05-11 04:28:58 +03:00
EricPlayZ ef4e179600 implemented proper config for teleport locations list 2024-05-11 03:33:42 +03:00
EricPlayZ b17ce3e9a9 - Added "Teleport to Coords" with X, Y, Z inputs (Teleport)
- Added "Saved Locations" section in Teleport menu, with the ability of saving, deleting and teleporting to said locations; these locations are saved in the config file and will contain a name and a set of coordinates for each location
2024-05-11 01:45:05 +03:00
EricPlayZ 55e4712990 fix text 2024-05-10 19:28:55 +03:00
EricPlayZ 870a625728 - Added "Unlimited Items" (Player) - Stops the game from lowering the amount of items such as consumables / throwables when using them, alongside other inventory items such as ammo, lockpicks and even money; WARNING: Dropping the entire amount of an item will remove the item from your inventory, whilst also being dropped 2024-05-10 19:21:19 +03:00
EricPlayZ 1bd10e06d6 - Added "Old World Money" slider (Player) 2024-05-10 05:47:33 +03:00
EricPlayZ ba09473499 fixed some weapon features 2024-05-10 04:48:51 +03:00
EricPlayZ 7876a52b39 - Added "One-Hit Kill" (Player)
- Added "Unlimited Ammo" (Weapon)
- Added "No Spread" (Weapon)
- Added "No Recoil" (Weapon)
- Added "Instant Reload" (Weapon)
2024-05-10 04:27:52 +03:00
EricPlayZ a7435e3088 add tooltip 2024-05-10 03:16:48 +03:00
EricPlayZ 725b0bff41 - Added "Current Weapon Durability" slider (Weapon) - currently only works while your weapon is physically equipped in your hand
- Added "Unlimited Durability" (Weapon)
2024-05-10 03:05:31 +03:00
EricPlayZ 7dc2fd4de1 fixed compilation error 2024-05-09 18:43:21 +03:00
EricPlayZ da99802546 - Added "Lens Distortion" slider (Camera)
- Fixed options that make use of player variables not returning back to their original value after disabling them
- Added tooltips to sliders as well
2024-05-09 18:42:06 +03:00
EricPlayZ bd19d3f45a - Added "Disable Head Correction" (Player) - disables centering of the player's hands to the screen while jumping
- fixed issue with Disable Air Control not working properly
2024-05-09 04:33:09 +03:00
EricPlayZ 05fcf607f1 - Added "Disable Air Control" (Player)
- Fixed blood overlay still displaying after falling from a great height with "God Mode" (Player) enabled
2024-05-09 01:45:00 +03:00
EricPlayZ fa6a9e0fb8 added proper vtable checking for most classes 2024-05-08 18:22:22 +03:00
EricPlayZ f692641c28 - Added compatibility with v1.16.2 hotfix update
- Changed the way the mod menu gets the list of player variables, meaning the player variables list should self-update, with no manual intervention required even after a game update
2024-05-08 17:19:18 +03:00
EricPlayZ 0b6c04f107 added RTTI utils which helps me get vtables using RTTI names, yey! no more weird offset stuff 2024-05-08 04:19:16 +03:00
EricPlayZ e3a90c6d58 - Added the ability of using .PAK mods inside "EGameTools\UserModFiles"; just drag and drop a .PAK inside the folder, rename it to whatever you like and enjoy! CREDITS TO @12brendon34 on Discord for finding out how to implement this feature!
- Added "Allow Grapple Hook in Safezone" (Player)
2024-05-07 05:37:29 +03:00
EricPlayZ 7d048316e9 - Added "Unlimited Stamina" (Player)
- Added tooltips when hovering over buttons inside the mod menu
2024-05-06 21:15:31 +03:00
EricPlayZ a580147272 - Fixed immunity drastically being lowered while rapidly changing the time forward with the "Time" slider (World) at night
- Added Invisible to Enemies to config cuz I forgot oops
2024-05-04 03:50:46 +03:00
EricPlayZ 3a51834d4b - Added "Invisible to Enemies" (Player)
- Changed Unlimited Immunity to use player variables instead of function hooking
2024-05-04 00:58:15 +03:00
EricPlayZ 1dcd0b884e - Added "Player Immunity" slider (Player)
- Fixed issue with Unlimited Immunity not working whenever somebody hits you
- Changed version to v1.2.0 (because it's a big update :D)
2024-05-03 22:19:16 +03:00
EricPlayZ e2be96af15 implemented proper savegame CRC check bypass, added byte patching and fixed a few bugs 2024-05-03 17:16:07 +03:00
EricPlayZ f4699cdd30 Added "Unlimited Immunity" (Player) 2024-05-03 04:06:02 +03:00
EricPlayZ 65e2dc3d44 Fixed "Disable Out of Bounds Timer" (Player) not working in missions 2024-05-03 01:22:19 +03:00
EricPlayZ 0ec06592c4 Fixed volatiles still being able to kill you when they jump on top of you while "God Mode" (Player) is enabled 2024-05-03 00:20:21 +03:00
EricPlayZ 0f3cd3546e Fixed "God Mode" (Player) not working properly or at all in multiplayer 2024-05-02 23:32:28 +03:00
EricPlayZ 0755d8523f change game and mod version 2024-05-02 05:05:32 +03:00
EricPlayZ ba953c90be - Fixed long paths to mods inside UserModFiles causing a game crash or causing the mods to not load at all
- Added "Increase Data PAKs Limit" (Misc; requires game restart to apply) - you can now add more than 8 data PAKs, e.g. data8.pak, data9.pak, data10.pak, etc, up to 200 PAKs in total
- Added "Disable Data PAKs CRC Check" (Misc requires game restart to apply) - stops the game from detecting data PAKs, which allows you to use data PAK mods in multiplayer as well
- Added "Disable Savegame CRC Check" (Misc requires game restart to apply) - stops the game from falsely saying your savegame is corrupt whenever you modify it
- Changed the config system to only write to the config file whenever there's a change in the mod menu
2024-05-02 05:04:50 +03:00
EricPlayZ 9a980ca48c backup commit :D 2024-05-01 03:42:17 +03:00
EricPlayZ 4879e68867 switch to new game speed handler 2024-04-29 21:19:55 +03:00
EricPlayZ 4ca2ddf7b2 - Added compatibility with v1.16.1 hotfix update
- Fixed God Mode staying enabled after toggling FreeCam off
- Fixed player variables saving and loading using old version of player_variables.scr (which makes Max Health drop to negative infinite)

backup commit before transitioning to new game speed handler
2024-04-29 20:09:17 +03:00
1197 changed files with 544686 additions and 48435 deletions
@@ -0,0 +1,172 @@
# AI Agent Guide: Building a Full MCP Server for CheatEngine
---
## ⚠️ CRITICAL CONFIGURATION
### 1. BSOD PREVENTION
**You MUST disable:** Cheat Engine → Settings → Extra → **"Query memory region routines"**.
- **Enabled:** Causes `CLOCK_WATCHDOG_TIMEOUT` BSODs due to conflicts with DBVM/Anti-Cheat when scanning protected pages.
- **Disabled:** Scanning works perfectly and safely (v11 verified).
### 2. ANTI-CHEAT SAFETY
- **DO NOT** use software breakpoints (0xCC/Int3). Use **Hardware Debug Registers (DR0-DR3)** breakpoints.
- **DO NOT** write to memory.
- **USE** DBVM tools (`start_dbvm_watch`) for invisible tracing at Ring -1.
---
## 1. OBJECTIVE
A **production-grade Model Context Protocol (MCP) server** acts as the "Universal Key" to the game engine, allowing AI agents to:
1. **Read & Analyze** any part of memory (static or dynamic).
2. **Trace Execution** invisible to anti-cheat (Hypervisor/Hardware BPs).
3. **Reverse Engineer** complex structures and functions on the fly.
### Performance Targets
- **Architecture:** Multi-Threaded Named Pipe (Async I/O).
- **Latency:** <2ms per command.
- **Reliability:** 100% Robust against freezes via `thread.synchronize`.
---
## 2. SYSTEM ARCHITECTURE (v11.4.0)
### The "v11/v99" Bridge
The system uses a highly optimized Named Pipe architecture with a dedicated worker thread in Lua to handle blocking I/O, ensuring the main Cheat Engine GUI/Thread never freezes.
### Universal 32/64-bit Architecture Support
The bridge automatically adapts to the target process architecture:
- **`getArchInfo()`** - Dynamically detects 32-bit vs 64-bit target
- **`captureRegisters()`** - Captures RAX/RBX (x64) or EAX/EBX (x86) correctly
- **`captureStack()`** - Reads stack with correct pointer size (8/4 bytes)
- **`readPointer()`** - Used throughout for automatic pointer size handling
- **Function Analysis** - Detects both x86 (`55 8B EC`) and x64 (`55 48 89 E5`, `48 83 EC xx`) prologues
**Connection Details:**
- **Pipe Name:** `\\.\pipe\CE_MCP_Bridge_v99`
- **Protocol:** Length-Prefixed JSON-RPC
- **Flow:** Python `FastMCP` Server <-> Named Pipe <-> Lua Worker Thread <-> Main Thread (`synchronize`)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ AI Agent (Claude/Cursor/Copilot) │
│ │ │
│ ▼ MCP Protocol (JSON-RPC over stdio) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ mcp_cheatengine.py (Python MCP Server) │ │
│ │ - Translates MCP tools to JSON-RPC │ │
│ │ - Connects to \\.\pipe\CE_MCP_Bridge_v99 │ │
│ └───────────────────────────┬─────────────────────────────────────┘ │
│ │ Named Pipe (Async) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ CheatEngine (Running, attached to .exe) │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ ce_mcp_bridge.lua │ │ │
│ │ │ ┌─────────────────┐ ┌─────────────────────┐ │ │ │
│ │ │ │ Worker Thread │◄────►│ Main Thread (GUI) │ │ │ │
│ │ │ │ (Blocking I/O) │ Sync │ (Safe API Execution)│ │ │ │
│ │ │ └─────────────────┘ └─────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 3. COMPREHENSIVE TOOLSET (40+ Commands)
The v11.4.0 implementation provides a complete arsenal for reverse engineering with full 32/64-bit compatibility.
### 🔍 Memory Reading & Scanning
| Tool | Description |
|------|-------------|
| `read_memory(addr, size)` | Read raw bytes. |
| `read_integer(addr, type)` | Read Byte, Word, Dword, Qword, Float, Double. |
| `read_string(addr, len)` | Read ASCII/UTF-16 strings. |
| `read_pointer_chain(base, offsets)` | **CRITICAL**: Follow dynamic pointer paths `[base+10]+20`. |
| `scan_all(val, type, prot)` | Full memory scanner (like CE GUI). **Safe Mode Enabled.** |
| `aob_scan(pattern)` | Find array of bytes `48 8B 05 ??`. |
| `generate_signature(addr)` | Create unique AOB signature for an address. |
### 🧬 Structure & Code Analysis
| Tool | Description |
|------|-------------|
| `dissect_structure(addr)` | **AI-Powered**: Auto-guess fields, types, and values at address. |
| `get_rtti_classname(addr)` | Identify C++ object types (e.g., `NpcUser`, `CItem`). |
| `analyze_function(addr)` | Find all `CALL`s made by a function. |
| `find_references(addr)` | Find what code uses this data (Cross-References). |
| `find_call_references(func)` | Find who calls this function (Call Graph). |
| `find_function_boundaries(addr)` | Locate function start/end/prologue. |
### 🐞 Debugging (Anti-Cheat Safe)
| Tool | Description |
|------|-------------|
| `set_breakpoint(addr)` | **Hardware BP**: Triggers on execution. Logs registers. |
| `set_data_breakpoint(addr)` | **Watchpoint**: Triggers on Write/Read access. |
| `get_breakpoint_hits()` | Retrieve hit logs (Registers, Stack, Timestamp). |
### 🚀 DBVM Hypervisor (Ring -1)
| Tool | Description |
|------|-------------|
| `start_dbvm_watch(addr)` | **Invisible Trace**: use physical memory hooks. Undetectable. |
| `get_physical_address(addr)` | Resolve Virtual -> Physical address. |
Full commands list at `MCP_Bridge_Command_Reference.md`
---
## 4. CURRENT STATUS
| Component | Status | Version | Notes |
|-----------|--------|---------|-------|
| **Lua Bridge** | ✅ **ACTIVE** | v11.4.0 | `ce_mcp_bridge.lua` (Load in CE) |
| **Python Server** | ✅ **ACTIVE** | v11.4.0 | `mcp_cheatengine.py` (Run in Agent) |
| **Test Suite** | ✅ **VERIFIED** | v3 | `test_mcp.py` (36/37 passing) |
| **Documentation** | ✅ **UPDATED** | v5.1 | `MCP_Bridge_Command_Reference.md` |
### Recent Fixes (v11.4.0)
- **Zombie Cleanup**: Added `cleanupZombieState()` to remove orphaned breakpoints/watches on reload
- **Script Reload Safety**: Prevents game freezes when reloading script with active resources
### Previous Fixes (v11.3.1)
- **Pointer Chain Reading**: Fixed to use `readPointer()` for final values (32/64-bit safe)
- **Function Analysis**: Enhanced to detect x86 and x64 function prologues
- **CALL Detection**: Now includes indirect CALL instructions (`FF /2`)
- **Architecture Field**: All analysis commands include `arch` field in response
---
## 5. BEST PRACTICES FOR AGENTS
1. **Always Check `ping()`**: verify connection before starting heavy tasks.
2. **Use `read_pointer_chain`**: simpler and faster than multiple `read_pointer` calls.
3. **Structure Analysis**: Use `dissect_structure` on unknown pointers to understand their layout immediately.
4. **Safe Scanning**: `scan_all` scans in **User Mode** range only to prevent BSODs (`0x7FFFF...`).
5. **RTTI is King**: Use `get_rtti_classname` to identify what an object is instantly.
6. **Check Architecture**: Use `get_process_info()` to check `targetIs64Bit` before architecture-specific operations.
7. **DBVM for Stealth**: Use `start_dbvm_watch` for invisible Ring -1 tracing (if DBVM is active).
8. **Cleanup Breakpoints**: Always call `remove_breakpoint` or `stop_dbvm_watch` after monitoring.
---
## 6. VERIFICATION
The MCP bridge has been thoroughly validated with a comprehensive test suite:
```
test_mcp.py v3 Results:
================================================
✅ Memory Reading: 6/6 tests passed (with data validation)
✅ Process Info: 4/4 tests passed (architecture checks)
✅ Code Analysis: 8/8 tests passed (proper entry points)
✅ Breakpoints: 4/4 tests passed (setup/cleanup verified)
✅ DBVM Functions: 3/3 tests passed (graceful skip if inactive)
✅ Utility Commands: 11/11 tests passed
⏭️ Skipped: 1 test (generate_signature - blocking)
------------------------------------------------
Total: 36/37 PASSED (100% success rate)
```
Run the test suite with: `python test_mcp.py`
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
# Chrome Engine Dynamic Reflection (CRTTI) Guide
This document provides the technical specifications for the internal reflection system used in Techland's Chrome Engine (specifically Dying Light 2). Use this information to maintain and expand the Dynamic SDK.
---
## 1. Core Architecture
The engine maintains a global registry of all reflected classes (`CRTTI`) and their members (`CRTTIField`). Unlike standard C++ RTTI, this system provides exact memory offsets for fields at runtime.
### The Manager
- **Static Pointer**: `engine_x64_rwdi.dll + 0x1FFC9D0`
- **Primary Export**: `GetRTTIManager()` (Returns the `CRTTIManager*` instance).
### Key Engine APIs (DL2 Mangled Names)
- **Find Class**: `?FindClass@CRTTIManager@@UEBAPEBVCRTTI@@V?$string_const@D@ttl@@W4TYPE@FFindClass@1@_N@Z`
- *Args*: `RCX` (Manager), `RDX` (ttl::string_const), `R8` (Type Mask, use 3), `R9` (bool, use false).
- **Find Field**: `?FindField@CRTTI@@QEBAPEBVCRTTIField@@PEBD@Z`
- *Args*: `RCX` (CRTTI*), `RDX` (const char* name).
---
## 2. The String Table Breakthrough
Techland uses a deduplicated string table for all reflected names. A `ttl::string_const` or a name pointer in a struct is **never** a raw `char*`.
### Indirection Logic:
1. **Indirection 1**: The structure (e.g., `CRTTI + 0x10`) holds a pointer to a **String Table Entry**.
2. **Indirection 2**: The String Table Entry contains a **Tagged Pointer**.
3. **Tagging**: The top 3 bits of the pointer are used for flags (e.g., `0x8` prefix).
4. **Buffer**: After masking with `0x1FFFFFFFFFFFFFFF`, you get the actual heap address of the null-terminated string.
**C++ Implementation:**
```cpp
const char* GetName() const {
uintptr_t* pEntry = UntagPointer(m_pNamePtr);
uintptr_t taggedAddr = *pEntry;
return reinterpret_cast<const char*>(taggedAddr & 0x1FFFFFFFFFFFFFFF);
}
```
---
## 3. Data Structure Layouts (DL2)
### `CRTTI` Structure
| Offset | Type | Description |
|--------|------|-------------|
| 0x00 | void* | VTable |
| 0x10 | uintptr_t* | **Name Entry Pointer** (see String Table Logic) |
| 0x18 | uint32_t | Class Byte Size |
| 0x28 | CRTTIField** | **Fields Array** (Tagged Pointer) |
| 0x30 | uint32_t | Field Count |
| 0xE8 | CRTTI* | **Base Class Pointer** (Tagged) |
### `CRTTIField` Structure
| Offset | Type | Description |
|--------|------|-------------|
| 0x08 | uintptr_t* | **Field Name Entry Pointer** |
| 0x50 | uint32_t | **Byte Offset** within the class instance |
---
## 4. Usage Strategy ("RED4ext" Approach)
To keep the SDK resilient to game updates:
1. **Don't define hardcoded structs** for complex classes.
2. **Use the `DynamicObject` wrapper**:
- Wrap a game pointer (`void*`).
- Use `RTTIManager::GetClass("ClassName")` to get the metadata.
- Use `pCRTTI->FindField("m_MemberName")` to get the offset live.
- Read/Write memory at `base_addr + offset`.
This ensures that even if a game patch moves `m_Fov` from `0x120` to `0x138`, your mod will still find it instantly.
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
{
"mcpServers": {
"ida-pro-mcp": {
"type": "http",
"url": "http://127.0.0.1:13337/mcp"
},
"cheatengine": {
"command": "python",
"args": ["C:\\Users\\EricPlayZ\\Desktop\\cheatengine-mcp-bridge-main\\MCP_Server\\mcp_cheatengine.py"]
}
}
}
+410 -12
View File
@@ -1,13 +1,411 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
_IDAScripts/.env
_IDAScripts/generated/*.json
/.vs/EGameTools
/x64
/EGameTools/x64
/EGameTools/EGameTools.vcxproj.user
/Tools/ExtractPlayerVarsFromDecompiledFunc/bin
/Tools/ExtractPlayerVarsFromDecompiledFunc/.vs
/Tools/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc/bin
/Tools/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc/obj
/Tools/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc.csproj.user
# Ultimate-ASI-Loader
data/
!data/**/*.ini
# Temporary files created by Visual Studio extensions
*.TMP
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
/_IDAScripts/generated/parsed-classes.json
+47
View File
@@ -0,0 +1,47 @@
# EGameSDK / EGameTools Development Context
## Project Overview
Building a C++ SDK and Mod Menu for **Dying Light 2**.
**Core Goal**: Move away from hardcoded, version-specific memory addresses and offsets, and build a highly resilient, dynamic system capable of surviving game updates.
## The Problem: Dynamic Instance & Offset Discovery
We attempted to build a RED4ext-like RTTI (Run-Time Type Information) system to dynamically find class instances and property offsets.
### What We Tried (RTTI Approach)
1. **CRTTI Parsing**: Successfully parsed Techland's RTTI structure, handling 48-bit pointer tagging (`0x0000FFFFFFFFFFFF`) and inline storage heuristics.
2. **Instance Discovery (`GetFirstInstance`)**: Attempted to dynamically find the `CGame` and `PlayerDI_PH` instances by scanning:
- The `IFactory` global list (only contained 160 objects, missing core singletons).
- The `CRTTI` object's internal instance list at offset `+0x38` (empty/sentinel for `CGame`).
- `CSerializableObject::GetLoadedGSObjectsList` (returned null).
3. **The `CGame` Singleton**: Discovered that `CGame` is not tracked in standard RTTI lists. It is retrieved via a static pointer to `CLobbySteam` (`0x2778058`), with `CGame` located at `[CLobbySteam + 0xF8]`.
4. **Missing Properties**: Tried to use RTTI to dynamically find the offsets for `maxImmunity` and `nightrunnerTimer` inside `PlayerInfectionModule`.
- **Discovery**: These variables do _not_ exist in the standard RTTI registry. They are internal C++ logic variables not exposed to the game's reflection system.
- We found some of them exposed via the `ChromeSpy::SEventPlayer` debugging system (`sub_14D2720`), but relying on RTTI for internal logic variables proved to be a dead end.
### The Tools We Used (And Abandoned)
- **Cheat Engine `scan_all`**: Abandoned. Caused severe RAM spikes and system hangs.
- **Cheat Engine `evaluate_lua`**: Abandoned. Caused game crashes when executing engine vtable functions (like `GetRTTI`) mid-execution.
---
## The Pivot: Instruction-Based Pattern Scanning
Because RTTI does not track internal logic variables (`maxImmunity`, `nightrunnerTimer`, etc.) or robustly track singleton instances, **we are abandoning RTTI for offset discovery.**
**New Strategy**: Surgical Assembly Pattern Scanning.
Instead of scanning for RTTI names, we will use Cheat Engine to find the exact assembly instructions that read/write to the desired class offsets (e.g., `movss xmm0, [rcx + 2Ch]`). We will signature-scan these specific functions and extract the offset (`0x2C`) dynamically from the instruction bytes.
---
## Architectural Review: `OffsetManager` Class
The current `OffsetManager` handles patterns via macros (`AddPattern`, `AddDynamicPattern`, `AddStaticOffset`), but requires refactoring to support the new strategy efficiently.
### Identified Flaws in Current Implementation:
1. **Header Bloat**: `AddPattern` places raw byte patterns directly in `Offsets.h`. Changing a pattern forces a recompilation of every file that includes the header.
- _Fix_: Move all pattern strings to `Offsets.cpp` and only expose the getter in the header.
+54
View File
@@ -0,0 +1,54 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33530.505
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EGameTools", "EGameTools\EGameTools.vcxproj", "{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}"
ProjectSection(ProjectDependencies) = postProject
{152BCFEC-E42D-4370-AEDD-36F044085C0C} = {152BCFEC-E42D-4370-AEDD-36F044085C0C}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EGameSDK", "EGameSDK\EGameSDK.vcxproj", "{152BCFEC-E42D-4370-AEDD-36F044085C0C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ultimate-ASI-Loader-x64", "Ultimate-ASI-Loader-x64\Ultimate-ASI-Loader-x64.vcxproj", "{47F0BD81-B327-0619-BC31-6A3C28E82C12}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Debug|x64.ActiveCfg = Debug|x64
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Debug|x64.Build.0 = Debug|x64
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Debug|x86.ActiveCfg = Debug|x64
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Debug|x86.Build.0 = Debug|x64
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Release|x64.ActiveCfg = Release|x64
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Release|x64.Build.0 = Release|x64
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Release|x86.ActiveCfg = Release|x64
{6399CAFA-D04A-44FB-8C1D-1FC3A3B59C00}.Release|x86.Build.0 = Release|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Debug|x64.ActiveCfg = Debug|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Debug|x64.Build.0 = Debug|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Debug|x86.ActiveCfg = Debug|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Debug|x86.Build.0 = Debug|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Release|x64.ActiveCfg = Release|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Release|x64.Build.0 = Release|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Release|x86.ActiveCfg = Release|x64
{152BCFEC-E42D-4370-AEDD-36F044085C0C}.Release|x86.Build.0 = Release|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Debug|x64.ActiveCfg = Debug_Release|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Debug|x64.Build.0 = Debug_Release|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Debug|x86.ActiveCfg = Debug|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Debug|x86.Build.0 = Debug|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Release|x64.ActiveCfg = Release|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Release|x64.Build.0 = Release|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Release|x86.ActiveCfg = Release|x64
{47F0BD81-B327-0619-BC31-6A3C28E82C12}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AC733DAA-3005-4E3E-85AF-48E08162D2DE}
EndGlobalSection
EndGlobal
+272
View File
@@ -0,0 +1,272 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="deps\memscan\memscan.c" />
<ClCompile Include="deps\memscan\util\util.c" />
<ClCompile Include="src\ClassHelpers.cpp" />
<ClCompile Include="src\Core\Core.cpp" />
<ClCompile Include="src\Core\SaveGameManager.cpp" />
<ClCompile Include="src\Core\SteamAPI.cpp" />
<ClCompile Include="src\dllmain.cpp" />
<ClCompile Include="src\Engine\CBulletPhysicsCharacter.cpp" />
<ClCompile Include="src\Engine\CGame.cpp" />
<ClCompile Include="src\Engine\CGSObject.cpp" />
<ClCompile Include="src\Engine\CGSObject2.cpp" />
<ClCompile Include="src\Engine\CInput.cpp" />
<ClCompile Include="src\Engine\CLevel.cpp" />
<ClCompile Include="src\Engine\CLevel2.cpp" />
<ClCompile Include="src\Engine\CLobbySteam.cpp" />
<ClCompile Include="src\Engine\CoPhysicsProperty.cpp" />
<ClCompile Include="src\Engine\IBaseCamera.cpp" />
<ClCompile Include="src\Engine\IControlObject.cpp" />
<ClCompile Include="src\Engine\IPhysicsCharacter.cpp" />
<ClCompile Include="src\Engine\RTTIManager.cpp" />
<ClCompile Include="src\Engine\CVars.cpp" />
<ClCompile Include="src\Engine\CVideoSettings.cpp" />
<ClCompile Include="src\Engine\Engine_Misc.cpp" />
<ClCompile Include="src\Engine\VarBase.cpp" />
<ClCompile Include="src\Engine\VarManagerBase.cpp" />
<ClCompile Include="src\Engine\VarMapBase.cpp" />
<ClCompile Include="src\Engine\VarRef.cpp" />
<ClCompile Include="src\GamePH\CoPlayerRestrictions.cpp" />
<ClCompile Include="src\GamePH\DayNightCycle.cpp" />
<ClCompile Include="src\GamePH\FreeCamera.cpp" />
<ClCompile Include="src\GamePH\GameDI_PH.cpp" />
<ClCompile Include="src\GamePH\GameDI_PH2.cpp" />
<ClCompile Include="src\GamePH\GamePH_Hooks.cpp" />
<ClCompile Include="src\GamePH\GamePH_Misc.cpp" />
<ClCompile Include="src\GamePH\InventoryContainerDI.cpp" />
<ClCompile Include="src\GamePH\InventoryItem.cpp" />
<ClCompile Include="src\GamePH\LevelDI.cpp" />
<ClCompile Include="src\GamePH\LocalClientDI.cpp" />
<ClCompile Include="src\GamePH\LogicalPlayer.cpp" />
<ClCompile Include="src\GamePH\PlayerDI_PH.cpp" />
<ClCompile Include="src\GamePH\PlayerHealthModule.cpp" />
<ClCompile Include="src\GamePH\PlayerInfectionModule.cpp" />
<ClCompile Include="src\GamePH\PlayerState.cpp" />
<ClCompile Include="src\GamePH\PlayerVariables.cpp" />
<ClCompile Include="src\GamePH\SessionCooperativeDI.cpp" />
<ClCompile Include="src\GamePH\TimeWeather\CSystem.cpp" />
<ClCompile Include="src\GamePH\TPPCameraDI.cpp" />
<ClCompile Include="src\mtx34.cpp" />
<ClCompile Include="src\Offsets.cpp" />
<ClCompile Include="src\Utils\Files.cpp" />
<ClCompile Include="src\Utils\Hook.cpp" />
<ClCompile Include="src\Utils\Memory.cpp" />
<ClCompile Include="src\Utils\RTTI.cpp" />
<ClCompile Include="src\Utils\Sigscan.cpp" />
<ClCompile Include="src\Utils\Time.cpp" />
<ClCompile Include="src\Utils\Values.cpp" />
<ClCompile Include="src\Utils\WinMemory.cpp" />
<ClCompile Include="src\vec2.cpp" />
<ClCompile Include="src\vec3.cpp" />
<ClCompile Include="src\vec4.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\EGSDK\ClassHelpers.h" />
<ClInclude Include="include\EGSDK\Core\Core.h" />
<ClInclude Include="include\EGSDK\Core\SaveGameManager.h" />
<ClInclude Include="include\EGSDK\Core\SteamAPI.h" />
<ClInclude Include="include\EGSDK\Engine\CBaseCamera.h" />
<ClInclude Include="include\EGSDK\Engine\CBulletPhysicsCharacter.h" />
<ClInclude Include="include\EGSDK\Engine\CGame.h" />
<ClInclude Include="include\EGSDK\Engine\CGSObject.h" />
<ClInclude Include="include\EGSDK\Engine\CGSObject2.h" />
<ClInclude Include="include\EGSDK\Engine\CInput.h" />
<ClInclude Include="include\EGSDK\Engine\CLevel.h" />
<ClInclude Include="include\EGSDK\Engine\CLevel2.h" />
<ClInclude Include="include\EGSDK\Engine\CLobbySteam.h" />
<ClInclude Include="include\EGSDK\Engine\CoPhysicsProperty.h" />
<ClInclude Include="include\EGSDK\Engine\CRTTI.h" />
<ClInclude Include="include\EGSDK\Engine\RTTIManager.h" />
<ClInclude Include="include\EGSDK\Engine\IBaseCamera.h" />
<ClInclude Include="include\EGSDK\Engine\IControlObject.h" />
<ClInclude Include="include\EGSDK\Engine\IPhysicsCharacter.h" />
<ClInclude Include="include\EGSDK\Engine\CVars.h" />
<ClInclude Include="include\EGSDK\Engine\CVideoSettings.h" />
<ClInclude Include="include\EGSDK\Engine\Engine_Misc.h" />
<ClInclude Include="include\EGSDK\Engine\VarBase.h" />
<ClInclude Include="include\EGSDK\Engine\VarManagerBase.h" />
<ClInclude Include="include\EGSDK\Engine\VarMapBase.h" />
<ClInclude Include="include\EGSDK\Engine\VarRef.h" />
<ClInclude Include="include\EGSDK\Exports.h" />
<ClInclude Include="include\EGSDK\GamePH\CameraFPPDI.h" />
<ClInclude Include="include\EGSDK\GamePH\CoBaseCameraProxy.h" />
<ClInclude Include="include\EGSDK\GamePH\CoPlayerRestrictions.h" />
<ClInclude Include="include\EGSDK\GamePH\DayNightCycle.h" />
<ClInclude Include="include\EGSDK\GamePH\FreeCamera.h" />
<ClInclude Include="include\EGSDK\GamePH\GameDI_PH.h" />
<ClInclude Include="include\EGSDK\GamePH\GameDI_PH2.h" />
<ClInclude Include="include\EGSDK\GamePH\GamePH_Hooks.h" />
<ClInclude Include="include\EGSDK\GamePH\GamePH_Misc.h" />
<ClInclude Include="include\EGSDK\GamePH\InventoryContainerDI.h" />
<ClInclude Include="include\EGSDK\GamePH\InventoryItem.h" />
<ClInclude Include="include\EGSDK\GamePH\InventoryMoney.h" />
<ClInclude Include="include\EGSDK\GamePH\ItemDescWithContext.h" />
<ClInclude Include="include\EGSDK\GamePH\LevelDI.h" />
<ClInclude Include="include\EGSDK\GamePH\LocalClientDI.h" />
<ClInclude Include="include\EGSDK\GamePH\LogicalPlayer.h" />
<ClInclude Include="include\EGSDK\GamePH\PlayerDI_PH.h" />
<ClInclude Include="include\EGSDK\GamePH\PlayerFppVis_PH.h" />
<ClInclude Include="include\EGSDK\GamePH\PlayerHealthModule.h" />
<ClInclude Include="include\EGSDK\GamePH\PlayerInfectionModule.h" />
<ClInclude Include="include\EGSDK\GamePH\PlayerState.h" />
<ClInclude Include="include\EGSDK\GamePH\PlayerVariables.h" />
<ClInclude Include="include\EGSDK\GamePH\SessionCooperativeDI.h" />
<ClInclude Include="include\EGSDK\GamePH\TimeWeather\CSystem.h" />
<ClInclude Include="include\EGSDK\GamePH\TimeWeather\EWeather.h" />
<ClInclude Include="include\EGSDK\GamePH\TimeWeather\ISubsystem.h" />
<ClInclude Include="include\EGSDK\GamePH\TPPCameraDI.h" />
<ClInclude Include="include\EGSDK\Imports.h" />
<ClInclude Include="include\EGSDK\mtx34.h" />
<ClInclude Include="include\EGSDK\Offsets.h" />
<ClInclude Include="include\EGSDK\Utils\Files.h" />
<ClInclude Include="include\EGSDK\Utils\Hook.h" />
<ClInclude Include="include\EGSDK\Utils\Memory.h" />
<ClInclude Include="include\EGSDK\Utils\RTTI.h" />
<ClInclude Include="include\EGSDK\Utils\Sigscan.h" />
<ClInclude Include="include\EGSDK\Utils\Time.h" />
<ClInclude Include="include\EGSDK\Utils\Values.h" />
<ClInclude Include="include\EGSDK\Utils\WinMemory.h" />
<ClInclude Include="include\EGSDK\vec2.h" />
<ClInclude Include="include\EGSDK\vec3.h" />
<ClInclude Include="include\EGSDK\vec4.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{152BCFEC-E42D-4370-AEDD-36F044085C0C}</ProjectGuid>
<RootNamespace>EGameSDK</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>EGameSDK</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetExt>.dll</TargetExt>
<TargetName>EGameSDK</TargetName>
<GenerateManifest>true</GenerateManifest>
<LibraryPath>$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64)</LibraryPath>
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetName>EGameSDK</TargetName>
<TargetExt>.dll</TargetExt>
<LibraryPath>$(LibraryPath)</LibraryPath>
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<VcpkgUseStatic>true</VcpkgUseStatic>
<VcpkgTriplet>x64-windows</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<VcpkgUseStatic>true</VcpkgUseStatic>
<VcpkgTriplet>x64-windows</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;EGameSDK_EXPORTS;_WINDOWS;_USRDLL;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<Optimization>
</Optimization>
<IntrinsicFunctions>
</IntrinsicFunctions>
<AdditionalIncludeDirectories>include;deps</AdditionalIncludeDirectories>
<PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_$(PlatformTarget).pch</PrecompiledHeaderOutputFile>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>DebugFastLink</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>DbgHelp.lib;Version.lib;libMinHook-x64-v141-mdd.lib;engine_x64_rwdi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OptimizeReferences>
</OptimizeReferences>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<AdditionalLibraryDirectories>deps\MinHook\lib;deps\game_libs;</AdditionalLibraryDirectories>
<AssemblyDebug>true</AssemblyDebug>
<ImportLibrary />
<FixedBaseAddress>
</FixedBaseAddress>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;EGameSDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>include;deps;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_$(PlatformTarget).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>deps\MinHook\lib;deps\game_libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>DbgHelp.lib;Version.lib;libMinHook-x64-v141-mdd.lib;engine_x64_rwdi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
<ImportLibrary />
<AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
<AssemblyDebug>true</AssemblyDebug>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
+438
View File
@@ -0,0 +1,438 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="deps\memscan\memscan.c">
<Filter>deps\memscan</Filter>
</ClCompile>
<ClCompile Include="deps\memscan\util\util.c">
<Filter>deps\memscan\util</Filter>
</ClCompile>
<ClCompile Include="src\Core\Core.cpp">
<Filter>src\Core</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CGSObject.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CGSObject2.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CInput.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CLevel.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CLevel2.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CLobbySteam.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CoPhysicsProperty.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CVideoSettings.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\Engine_Misc.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CBulletPhysicsCharacter.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CGame.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\DayNightCycle.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\FreeCamera.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\GameDI_PH.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\GameDI_PH2.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\GamePH_Hooks.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\GamePH_Misc.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\InventoryContainerDI.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\InventoryItem.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\LevelDI.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\LocalClientDI.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\LogicalPlayer.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\PlayerDI_PH.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\PlayerHealthModule.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\PlayerInfectionModule.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\PlayerState.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\PlayerVariables.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\SessionCooperativeDI.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\TPPCameraDI.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\TimeWeather\CSystem.cpp">
<Filter>src\GamePH\TimeWeather</Filter>
</ClCompile>
<ClCompile Include="src\Utils\Hook.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\Utils\Memory.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\Utils\RTTI.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\Utils\Sigscan.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\Utils\Time.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\Utils\Values.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\Utils\Files.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\dllmain.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\vec3.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\ClassHelpers.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\GamePH\CoPlayerRestrictions.cpp">
<Filter>src\GamePH</Filter>
</ClCompile>
<ClCompile Include="src\Engine\CVars.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\IBaseCamera.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\IPhysicsCharacter.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\mtx34.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\vec4.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\Engine\IControlObject.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\vec2.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\Engine\VarBase.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Core\SaveGameManager.cpp">
<Filter>src\Core</Filter>
</ClCompile>
<ClCompile Include="src\Core\SteamAPI.cpp">
<Filter>src\Core</Filter>
</ClCompile>
<ClCompile Include="src\Offsets.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\Utils\WinMemory.cpp">
<Filter>src\Utils</Filter>
</ClCompile>
<ClCompile Include="src\Engine\VarRef.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\VarMapBase.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\VarManagerBase.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
<ClCompile Include="src\Engine\RTTIManager.cpp">
<Filter>src\Engine</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="include">
<UniqueIdentifier>{240d20d1-1840-4efd-a681-fac101427937}</UniqueIdentifier>
</Filter>
<Filter Include="src">
<UniqueIdentifier>{218d6c51-9cf3-40d0-bf4d-bf541b5a1087}</UniqueIdentifier>
</Filter>
<Filter Include="deps">
<UniqueIdentifier>{12e04078-a701-4cf3-91c0-213a9d89f946}</UniqueIdentifier>
</Filter>
<Filter Include="deps\memscan">
<UniqueIdentifier>{3dc097d2-c84a-4a44-ac24-7baa23de9450}</UniqueIdentifier>
</Filter>
<Filter Include="deps\memscan\util">
<UniqueIdentifier>{8f001c65-e2d0-41a7-843f-c6f83948525f}</UniqueIdentifier>
</Filter>
<Filter Include="include\Core">
<UniqueIdentifier>{075c0a95-cc22-4a85-88a7-af8dcc522167}</UniqueIdentifier>
</Filter>
<Filter Include="include\Engine">
<UniqueIdentifier>{d6e14061-d739-4cb4-95cd-131086b5365a}</UniqueIdentifier>
</Filter>
<Filter Include="include\GamePH">
<UniqueIdentifier>{ad009156-fe7a-4150-a5e2-74bd330276bc}</UniqueIdentifier>
</Filter>
<Filter Include="include\Utils">
<UniqueIdentifier>{5b9dd038-d0bd-4c39-ab73-8eefb4caa7a3}</UniqueIdentifier>
</Filter>
<Filter Include="include\GamePH\TimeWeather">
<UniqueIdentifier>{b57ade37-61c4-4b2e-aa2f-2bb3539e310c}</UniqueIdentifier>
</Filter>
<Filter Include="src\Core">
<UniqueIdentifier>{f93cdab4-4b15-4043-a202-b954c2ff0428}</UniqueIdentifier>
</Filter>
<Filter Include="src\Engine">
<UniqueIdentifier>{76ff4605-50c3-4fee-a531-b478f29261f9}</UniqueIdentifier>
</Filter>
<Filter Include="src\GamePH">
<UniqueIdentifier>{b920b792-6899-486d-85ad-d8415004ab4a}</UniqueIdentifier>
</Filter>
<Filter Include="src\Utils">
<UniqueIdentifier>{6b391f0c-15fb-4b15-93b0-3f4b6863e684}</UniqueIdentifier>
</Filter>
<Filter Include="src\GamePH\TimeWeather">
<UniqueIdentifier>{5af79ed5-d275-484f-b7b0-0f23aef9f33d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\EGSDK\ClassHelpers.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Exports.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Offsets.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\vec3.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\Hook.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\Memory.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\RTTI.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\Sigscan.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\Time.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\Values.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\Files.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\CoBaseCameraProxy.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\DayNightCycle.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\FreeCamera.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\GameDI_PH.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\GameDI_PH2.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\GamePH_Misc.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\InventoryContainerDI.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\InventoryItem.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\InventoryMoney.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\ItemDescWithContext.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\LevelDI.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\LocalClientDI.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\LogicalPlayer.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\PlayerDI_PH.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\PlayerHealthModule.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\PlayerInfectionModule.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\PlayerState.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\PlayerVariables.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\SessionCooperativeDI.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\TPPCameraDI.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\TimeWeather\EWeather.h">
<Filter>include\GamePH\TimeWeather</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\TimeWeather\CSystem.h">
<Filter>include\GamePH\TimeWeather</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CGSObject.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CGSObject2.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CInput.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CLevel.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CLevel2.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CLobbySteam.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CoPhysicsProperty.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CVideoSettings.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\Engine_Misc.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CBaseCamera.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CBulletPhysicsCharacter.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CGame.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Core\Core.h">
<Filter>include\Core</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\GamePH_Hooks.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\CoPlayerRestrictions.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Core\SaveGameManager.h">
<Filter>include\Core</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Core\SteamAPI.h">
<Filter>include\Core</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CVars.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Utils\WinMemory.h">
<Filter>include\Utils</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\IBaseCamera.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\IPhysicsCharacter.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\TimeWeather\ISubsystem.h">
<Filter>include\GamePH\TimeWeather</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\mtx34.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\vec4.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\IControlObject.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\CameraFPPDI.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\GamePH\PlayerFppVis_PH.h">
<Filter>include\GamePH</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\vec2.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\VarMapBase.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\VarManagerBase.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\VarBase.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\VarRef.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Imports.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\CRTTI.h">
<Filter>include\Engine</Filter>
</ClInclude>
<ClInclude Include="include\EGSDK\Engine\RTTIManager.h">
<Filter>include\Engine</Filter>
</ClInclude>
</ItemGroup>
</Project>
+682
View File
@@ -0,0 +1,682 @@
/*
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2020 - 2020 Daniil Dudkin.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef FIXED_STRING_HPP
#define FIXED_STRING_HPP
#include <array>
#include <functional>
#include <iterator>
#include <ostream>
#include <string_view>
#include <type_traits>
#define FIXSTR_VERSION_MAJOR 0
#define FIXSTR_VERSION_MINOR 1
#define FIXSTR_VERSION_PATCH 1
#define FIXSTR_CPP20_CHAR8T_PRESENT __cpp_char8_t
#define FIXSTR_CPP20_SPACESHIP_OPERATOR_PRESENT __cpp_lib_three_way_comparison
#define FIXSTR_CPP20_CONSTEXPR_ALGORITHMS_PRESENT (__cpp_lib_constexpr_algorithms)
#ifdef _MSC_VER
#define FIXSTR_CPP_VERSION _MSVC_LANG
#else
#define FIXSTR_CPP_VERSION __cplusplus
#endif // _MSC_VER
// Note that when ICC or Clang is in use, FIXSTR_GCC_VERSION might not fully match the actual GCC version on the system.
#define FIXSTR_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
// According to clang documentation, version can be vendor specific
#define FIXSTR_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
#if FIXSTR_GCC_VERSION >= 100'000 && FIXSTR_CPP_VERSION > 201703L
#define FIXSTR_CPP20_CNTTP_PRESENT 1
#elif __cpp_nontype_template_args >= 201911
#define FIXSTR_CPP20_CNTTP_PRESENT 1
#elif __cpp_nontype_template_parameter_class >= 201806
#define FIXSTR_CPP20_CNTTP_PRESENT 1
#else
// Other compilers do not support cNTTP just yet
#define FIXSTR_CPP20_CNTTP_PRESENT 0
#endif // FIXSTR_CPP20_CNTTP_PRESENT
namespace fixstr
{
namespace details
{
template <typename InputIterator, typename OutputIterator>
constexpr OutputIterator copy(InputIterator first, InputIterator last, OutputIterator d_first)
{
#if FIXSTR_CPP20_CONSTEXPR_ALGORITHMS_PRESENT
return std::copy(first, last, d_first);
#else
while (first != last)
{
*d_first++ = *first++;
}
return d_first;
#endif // FIXSTR_CPP20_CONSTEXPR_ALGORITHMS_PRESENT
}
template <typename ForwardIterator, typename T>
constexpr void fill(ForwardIterator first, ForwardIterator last, const T& value)
{
#if FIXSTR_CPP20_CONSTEXPR_ALGORITHMS_PRESENT
std::fill(first, last, value);
#else
for (; first != last; ++first)
{
*first = value;
}
#endif // FIXSTR_CPP20_CONSTEXPR_ALGORITHMS_PRESENT
}
} // namespace details
template <typename TChar, std::size_t N, typename TTraits = std::char_traits<TChar>>
struct basic_fixed_string // NOLINT(cppcoreguidelines-special-member-functions)
{
// exposition only
using storage_type = std::array<TChar, N + 1>;
storage_type _data{};
using traits_type = TTraits;
using value_type = TChar;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = typename storage_type::iterator;
using const_iterator = typename storage_type::const_iterator;
using reverse_iterator = typename storage_type::reverse_iterator;
using const_reverse_iterator = typename storage_type::const_reverse_iterator;
using size_type = size_t;
using difference_type = ptrdiff_t;
using string_view_type = std::basic_string_view<value_type, traits_type>;
static constexpr auto npos = string_view_type::npos;
constexpr basic_fixed_string() noexcept = default;
constexpr basic_fixed_string(const value_type (&array)[N + 1]) noexcept // NOLINT(google-explicit-constructor)
{
details::copy(std::begin(array), std::end(array), _data.begin());
}
constexpr basic_fixed_string& operator=(const value_type (&array)[N + 1]) noexcept
{
details::copy(std::begin(array), std::end(array), _data.begin());
return *this;
}
// iterators
[[nodiscard]] constexpr iterator begin() noexcept { return _data.begin(); }
[[nodiscard]] constexpr const_iterator begin() const noexcept { return _data.begin(); }
[[nodiscard]] constexpr iterator end() noexcept { return _data.end() - 1; }
[[nodiscard]] constexpr const_iterator end() const noexcept { return _data.end() - 1; }
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return _data.cbegin(); }
[[nodiscard]] constexpr const_iterator cend() const noexcept { return _data.cend() - 1; }
[[nodiscard]] constexpr reverse_iterator rbegin() noexcept { return _data.rbegin() + 1; }
[[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return _data.rbegin() + 1; }
[[nodiscard]] constexpr reverse_iterator rend() noexcept { return _data.rend(); }
[[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return _data.rend(); }
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return _data.crbegin() + 1; }
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return _data.crend(); }
private:
[[nodiscard]] constexpr static bool static_empty() noexcept { return N == 0; }
public:
// capacity
[[nodiscard]] constexpr size_type size() const noexcept { return N; }
[[nodiscard]] constexpr size_type length() const noexcept { return N; }
[[nodiscard]] constexpr size_type max_size() const noexcept { return N; }
[[nodiscard]] constexpr bool empty() const noexcept { return static_empty(); }
// element access
[[nodiscard]] constexpr reference operator[](size_type n) { return _data[n]; }
[[nodiscard]] constexpr const_reference operator[](size_type n) const { return _data[n]; }
[[nodiscard]] constexpr reference at(size_type n) { return _data.at(n); }
[[nodiscard]] constexpr const_reference at(size_type n) const { return _data.at(n); }
// The lack of C++20 concepts is disappointing
// Basically what every `template<...>` line means is `requires (!empty())`
template <typename..., bool NonEmpty = !static_empty(), typename = std::enable_if_t<NonEmpty>>
[[nodiscard]] constexpr reference front() noexcept
{
return _data.front();
}
template <typename..., bool NonEmpty = !static_empty(), typename = std::enable_if_t<NonEmpty>>
[[nodiscard]] constexpr const_reference front() const noexcept
{
return _data.front();
}
template <typename..., bool NonEmpty = !static_empty(), typename = std::enable_if_t<NonEmpty>>
[[nodiscard]] constexpr reference back() noexcept
{
return _data[size() - 1];
}
template <typename..., bool NonEmpty = !static_empty(), typename = std::enable_if_t<NonEmpty>>
[[nodiscard]] constexpr const_reference back() const noexcept
{
return _data[size() - 1];
}
[[nodiscard]] constexpr pointer data() noexcept { return _data.data(); }
[[nodiscard]] constexpr const_pointer data() const noexcept { return _data.data(); }
[[nodiscard]] constexpr const_pointer c_str() const noexcept { return data(); }
private:
template <size_t M>
using same_with_other_size = basic_fixed_string<value_type, M, traits_type>;
template <size_type pos, size_type count, size_type size>
constexpr static size_type calculate_substr_size()
{
if constexpr (pos >= size)
return 0;
constexpr size_type rcount = std::min(count, size - pos);
return rcount;
}
template <size_type pos, size_type count>
using substr_result_type = same_with_other_size<calculate_substr_size<pos, count, N>()>;
public:
// string operations
[[nodiscard]] constexpr operator string_view_type() const noexcept // NOLINT(google-explicit-constructor)
{
return {data(), N};
}
// clang-format off
template <size_type pos = 0, size_type count = npos,
typename..., bool IsPosInBounds = pos <= N, typename = std::enable_if_t<IsPosInBounds>>
[[nodiscard]] constexpr auto substr() const noexcept
-> substr_result_type<pos, count>
// clang-format on
{
substr_result_type<pos, count> result;
details::copy(begin() + pos, begin() + pos + result.size(), result.begin());
return result;
}
template <size_t M>
[[nodiscard]] constexpr size_type find(const same_with_other_size<M>& str, size_type pos = 0) const noexcept
{
if constexpr (M > N)
return npos;
return sv().find(str.sv(), pos);
}
[[nodiscard]] constexpr size_type find(string_view_type sv, size_type pos = 0) const noexcept { return sv().find(sv, pos); }
[[nodiscard]] constexpr size_type find(const value_type* s, size_type pos, size_type n) const { return sv().find(s, pos, n); }
[[nodiscard]] constexpr size_type find(const value_type* s, size_type pos = 0) const { return sv().find(s, pos); }
[[nodiscard]] constexpr size_type find(value_type c, size_type pos = 0) const noexcept { return sv().find(c, pos); }
template <size_t M>
[[nodiscard]] constexpr size_type rfind(const same_with_other_size<M>& str, size_type pos = npos) const noexcept
{
if constexpr (M > N)
return npos;
return sv().rfind(str.sv(), pos);
}
[[nodiscard]] constexpr size_type rfind(string_view_type sv, size_type pos = npos) const noexcept { return sv().rfind(sv, pos); }
[[nodiscard]] constexpr size_type rfind(const value_type* s, size_type pos, size_type n) const { return sv().rfind(s, pos, n); }
[[nodiscard]] constexpr size_type rfind(const value_type* s, size_type pos = npos) const { return sv().rfind(s, pos); }
[[nodiscard]] constexpr size_type rfind(value_type c, size_type pos = npos) const noexcept { return sv().rfind(c, pos); }
template <size_t M>
[[nodiscard]] constexpr size_type find_first_of(const same_with_other_size<M>& str, size_type pos = 0) const noexcept
{
if constexpr (M > N)
return npos;
return sv().find_first_of(str.sv(), pos);
}
[[nodiscard]] constexpr size_type find_first_of(string_view_type sv, size_type pos = 0) const noexcept { return sv().find_first_of(sv, pos); }
[[nodiscard]] constexpr size_type find_first_of(const value_type* s, size_type pos, size_type n) const { return sv().find_first_of(s, pos, n); }
[[nodiscard]] constexpr size_type find_first_of(const value_type* s, size_type pos = 0) const { return sv().find_first_of(s, pos); }
[[nodiscard]] constexpr size_type find_first_of(value_type c, size_type pos = 0) const noexcept { return sv().find_first_of(c, pos); }
template <size_t M>
[[nodiscard]] constexpr size_type find_last_of(const same_with_other_size<M>& str, size_type pos = npos) const noexcept
{
if constexpr (M > N)
return npos;
return sv().find_last_of(str.sv(), pos);
}
[[nodiscard]] constexpr size_type find_last_of(string_view_type sv, size_type pos = npos) const noexcept { return sv().find_last_of(sv, pos); }
[[nodiscard]] constexpr size_type find_last_of(const value_type* s, size_type pos, size_type n) const { return sv().find_last_of(s, pos, n); }
[[nodiscard]] constexpr size_type find_last_of(const value_type* s, size_type pos = npos) const { return sv().find_last_of(s, pos); }
[[nodiscard]] constexpr size_type find_last_of(value_type c, size_type pos = npos) const noexcept { return sv().find_last_of(c, pos); }
template <size_t M>
[[nodiscard]] constexpr size_type find_first_not_of(const same_with_other_size<M>& str, size_type pos = 0) const noexcept
{
if constexpr (M > N)
return npos;
return sv().find_first_of(str.sv(), pos);
}
[[nodiscard]] constexpr size_type find_first_not_of(string_view_type sv, size_type pos = 0) const noexcept { return sv().find_first_not_of(sv, pos); }
[[nodiscard]] constexpr size_type find_first_not_of(const value_type* s, size_type pos, size_type n) const { return sv().find_first_not_of(s, pos, n); }
[[nodiscard]] constexpr size_type find_first_not_of(const value_type* s, size_type pos = 0) const { return sv().find_first_not_of(s, pos); }
[[nodiscard]] constexpr size_type find_first_not_of(value_type c, size_type pos = 0) const noexcept { return sv().find_first_not_of(c, pos); }
template <size_t M>
[[nodiscard]] constexpr size_type find_last_not_of(const same_with_other_size<M>& str, size_type pos = npos) const noexcept
{
if constexpr (M > N)
return npos;
return sv().find_last_of(str.sv(), pos);
}
[[nodiscard]] constexpr size_type find_last_not_of(string_view_type sv, size_type pos = npos) const noexcept { return sv().find_last_not_of(sv, pos); }
[[nodiscard]] constexpr size_type find_last_not_of(const value_type* s, size_type pos, size_type n) const { return sv().find_last_not_of(s, pos, n); }
[[nodiscard]] constexpr size_type find_last_not_of(const value_type* s, size_type pos = npos) const { return sv().find_last_not_of(s, pos); }
[[nodiscard]] constexpr size_type find_last_not_of(value_type c, size_type pos = npos) const noexcept { return sv().find_last_not_of(c, pos); }
[[nodiscard]] constexpr int compare(string_view_type v) const noexcept { return sv().compare(v); }
[[nodiscard]] constexpr int compare(size_type pos1, size_type count1, string_view_type v) const { return sv().compare(pos1, count1, v); }
[[nodiscard]] constexpr int compare(size_type pos1, size_type count1, string_view_type v, size_type pos2, size_type count2) const
{
return sv().compare(pos1, count1, v, pos2, count2);
}
[[nodiscard]] constexpr int compare(const value_type* s) const { return sv().compare(s); }
[[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const value_type* s) const { return sv().compare(pos1, count1, s); }
[[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const value_type* s, size_type count2) const
{
return sv().compare(pos1, count1, s, count2);
}
[[nodiscard]] constexpr bool starts_with(string_view_type v) const noexcept { return sv().substr(0, v.size()) == v; }
[[nodiscard]] constexpr bool starts_with(char c) const noexcept { return !empty() && traits_type::eq(front(), c); }
[[nodiscard]] constexpr bool starts_with(const value_type* s) const noexcept { return starts_with(string_view_type(s)); }
[[nodiscard]] constexpr bool ends_with(string_view_type sv) const noexcept { return size() >= sv.size() && compare(size() - sv.size(), npos, sv) == 0; }
[[nodiscard]] constexpr bool ends_with(value_type c) const noexcept { return !empty() && traits_type::eq(back(), c); }
[[nodiscard]] constexpr bool ends_with(const value_type* s) const { return ends_with(string_view_type(s)); }
[[nodiscard]] constexpr bool contains(string_view_type sv) const noexcept { return find(sv) != npos; }
[[nodiscard]] constexpr bool contains(value_type c) const noexcept { return find(c) != npos; }
[[nodiscard]] constexpr bool contains(const value_type* s) const { return find(s) != npos; }
void swap(basic_fixed_string& other) noexcept(std::is_nothrow_swappable_v<storage_type>) { _data.swap(other._data); }
private:
constexpr string_view_type sv() const { return *this; }
};
template <typename TChar, typename TTraits, size_t N>
void swap(basic_fixed_string<TChar, N, TTraits>& lhs, basic_fixed_string<TChar, N, TTraits>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <typename TChar, typename TTraits, size_t M1, size_t M2>
[[nodiscard]] constexpr bool operator==(const basic_fixed_string<TChar, M1, TTraits>& lhs, const basic_fixed_string<TChar, M2, TTraits>& rhs)
{
if constexpr (M1 != M2)
return false;
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) == rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator==(const basic_fixed_string<TChar, N, TTraits>& lhs, std::basic_string_view<TChar, TTraits> rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) == rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator==(std::basic_string_view<TChar, TTraits> lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
using rhs_type = std::decay_t<decltype(rhs)>;
using sv_type = typename rhs_type::string_view_type;
return lhs == static_cast<sv_type>(rhs);
}
#if FIXSTR_CPP20_SPACESHIP_OPERATOR_PRESENT
template <typename TChar, typename TTraits, size_t M1, size_t M2>
[[nodiscard]] constexpr auto operator<=>(const basic_fixed_string<TChar, M1, TTraits>& lhs, const basic_fixed_string<TChar, M2, TTraits>& rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) <=> rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr auto operator<=>(const basic_fixed_string<TChar, N, TTraits>& lhs, std::basic_string_view<TChar, TTraits> rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) <=> rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr auto operator<=>(std::basic_string_view<TChar, TTraits> lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
using rhs_type = std::decay_t<decltype(rhs)>;
using sv_type = typename rhs_type::string_view_type;
return lhs <=> static_cast<sv_type>(rhs);
}
#else
template <typename TChar, typename TTraits, size_t M1, size_t M2>
[[nodiscard]] constexpr bool operator!=(const basic_fixed_string<TChar, M1, TTraits>& lhs, const basic_fixed_string<TChar, M2, TTraits>& rhs)
{
if constexpr (M1 != M2)
return true;
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) != rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator!=(const basic_fixed_string<TChar, N, TTraits>& lhs, std::basic_string_view<TChar, TTraits> rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) != rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator!=(std::basic_string_view<TChar, TTraits> lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
using rhs_type = std::decay_t<decltype(rhs)>;
using sv_type = typename rhs_type::string_view_type;
return lhs != static_cast<sv_type>(rhs);
}
template <typename TChar, typename TTraits, size_t M1, size_t M2>
[[nodiscard]] constexpr bool operator<(const basic_fixed_string<TChar, M1, TTraits>& lhs, const basic_fixed_string<TChar, M2, TTraits>& rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) < rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator<(const basic_fixed_string<TChar, N, TTraits>& lhs, std::basic_string_view<TChar, TTraits> rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) < rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator<(std::basic_string_view<TChar, TTraits> lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
using rhs_type = std::decay_t<decltype(rhs)>;
using sv_type = typename rhs_type::string_view_type;
return lhs < static_cast<sv_type>(rhs);
}
template <typename TChar, typename TTraits, size_t M1, size_t M2>
[[nodiscard]] constexpr bool operator<=(const basic_fixed_string<TChar, M1, TTraits>& lhs, const basic_fixed_string<TChar, M2, TTraits>& rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) <= rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator<=(const basic_fixed_string<TChar, N, TTraits>& lhs, std::basic_string_view<TChar, TTraits> rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) <= rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator<=(std::basic_string_view<TChar, TTraits> lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
using rhs_type = std::decay_t<decltype(rhs)>;
using sv_type = typename rhs_type::string_view_type;
return lhs <= static_cast<sv_type>(rhs);
}
template <typename TChar, typename TTraits, size_t M1, size_t M2>
[[nodiscard]] constexpr bool operator>(const basic_fixed_string<TChar, M1, TTraits>& lhs, const basic_fixed_string<TChar, M2, TTraits>& rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) > rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator>(const basic_fixed_string<TChar, N, TTraits>& lhs, std::basic_string_view<TChar, TTraits> rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) > rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator>(std::basic_string_view<TChar, TTraits> lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
using rhs_type = std::decay_t<decltype(rhs)>;
using sv_type = typename rhs_type::string_view_type;
return lhs > static_cast<sv_type>(rhs);
}
template <typename TChar, typename TTraits, size_t M1, size_t M2>
[[nodiscard]] constexpr bool operator>=(const basic_fixed_string<TChar, M1, TTraits>& lhs, const basic_fixed_string<TChar, M2, TTraits>& rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) >= rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator>=(const basic_fixed_string<TChar, N, TTraits>& lhs, std::basic_string_view<TChar, TTraits> rhs)
{
using lhs_type = std::decay_t<decltype(lhs)>;
using sv_type = typename lhs_type::string_view_type;
return static_cast<sv_type>(lhs) >= rhs;
}
template <typename TChar, typename TTraits, size_t N>
[[nodiscard]] constexpr bool operator>=(std::basic_string_view<TChar, TTraits> lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
using rhs_type = std::decay_t<decltype(rhs)>;
using sv_type = typename rhs_type::string_view_type;
return lhs >= static_cast<sv_type>(rhs);
}
#endif // FIXSTR_CPP20_SPACESHIP_OPERATOR_PRESENT
template <typename TChar, size_t N>
basic_fixed_string(const TChar (&)[N]) -> basic_fixed_string<TChar, N - 1>;
// Early GCC versions that support cNTTP were not able to deduce size_t parameter
// of basic_fixed_string when fixed_string and other typedef were just type aliases.
// That's why the following code is written in this way.
template <size_t N>
struct fixed_string : basic_fixed_string<char, N>
{
using basic_fixed_string<char, N>::basic_fixed_string;
};
template <std::size_t N>
fixed_string(const char (&)[N]) -> fixed_string<N - 1>;
#if FIXSTR_CPP20_CHAR8T_PRESENT
template <size_t N>
struct fixed_u8string : basic_fixed_string<char8_t, N>
{
using basic_fixed_string<char8_t, N>::basic_fixed_string;
};
template <std::size_t N>
fixed_u8string(const char8_t (&)[N]) -> fixed_u8string<N - 1>;
#endif // FIXSTR_CPP20_CHAR8T_PRESENT
template <size_t N>
struct fixed_u16string : basic_fixed_string<char16_t, N>
{
using basic_fixed_string<char16_t, N>::basic_fixed_string;
};
template <std::size_t N>
fixed_u16string(const char16_t (&)[N]) -> fixed_u16string<N - 1>;
template <size_t N>
struct fixed_u32string : basic_fixed_string<char32_t, N>
{
using basic_fixed_string<char32_t, N>::basic_fixed_string;
};
template <std::size_t N>
fixed_u32string(const char32_t (&)[N]) -> fixed_u32string<N - 1>;
template <size_t N>
struct fixed_wstring : basic_fixed_string<wchar_t, N>
{
using basic_fixed_string<wchar_t, N>::basic_fixed_string;
};
template <std::size_t N>
fixed_wstring(const wchar_t (&)[N]) -> fixed_wstring<N - 1>;
template <typename TChar, size_t N, size_t M, typename TTraits>
constexpr basic_fixed_string<TChar, N + M, TTraits> operator+(const basic_fixed_string<TChar, N, TTraits>& lhs, const basic_fixed_string<TChar, M, TTraits>& rhs)
{
basic_fixed_string<TChar, N + M, TTraits> result;
details::copy(lhs.begin(), lhs.end(), result.begin());
details::copy(rhs.begin(), rhs.end(), result.begin() + N);
return result;
}
template <typename TChar, size_t N, size_t M, typename TTraits>
constexpr basic_fixed_string<TChar, N - 1 + M, TTraits> operator+(const TChar (&lhs)[N], const basic_fixed_string<TChar, M, TTraits>& rhs)
{
basic_fixed_string lhs2 = lhs;
return lhs2 + rhs;
}
template <typename TChar, size_t N, size_t M, typename TTraits>
constexpr basic_fixed_string<TChar, N + M - 1, TTraits> operator+(const basic_fixed_string<TChar, N, TTraits>& lhs, const TChar (&rhs)[M])
{
basic_fixed_string rhs2 = rhs;
return lhs + rhs2;
}
namespace details
{
template <typename TChar>
constexpr basic_fixed_string<TChar, 1> from_char(TChar ch)
{
basic_fixed_string<TChar, 1> fs;
fs[0] = ch;
return fs;
}
} // namespace details
template <typename TChar, size_t N, typename TTraits>
constexpr basic_fixed_string<TChar, N + 1, TTraits> operator+(TChar lhs, const basic_fixed_string<TChar, N, TTraits>& rhs)
{
return details::from_char(lhs) + rhs;
}
template <typename TChar, size_t N, typename TTraits>
constexpr basic_fixed_string<TChar, N + 1, TTraits> operator+(const basic_fixed_string<TChar, N, TTraits>& lhs, TChar rhs)
{
return lhs + details::from_char(rhs);
}
template <typename TChar, size_t N, typename TTraits>
std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const basic_fixed_string<TChar, N, TTraits>& str)
{
out << str.data();
return out;
}
} // namespace fixstr
// hash support
namespace std
{
template <size_t N>
struct hash<fixstr::fixed_string<N>>
{
using argument_type = fixstr::fixed_string<N>;
size_t operator()(const argument_type& str) const
{
using sv_t = typename argument_type::string_view_type;
return std::hash<sv_t>()(static_cast<sv_t>(str));
}
};
#if FIXSTR_CPP20_CHAR8T_PRESENT
template <size_t N>
struct hash<fixstr::fixed_u8string<N>>
{
using argument_type = fixstr::fixed_u8string<N>;
size_t operator()(const argument_type& str) const
{
using sv_t = typename argument_type::string_view_type;
return std::hash<sv_t>()(static_cast<sv_t>(str));
}
};
#endif // FIXSTR_CPP20_CHAR8T_PRESENT
template <size_t N>
struct hash<fixstr::fixed_u16string<N>>
{
using argument_type = fixstr::fixed_u16string<N>;
size_t operator()(const argument_type& str) const
{
using sv_t = typename argument_type::string_view_type;
return std::hash<sv_t>()(static_cast<sv_t>(str));
}
};
template <size_t N>
struct hash<fixstr::fixed_u32string<N>>
{
using argument_type = fixstr::fixed_u32string<N>;
size_t operator()(const argument_type& str) const
{
using sv_t = typename argument_type::string_view_type;
return std::hash<sv_t>()(static_cast<sv_t>(str));
}
};
template <size_t N>
struct hash<fixstr::fixed_wstring<N>>
{
using argument_type = fixstr::fixed_wstring<N>;
size_t operator()(const argument_type& str) const
{
using sv_t = typename argument_type::string_view_type;
return std::hash<sv_t>()(static_cast<sv_t>(str));
}
};
} // namespace std
#endif // FIXED_STRING_HPP
Binary file not shown.
+935
View File
@@ -0,0 +1,935 @@
#include "memscan.h"
#include <memory.h> // memcmp
#include <string.h> // strlen
/* Types */
typedef struct {
/* example: {0x55, 0x8B, 0xEC} */
const ms_ubyte_t* m_data;
/* the former will result in '3' */
ms_usize_t m_size;
/* the n-th instance of a pattern within range, 0 - first */
ms_usize_t m_match;
} ms_bytes_t;
typedef struct {
/* "55 8B EC" => ms_bytes_t({0x55, 0x8B, 0xEC}, 3, match)
* must be null terminated */
const char* m_data;
/* the n-th instance of a pattern within range, 0 - first */
ms_usize_t m_match;
} ms_bytes_str_t;
typedef struct {
/* example: {0x8B, 0x0D} */
const ms_ubyte_t* m_data;
/* the former will result in '3' */
ms_usize_t m_size;
/* the n-th instance of a match within range, 0 - first */
ms_usize_t m_match;
ms_follow_direction_t m_direction;
} ms_find_bytes_t;
typedef struct {
/* "8B 0C" => ms_find_bytes_t({0x8B, 0x0C}, 2, match, direction)
* must be null terminated */
const char* m_data;
/* the n-th instance of a match within range, 0 - first */
ms_usize_t m_match;
ms_follow_direction_t m_direction;
} ms_find_str_t;
typedef struct {
/* numerical form of pointer to look for */
ms_uptr_t m_pointer;
/* the n-th instance of a match within range, 0 - first */
ms_usize_t m_match;
bool m_swap_endianness;
} ms_xref_t;
typedef struct {
/* the string to look for */
const char* m_data;
/* string size */
ms_usize_t m_size;
/* the n-th instance of the reversed endianness address to the first
* character of the string, 0 - first */
ms_usize_t m_match;
} ms_string_xref_t;
typedef enum {
/* won't be reached if MEMSCAN_UNSAFE_OPTIMIZATIONS are on */
MS_FOLLOW_STATUS_NO_VALID_FOLLOW_INFO = 0,
/* */
/* overbounds on start */
MS_FOLLOW_STATUS_FAIL_LHS,
/* overbounds on end */
MS_FOLLOW_STATUS_FAIL_RHS,
/* broke before reaching match */
MS_FOLLOW_STATUS_INCOMPLETE,
/* follow has succeeded, state was set to OK */
MS_FOLLOW_STATUS_OK
} ms_follow_status_t;
typedef struct {
ms_uptr_t m_address;
ms_follow_status_t m_status;
} ms_follow_t;
/* Methods */
/* Static */
static ms_follow_t
follow_until(const ms_uptr_t copy, const ms_uptr_t start, const ms_uptr_t end,
const ms_find_bytes_t* restrict find_bytes)
{
/* data */
ms_follow_t result = {0};
ms_uptr_t address = copy;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (find_bytes == NULL ||
(find_bytes != NULL &&
(find_bytes->m_data == NULL || find_bytes->m_size == 0))) {
result.m_address = 0;
result.m_status = MS_FOLLOW_STATUS_NO_VALID_FOLLOW_INFO;
goto return_label;
}
#endif
/* the following will run at least once, so you can follow regardless of
* current address. it will break upon a NULL address, or after the first
* overbounds byte, or upon a match in memory. */
ms_usize_t match = MEMSCAN_FIRST_MATCH;
int status;
retry_label:
status = memcmp((void*)address, find_bytes->m_data, find_bytes->m_size);
while ((void*)address != NULL && address >= (start - 1) &&
address <= (end + 1) && status != 0) {
address +=
find_bytes->m_direction == MS_FOLLOW_DIRECTION_BACKWARDS ? -1 : 1;
status = memcmp((void*)address, find_bytes->m_data, find_bytes->m_size);
}
result.m_address = address;
/* if the address is smaller than start, then it is overbounds on the lhs,
* otherwise, if it is smaller than end, it is overbounds no the rhs,
* otherwise, if status is != 0, it is OK, otherwise, it is an incomplete
* follow */
result.m_status = (result.m_address < start
? MS_FOLLOW_STATUS_FAIL_LHS
: (result.m_address > end
? MS_FOLLOW_STATUS_FAIL_RHS
: (status != 0 ? MS_FOLLOW_STATUS_INCOMPLETE
: MS_FOLLOW_STATUS_OK)));
/* verify match */
if (match != find_bytes->m_match &&
result.m_status == MS_FOLLOW_STATUS_OK) {
++match;
address +=
find_bytes->m_direction == MS_FOLLOW_DIRECTION_BACKWARDS ? -1 : 1;
goto retry_label;
}
return_label:
return result;
}
/* */
static ms_result_t
memscan_find_pattern_bb_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_t* restrict bytes,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (bytes == NULL || (bytes && bytes->m_data == NULL)) {
result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
goto return_label;
}
#endif
/* data */
/* this value is meant to be used to deduce how many actual bytes were input
* to the formation, scaling to every platform's sizeof(unsigned char) */
const ms_usize_t bytes_len = bytes->m_size / sizeof *bytes->m_data;
/* here, bytes->m_size is used because we're doing pointer arithmetic */
const ms_ubyte_t* finish = (ms_ubyte_t*)(end - bytes->m_size);
ms_usize_t match = MEMSCAN_FIRST_MATCH;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (start >= (ms_uptr_t)finish) {
result.m_status = MS_RESULT_NO_VALID_SPAN;
goto return_label;
}
#endif
/* loop over every byte from start to end */
for (ms_ubyte_t* current = (ms_ubyte_t*)start; current < finish;
++current) {
ms_result_status_t status = MS_RESULT_STATUS_FOUND;
/* run a scan from current byte to current byte + bytes_len to verify if
* we have a match */
for (ms_usize_t walker = 0; walker < bytes_len; ++walker) {
/* if the current byte at walker indice isn't equal to the current
* signature byte if it isn't our wildcard */
if (current[walker] != bytes->m_data[walker] &&
bytes->m_data[walker] != k_memscan_wildcard) {
/* declare the state as not found for the current iteration
* and go to the next one */
status = MS_RESULT_STATUS_NOT_FOUND;
break;
}
}
/* if the state wasn't altered (as in, the checks have passed and the
* current byte matches our signature) */
if (status == MS_RESULT_STATUS_FOUND) {
/* verify n-th match */
if (match != bytes->m_match) {
++match;
continue;
}
/* pass our address and status to result */
result.m_address = (ms_uptr_t)current;
result.m_status = status;
/* attempt a follow */
const ms_follow_t follow =
follow_until(result.m_address, start, end, find_bytes);
switch (follow.m_status) {
case MS_FOLLOW_STATUS_OK: {
/* change the result address to the follow result,
* started from the original one */
result.m_address = follow.m_address;
} break;
case MS_FOLLOW_STATUS_FAIL_LHS:
case MS_FOLLOW_STATUS_FAIL_RHS: {
/* this is prioritized over MS_FOLLOW_STATUS_INCOMPLETE */
/* deduce if it was either a left side overbounds
* failure or right side overbounds failure for the
* possibility of still allowing a result depending on
* user response */
result.m_status =
follow.m_status == MS_FOLLOW_STATUS_FAIL_LHS
? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
: MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_RHS;
} break;
case MS_FOLLOW_STATUS_INCOMPLETE: {
/* a condition to break the follow was met before there was
* a match in memory between the seeked byte-code and the
* byte-code at from it's current address for the seeked
* byte-code length */
result.m_status =
MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_INCOMPLETE;
} break;
}
goto return_label;
}
}
/* we haven't found an address for our signature, so we will set result
* state to NOT FOUND */
result.m_status = MS_RESULT_STATUS_NOT_FOUND;
return_label:
return result;
}
/* */
static ms_result_t
memscan_find_pattern_bs_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_t* restrict bytes,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_pattern_bb_impl(start, end, bytes, NULL);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = pattern.m_data;
find_bytes_from_str.m_size = pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
/* pass converted data to base implementation */
result = memscan_find_pattern_bb_impl(start, end, bytes,
&find_bytes_from_str);
util_free_pattern(&pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_pattern_sb_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_str_t* restrict bytes,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (bytes == NULL || (bytes && bytes->m_data == NULL))
result = memscan_find_pattern_bb_impl(start, end, NULL, find_bytes);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(bytes->m_data, strlen(bytes->m_data));
ms_bytes_t bytes_from_str = {0};
bytes_from_str.m_data = pattern.m_data;
bytes_from_str.m_size = pattern.m_size;
bytes_from_str.m_match = bytes->m_match;
/* pass converted data to base implementation */
result = memscan_find_pattern_bb_impl(start, end, &bytes_from_str,
find_bytes);
util_free_pattern(&pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_pattern_ss_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_str_t* restrict bytes,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_pattern_sb_impl(start, end, bytes, NULL);
else {
/* data */
ms_pattern_t find_pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = find_pattern.m_data;
find_bytes_from_str.m_size = find_pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
/* pass data to our string, bytes wrapper */
result = memscan_find_pattern_sb_impl(start, end, bytes,
&find_bytes_from_str);
util_free_pattern(&find_pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_pattern_nfb_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_t* restrict bytes)
{
/* pass data to our base implementation. the data being NULL
* will prevent a follow from happening */
return memscan_find_pattern_bb_impl(start, end, bytes, NULL);
}
/* */
static ms_result_t
memscan_find_pattern_nfs_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_str_t* restrict bytes)
{
/* pass data to our string, bytes wrapper. the data being NULL
* will prevent a follow from happening */
return memscan_find_pattern_sb_impl(start, end, bytes, NULL);
}
/* */
static ms_result_t
memscan_find_xref_b_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_xref_t* restrict xref,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (xref == NULL /* data is handled in the pattern finder */) {
result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
goto return_label;
}
#endif
/* data */
ms_bytes_t from_xref = {0};
from_xref.m_data =
util_ptr_to_byteset(xref->m_pointer, xref->m_swap_endianness);
from_xref.m_size = MEMSCAN_BYTESET_SIZE;
from_xref.m_match = xref->m_match;
/* pass data to our no follow, bytes wrapper */
result = memscan_find_pattern_bb_impl(start, end, &from_xref, find_bytes);
return_label:
return result;
}
/* */
static ms_result_t
memscan_find_xref_s_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_xref_t* restrict xref,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_xref_b_impl(start, end, xref, NULL);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = pattern.m_data;
find_bytes_from_str.m_size = pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
result =
memscan_find_xref_b_impl(start, end, xref, &find_bytes_from_str);
util_free_pattern(&pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_xref_nf_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_xref_t* restrict xref)
{
return memscan_find_xref_b_impl(start, end, xref, NULL);
}
/* */
static ms_result_t
memscan_find_string_b_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_string_xref_t* restrict string,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (string == NULL /* data is handled in the pattern finder */) {
result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
goto return_label;
}
#endif
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = (const ms_ubyte_t*)string->m_data;
bytes.m_size = string->m_size;
bytes.m_match = 0;
ms_result_t string_find = memscan_find_pattern_nfb_impl(start, end, &bytes);
if (string_find.m_status != MS_RESULT_STATUS_FOUND) {
result.m_status = string_find.m_status;
goto return_label;
}
ms_xref_t xref = {0};
xref.m_pointer = string_find.m_address;
xref.m_match = string->m_match;
xref.m_swap_endianness = true;
result = memscan_find_xref_b_impl(start, end, &xref, find_bytes);
return_label:
return result;
}
static ms_result_t
memscan_find_string_s_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_string_xref_t* restrict string,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_string_b_impl(start, end, string, NULL);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = pattern.m_data;
find_bytes_from_str.m_size = pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
result = memscan_find_string_b_impl(start, end, string,
&find_bytes_from_str);
util_free_pattern(&pattern);
}
return result;
}
static ms_result_t
memscan_find_string_nf_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_string_xref_t* restrict string)
{
return memscan_find_string_b_impl(start, end, string, NULL);
}
/* Prototype Bodies */
ms_result_t
memscan_find_pattern_bb(const ms_uptr_t start, const ms_uptr_t end,
const ms_ubyte_t* pattern,
const ms_usize_t pattern_size,
const ms_usize_t pattern_nth_match,
const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = pattern;
bytes.m_size = pattern_size;
bytes.m_match = pattern_nth_match;
ms_find_bytes_t find = {0};
find.m_data = follow_pattern;
find.m_size = follow_pattern_size;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_bb_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_bs(const ms_uptr_t start, const ms_uptr_t end,
const ms_ubyte_t* pattern,
const ms_usize_t pattern_size,
const ms_usize_t pattern_nth_match,
const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = pattern;
bytes.m_size = pattern_size;
bytes.m_match = pattern_nth_match;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_bs_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_sb(const ms_uptr_t start, const ms_uptr_t end,
const char* pattern, const ms_usize_t pattern_nth_match,
const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_str_t bytes = {0};
bytes.m_data = pattern;
bytes.m_match = pattern_nth_match;
ms_find_bytes_t find = {0};
find.m_data = follow_pattern;
find.m_size = follow_pattern_size;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_sb_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_ss(const ms_uptr_t start, const ms_uptr_t end,
const char* pattern, const ms_usize_t pattern_nth_match,
const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_str_t bytes = {0};
bytes.m_data = pattern;
bytes.m_match = pattern_nth_match;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_ss_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_nfb(const ms_uptr_t start, const ms_uptr_t end,
const ms_ubyte_t* pattern,
const ms_usize_t pattern_size,
const ms_usize_t pattern_nth_match)
{
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = pattern;
bytes.m_size = pattern_size;
bytes.m_match = pattern_nth_match;
/* pass structured data to manager */
return memscan_find_pattern_nfb_impl(start, end, &bytes);
}
/* */
ms_result_t
memscan_find_pattern_nfs(const ms_uptr_t start, const ms_uptr_t end,
const char* pattern,
const ms_usize_t pattern_nth_match)
{
/* data */
ms_bytes_str_t bytes = {0};
bytes.m_data = pattern;
bytes.m_match = pattern_nth_match;
/* pass structured data to manager */
return memscan_find_pattern_nfs_impl(start, end, &bytes);
}
/* */
ms_result_t
memscan_find_xref_b(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t content, const ms_usize_t content_nth_match,
bool swap_endianness, const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_xref_t xref = {0};
xref.m_pointer = content;
xref.m_match = content_nth_match;
xref.m_swap_endianness = swap_endianness;
ms_find_bytes_t find_bytes = {0};
find_bytes.m_data = follow_pattern;
find_bytes.m_size = follow_pattern_size;
find_bytes.m_match = follow_nth_match;
find_bytes.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_xref_b_impl(start, end, &xref, &find_bytes);
}
/* */
ms_result_t
memscan_find_xref_at_b(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t address, const ms_usize_t nth_match,
bool swap_endianness, const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
return memscan_find_xref_b(start, end, *(ms_uptr_t*)address, nth_match,
swap_endianness, follow_pattern,
follow_pattern_size, follow_nth_match,
follow_direction);
}
/* */
ms_result_t
memscan_find_xref_s(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t content, const ms_usize_t content_nth_match,
bool swap_endianness, const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_xref_t xref = {0};
xref.m_pointer = content;
xref.m_match = content_nth_match;
xref.m_swap_endianness = swap_endianness;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_xref_s_impl(start, end, &xref, &find);
}
/* */
ms_result_t
memscan_find_xref_at_s(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t address, const ms_usize_t nth_match,
bool swap_endianness, const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
return memscan_find_xref_s(start, end, *(ms_uptr_t*)address, nth_match,
swap_endianness, follow_pattern,
follow_nth_match, follow_direction);
}
/* */
ms_result_t
memscan_find_xref_nf(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t content,
const ms_usize_t content_nth_match, bool swap_endianness)
{
/* data */
ms_xref_t xref = {0};
xref.m_pointer = content;
xref.m_match = content_nth_match;
xref.m_swap_endianness = swap_endianness;
/* pass structured data to manager */
return memscan_find_xref_nf_impl(start, end, &xref);
}
/* */
ms_result_t
memscan_find_xref_at_nf(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t address, const ms_usize_t nth_match,
bool swap_endianness)
{
return memscan_find_xref_nf(start, end, *(ms_uptr_t*)address, nth_match,
swap_endianness);
}
/* */
extern ms_result_t
memscan_find_string_b(const ms_uptr_t start, const ms_uptr_t end,
const char* text, const ms_usize_t text_size,
const ms_usize_t nth_match,
const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_string_xref_t xref = {0};
xref.m_data = text;
xref.m_size = text_size;
xref.m_match = nth_match;
ms_find_bytes_t find_bytes = {0};
find_bytes.m_data = follow_pattern;
find_bytes.m_size = follow_pattern_size;
find_bytes.m_match = follow_nth_match;
find_bytes.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_string_b_impl(start, end, &xref, &find_bytes);
}
/* */
ms_result_t
memscan_find_string_s(const ms_uptr_t start, const ms_uptr_t end,
const char* text, const ms_usize_t text_size,
const ms_usize_t nth_match, const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_string_xref_t xref = {0};
xref.m_data = text;
xref.m_size = text_size;
xref.m_match = nth_match;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_string_s_impl(start, end, &xref, &find);
}
/* */
ms_result_t
memscan_find_string_nf(const ms_uptr_t start, const ms_uptr_t end,
const char* text, const ms_usize_t text_size,
const ms_usize_t nth_match)
{
/* data */
ms_string_xref_t xref = {0};
xref.m_data = text;
xref.m_size = text_size;
xref.m_match = nth_match;
/* pass structured data to manager */
return memscan_find_string_nf_impl(start, end, &xref);
}
File diff suppressed because it is too large Load Diff
+102
View File
@@ -0,0 +1,102 @@
#include "util.h"
#include <stdlib.h>
#include <string.h>
ms_pattern_t
util_build_pattern(const char* data, const ms_usize_t data_size)
{
ms_pattern_t result = {0};
result.m_data = NULL;
result.m_size = 0;
#if !UTIL_UNSAFE_OPTIMIZATIONS
if (data == NULL) {
result.m_status = MS_BUILD_STATUS_NO_DATA;
goto leave;
}
#endif
size_t len = data_size;
#if !UTIL_UNSAFE_OPTIMIZATIONS
if (len == 0) {
result.m_status = MS_BUILD_STATUS_SHORT_DATA;
goto leave;
}
#endif
/* data */
char* start = (char*)data;
char* end = (char*)(data + len);
/* precompute allocation size */
ms_usize_t size = 0;
for (char* current = start; current < end; ++current)
++size;
ms_ubyte_t* bytes = (ms_ubyte_t*)malloc(size * sizeof *bytes);
/* prefetched */
ms_usize_t indice = 0;
for (char* current = start; current < end; ++current) {
/* hex substring conversion */
bytes[indice++] = (ms_ubyte_t)strtoul(current, &current, 16);
}
result.m_data = bytes;
result.m_size = indice;
result.m_status = MS_BUILD_STATUS_OK;
leave:
return result;
}
/* */
ms_free_t
util_free_pattern(ms_pattern_t* pattern)
{
#if !UTIL_UNSAFE_OPTIMIZATIONS
if (pattern == NULL) {
return MS_FREE_NO;
}
#endif
ms_free_t result = MS_FREE_NO;
if (pattern->m_status == MS_BUILD_STATUS_OK) {
free(pattern->m_data);
pattern->m_data = NULL;
result = MS_FREE_YES;
}
pattern->m_size = 0;
return result;
}
ms_ubyte_t*
util_ptr_to_byteset(const ms_uptr_t num, bool swap_endianness)
{
/* data */
static ms_ubyte_t bytes[MEMSCAN_BYTESET_SIZE] = {0};
for (ms_usize_t i = 0; i < MEMSCAN_BYTESET_SIZE; ++i) {
/* shift formation to get current indice */
bytes[swap_endianness ? i : MEMSCAN_BYTESET_SIZE - i - 1] =
(ms_ubyte_t)(num >> (i * CHAR_BIT));
}
return bytes;
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
/* Includes */
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
/* Types */
typedef uint8_t ms_ubyte_t;
typedef uintptr_t ms_uptr_t;
typedef size_t ms_usize_t;
/* Extern */
#if __cplusplus
#define MEMSCAN_EXTERN extern "C"
#else
#define MEMSCAN_EXTERN extern
#endif
typedef enum {
/* nothing to free, or there's a condition preventing the process */
MS_FREE_NO = 0,
/* the data was found present and then freed */
MS_FREE_YES
} ms_free_t;
typedef enum {
/* won't be reached unless UTIL_UNSAFE_OPTIMIZATIONS is off */
/* passed data was NULL */
MS_BUILD_STATUS_NO_DATA = 0,
/* data len was 0 */
MS_BUILD_STATUS_SHORT_DATA,
/* */
/* generation has succeeded, status was set to OK */
MS_BUILD_STATUS_OK
} ms_build_status_t;
typedef struct {
ms_ubyte_t* m_data;
ms_usize_t m_size;
ms_build_status_t m_status;
} ms_pattern_t;
/* Methods */
/**
* @brief Generate byte code array from byte-code style string
*
* @param data Example: "AA BB CC DD EE FF", equivalent to
* (ms_ubyte_t*)"\xAA\xBB\xCC\xDD\xEE\xFF"
* @param data_size Size of 'data'
* @return Refer to ms_pattern_t for documentation
*/
MEMSCAN_EXTERN ms_pattern_t
util_build_pattern(const char* data, const ms_usize_t data_size);
/**
* @brief Deallocate pattern array after usage
*
* @param pattern Reference to the pattern construct
* @return Refer to ms_free_t for documentation
*/
MEMSCAN_EXTERN ms_free_t
util_free_pattern(ms_pattern_t* pattern);
/**
* @brief Convert pointer in numerical form to byteset of MEMSCAN_BYTESET_SIZE
* bytes
*
* @param num Value to convert
* @param swap_endianness Whether to swap endianness or not
* @return Value as a MEMSCAN_BYTESET_SIZE bytes array
*/
MEMSCAN_EXTERN ms_ubyte_t*
util_ptr_to_byteset(const ms_uptr_t num, bool swap_endianness);
/* Constants */
#define MEMSCAN_BYTESET_SIZE (sizeof(ms_uptr_t) / sizeof(ms_ubyte_t))
#define MEMSCAN_POINTER_BITS (sizeof(ms_uptr_t) * CHAR_BIT)
@@ -81,7 +81,7 @@
#if FMT_USE_CONSTEXPR
#define SPDLOG_CONSTEXPR_FUNC FMT_CONSTEXPR
#else
#define SPDLOG_CONSTEXPR_FUNC inline
#define SPDLOG_CONSTEXPR_FUNC inline
#endif
#endif
@@ -319,7 +319,7 @@ struct source_loc {
line{line_in},
funcname{funcname_in} {}
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line == 0; }
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line <= 0; }
const char *filename{nullptr};
int line{0};
const char *funcname{nullptr};
@@ -7,6 +7,8 @@
#include <cassert>
#include <vector>
#include "spdlog/common.h"
namespace spdlog {
namespace details {
template <typename T>
@@ -98,7 +98,7 @@ SPDLOG_INLINE void file_helper::close() {
}
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) {
if(fd_ == nullptr) return;
if (fd_ == nullptr) return;
size_t msg_size = buf.size();
auto data = buf.data();
if (std::fwrite(data, 1, msg_size, fd_) != msg_size) {
@@ -148,19 +148,19 @@ public:
#endif
size_t overrun_counter() {
std::unique_lock<std::mutex> lock(queue_mutex_);
std::lock_guard<std::mutex> lock(queue_mutex_);
return q_.overrun_counter();
}
size_t discard_counter() { return discard_counter_.load(std::memory_order_relaxed); }
size_t size() {
std::unique_lock<std::mutex> lock(queue_mutex_);
std::lock_guard<std::mutex> lock(queue_mutex_);
return q_.size();
}
void reset_overrun_counter() {
std::unique_lock<std::mutex> lock(queue_mutex_);
std::lock_guard<std::mutex> lock(queue_mutex_);
q_.reset_overrun_counter();
}
@@ -439,7 +439,7 @@ SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT {
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1) {
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 4 - 1) {
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
}
@@ -450,7 +450,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
}
int result_size = static_cast<int>(target.capacity());
if ((wstr_size + 1) * 2 > result_size) {
if ((wstr_size + 1) * 4 > result_size) {
result_size =
::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
}
@@ -483,12 +483,12 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
// find the size to allocate for the result buffer
int result_size =
::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, NULL, 0);
if (result_size > 0) {
target.resize(result_size);
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size,
target.data(), result_size);
result_size = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(),
result_size);
if (result_size > 0) {
assert(result_size == target.size());
return;
@@ -534,6 +534,15 @@ SPDLOG_INLINE bool create_dir(const filename_t &path) {
}
auto subdir = path.substr(0, token_pos);
#ifdef _WIN32
// if subdir is just a drive letter, add a slash e.g. "c:"=>"c:\",
// otherwise path_exists(subdir) returns false (issue #3079)
const bool is_drive = subdir.length() == 2 && subdir[1] == ':';
if (is_drive) {
subdir += '\\';
token_pos++;
}
#endif
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) {
return false; // return error if failed creating dir
@@ -99,10 +99,6 @@ SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get()
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
// remove previous default logger from the map
if (default_logger_ != nullptr) {
loggers_.erase(default_logger_->name());
}
if (new_default_logger != nullptr) {
loggers_[new_default_logger->name()] = new_default_logger;
}
@@ -42,8 +42,10 @@ public:
// another.
logger *get_default_raw();
// set default logger.
// set default logger and add it to the registry if not registered already.
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
// Note: Make sure to unregister it when no longer needed or before calling again with a new
// logger.
void set_default_logger(std::shared_ptr<logger> new_default_logger);
void set_tp(std::shared_ptr<thread_pool> tp);
@@ -69,8 +71,8 @@ public:
}
std::unique_ptr<periodic_worker> &get_flusher() {
std::lock_guard<std::mutex> lock(flusher_mutex_);
return periodic_flusher_;
std::lock_guard<std::mutex> lock(flusher_mutex_);
return periodic_flusher_;
}
void set_error_handler(err_handler handler);
@@ -1,4 +1,4 @@
// Formatting library for C++ - dynamic format arguments
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
@@ -8,11 +8,13 @@
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#ifndef FMT_MODULE
# include <functional> // std::reference_wrapper
# include <memory> // std::unique_ptr
# include <vector>
#endif
#include "core.h"
#include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE
@@ -22,20 +24,24 @@ template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
template <typename T> auto unwrap(const T& v) -> const T& { return v; }
template <typename T>
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(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 <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> 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 <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
class dynamic_arg_list {
template <typename T> struct typed_node : node<> {
T value;
@@ -50,7 +56,7 @@ class dynamic_arg_list {
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
@@ -61,14 +67,10 @@ 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 <typename Context>
class dynamic_format_arg_store
@@ -110,14 +112,14 @@ class dynamic_format_arg_store
friend class basic_format_args<Context>;
unsigned long long get_types() const {
auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
const basic_format_arg<Context>* data() const {
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
@@ -146,22 +148,20 @@ class dynamic_format_arg_store
constexpr dynamic_format_arg_store() = default;
/**
\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<fmt::format_context> 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<fmt::format_context> store;
* store.push_back(42);
* store.push_back("abc");
* store.push_back(1.5f);
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
@@ -170,20 +170,18 @@ class dynamic_format_arg_store
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> 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<fmt::format_context> 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 <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
@@ -192,10 +190,10 @@ 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 <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
@@ -208,19 +206,15 @@ class dynamic_format_arg_store
}
}
/** 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();
}
/**
\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");
File diff suppressed because it is too large Load Diff
@@ -11,7 +11,7 @@
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
@@ -203,7 +203,7 @@ struct rgb {
uint8_t b;
};
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@@ -225,22 +225,21 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace detail
FMT_END_DETAIL_NAMESPACE
/** 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 {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), set_background_color(), ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
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"));
report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
@@ -249,7 +248,7 @@ class text_style {
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"));
report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
@@ -258,29 +257,29 @@ class text_style {
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
-> text_style {
return lhs |= rhs;
}
FMT_CONSTEXPR bool has_foreground() const noexcept {
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const noexcept {
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const noexcept {
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
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;
}
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;
}
@@ -298,9 +297,11 @@ class text_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;
@@ -309,21 +310,24 @@ class text_style {
emphasis ems;
};
/** Creates a text style from the foreground (text) color. */
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
/// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
/// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return text_style(false, background);
}
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;
}
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@@ -385,9 +389,9 @@ template <typename Char> struct ansi_color_escape {
}
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<Char>::length(buffer);
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
}
private:
@@ -401,62 +405,45 @@ template <typename Char> struct ansi_color_escape {
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(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<uint8_t>(em) & static_cast<uint8_t>(mask);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) noexcept {
FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) noexcept {
FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em);
}
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
int result = std::fputs(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
int result = std::fputws(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg {
template <typename T> struct styled_arg : detail::view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
void vformat_to(
buffer<Char>& buf, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffered_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
@@ -477,118 +464,94 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
if (has_style) detail::reset_color<Char>(buf);
}
FMT_END_DETAIL_NAMESPACE
} // namespace detail
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, detail::to_string_view(format), args);
if (detail::is_utf8()) {
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
} else {
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
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 <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::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<buffer_context<char_t<S>>>(args...));
template <typename... T>
void print(FILE* f, const text_style& ts, format_string<T...> fmt,
T&&... args) {
vprint(f, ts, fmt, fmt::make_format_args(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 <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
template <typename... T>
void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
inline auto vformat(const 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 <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> 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<buffer_context<Char>>(args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
* Formats arguments and returns the result as a string using ANSI escape
* sequences to specify text formatting.
*
* **Example**:
*
* ```
* #include <fmt/color.h>
* std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
* "The answer is {}", 42);
* ```
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
template <typename... T>
inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
-> std::string {
return fmt::vformat(ts, fmt, fmt::make_format_args(args...));
}
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
format_args args) -> OutputIt {
auto&& buf = detail::get_buffer<char>(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<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(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<char> out;
* fmt::format_to(std::back_inserter(out),
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, const text_style& ts,
format_string<T...> fmt, T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt, fmt::make_format_args(args...));
}
template <typename T, typename Char>
@@ -628,16 +591,14 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
};
/**
\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 <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
@@ -645,7 +606,7 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts)
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
}
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_
@@ -8,117 +8,41 @@
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
FMT_EXPORT class compiled_string {};
namespace detail {
template <typename T, typename InputIt>
FMT_CONSTEXPR inline auto copy(InputIt begin, InputIt end, counting_iterator it)
-> counting_iterator {
return it + (end - begin);
}
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\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, explicit)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
@@ -135,7 +59,7 @@ struct udl_compiled_string : compiled_string {
#endif
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
auto first(const T& value, const Tail&...) -> const T& {
return value;
}
@@ -196,7 +120,8 @@ template <typename Char> struct code_unit {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
*out++ = value;
return out;
}
};
@@ -220,7 +145,12 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
return write<Char>(out, get_arg_checked<T, N>(args...));
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
}
return write<Char>(out, arg);
}
};
@@ -308,13 +238,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
@@ -331,14 +260,14 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id;
};
constexpr int manual_indexing_id = -1;
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
str.remove_prefix(pos);
auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
next_arg_id);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
@@ -348,22 +277,18 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id;
constexpr int operator()() {
constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int operator()(int id) {
constexpr int on_index(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
constexpr int on_name(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
};
template <typename Char> struct parse_arg_id_result {
@@ -389,14 +314,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
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<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
} else {
@@ -409,7 +333,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
fmt);
}
}
}
@@ -417,22 +341,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 <typename Args, size_t POS, int ID, typename S>
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<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(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<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(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<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(
format_str);
POS + 1, ID, next_id>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@@ -448,60 +371,55 @@ constexpr auto compile_format_string(S format_str) {
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
fmt);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) {
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<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
return result;
}
}
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
@@ -566,33 +484,33 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) {
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
format_str, std::forward<Args>(args)...);
return {it.base(), it.count()};
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::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();
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
return fmt::format_to(detail::counting_iterator(), fmt, args...).count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) {
void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), format_str, args...);
fmt::format_to(std::back_inserter(buffer), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
@@ -605,7 +523,7 @@ template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_
+5
View File
@@ -0,0 +1,5 @@
// 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.
#include "format.h"
@@ -8,21 +8,19 @@
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
#include <algorithm>
#include <cctype>
#include <cerrno> // errno
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstring> // std::memmove
#include <cwchar>
#include <exception>
#ifndef FMT_MODULE
# include <algorithm>
# include <cerrno> // errno
# include <climits>
# include <cmath>
# include <exception>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
# endif
#endif
#ifdef _WIN32
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
# include <io.h> // _isatty
#endif
@@ -40,10 +38,6 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
std::terminate();
}
FMT_FUNC void throw_format_error(const char* message) {
FMT_THROW(format_error(message));
}
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept {
// Report error code making sure that the output fits into
@@ -60,10 +54,10 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
++error_code_size;
}
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = buffer_appender<char>(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, "");
}
@@ -77,9 +71,8 @@ FMT_FUNC void report_error(format_func func, int error_code,
}
// 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_fully(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")));
}
@@ -90,7 +83,7 @@ locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
template <typename Locale> Locale locale_ref::get() const {
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, std::locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
}
@@ -102,7 +95,8 @@ FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep};
}
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
template <typename Char>
FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point();
}
@@ -115,96 +109,81 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
return '.';
}
#endif
FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#ifdef FMT_STATIC_THOUSANDS_SEPARATOR
value.visit(loc_writer<>{
out, specs, std::string(1, FMT_STATIC_THOUSANDS_SEPARATOR), "\3", "."});
return true;
#else
auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding.
using facet = format_facet<std::locale>;
if (std::has_facet<facet>(locale))
return std::use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs);
#endif
}
} // namespace detail
#if !FMT_MSC_VERSION
FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
FMT_FUNC void report_error(const char* message) {
FMT_THROW(format_error(message));
}
template <typename Locale> typename Locale::id format_facet<Locale>::id;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
grouping_ = numpunct.grouping();
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
}
template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
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 format_str,
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(format_str, args));
return std::system_error(ec, vformat(fmt, args));
}
namespace detail {
template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
template <typename F>
inline auto operator==(basic_fp<F> x, basic_fp<F> 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_CONSTEXPR 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_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63;
return (n >> r) | (n << (64 - r));
}
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
#elif defined(_MSC_VER) && defined(_M_X64)
auto result = uint128_fallback();
result.lo_ = _umul128(x, y, &result.hi_);
return result;
#else
const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
uint64_t a = x >> 32;
uint64_t b = x & mask;
uint64_t c = y >> 32;
uint64_t d = y & mask;
uint64_t ac = a * c;
uint64_t bc = b * c;
uint64_t ad = a * d;
uint64_t bd = b * d;
uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
(intermediate << 32) + (bd & mask)};
#endif
}
// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
namespace dragonbox {
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return static_cast<uint64_t>(p >> 64);
#elif defined(_MSC_VER) && defined(_M_X64)
return __umulh(x, y);
#else
return umul128(x, y).high();
#endif
}
// 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 {
uint128_fallback r = umul128(x, y.high());
r += umul128_upper64(x, y.low());
return r;
}
// 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<uint64_t>(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()};
@@ -212,29 +191,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;
}
// 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 {
FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
return (e * 315653) >> 20;
}
// Various fast log computations.
inline int floor_log2_pow10(int e) noexcept {
FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
return (e * 1741647) >> 19;
}
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;
}
static constexpr struct {
FMT_INLINE_VARIABLE constexpr struct {
uint32_t divisor;
int shift_amount;
} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
@@ -243,7 +210,7 @@ static constexpr struct {
// divisible by pow(10, N).
// Precondition: n <= pow(10, N + 1).
template <int N>
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,
@@ -268,7 +235,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 <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
template <int N> 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 =
@@ -277,24 +244,24 @@ template <int N> 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<uint32_t>((static_cast<uint64_t>(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;
}
// Various subroutines using pow10 cache
template <class T> struct cache_accessor;
template <typename T> struct cache_accessor;
template <> struct cache_accessor<float> {
using carrier_uint = float_info<float>::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<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
@@ -336,20 +303,23 @@ template <> struct cache_accessor<float> {
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<carrier_uint>(r >> 32),
static_cast<carrier_uint>(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<uint32_t>(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, "");
@@ -358,22 +328,22 @@ template <> struct cache_accessor<float> {
static_cast<uint32_t>(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<carrier_uint>(
(cache - (cache >> (num_significand_bits<float>() + 2))) >>
(64 - num_significand_bits<float>() - 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<carrier_uint>(
(cache + (cache >> (num_significand_bits<float>() + 1))) >>
(64 - num_significand_bits<float>() - 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<carrier_uint>(
cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
1) /
@@ -385,7 +355,7 @@ template <> struct cache_accessor<double> {
using carrier_uint = float_info<double>::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<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
@@ -1009,8 +979,22 @@ template <> struct cache_accessor<double> {
{0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
{0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
{0xc5a05277621be293, 0xc7098b7305241886},
{ 0xf70867153aa2db38,
0xb8cbee4fc66d1ea8 }
{0xf70867153aa2db38, 0xb8cbee4fc66d1ea8},
{0x9a65406d44a5c903, 0x737f74f1dc043329},
{0xc0fe908895cf3b44, 0x505f522e53053ff3},
{0xf13e34aabb430a15, 0x647726b9e7c68ff0},
{0x96c6e0eab509e64d, 0x5eca783430dc19f6},
{0xbc789925624c5fe0, 0xb67d16413d132073},
{0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890},
{0x933e37a534cbaae7, 0x8e91b962f7b6f15a},
{0xb80dc58e81fe95a1, 0x723627bbb5a4adb1},
{0xe61136f2227e3b09, 0xcec3b1aaa30dd91d},
{0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2},
{0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e},
{0xe0accfa875af45a7, 0x93eb1b80a33b8606},
{0x8c6c01c9498d8b88, 0xbc72f130660533c4},
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
{0xdb68c2ca82ed2a05, 0xa67398db9f6820e2},
#else
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@@ -1034,8 +1018,8 @@ template <> struct cache_accessor<double> {
{0x8da471a9de737e24, 0x5ceaecfed289e5d3},
{0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
{0xb8da1662e7b00a17, 0x3d6a751f3b936244},
{ 0x95527a5202df0ccb,
0x0f37801e0c43ebc9 }
{0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
{0xf13e34aabb430a15, 0x647726b9e7c68ff0}
#endif
};
@@ -1095,19 +1079,22 @@ template <> struct cache_accessor<double> {
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(cache_entry_type const& cache, int beta) noexcept
-> uint32_t {
return static_cast<uint32_t>(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, "");
@@ -1116,31 +1103,35 @@ template <> struct cache_accessor<double> {
((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<double>() + 2))) >>
(64 - num_significand_bits<double>() - 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<double>() + 1))) >>
(64 - num_significand_bits<double>() - 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<double>() - 2 - beta)) +
1) /
2;
}
};
FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback {
return cache_accessor<double>::get_cached_power(k);
}
// Various integer checks
template <class T>
bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
template <typename T>
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 &&
@@ -1148,12 +1139,12 @@ bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
}
// Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
FMT_ASSERT(n != 0, "");
const uint32_t mod_inv_5 = 0xcccccccd;
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
// 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
int s = 0;
while (true) {
auto q = rotr(n * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break;
@@ -1165,7 +1156,6 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
n = q;
s |= 1;
}
return s;
}
@@ -1179,32 +1169,17 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
// If yes, work with the quotient.
// If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
const uint32_t mod_inv_5 = 0xcccccccd;
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
int s = 8;
while (true) {
auto q = rotr(n32 * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break;
n32 = q;
s += 2;
}
auto q = rotr(n32 * mod_inv_5, 1);
if (q <= max_value<uint32_t>() / 10) {
n32 = q;
s |= 1;
}
// ... and use the 32 bit variant of the function
int s = remove_trailing_zeros(n32, 8);
n = n32;
return s;
}
// If n is not divisible by 10^8, work with n itself.
const uint64_t mod_inv_5 = 0xcccccccccccccccd;
const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5
int s = 0;
while (true) {
@@ -1223,7 +1198,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
}
// The main algorithm for shorter interval case
template <class T>
template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
decimal_fp<T> ret_value;
// Compute k and beta
@@ -1270,7 +1245,7 @@ FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
return ret_value;
}
template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
template <typename T> auto to_decimal(T x) noexcept -> decimal_fp<T> {
// Step 1: integer promotion & Schubfach multiplier calculation.
using carrier_uint = typename float_info<T>::carrier_uint;
@@ -1394,17 +1369,6 @@ small_divisor_case_label:
return ret_value;
}
} // namespace dragonbox
#ifdef _MSC_VER
FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...)
-> int {
auto args = va_list();
va_start(args, fmt);
int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
va_end(args);
return result;
}
#endif
} // namespace detail
template <> struct formatter<detail::bigint> {
@@ -1413,23 +1377,22 @@ template <> struct formatter<detail::bigint> {
return ctx.begin();
}
template <typename FormatContext>
auto format(const detail::bigint& n, FormatContext& ctx) const ->
typename FormatContext::iterator {
auto format(const detail::bigint& n, format_context& ctx) const
-> format_context::iterator {
auto out = ctx.out();
bool first = true;
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;
}
};
@@ -1453,7 +1416,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& 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(...) {}
@@ -1465,7 +1428,7 @@ FMT_FUNC void report_system_error(int error_code,
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();
@@ -1474,57 +1437,299 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
}
namespace detail {
#ifdef _WIN32
template <typename T> struct span {
T* data;
size_t size;
};
template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
_lock_file(f);
}
template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
_unlock_file(f);
}
#ifndef getc_unlocked
template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) {
return _fgetc_nolock(f);
}
#endif
template <typename F = FILE, typename Enable = void>
struct has_flockfile : std::false_type {};
template <typename F>
struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>>
: std::true_type {};
// A FILE wrapper. F is FILE defined as a template parameter to make system API
// detection work.
template <typename F> 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 <typename F> class glibc_file : public file_base<F> {
private:
enum {
line_buffered = 0x200, // _IO_LINE_BUF
unbuffered = 2 // _IO_UNBUFFERED
};
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool {
return (this->file_->_flags & unbuffered) == 0;
}
void init_buffer() {
if (this->file_->_IO_write_ptr) 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<const char> {
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<char> {
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; }
bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
}
void flush() { fflush_unlocked(this->file_); }
};
// A FILE wrapper for Apple's libc.
template <typename F> class apple_file : public file_base<F> {
private:
enum {
line_buffered = 1, // __SNBF
unbuffered = 2 // __SLBF
};
public:
using file_base<F>::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.
putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
auto get_read_buffer() const -> span<const char> {
return {reinterpret_cast<char*>(this->file_->_p),
to_unsigned(this->file_->_r)};
}
auto get_write_buffer() const -> span<char> {
return {reinterpret_cast<char*>(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;
}
bool needs_flush() const {
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 <typename F> class fallback_file : public file_base<F> {
private:
char next_; // The next unconsumed character in the buffer.
bool has_next_ = false;
public:
using file_base<F>::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<const char> {
return {&next_, has_next_ ? 1u : 0u};
}
auto get_write_buffer() const -> span<char> { return {nullptr, 0}; }
void advance_write_buffer(size_t) {}
auto get() -> int {
has_next_ = false;
return file_base<F>::get();
}
void unget(char c) {
file_base<F>::unget(c);
next_ = c;
has_next_ = true;
}
};
#ifndef FMT_USE_FALLBACK_FILE
# define FMT_USE_FALLBACK_FILE 1
#endif
template <typename F,
FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)>
auto get_file(F* f, int) -> apple_file<F> {
return f;
}
template <typename F,
FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 && !FMT_USE_FALLBACK_FILE)>
inline auto get_file(F* f, int) -> glibc_file<F> {
return f;
}
inline auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; }
using file_ref = decltype(get_file(static_cast<FILE*>(nullptr), 0));
template <typename F = FILE, typename Enable = void>
class file_print_buffer : public buffer<char> {
public:
explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {}
};
template <typename F>
class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
: public buffer<char> {
private:
file_ref file_;
static void grow(buffer<char>& base, size_t) {
auto& self = static_cast<file_print_buffer&>(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<sizeof(long) == 4, unsigned long, unsigned>;
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)) {
detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
auto written = detail::dword();
if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
u16.c_str(), static_cast<uint32_t>(u16.size()),
&written, nullptr)) {
return true;
}
}
// We return false if the file descriptor was not TTY, or it was but
// SetConsoleW failed which can happen if the output has been redirected to
// NUL. In both cases when we return false, we should attempt to do regular
// write via fwrite or std::ostream::write.
return false;
FMT_FUNC bool write_console(int fd, string_view text) {
auto u16 = utf8_to_utf16(text);
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<dword>(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,
bool newline) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
if (newline) buffer.push_back('\n');
fwrite_fully(buffer.data(), buffer.size(), f);
}
#endif
FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
if (write_console(f, text)) return;
#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
detail::fwrite_fully(text.data(), 1, text.size(), f);
fwrite_fully(text.data(), text.size(), f);
}
} // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
detail::vformat_to(buffer, format_str, 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()});
}
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
format_args args) {
memory_buffer buffer;
detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f);
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);
}
#endif
FMT_FUNC void vprint(string_view format_str, format_args args) {
vprint(stdout, format_str, 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);
}
namespace detail {
@@ -8,17 +8,19 @@
#ifndef FMT_OS_H_
#define FMT_OS_H_
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif
#endif // FMT_MODULE
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
# if FMT_HAS_INCLUDE("winapifamily.h")
@@ -46,6 +48,7 @@
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
@@ -71,132 +74,78 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
* A reference to a null-terminated string. It can be constructed from a C
* string or `std::string`.
*
* You can use one of the following type aliases for common character types:
*
* +---------------+-----------------------------+
* | Type | Definition |
* +===============+=============================+
* | cstring_view | basic_cstring_view<char> |
* +---------------+-----------------------------+
* | wcstring_view | basic_cstring_view<wchar_t> |
* +---------------+-----------------------------+
*
* This class is most useful as a parameter type for functions that wrap C APIs.
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
/// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
/// Constructs a string reference from an `std::string` object.
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
/// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
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()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE
}
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args);
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
* Constructs a `std::system_error` object with the description of the form
*
* <message>: <system-message>
*
* where `<message>` is the formatted message and `<system-message>` is the
* system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
* If `error_code` is not a valid error code such as -1, the system message
* will look like "error -1".
*
* **Example**:
*
* // This throws a system_error with the description
* // cannot open file 'madeup': The system cannot find the file
* specified.
* // or similar (system message may vary).
* const char *filename = "madeup";
* LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) {
* throw fmt::windows_error(GetLastError(),
* "cannot open file '{}'", filename);
* }
*/
template <typename... Args>
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
@@ -207,7 +156,7 @@ std::system_error windows_error(int error_code, string_view message,
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
#else
inline const std::error_category& system_category() noexcept {
inline auto system_category() noexcept -> const std::error_category& {
return std::system_category();
}
#endif // _WIN32
@@ -244,7 +193,7 @@ class buffered_file {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
@@ -258,21 +207,20 @@ class buffered_file {
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const noexcept { return file_; }
auto get() const noexcept -> FILE* { return file_; }
FMT_API int descriptor() const;
FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
template <typename... T>
inline void print(string_view fmt, const T&... args) {
const auto& vargs = fmt::make_format_args(args...);
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
: fmt::vprint(file_, fmt, vargs);
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as
@@ -286,6 +234,8 @@ class FMT_API file {
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public:
// Possible values for the oflag argument to the constructor.
enum {
@@ -310,7 +260,7 @@ class FMT_API file {
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
@@ -321,24 +271,24 @@ class FMT_API file {
~file() noexcept;
// Returns the file descriptor.
int descriptor() const noexcept { return fd_; }
auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
long long size() const;
auto size() const -> long long;
// Attempts to read count bytes from the file into the specified buffer.
size_t read(void* buffer, size_t count);
auto read(void* buffer, size_t count) -> size_t;
// Attempts to write count bytes from the specified buffer to the file.
size_t write(const void* buffer, size_t count);
auto write(const void* buffer, size_t count) -> size_t;
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static file dup(int fd);
static auto dup(int fd) -> file;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
@@ -348,24 +298,35 @@ class FMT_API file {
// necessary.
void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
buffered_file fdopen(const char* mode);
auto fdopen(const char* mode) -> buffered_file;
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
};
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size.
long getpagesize();
auto getpagesize() -> long;
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
struct buffer_size {
buffer_size() = default;
size_t value = 0;
buffer_size operator=(size_t val) const {
auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
@@ -397,82 +358,82 @@ struct ostream_params {
# endif
};
FMT_END_DETAIL_NAMESPACE
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> {
class file_buffer final : public buffer<char> {
private:
file file_;
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
FMT_API static void grow(buffer<char>& buf, size_t);
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other) noexcept;
FMT_API ~file_buffer();
void flush() {
if (size() == 0) return;
file_.write(data(), size());
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
};
/**
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file.
*/
} // namespace detail
constexpr auto buffer_size = detail::buffer_size();
/// A fast output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream {
private:
FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
ostream(cstring_view path, const detail::ostream_params& params)
: buffer_(path, params) {}
public:
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
~ostream();
void flush() { buffer_.flush(); }
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); }
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt,
fmt::make_format_args(args...));
vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...));
}
};
/**
\rst
Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
* Opens a file for writing. Supported parameters passed in `params`:
*
* - `<integer>`: Flags passed to [open](
* https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
* (`file::WRONLY | file::CREATE | file::TRUNC` by default)
* - `buffer_size=<integer>`: Output buffer size
*
* **Example**:
*
* auto out = fmt::output_file("guide.txt");
* out.print("Don't {}", "Panic");
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
inline auto output_file(cstring_view path, T... params) -> ostream {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_OS_H_
+211
View File
@@ -0,0 +1,211 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "chrono.h" // formatbuf
FMT_BEGIN_NAMESPACE
namespace detail {
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
#if FMT_MSC_VERSION
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else
return false;
#else
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
// Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
void set_debug_format() = delete;
template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>();
detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
};
using ostream_formatter = basic_ostream_formatter<char>;
template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> {
template <typename Context>
auto format(detail::streamed_view<T> view, Context& ctx) const
-> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx);
}
};
/**
* Returns a view that formats `value` via an ostream `operator<<`.
*
* **Example**:
*
* fmt::print("Current thread id: {}\n",
* fmt::streamed(std::this_thread::get_id()));
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
namespace detail {
inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
} // namespace detail
FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
typename detail::vformat_args<Char>::type args) {
auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args);
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
detail::write_buffer(os, buffer);
}
/**
* Prints formatted data to the stream `os`.
*
* **Example**:
*
* fmt::print(cerr, "Don't {}!", "panic");
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
if (detail::use_utf8())
vprint(os, fmt, vargs);
else
detail::vprint_directly(os, fmt, vargs);
}
FMT_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_
+656
View File
@@ -0,0 +1,656 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public:
using char_type = Char;
using parse_context_type = basic_format_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/// Constructs a `printf_context` object. References to the arguments are
/// stored in the context object so make sure they have appropriate lifetimes.
basic_printf_context(basic_appender<Char> out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
};
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static auto fits_in_int(int) -> bool { return true; }
};
struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> int {
report_error("precision is not integer");
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
struct is_zero_int {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> bool {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> bool {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
auto n = static_cast<int>(static_cast<target_type>(value));
arg_ = detail::make_arg<Context>(n);
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
arg_ = detail::make_arg<Context>(n);
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else {
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
arg_ = detail::make_arg<Context>(n);
}
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
arg.visit(arg_converter<T, Context>(arg, type));
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
auto c = static_cast<typename Context::char_type>(value);
arg_ = detail::make_arg<Context>(c);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> auto operator()(T) -> const Char* { return nullptr; }
auto operator()(const Char* s) -> const Char* { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class printf_width_handler {
private:
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> unsigned {
report_error("width is not integer");
return 0;
}
};
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The `printf` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<Char>;
context_type& context_;
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = presentation_type::none;
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
public:
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
void operator()(monostate value) { base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (!std::is_same<T, Char>::value) {
base::operator()(value);
return;
}
format_specs s = this->specs;
if (s.type != presentation_type::none && s.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
s.sign = sign::none;
s.alt = false;
s.fill = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (s.align == align::none || s.align == align::numeric)
s.align = align::right;
write<Char>(this->out, static_cast<Char>(value), s);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void operator()(T value) {
base::operator()(value);
}
void operator()(const char* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
void operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
void operator()(basic_string_view<Char> value) { base::operator()(value); }
void operator()(const void* value) {
if (value)
base::operator()(value);
else
write_null_pointer();
}
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = basic_format_parse_context<Char>({});
handle.format(parse_ctx, context_);
}
};
template <typename Char>
void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill = '0';
break;
case ' ':
if (specs.sign != sign::plus) specs.sign = sign::space;
break;
case '#':
specs.alt = true;
break;
default:
return;
}
}
}
template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
int value = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.fill = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
if (value == -1) report_error("number is too big");
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
return arg_index;
}
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
case 'X':
upper = true;
FMT_FALLTHROUGH;
case 'x':
return in(t, integral_set) ? pt::hex : pt::none;
case 'E':
upper = true;
FMT_FALLTHROUGH;
case 'e':
return in(t, float_set) ? pt::exp : pt::none;
case 'F':
upper = true;
FMT_FALLTHROUGH;
case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
case 'G':
upper = true;
FMT_FALLTHROUGH;
case 'g':
return in(t, float_set) ? pt::general : pt::none;
case 'A':
upper = true;
FMT_FALLTHROUGH;
case 'a':
return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default:
return pt::none;
}
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using iterator = basic_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_format_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
auto get_arg = [&](int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
const Char* end = parse_ctx.end();
auto it = start;
while (it != end) {
if (!find<false, Char>(it, end, '%', it)) {
it = end; // find leaves it == nullptr if it doesn't find '%'.
break;
}
Char c = *it++;
if (it != end && *it == c) {
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
start = ++it;
continue;
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs();
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) report_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else {
specs.precision = 0;
}
}
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) {
// Ignore '0' for non-numeric types or if '-' present.
specs.fill = ' ';
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv);
}
if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
if (specs.fill.template get<Char>() == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (type) {
case 'i':
case 'u':
type = 'd';
break;
case 'c':
arg.visit(char_converter<basic_printf_context<Char>>(arg));
break;
}
}
bool upper = false;
specs.type = parse_printf_presentation_type(type, arg.type(), upper);
if (specs.type == presentation_type::none)
report_error("invalid format specifier");
specs.upper = upper;
start = it;
// Format argument.
arg.visit(printf_arg_formatter<Char>(out, specs, context));
}
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
} // namespace detail
using printf_context = basic_printf_context<char>;
using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/// Constructs an `format_arg_store` object that contains references to
/// arguments and can be implicitly converted to `printf_args`.
template <typename Char = char, typename... T>
inline auto make_printf_args(T&... args)
-> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
return fmt::make_format_args<basic_printf_context<Char>>(args...);
}
template <typename Char> struct vprintf_args {
using type = basic_format_args<basic_printf_context<Char>>;
};
template <typename Char>
inline auto vsprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
return to_string(buf);
}
/**
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
*
* **Example**:
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
* Formats `args` according to specifications in `fmt` and writes the output
* to `f`.
*
* **Example**:
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
}
/**
* Formats `args` according to specifications in `fmt` and writes the output
* to `stdout`.
*
* **Example**:
*
* fmt::printf("Elapsed time: %.2f seconds", 1.23);
*/
template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_
+882
View File
@@ -0,0 +1,882 @@
// Formatting library for C++ - range and tuple support
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_EXPORT
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail {
template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T> class is_set {
template <typename U> static auto check(U*) -> typename U::key_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
# define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \
static_assert( \
true, "") // This makes it so that a semicolon is required after the
// macro, which helps clang-format handle the formatting.
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
// ADL overloads. Only participate in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
return end(static_cast<T&&>(rng));
}
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T, void_t<decltype(*detail::range_begin(
std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(
std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
decltype(detail::range_end(std::declval<T&>())),
// the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types
int>> : std::true_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
# undef FMT_DECLTYPE_RETURN
#endif
// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <typename T>
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is>
static auto all_true(index_sequence<Is...>,
integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
static auto all_true(...) -> std::false_type;
template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
index_sequence<Is...>{},
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));
public:
static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
template <typename Tuple, typename F, size_t... Is>
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
using std::get;
// Using a free function get<Is>(Tuple) now.
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
ignore_unused(unused);
}
template <typename Tuple, typename F>
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
std::forward<Tuple>(t), std::forward<F>(f));
}
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
using std::get;
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
ignore_unused(unused);
}
template <typename Tuple1, typename Tuple2, typename F>
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
std::forward<F>(f));
}
namespace tuple {
// Workaround a bug in MSVC 2019 (v140).
template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
// Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
template <typename T>
using range_reference_type = typename range_reference_type_impl<T>::type;
#else
template <typename Range>
using range_reference_type =
decltype(*detail::range_begin(std::declval<Range&>()));
#endif
// We don't use the Range's value_type for anything, but we do need the Range's
// reference type, with cv-ref stripped.
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Formatter>
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
-> decltype(f.set_debug_format(set)) {
f.set_debug_format(set);
}
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
// These are not generic lambdas for compatibility with C++11.
template <typename ParseContext> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
ParseContext& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) {
if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
ctx.advance_to(f.format(v, ctx));
++i;
}
int i;
FormatContext& ctx;
basic_string_view<char_type> separator;
};
} // namespace detail
template <typename T> struct is_tuple_like {
static constexpr const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
struct formatter<Tuple, Char,
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
fmt::is_tuple_formattable<Tuple, Char>::value>> {
private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '('>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ')'>{};
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() && *it != '}') report_error("invalid format specifier");
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
return it;
}
template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
detail::for_each2(
formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return detail::copy<Char>(closing_bracket_, ctx.out());
}
};
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
namespace detail {
template <typename Context> struct range_mapper {
using mapper = arg_mapper<Context>;
template <typename T,
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value) -> T&& {
return static_cast<T&&>(value);
}
template <typename T,
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value)
-> decltype(mapper().map(static_cast<T&&>(value))) {
return mapper().map(static_cast<T&&>(value));
}
};
template <typename Char, typename Element>
using range_formatter_type =
formatter<remove_cvref_t<decltype(range_mapper<buffered_context<Char>>{}
.map(std::declval<Element>()))>,
Char>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
template <typename R, typename Char>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
#endif
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
template <typename P> struct conjunction<P> : P {};
template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
template <typename T, typename Char>
struct range_formatter<
T, Char,
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
is_formattable<T, Char>>::value>> {
private:
detail::range_formatter_type<Char, T> underlying_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{};
bool is_debug = false;
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.type = presentation_type::debug;
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}
public:
FMT_CONSTEXPR range_formatter() {}
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
return underlying_;
}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
detail::maybe_set_debug_format(underlying_, true);
if (it == end) return underlying_.parse(ctx);
switch (detail::to_ascii(*it)) {
case 'n':
set_brackets({}, {});
++it;
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
if (it == end || *it != 's') report_error("invalid format specifier");
FMT_FALLTHROUGH;
case 's':
if (!std::is_same<T, Char>::value)
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
detail::maybe_set_debug_format(underlying_, false);
++it;
}
ctx.advance_to(it);
return underlying_.parse(ctx);
}
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
auto mapper = detail::range_mapper<buffered_context<Char>>();
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
if (is_debug) return write_debug_string(out, std::move(it), end);
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) {
if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out);
auto&& item = *it; // Need an lvalue
out = underlying_.format(mapper.map(item), ctx);
++i;
}
out = detail::copy<Char>(closing_bracket_, out);
return out;
}
};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_format_kind
: conditional_t<
is_range<T, Char>::value, detail::range_format_kind_<T>,
std::integral_constant<range_format, range_format::disabled>> {};
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<
bool_constant<
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
range_format_kind<R, Char>::value != range_format::debug_string>
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
,
detail::is_formattable_delayed<R, Char>
#endif
>::value>> {
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
public:
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return range_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
};
// A map formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::map>> {
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
decltype(detail::tuple::get_formatters<element_type, Char>(
detail::tuple_index_sequence<element_type>())) formatters_;
bool no_delimiters_ = false;
public:
FMT_CONSTEXPR formatter() {}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
if (detail::to_ascii(*it) == 'n') {
no_delimiters_ = true;
++it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
++it;
}
ctx.advance_to(it);
}
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
return it;
}
template <typename FormatContext>
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
auto mapper = detail::range_mapper<buffered_context<Char>>();
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
for (auto&& value : map) {
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
detail::for_each2(formatters_, mapper.map(value),
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
++i;
}
basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
if (!no_delimiters_) out = detail::copy<Char>(close, out);
return out;
}
};
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type =
conditional_t<std::is_constructible<
detail::std_string_view<Char>,
decltype(detail::range_begin(std::declval<R>())),
decltype(detail::range_end(std::declval<R>()))>::value,
detail::std_string_view<Char>, std::basic_string<Char>>;
formatter<string_type, Char> underlying_;
public:
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
return out;
}
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
using view_ref = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>&,
join_view<It, Sentinel, Char>&&>;
public:
using nonlocking = void;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(view_ref& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto it = std::forward<view_ref>(value).begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
return out;
}
};
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/**
* Returns a view that formats `range` with elements separated by `sep`.
*
* **Example**:
*
* auto v = std::vector<int>{1, 2, 3};
* fmt::print("{}", fmt::join(v, ", "));
* // Output: 1, 2, 3
*
* `fmt::join` applies passed format specifiers to the range elements:
*
* fmt::print("{:02}", fmt::join(v, ", "));
* // Output: 01, 02, 03
*/
template <typename Range>
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}
template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision.
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx,
std::integral_constant<size_t, sizeof...(T)>());
}
private:
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
template <typename ParseContext>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
std::integral_constant<size_t, 0>)
-> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename ParseContext, size_t N>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
std::integral_constant<size_t, N>)
-> decltype(ctx.begin()) {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
report_error("incompatible format specs for tuple elements");
}
#endif
return end;
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
auto out = std::get<sizeof...(T) - N>(formatters_)
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
}
};
namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& t) -> all {
return {t.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(t), ctx);
}
};
FMT_BEGIN_EXPORT
/**
* Returns an object that formats `std::tuple` with elements separated by `sep`.
*
* **Example**:
*
* auto t = std::tuple<int, char>{1, 'a'};
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
return {tuple, sep};
}
/**
* Returns an object that formats `std::initializer_list` with elements
* separated by `sep`.
*
* **Example**:
*
* fmt::print("{}", fmt::join({1, 2, 3}, ", "));
* // Output: "1, 2, 3"
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
+699
View File
@@ -0,0 +1,699 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include "format.h"
#include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) 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 && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
auto path_string =
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<is_formattable<T, Char>::value &&
is_formattable<E, Char>::value>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v);
},
value);
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes<Char>(out, ec.category().name(), format_specs());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.sign = sign::plus;
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.width_ref.kind != detail::arg_id_kind::none ||
specs.precision_ref.kind != detail::arg_id_kind::none) {
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.fill = specs.fill;
outer_specs.align = specs.align;
specs.width = 0;
specs.fill = {};
specs.align = align::none;
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_
+322
View File
@@ -0,0 +1,322 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_
#include "color.h"
#include "format.h"
#include "ranges.h"
#ifndef FMT_MODULE
# include <cwchar>
# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
# endif
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
template <typename S, typename = void> struct format_string_char {};
template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<S, enable_if_t<is_compile_string<S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <>
struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled()> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
return fmt::make_format_args<wformat_context>(args...);
}
inline namespace literals {
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
constexpr auto operator""_a(const wchar_t* s, size_t)
-> detail::udl_arg<wchar_t> {
return {s};
}
#endif
} // namespace literals
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
}
template <typename Range>
auto join(Range&& range, wstring_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, format_str, args);
return to_string(buf);
}
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& format_str,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, T&&... args)
-> std::basic_string<Char> {
return detail::vformat(
loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
typename detail::vformat_args<Char>::type args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(format_str), args);
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& format_str,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(format_str), args,
detail::locale_ref(loc));
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n,
basic_string_view<Char> format_str,
typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
auto buf = wmemory_buffer();
detail::vformat_to(buf, fmt, args);
buf.push_back(L'\0');
if (std::fputws(buf.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
inline void vprint(wstring_view fmt, wformat_args args) {
vprint(stdout, fmt, args);
}
template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_
@@ -20,10 +20,6 @@
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
// enable the 'n' flag in for backward compatibility with fmt 6.x
#define FMT_DEPRECATED_N_SPECIFIER
// enable ostream formatting for backward compatibility with fmt 8.x
#define FMT_DEPRECATED_OSTREAM
#include <spdlog/fmt/bundled/core.h>
#include <spdlog/fmt/bundled/format.h>
+50
View File
@@ -0,0 +1,50 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#if defined(SPDLOG_NO_TLS)
#error "This header requires thread local storage support, but SPDLOG_NO_TLS is defined."
#endif
#include <map>
#include <string>
#include <spdlog/common.h>
// MDC is a simple map of key->string values stored in thread local storage whose content will be printed by the loggers.
// Note: Not supported in async mode (thread local storage - so the async thread pool have different copy).
//
// Usage example:
// spdlog::mdc::put("mdc_key_1", "mdc_value_1");
// spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info] [mdc_key_1:mdc_value_1] Hello, World!
namespace spdlog {
class SPDLOG_API mdc {
public:
using mdc_map_t = std::map<std::string, std::string>;
static void put(const std::string &key, const std::string &value) {
get_context()[key] = value;
}
static std::string get(const std::string &key) {
auto &context = get_context();
auto it = context.find(key);
if (it != context.end()) {
return it->second;
}
return "";
}
static void remove(const std::string &key) { get_context().erase(key); }
static void clear() { get_context().clear(); }
static mdc_map_t &get_context() {
static thread_local mdc_map_t context;
return context;
}
};
} // namespace spdlog
@@ -10,6 +10,11 @@
#include <spdlog/details/fmt_helper.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/details/os.h>
#ifndef SPDLOG_NO_TLS
#include <spdlog/mdc.h>
#endif
#include <spdlog/fmt/fmt.h>
#include <spdlog/formatter.h>
@@ -175,7 +180,7 @@ public:
// Abbreviated month
static const std::array<const char *, 12> months{
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}};
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}};
template <typename ScopedPadder>
class b_formatter final : public flag_formatter {
@@ -783,6 +788,49 @@ private:
log_clock::time_point last_message_time_;
};
// Class for formatting Mapped Diagnostic Context (MDC) in log messages.
// Example: [logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message
#ifndef SPDLOG_NO_TLS
template <typename ScopedPadder>
class mdc_formatter : public flag_formatter {
public:
explicit mdc_formatter(padding_info padinfo)
: flag_formatter(padinfo) {}
void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
auto &mdc_map = mdc::get_context();
if (mdc_map.empty()) {
ScopedPadder p(0, padinfo_, dest);
return;
} else {
format_mdc(mdc_map, dest);
}
}
void format_mdc(const mdc::mdc_map_t &mdc_map, memory_buf_t &dest) {
auto last_element = --mdc_map.end();
for (auto it = mdc_map.begin(); it != mdc_map.end(); ++it) {
auto &pair = *it;
const auto &key = pair.first;
const auto &value = pair.second;
size_t content_size = key.size() + value.size() + 1; // 1 for ':'
if (it != last_element) {
content_size++; // 1 for ' '
}
ScopedPadder p(content_size, padinfo_, dest);
fmt_helper::append_string_view(key, dest);
fmt_helper::append_string_view(":", dest);
fmt_helper::append_string_view(value, dest);
if (it != last_element) {
fmt_helper::append_string_view(" ", dest);
}
}
}
};
#endif
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v
class full_formatter final : public flag_formatter {
@@ -858,6 +906,17 @@ public:
dest.push_back(']');
dest.push_back(' ');
}
#ifndef SPDLOG_NO_TLS
// add mdc if present
auto &mdc_map = mdc::get_context();
if (!mdc_map.empty()) {
dest.push_back('[');
mdc_formatter_.format_mdc(mdc_map, dest);
dest.push_back(']');
dest.push_back(' ');
}
#endif
// fmt_helper::append_string_view(msg.msg(), dest);
fmt_helper::append_string_view(msg.payload, dest);
}
@@ -865,6 +924,11 @@ public:
private:
std::chrono::seconds cache_timestamp_{0};
memory_buf_t cached_datetime_;
#ifndef SPDLOG_NO_TLS
mdc_formatter<null_scoped_padder> mdc_formatter_{padding_info{}};
#endif
};
} // namespace details
@@ -1159,6 +1223,12 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
padding));
break;
#ifndef SPDLOG_NO_TLS // mdc formatter requires TLS support
case ('&'):
formatters_.push_back(details::make_unique<details::mdc_formatter<Padder>>(padding));
break;
#endif
default: // Unknown flag appears as is
auto unknown_flag = details::make_unique<details::aggregate_formatter>();
@@ -40,7 +40,7 @@ public:
void log(const details::log_msg &msg) override;
void flush() override;
void set_pattern(const std::string &pattern) final;
void set_pattern(const std::string &pattern) final override;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
// Formatting codes
@@ -28,10 +28,10 @@ public:
base_sink &operator=(const base_sink &) = delete;
base_sink &operator=(base_sink &&) = delete;
void log(const details::log_msg &msg) final;
void flush() final;
void set_pattern(const std::string &pattern) final;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
void log(const details::log_msg &msg) final override;
void flush() final override;
void set_pattern(const std::string &pattern) final override;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final override;
protected:
// sink formatter
@@ -27,7 +27,7 @@ public:
protected:
void sink_it_(const details::log_msg &msg) override { callback_(msg); }
void flush_() override{};
void flush_() override{}
private:
custom_log_callback callback_;
@@ -62,6 +62,9 @@ struct daily_filename_format_calculator {
* Rotating file sink based on date.
* If truncate != false , the created file will be truncated.
* If max_files > 0, retain only the last max_files and delete previous.
* If max_files > 0, retain only the last max_files and delete previous.
* Note that old log files from previous executions will not be deleted by this class,
* rotation and deletion is only applied while the program is running.
*/
template <typename Mutex, typename FileNameCalc = daily_filename_calculator>
class daily_file_sink final : public base_sink<Mutex> {
@@ -39,6 +39,8 @@ struct hourly_filename_calculator {
* Rotating file sink based on time.
* If truncate != false , the created file will be truncated.
* If max_files > 0, retain only the last max_files and delete previous.
* Note that old log files from previous executions will not be deleted by this class,
* rotation and deletion is only applied while the program is running.
*/
template <typename Mutex, typename FileNameCalc = hourly_filename_calculator>
class hourly_file_sink final : public base_sink<Mutex> {

Some files were not shown because too many files have changed in this diff Show More