Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Algol68
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C with Coccinelle
C++ with Coccinelle
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Mojo
Nim
Numba
Nix
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
PTX
Python
Racket
Raku
Ruby
Rust
Sail
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
Triton
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Zig
Javascript
GIMPLE
Ygen
sway
c source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502 cc65 2.17
6502 cc65 2.18
6502 cc65 2.19
6502 cc65 trunk
ARM GCC 10.2.0 (linux)
ARM GCC 10.2.1 (none)
ARM GCC 10.3.0 (linux)
ARM GCC 10.3.1 (2021.07 none)
ARM GCC 10.3.1 (2021.10 none)
ARM GCC 10.5.0
ARM GCC 11.1.0 (linux)
ARM GCC 11.2.0 (linux)
ARM GCC 11.2.1 (none)
ARM GCC 11.3.0 (linux)
ARM GCC 11.4.0
ARM GCC 12.1.0 (linux)
ARM GCC 12.2.0 (linux)
ARM GCC 12.3.0
ARM GCC 12.4.0
ARM GCC 12.5.0
ARM GCC 13.1.0 (linux)
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 13.3.0
ARM GCC 13.3.0 (unknown-eabi)
ARM GCC 13.4.0
ARM GCC 13.4.0 (unknown-eabi)
ARM GCC 14.1.0
ARM GCC 14.1.0 (unknown-eabi)
ARM GCC 14.2.0
ARM GCC 14.2.0 (unknown-eabi)
ARM GCC 14.3.0
ARM GCC 14.3.0 (unknown-eabi)
ARM GCC 15.1.0
ARM GCC 15.1.0 (unknown-eabi)
ARM GCC 15.2.0
ARM GCC 15.2.0 (unknown-eabi)
ARM GCC 4.5.4 (linux)
ARM GCC 4.6.4 (linux)
ARM GCC 5.4 (linux)
ARM GCC 5.4.1 (none)
ARM GCC 6.3.0 (linux)
ARM GCC 6.4.0 (linux)
ARM GCC 7.2.1 (none)
ARM GCC 7.3.0 (linux)
ARM GCC 7.5.0 (linux)
ARM GCC 8.2.0 (WinCE)
ARM GCC 8.2.0 (linux)
ARM GCC 8.3.1 (none)
ARM GCC 8.5.0 (linux)
ARM GCC 9.2.1 (none)
ARM GCC 9.3.0 (linux)
ARM GCC trunk (linux)
ARM msvc v19.0 (ex-WINE)
ARM msvc v19.10 (ex-WINE)
ARM msvc v19.14 (ex-WINE)
ARM64 GCC 10.2.0
ARM64 GCC 10.3.0
ARM64 GCC 10.4.0
ARM64 GCC 10.5.0
ARM64 GCC 11.1.0
ARM64 GCC 11.2.0
ARM64 GCC 11.3.0
ARM64 GCC 11.4.0
ARM64 GCC 12.1.0
ARM64 GCC 12.2.0
ARM64 GCC 12.3.0
ARM64 GCC 12.4.0
ARM64 GCC 12.5.0
ARM64 GCC 13.1.0
ARM64 GCC 13.2.0
ARM64 GCC 13.3.0
ARM64 GCC 13.4.0
ARM64 GCC 14.1.0
ARM64 GCC 14.2.0
ARM64 GCC 14.3.0
ARM64 GCC 15.1.0
ARM64 GCC 15.2.0
ARM64 GCC 4.9.4
ARM64 GCC 5.4
ARM64 GCC 5.5.0
ARM64 GCC 6.3
ARM64 GCC 6.4.0
ARM64 GCC 7.3.0
ARM64 GCC 7.5.0
ARM64 GCC 8.2.0
ARM64 GCC 8.5.0
ARM64 GCC 9.3.0
ARM64 GCC 9.4.0
ARM64 GCC 9.5.0
ARM64 GCC trunk
ARM64 Morello GCC 10.1.0 Alpha 1
ARM64 Morello GCC 10.1.2 Alpha 2
ARM64 msvc v19.14 (ex-WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 12.4.0
AVR gcc 12.5.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 13.4.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 14.3.0
AVR gcc 15.1.0
AVR gcc 15.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF clang 19.1.0
BPF clang 20.1.0
BPF clang 21.1.0
BPF gcc 13.1.0
BPF gcc 13.2.0
BPF gcc 13.3.0
BPF gcc 13.4.0
BPF gcc 14.1.0
BPF gcc 14.2.0
BPF gcc 14.3.0
BPF gcc 15.1.0
BPF gcc 15.2.0
BPF gcc trunk
C2Rust (master)
Chibicc 2020-12-07
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
HPPA gcc 14.3.0
HPPA gcc 15.1.0
HPPA gcc 15.2.0
K1C gcc 7.4
K1C gcc 7.5
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
KVX ACB 5.0.0 (GCC 12.2.1)
KVX ACB 5.2.0 (GCC 13.2.1)
LC3 (trunk)
M68K clang (trunk)
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 13.4.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68K gcc 14.3.0
M68K gcc 15.1.0
M68K gcc 15.2.0
MRISC32 gcc (trunk)
MSP430 gcc 12.1.0
MSP430 gcc 12.2.0
MSP430 gcc 12.3.0
MSP430 gcc 12.4.0
MSP430 gcc 12.5.0
MSP430 gcc 13.1.0
MSP430 gcc 13.2.0
MSP430 gcc 13.3.0
MSP430 gcc 13.4.0
MSP430 gcc 14.1.0
MSP430 gcc 14.2.0
MSP430 gcc 14.3.0
MSP430 gcc 15.1.0
MSP430 gcc 15.2.0
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
ORCA/C 2.1.0
ORCA/C 2.2.0
ORCA/C 2.2.1
POWER64 gcc 11.2.0
POWER64 gcc 12.1.0
POWER64 gcc 12.2.0
POWER64 gcc 12.3.0
POWER64 gcc 12.4.0
POWER64 gcc 12.5.0
POWER64 gcc 13.1.0
POWER64 gcc 13.2.0
POWER64 gcc 13.3.0
POWER64 gcc 13.4.0
POWER64 gcc 14.1.0
POWER64 gcc 14.2.0
POWER64 gcc 14.3.0
POWER64 gcc 15.1.0
POWER64 gcc 15.2.0
POWER64 gcc trunk
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 12.4.0
RISC-V (32-bits) gcc 12.5.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 13.3.0
RISC-V (32-bits) gcc 13.4.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 14.3.0
RISC-V (32-bits) gcc 15.1.0
RISC-V (32-bits) gcc 15.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 12.4.0
RISC-V (64-bits) gcc 12.5.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 13.3.0
RISC-V (64-bits) gcc 13.4.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 14.3.0
RISC-V (64-bits) gcc 15.1.0
RISC-V (64-bits) gcc 15.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 19.1.0
RISC-V rv32gc clang 20.1.0
RISC-V rv32gc clang 21.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 19.1.0
RISC-V rv64gc clang 20.1.0
RISC-V rv64gc clang 21.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SDCC 4.0.0
SDCC 4.1.0
SDCC 4.2.0
SDCC 4.3.0
SDCC 4.4.0
SDCC 4.5.0
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 12.4.0
SPARC LEON gcc 12.5.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 13.4.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC LEON gcc 14.3.0
SPARC LEON gcc 15.1.0
SPARC LEON gcc 15.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 12.5.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 13.4.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC gcc 14.3.0
SPARC gcc 15.1.0
SPARC gcc 15.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 12.5.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 13.4.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
SPARC64 gcc 14.3.0
SPARC64 gcc 15.1.0
SPARC64 gcc 15.2.0
TCC (trunk)
TCC 0.9.27
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 12.5.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 13.4.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI C6x gcc 14.3.0
TI C6x gcc 15.1.0
TI C6x gcc 15.2.0
TI CL430 21.6.1
Tricore gcc 11.3.0 (EEESlab)
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
VAX gcc NetBSDELF 12.4.0 (Apr 16 05:27 2025)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 14.2.0 (20241119)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 14.2.0 (20241119)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 14.2.0 (20241119)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.20 VS16.0
arm64 msvc v19.21 VS16.1
arm64 msvc v19.22 VS16.2
arm64 msvc v19.23 VS16.3
arm64 msvc v19.24 VS16.4
arm64 msvc v19.25 VS16.5
arm64 msvc v19.27 VS16.7
arm64 msvc v19.28 VS16.8
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30 VS17.0
arm64 msvc v19.31 VS17.1
arm64 msvc v19.32 VS17.2
arm64 msvc v19.33 VS17.3
arm64 msvc v19.34 VS17.4
arm64 msvc v19.35 VS17.5
arm64 msvc v19.36 VS17.6
arm64 msvc v19.37 VS17.7
arm64 msvc v19.38 VS17.8
arm64 msvc v19.39 VS17.9
arm64 msvc v19.40 VS17.10
arm64 msvc v19.41 VS17.11
arm64 msvc v19.42 VS17.12
arm64 msvc v19.43 VS17.13
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 12.0.0
armv7-a clang 12.0.1
armv7-a clang 13.0.0
armv7-a clang 13.0.1
armv7-a clang 14.0.0
armv7-a clang 15.0.0
armv7-a clang 16.0.0
armv7-a clang 17.0.1
armv7-a clang 18.1.0
armv7-a clang 19.1.0
armv7-a clang 20.1.0
armv7-a clang 21.1.0
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 12.0.1
armv8-a clang 13.0.0
armv8-a clang 13.0.1
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 19.1.0
armv8-a clang 20.1.0
armv8-a clang 21.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clang 12 for DPU (rel 2023.2.0)
cproc-master
ez80-clang 15.0.0
ez80-clang 15.0.7
llvm-mos commander X16
llvm-mos commodore 64
llvm-mos mega65
llvm-mos nes-cnrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos osi-c1p
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 12.4.0
loongarch64 gcc 12.5.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 13.4.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
loongarch64 gcc 14.3.0
loongarch64 gcc 15.1.0
loongarch64 gcc 15.2.0
mips (el) gcc 12.1.0
mips (el) gcc 12.2.0
mips (el) gcc 12.3.0
mips (el) gcc 12.4.0
mips (el) gcc 12.5.0
mips (el) gcc 13.1.0
mips (el) gcc 13.2.0
mips (el) gcc 13.3.0
mips (el) gcc 13.4.0
mips (el) gcc 14.1.0
mips (el) gcc 14.2.0
mips (el) gcc 14.3.0
mips (el) gcc 15.1.0
mips (el) gcc 15.2.0
mips (el) gcc 4.9.4
mips (el) gcc 5.4
mips (el) gcc 5.5.0
mips (el) gcc 9.5.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips clang 19.1.0
mips clang 20.1.0
mips clang 21.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 12.4.0
mips gcc 12.5.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 13.4.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 14.3.0
mips gcc 15.1.0
mips gcc 15.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 12.4.0
mips64 (el) gcc 12.5.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 13.4.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 14.3.0
mips64 (el) gcc 15.1.0
mips64 (el) gcc 15.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 clang 19.1.0
mips64 clang 20.1.0
mips64 clang 21.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 12.4.0
mips64 gcc 12.5.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 13.4.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 14.3.0
mips64 gcc 15.1.0
mips64 gcc 15.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mips64el clang 19.1.0
mips64el clang 20.1.0
mips64el clang 21.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel clang 19.1.0
mipsel clang 20.1.0
mipsel clang 21.1.0
movfuscator (trunk)
nanoMIPS gcc 6.3.0
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 12.4.0
power gcc 12.5.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 13.4.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 14.3.0
power gcc 15.1.0
power gcc 15.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 12.4.0
power64le gcc 12.5.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 13.4.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 14.3.0
power64le gcc 15.1.0
power64le gcc 15.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
ppci 0.5.5
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 12.4.0
s390x gcc 12.5.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 13.4.0
s390x gcc 14.1.0
s390x gcc 14.2.0
s390x gcc 14.3.0
s390x gcc 15.1.0
s390x gcc 15.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 12.5.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 13.4.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 14.3.0
sh gcc 15.1.0
sh gcc 15.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (ex-WINE)
x64 msvc v19.10 (ex-WINE)
x64 msvc v19.14 (ex-WINE)
x64 msvc v19.20 VS16.0
x64 msvc v19.21 VS16.1
x64 msvc v19.22 VS16.2
x64 msvc v19.23 VS16.3
x64 msvc v19.24 VS16.4
x64 msvc v19.25 VS16.5
x64 msvc v19.27 VS16.7
x64 msvc v19.28 VS16.8
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30 VS17.0
x64 msvc v19.31 VS17.1
x64 msvc v19.32 VS17.2
x64 msvc v19.33 VS17.3
x64 msvc v19.34 VS17.4
x64 msvc v19.35 VS17.5
x64 msvc v19.36 VS17.6
x64 msvc v19.37 VS17.7
x64 msvc v19.38 VS17.8
x64 msvc v19.39 VS17.9
x64 msvc v19.40 VS17.10
x64 msvc v19.41 VS17.11
x64 msvc v19.42 VS17.12
x64 msvc v19.43 VS17.13
x64 msvc v19.latest
x86 CompCert 3.10
x86 CompCert 3.11
x86 CompCert 3.12
x86 CompCert 3.9
x86 gcc 1.27
x86 msvc v19.0 (ex-WINE)
x86 msvc v19.10 (ex-WINE)
x86 msvc v19.14 (ex-WINE)
x86 msvc v19.20 VS16.0
x86 msvc v19.21 VS16.1
x86 msvc v19.22 VS16.2
x86 msvc v19.23 VS16.3
x86 msvc v19.24 VS16.4
x86 msvc v19.25 VS16.5
x86 msvc v19.27 VS16.7
x86 msvc v19.28 VS16.8
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30 VS17.0
x86 msvc v19.31 VS17.1
x86 msvc v19.32 VS17.2
x86 msvc v19.33 VS17.3
x86 msvc v19.34 VS17.4
x86 msvc v19.35 VS17.5
x86 msvc v19.36 VS17.6
x86 msvc v19.37 VS17.7
x86 msvc v19.38 VS17.8
x86 msvc v19.39 VS17.9
x86 msvc v19.40 VS17.10
x86 msvc v19.41 VS17.11
x86 msvc v19.42 VS17.12
x86 msvc v19.43 VS17.13
x86 msvc v19.latest
x86 nvc 24.11
x86 nvc 24.9
x86 nvc 25.1
x86 nvc 25.3
x86 nvc 25.5
x86 nvc 25.7
x86 tendra (trunk)
x86-64 clang (assertions trunk)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 15.0.0
x86-64 clang 16.0.0
x86-64 clang 17.0.1
x86-64 clang 18.1.0
x86-64 clang 19.1.0
x86-64 clang 20.1.0
x86-64 clang 21.1.0
x86-64 clang 3.0.0
x86-64 clang 3.1
x86-64 clang 3.2
x86-64 clang 3.3
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.7
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.1
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.3 (assertions)
x86-64 gcc 10.4
x86-64 gcc 10.4 (assertions)
x86-64 gcc 10.5
x86-64 gcc 10.5 (assertions)
x86-64 gcc 11.1
x86-64 gcc 11.1 (assertions)
x86-64 gcc 11.2
x86-64 gcc 11.2 (assertions)
x86-64 gcc 11.3
x86-64 gcc 11.3 (assertions)
x86-64 gcc 11.4
x86-64 gcc 11.4 (assertions)
x86-64 gcc 12.1
x86-64 gcc 12.1 (assertions)
x86-64 gcc 12.2
x86-64 gcc 12.2 (assertions)
x86-64 gcc 12.3
x86-64 gcc 12.3 (assertions)
x86-64 gcc 12.4
x86-64 gcc 12.4 (assertions)
x86-64 gcc 12.5
x86-64 gcc 12.5 (assertions)
x86-64 gcc 13.1
x86-64 gcc 13.1 (assertions)
x86-64 gcc 13.2
x86-64 gcc 13.2 (assertions)
x86-64 gcc 13.3
x86-64 gcc 13.3 (assertions)
x86-64 gcc 13.4
x86-64 gcc 13.4 (assertions)
x86-64 gcc 14.1
x86-64 gcc 14.1 (assertions)
x86-64 gcc 14.2
x86-64 gcc 14.2 (assertions)
x86-64 gcc 14.3
x86-64 gcc 14.3 (assertions)
x86-64 gcc 15.1
x86-64 gcc 15.1 (assertions)
x86-64 gcc 15.2
x86-64 gcc 15.2 (assertions)
x86-64 gcc 3.4.6
x86-64 gcc 4.0.4
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.5
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx (latest)
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2024.0.0
x86_64 CompCert 3.10
x86_64 CompCert 3.11
x86_64 CompCert 3.12
x86_64 CompCert 3.9
z180-clang 15.0.0
z180-clang 15.0.7
z80-clang 15.0.0
z80-clang 15.0.7
z88dk 2.2
zig cc 0.10.0
zig cc 0.11.0
zig cc 0.12.0
zig cc 0.12.1
zig cc 0.13.0
zig cc 0.14.0
zig cc 0.14.1
zig cc 0.15.1
zig cc 0.6.0
zig cc 0.7.0
zig cc 0.7.1
zig cc 0.8.0
zig cc 0.9.0
zig cc trunk
Options
Source code
#define FMT_IMPLEMENTATION #define FMT_FLUSH_AFTER_NEWLINES #include "fmt.h" int main(void) { FMT_STDOUT("Bool: {} or {}\n", FMT(bool, true), FMT(bool, false)); FMT_STDOUT("ASCII char: '{}'\n", FMT(char, '@')); FMT_STDOUT("Unicode code point: '{}'\n", FMT(CodePoint, 0x1f41b)); FMT_STDOUT("C-style string: \"{}\"\n", FMT(String, "hello")); FMT_STDOUT( "UTF-16 string: \"{}\"\n", FMT(U16String, u"Оказывается, под линуксом строковые литералы с префиксом L кодируются в UTF-32. " u"А этот UTF-16 префикс появился только в C11."), ); FMT_STDOUT("Max 64-bit int: {}\n", FMT(i64, I64_MAX, .plus = true)); FMT_STDOUT("Min 64-bit int: {}\n", FMT(i64, I64_MIN)); FMT_STDOUT( "Hex int: 0x{}\n", FMT(u64, 0xb16b00b5llu, .hex = true, .uppercase = true, .precision = 16) ); FMT_STDOUT("Negative int in binary: 0b{}\n", FMT(i16, -64, .bin = true)); // This writer is stack-allocated. FmtWriter console = FMT_STDOUT_WRITER(); i32 cell_width = 5; i32 table_size = 21; for (i32 i = 0; i < table_size * table_size; i += 1) { i32 x = i % table_size, y = i / table_size; if (x == 0 && y == 0) { fmt_write_arg(&console, &FMT(String, "", .pad = cell_width)); } else if (x == 0 || y == 0) { fmt_write_arg(&console, &FMT(i32, x | y, .pad = cell_width, .padder = ' ')); } else { fmt_write_arg(&console, &FMT(i32, x * y, .pad = cell_width, .padder = ' ')); } if (x == table_size - 1) { fmt_write_char(&console, '\n'); } } }
c source #2
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502 cc65 2.17
6502 cc65 2.18
6502 cc65 2.19
6502 cc65 trunk
ARM GCC 10.2.0 (linux)
ARM GCC 10.2.1 (none)
ARM GCC 10.3.0 (linux)
ARM GCC 10.3.1 (2021.07 none)
ARM GCC 10.3.1 (2021.10 none)
ARM GCC 10.5.0
ARM GCC 11.1.0 (linux)
ARM GCC 11.2.0 (linux)
ARM GCC 11.2.1 (none)
ARM GCC 11.3.0 (linux)
ARM GCC 11.4.0
ARM GCC 12.1.0 (linux)
ARM GCC 12.2.0 (linux)
ARM GCC 12.3.0
ARM GCC 12.4.0
ARM GCC 12.5.0
ARM GCC 13.1.0 (linux)
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 13.3.0
ARM GCC 13.3.0 (unknown-eabi)
ARM GCC 13.4.0
ARM GCC 13.4.0 (unknown-eabi)
ARM GCC 14.1.0
ARM GCC 14.1.0 (unknown-eabi)
ARM GCC 14.2.0
ARM GCC 14.2.0 (unknown-eabi)
ARM GCC 14.3.0
ARM GCC 14.3.0 (unknown-eabi)
ARM GCC 15.1.0
ARM GCC 15.1.0 (unknown-eabi)
ARM GCC 15.2.0
ARM GCC 15.2.0 (unknown-eabi)
ARM GCC 4.5.4 (linux)
ARM GCC 4.6.4 (linux)
ARM GCC 5.4 (linux)
ARM GCC 5.4.1 (none)
ARM GCC 6.3.0 (linux)
ARM GCC 6.4.0 (linux)
ARM GCC 7.2.1 (none)
ARM GCC 7.3.0 (linux)
ARM GCC 7.5.0 (linux)
ARM GCC 8.2.0 (WinCE)
ARM GCC 8.2.0 (linux)
ARM GCC 8.3.1 (none)
ARM GCC 8.5.0 (linux)
ARM GCC 9.2.1 (none)
ARM GCC 9.3.0 (linux)
ARM GCC trunk (linux)
ARM msvc v19.0 (ex-WINE)
ARM msvc v19.10 (ex-WINE)
ARM msvc v19.14 (ex-WINE)
ARM64 GCC 10.2.0
ARM64 GCC 10.3.0
ARM64 GCC 10.4.0
ARM64 GCC 10.5.0
ARM64 GCC 11.1.0
ARM64 GCC 11.2.0
ARM64 GCC 11.3.0
ARM64 GCC 11.4.0
ARM64 GCC 12.1.0
ARM64 GCC 12.2.0
ARM64 GCC 12.3.0
ARM64 GCC 12.4.0
ARM64 GCC 12.5.0
ARM64 GCC 13.1.0
ARM64 GCC 13.2.0
ARM64 GCC 13.3.0
ARM64 GCC 13.4.0
ARM64 GCC 14.1.0
ARM64 GCC 14.2.0
ARM64 GCC 14.3.0
ARM64 GCC 15.1.0
ARM64 GCC 15.2.0
ARM64 GCC 4.9.4
ARM64 GCC 5.4
ARM64 GCC 5.5.0
ARM64 GCC 6.3
ARM64 GCC 6.4.0
ARM64 GCC 7.3.0
ARM64 GCC 7.5.0
ARM64 GCC 8.2.0
ARM64 GCC 8.5.0
ARM64 GCC 9.3.0
ARM64 GCC 9.4.0
ARM64 GCC 9.5.0
ARM64 GCC trunk
ARM64 Morello GCC 10.1.0 Alpha 1
ARM64 Morello GCC 10.1.2 Alpha 2
ARM64 msvc v19.14 (ex-WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 12.4.0
AVR gcc 12.5.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 13.4.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 14.3.0
AVR gcc 15.1.0
AVR gcc 15.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF clang 19.1.0
BPF clang 20.1.0
BPF clang 21.1.0
BPF gcc 13.1.0
BPF gcc 13.2.0
BPF gcc 13.3.0
BPF gcc 13.4.0
BPF gcc 14.1.0
BPF gcc 14.2.0
BPF gcc 14.3.0
BPF gcc 15.1.0
BPF gcc 15.2.0
BPF gcc trunk
C2Rust (master)
Chibicc 2020-12-07
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
HPPA gcc 14.3.0
HPPA gcc 15.1.0
HPPA gcc 15.2.0
K1C gcc 7.4
K1C gcc 7.5
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
KVX ACB 5.0.0 (GCC 12.2.1)
KVX ACB 5.2.0 (GCC 13.2.1)
LC3 (trunk)
M68K clang (trunk)
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 13.4.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68K gcc 14.3.0
M68K gcc 15.1.0
M68K gcc 15.2.0
MRISC32 gcc (trunk)
MSP430 gcc 12.1.0
MSP430 gcc 12.2.0
MSP430 gcc 12.3.0
MSP430 gcc 12.4.0
MSP430 gcc 12.5.0
MSP430 gcc 13.1.0
MSP430 gcc 13.2.0
MSP430 gcc 13.3.0
MSP430 gcc 13.4.0
MSP430 gcc 14.1.0
MSP430 gcc 14.2.0
MSP430 gcc 14.3.0
MSP430 gcc 15.1.0
MSP430 gcc 15.2.0
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
ORCA/C 2.1.0
ORCA/C 2.2.0
ORCA/C 2.2.1
POWER64 gcc 11.2.0
POWER64 gcc 12.1.0
POWER64 gcc 12.2.0
POWER64 gcc 12.3.0
POWER64 gcc 12.4.0
POWER64 gcc 12.5.0
POWER64 gcc 13.1.0
POWER64 gcc 13.2.0
POWER64 gcc 13.3.0
POWER64 gcc 13.4.0
POWER64 gcc 14.1.0
POWER64 gcc 14.2.0
POWER64 gcc 14.3.0
POWER64 gcc 15.1.0
POWER64 gcc 15.2.0
POWER64 gcc trunk
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 12.4.0
RISC-V (32-bits) gcc 12.5.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 13.3.0
RISC-V (32-bits) gcc 13.4.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 14.3.0
RISC-V (32-bits) gcc 15.1.0
RISC-V (32-bits) gcc 15.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 12.4.0
RISC-V (64-bits) gcc 12.5.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 13.3.0
RISC-V (64-bits) gcc 13.4.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 14.3.0
RISC-V (64-bits) gcc 15.1.0
RISC-V (64-bits) gcc 15.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 19.1.0
RISC-V rv32gc clang 20.1.0
RISC-V rv32gc clang 21.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 19.1.0
RISC-V rv64gc clang 20.1.0
RISC-V rv64gc clang 21.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SDCC 4.0.0
SDCC 4.1.0
SDCC 4.2.0
SDCC 4.3.0
SDCC 4.4.0
SDCC 4.5.0
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 12.4.0
SPARC LEON gcc 12.5.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 13.4.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC LEON gcc 14.3.0
SPARC LEON gcc 15.1.0
SPARC LEON gcc 15.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 12.5.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 13.4.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC gcc 14.3.0
SPARC gcc 15.1.0
SPARC gcc 15.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 12.5.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 13.4.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
SPARC64 gcc 14.3.0
SPARC64 gcc 15.1.0
SPARC64 gcc 15.2.0
TCC (trunk)
TCC 0.9.27
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 12.5.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 13.4.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI C6x gcc 14.3.0
TI C6x gcc 15.1.0
TI C6x gcc 15.2.0
TI CL430 21.6.1
Tricore gcc 11.3.0 (EEESlab)
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
VAX gcc NetBSDELF 12.4.0 (Apr 16 05:27 2025)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 14.2.0 (20241119)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 14.2.0 (20241119)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 14.2.0 (20241119)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.20 VS16.0
arm64 msvc v19.21 VS16.1
arm64 msvc v19.22 VS16.2
arm64 msvc v19.23 VS16.3
arm64 msvc v19.24 VS16.4
arm64 msvc v19.25 VS16.5
arm64 msvc v19.27 VS16.7
arm64 msvc v19.28 VS16.8
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30 VS17.0
arm64 msvc v19.31 VS17.1
arm64 msvc v19.32 VS17.2
arm64 msvc v19.33 VS17.3
arm64 msvc v19.34 VS17.4
arm64 msvc v19.35 VS17.5
arm64 msvc v19.36 VS17.6
arm64 msvc v19.37 VS17.7
arm64 msvc v19.38 VS17.8
arm64 msvc v19.39 VS17.9
arm64 msvc v19.40 VS17.10
arm64 msvc v19.41 VS17.11
arm64 msvc v19.42 VS17.12
arm64 msvc v19.43 VS17.13
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 12.0.0
armv7-a clang 12.0.1
armv7-a clang 13.0.0
armv7-a clang 13.0.1
armv7-a clang 14.0.0
armv7-a clang 15.0.0
armv7-a clang 16.0.0
armv7-a clang 17.0.1
armv7-a clang 18.1.0
armv7-a clang 19.1.0
armv7-a clang 20.1.0
armv7-a clang 21.1.0
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 12.0.1
armv8-a clang 13.0.0
armv8-a clang 13.0.1
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 19.1.0
armv8-a clang 20.1.0
armv8-a clang 21.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clang 12 for DPU (rel 2023.2.0)
cproc-master
ez80-clang 15.0.0
ez80-clang 15.0.7
llvm-mos commander X16
llvm-mos commodore 64
llvm-mos mega65
llvm-mos nes-cnrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos osi-c1p
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 12.4.0
loongarch64 gcc 12.5.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 13.4.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
loongarch64 gcc 14.3.0
loongarch64 gcc 15.1.0
loongarch64 gcc 15.2.0
mips (el) gcc 12.1.0
mips (el) gcc 12.2.0
mips (el) gcc 12.3.0
mips (el) gcc 12.4.0
mips (el) gcc 12.5.0
mips (el) gcc 13.1.0
mips (el) gcc 13.2.0
mips (el) gcc 13.3.0
mips (el) gcc 13.4.0
mips (el) gcc 14.1.0
mips (el) gcc 14.2.0
mips (el) gcc 14.3.0
mips (el) gcc 15.1.0
mips (el) gcc 15.2.0
mips (el) gcc 4.9.4
mips (el) gcc 5.4
mips (el) gcc 5.5.0
mips (el) gcc 9.5.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips clang 19.1.0
mips clang 20.1.0
mips clang 21.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 12.4.0
mips gcc 12.5.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 13.4.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 14.3.0
mips gcc 15.1.0
mips gcc 15.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 12.4.0
mips64 (el) gcc 12.5.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 13.4.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 14.3.0
mips64 (el) gcc 15.1.0
mips64 (el) gcc 15.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 clang 19.1.0
mips64 clang 20.1.0
mips64 clang 21.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 12.4.0
mips64 gcc 12.5.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 13.4.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 14.3.0
mips64 gcc 15.1.0
mips64 gcc 15.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mips64el clang 19.1.0
mips64el clang 20.1.0
mips64el clang 21.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel clang 19.1.0
mipsel clang 20.1.0
mipsel clang 21.1.0
movfuscator (trunk)
nanoMIPS gcc 6.3.0
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 12.4.0
power gcc 12.5.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 13.4.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 14.3.0
power gcc 15.1.0
power gcc 15.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 12.4.0
power64le gcc 12.5.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 13.4.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 14.3.0
power64le gcc 15.1.0
power64le gcc 15.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
ppci 0.5.5
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 12.4.0
s390x gcc 12.5.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 13.4.0
s390x gcc 14.1.0
s390x gcc 14.2.0
s390x gcc 14.3.0
s390x gcc 15.1.0
s390x gcc 15.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 12.5.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 13.4.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 14.3.0
sh gcc 15.1.0
sh gcc 15.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (ex-WINE)
x64 msvc v19.10 (ex-WINE)
x64 msvc v19.14 (ex-WINE)
x64 msvc v19.20 VS16.0
x64 msvc v19.21 VS16.1
x64 msvc v19.22 VS16.2
x64 msvc v19.23 VS16.3
x64 msvc v19.24 VS16.4
x64 msvc v19.25 VS16.5
x64 msvc v19.27 VS16.7
x64 msvc v19.28 VS16.8
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30 VS17.0
x64 msvc v19.31 VS17.1
x64 msvc v19.32 VS17.2
x64 msvc v19.33 VS17.3
x64 msvc v19.34 VS17.4
x64 msvc v19.35 VS17.5
x64 msvc v19.36 VS17.6
x64 msvc v19.37 VS17.7
x64 msvc v19.38 VS17.8
x64 msvc v19.39 VS17.9
x64 msvc v19.40 VS17.10
x64 msvc v19.41 VS17.11
x64 msvc v19.42 VS17.12
x64 msvc v19.43 VS17.13
x64 msvc v19.latest
x86 CompCert 3.10
x86 CompCert 3.11
x86 CompCert 3.12
x86 CompCert 3.9
x86 gcc 1.27
x86 msvc v19.0 (ex-WINE)
x86 msvc v19.10 (ex-WINE)
x86 msvc v19.14 (ex-WINE)
x86 msvc v19.20 VS16.0
x86 msvc v19.21 VS16.1
x86 msvc v19.22 VS16.2
x86 msvc v19.23 VS16.3
x86 msvc v19.24 VS16.4
x86 msvc v19.25 VS16.5
x86 msvc v19.27 VS16.7
x86 msvc v19.28 VS16.8
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30 VS17.0
x86 msvc v19.31 VS17.1
x86 msvc v19.32 VS17.2
x86 msvc v19.33 VS17.3
x86 msvc v19.34 VS17.4
x86 msvc v19.35 VS17.5
x86 msvc v19.36 VS17.6
x86 msvc v19.37 VS17.7
x86 msvc v19.38 VS17.8
x86 msvc v19.39 VS17.9
x86 msvc v19.40 VS17.10
x86 msvc v19.41 VS17.11
x86 msvc v19.42 VS17.12
x86 msvc v19.43 VS17.13
x86 msvc v19.latest
x86 nvc 24.11
x86 nvc 24.9
x86 nvc 25.1
x86 nvc 25.3
x86 nvc 25.5
x86 nvc 25.7
x86 tendra (trunk)
x86-64 clang (assertions trunk)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 15.0.0
x86-64 clang 16.0.0
x86-64 clang 17.0.1
x86-64 clang 18.1.0
x86-64 clang 19.1.0
x86-64 clang 20.1.0
x86-64 clang 21.1.0
x86-64 clang 3.0.0
x86-64 clang 3.1
x86-64 clang 3.2
x86-64 clang 3.3
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.7
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.1
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.3 (assertions)
x86-64 gcc 10.4
x86-64 gcc 10.4 (assertions)
x86-64 gcc 10.5
x86-64 gcc 10.5 (assertions)
x86-64 gcc 11.1
x86-64 gcc 11.1 (assertions)
x86-64 gcc 11.2
x86-64 gcc 11.2 (assertions)
x86-64 gcc 11.3
x86-64 gcc 11.3 (assertions)
x86-64 gcc 11.4
x86-64 gcc 11.4 (assertions)
x86-64 gcc 12.1
x86-64 gcc 12.1 (assertions)
x86-64 gcc 12.2
x86-64 gcc 12.2 (assertions)
x86-64 gcc 12.3
x86-64 gcc 12.3 (assertions)
x86-64 gcc 12.4
x86-64 gcc 12.4 (assertions)
x86-64 gcc 12.5
x86-64 gcc 12.5 (assertions)
x86-64 gcc 13.1
x86-64 gcc 13.1 (assertions)
x86-64 gcc 13.2
x86-64 gcc 13.2 (assertions)
x86-64 gcc 13.3
x86-64 gcc 13.3 (assertions)
x86-64 gcc 13.4
x86-64 gcc 13.4 (assertions)
x86-64 gcc 14.1
x86-64 gcc 14.1 (assertions)
x86-64 gcc 14.2
x86-64 gcc 14.2 (assertions)
x86-64 gcc 14.3
x86-64 gcc 14.3 (assertions)
x86-64 gcc 15.1
x86-64 gcc 15.1 (assertions)
x86-64 gcc 15.2
x86-64 gcc 15.2 (assertions)
x86-64 gcc 3.4.6
x86-64 gcc 4.0.4
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.5
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx (latest)
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2024.0.0
x86_64 CompCert 3.10
x86_64 CompCert 3.11
x86_64 CompCert 3.12
x86_64 CompCert 3.9
z180-clang 15.0.0
z180-clang 15.0.7
z80-clang 15.0.0
z80-clang 15.0.7
z88dk 2.2
zig cc 0.10.0
zig cc 0.11.0
zig cc 0.12.0
zig cc 0.12.1
zig cc 0.13.0
zig cc 0.14.0
zig cc 0.14.1
zig cc 0.15.1
zig cc 0.6.0
zig cc 0.7.0
zig cc 0.7.1
zig cc 0.8.0
zig cc 0.9.0
zig cc trunk
Options
Source code
// fmt.h // // Include library implementation into the translation unit: // #define FMT_IMPLEMENTATION #ifndef FMT_H #define FMT_H // ================================================================================================= // Configuration // ================================================================================================= // Automatically flush file and console writers after each newline. Otherwise, by default, writers // are only auto-flushed once the internal buffer gets full. // // Enable this option, so you don't have to manually flush the writers. // // #define FMT_FLUSH_AFTER_NEWLINES // Include an interface for dynamic allocation of writers: // #define FMT_DYN_INTERFACE // Define a custom interface implementation for writing into console and files. // // Otherwise, by default: // - WriteFile from kernel32 is used on Windows. // - write from <unistd.h> is used on Linux. #ifndef FMT_FILE_T #define FMT_USE_DEFAULT_FILE_INTERFACE #if defined(_WIN32) #define FMT_FILE_T void * #define FMT_FILE_STDOUT fmt__win32_console_handle(FMT_CONSOLE_STDOUT) #define FMT_FILE_STDERR fmt__win32_console_handle(FMT_CONSOLE_STDERR) #define FMT_FILE_WRITE(file, ptr, size) fmt__file_write(file, (u8 const *) (ptr), size) #elif defined(linux) #define FMT_FILE_T int #define FMT_FILE_STDOUT ((FMT_FILE_T) 1) #define FMT_FILE_STDERR ((FMT_FILE_T) 2) #define FMT_FILE_WRITE(file, ptr, size) fmt__file_write(file, (u8 const *) (ptr), size) #else #error "This platform is not supported by default. " \ "You need to manually define an interface for writing into console and files." #endif #endif // Define a custom allocator for dynamic allocation of writers. // // Otherwise, by default: // - HeapAlloc/HeapFree from kernel32 is used on Windows. // - malloc/free from <stdlib.h> is used on Linux. #if defined(FMT_DYN_INTERFACE) && !defined(FMT_ALLOCATOR_T) #define FMT_USE_DEFAULT_ALLOCATOR #if defined(_WIN32) #define FMT_ALLOCATOR_T void * #define FMT_ALLOCATOR NULL #define FMT_ALLOC(allocator, size) \ ((void) (allocator), HeapAlloc(GetProcessHeap(), 0, size)) #define FMT_DEALLOC(allocator, ptr) \ ((void) (allocator), HeapFree(GetProcessHeap(), 0, ptr)) #elif defined(linux) #define FMT_ALLOCATOR_T void * #define FMT_ALLOCATOR NULL #define FMT_ALLOC(allocator, size) ((void) (allocator), malloc(size)) #define FMT_DEALLOC(allocator, ptr) ((void) (allocator), free(ptr)) #else #error "This platform is not supported by default. " \ \ "You need to either define a custom allocator or disable the interface for " \ "dynamic allocation of writers." #endif #endif #ifndef FMT_BUFFER_SIZE #define FMT_BUFFER_SIZE ((isize) 4096) #endif // Make implementation private to the translation unit: #ifdef FMT_STATIC_IMPLEMENTATION #define FMT_API static #else #define FMT_API #endif #ifndef FMT_USIZE_T #define FMT_USIZE_T unsigned long #define FMT_ISIZE_T long #endif #ifndef FMT_U8_T #define FMT_U8_T unsigned char #define FMT_I8_T char #define FMT_U16_T unsigned short #define FMT_I16_T short #define FMT_U32_T unsigned int #define FMT_I32_T int #define FMT_U64_T unsigned long long #define FMT_I64_T long long #endif #ifndef FMT_MEMCPY #define FMT_MEMCPY fmt__nostd_memcpy #endif #ifndef FMT_MEMMOVE #define FMT_MEMMOVE fmt__nostd_memmove #endif // ================================================================================================= // Usage examples // ================================================================================================= // clang-format off // Formatting a variety of different types of values. #if 0 #define FMT_IMPLEMENTATION #include "fmt.h" int main(void) { FMT_STDOUT("Bool: {} or {}\n", FMT(bool, true), FMT(bool, false)); FMT_STDOUT("ASCII char: '{}'\n", FMT(char, '@')); FMT_STDOUT("Unicode code point: '{}'\n", FMT(CodePoint, 0x1f41b)); FMT_STDOUT("C-style string: \"{}\"\n", FMT(String, "hello")); FMT_STDOUT( "UTF-16 string: \"{}\"\n", FMT(U16String, u"Оказывается, под линуксом строковые литералы с префиксом L кодируются в UTF-32. " u"А этот UTF-16 префикс появился только в C11."), ); FMT_STDOUT("Max 64-bit int: {}\n", FMT(i64, I64_MAX, .plus = true)); FMT_STDOUT("Min 64-bit int: {}\n", FMT(i64, I64_MIN)); FMT_STDOUT( "Hex int: 0x{}\n", FMT(u32, 0xababa, .hex = true, .uppercase = true, .precision = 8) ); FMT_STDOUT("Negative int in binary: 0b{}\n", FMT(i16, -64, .bin = true)); // This writer is stack-allocated. FmtWriter console = FMT_STDOUT_WRITER(); i32 cell_width = 5; i32 table_size = 21; for (i32 i = 0; i < table_size * table_size; i += 1) { i32 x = i % table_size, y = i / table_size; if (x == 0 && y == 0) { fmt_write_arg(&console, &FMT(String, "", .pad = cell_width)); } else if (x == 0 || y == 0) { fmt_write_arg(&console, &FMT(i32, x | y, .pad = cell_width, .padder = ' ')); } else { fmt_write_arg(&console, &FMT(i32, x * y, .pad = cell_width, .padder = ' ')); } if (x == table_size - 1) { fmt_write_char(&console, '\n'); } } fmt_flush(&console); } #endif // Defining a custom formatter. #if 0 #define FMT_IMPLEMENTATION #include "fmt.h" typedef struct { i32 x, y; } Vec2; #define FMT_Vec2(type, name, value, ...) \ FMT_Pointer(type, name, value, .fmt = fmt_vec2, __VA_ARGS__) isize fmt_vec2(FmtWriter *writer, void const *vector) { Vec2 const *v = vector; return FMT_WRITE(writer, "vec2({}, {})", FMT(i32, v->x), FMT(i32, v->y)); } int main(void) { Vec2 vector = {42, -24}; FMT_STDOUT("{}\n", FMT(Vec2, &vector)); } #endif // Defining a custom allocator. #if 0 #include <stddef.h> // NULL #include <stdint.h> // uint8_t, intptr_t #include <stdlib.h> // exit #define FMT_ALLOCATOR_T MyAllocator * typedef struct { uint8_t buffer[8192]; intptr_t size; } MyAllocator; #define FMT_ALLOCATOR (&my_allocator) MyAllocator my_allocator = {.size = 0}; #define FMT_ALLOC my_alloc void *my_alloc(MyAllocator *my_alloc, intptr_t size) { if (my_alloc->size + size <= sizeof(my_alloc->buffer)) { uint8_t *ptr = my_alloc->buffer + my_alloc->size; my_alloc->size += size; return ptr; } else { return NULL; } } #define FMT_DEALLOC my_dealloc void my_dealloc(MyAllocator *my_alloc, void *ptr) { (void) my_alloc; (void) ptr; } #define FMT_IMPLEMENTATION #define FMT_DYN_INTERFACE #include "fmt.h" FmtWriter get_console(void) { FmtWriter console; if (!fmt_dyn_console_writer(FMT_ALLOCATOR, FMT_CONSOLE_STDOUT, &console)) { exit(1); } return console; } int main(void) { FmtWriter console = get_console(); fmt_write_string(&console, "Hey\n"); fmt_flush(&console); fmt_dyn_console_writer_destroy(FMT_ALLOCATOR, &console); } #endif // Rendering to a string + an example of how to use named args. #if 0 #include <stdlib.h> // malloc/free #define FMT_IMPLEMENTATION #include "fmt.h" int main(void) { char const *format = "{pill}Rendering{heart}to{heart}a{heart}string{heart}example{pill}\n"; FormatArg format_args[2] = { FMT_KEY(CodePoint, pill, 0x1f48a, .pad = 3, .align = FMT_ALIGN_CENTER), FMT_KEY(U16String, heart, u"🩷", .pad = 3, .align = FMT_ALIGN_CENTER), }; isize required_string_size = fmt_string_write_args(NULL, 0, format, format_args, 2); char *string = malloc(required_string_size); if (string == NULL) { return 1; } fmt_string_write_args(string, required_string_size, format, format_args, 2); FMT_STDOUT("{}", FMT(StringView, string, .size = required_string_size)); free(string); return 0; } #endif // clang-format on // ================================================================================================= // Typedefs used within the library interface // ================================================================================================= // I tried to make the library implementation compilable with a C++ compiler, however, heavy use of // compound literals in macros (e.g. within FMT and FMT_STDOUT) makes it mostly useless in C++. #ifdef __cplusplus extern "C" { #endif // "ifdef __STDC_VERSION__" check is there because MSVC doesn't define it unless you specify the // "/std" option explicitly. It would obviously be much better to include <stdbool.h> here instead // of reinventing the wheel, but I couldn't care less about C standard library headers, I'm sorry. // // "#define bool _Bool" instead of "typedef _Bool bool", so that you could #undef my bool, if you // don't like it. #if !defined(__cplusplus) && !defined(bool) \ && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 202300L) #define true 1 #define false 0 #define bool _Bool #endif // I don't want to prefix these types, so configure them to be the same as in the code where the // header gets included, if you happen to use the same alias names. (C doesn't complain when you // repeat the same typedef multiple times.) typedef FMT_U8_T u8; typedef FMT_I8_T i8; typedef FMT_U16_T u16; typedef FMT_I16_T i16; typedef FMT_U32_T u32; typedef FMT_I32_T i32; typedef FMT_U64_T u64; typedef FMT_I64_T i64; typedef FMT_USIZE_T usize; typedef FMT_ISIZE_T isize; // ================================================================================================= // Generic polymorphic writer interface // ================================================================================================= typedef void (*FmtWriteFunction)(void *writer_data, u8 const *data, isize size); typedef void (*FmtFlushFunction)(void *writer_data); typedef struct { void *data; FmtWriteFunction write; FmtFlushFunction flush; } FmtWriter; FMT_API void fmt_write(FmtWriter *writer, u8 const *data, isize size); FMT_API void fmt_flush(FmtWriter *writer); // ================================================================================================= // Specific writers' implementations // ================================================================================================= typedef struct { u8 *data; isize size; isize capacity; } FmtBuffer; typedef FMT_FILE_T FmtFile; typedef struct { FmtFile file; FmtBuffer buffer; } FmtFileWriter; FMT_API void fmt_file_writer_write(void *writer_data, u8 const *data, isize size); FMT_API void fmt_file_writer_flush(void *writer_data); typedef enum { FMT_CONSOLE_STDOUT, FMT_CONSOLE_STDERR, } FmtConsoleOutputType; FMT_API FmtFile fmt_console_handle(FmtConsoleOutputType console_type); typedef FmtFileWriter FmtConsoleWriter; typedef FmtBuffer FmtStringWriter; FMT_API void fmt_string_writer_write(void *writer_data, u8 const *data, isize size); FMT_API void fmt_string_writer_flush(void *writer_data); // ================================================================================================= // Dynamically allocated writers (they can be copied around and stored anywhere) // ================================================================================================= #ifdef FMT_DYN_INTERFACE typedef FMT_ALLOCATOR_T FmtAllocator; FMT_API bool fmt_dyn_file_writer(FmtAllocator allocator, FmtFile file, FmtWriter *writer); FMT_API void fmt_dyn_file_writer_destroy(FmtAllocator allocator, FmtWriter *writer); FMT_API bool fmt_dyn_console_writer( FmtAllocator allocator, FmtConsoleOutputType console_type, FmtWriter *writer ); FMT_API void fmt_dyn_console_writer_destroy(FmtAllocator allocator, FmtWriter *writer); FMT_API bool fmt_dyn_string_writer( FmtAllocator allocator, char *string, isize string_capacity, FmtWriter *writer ); FMT_API void fmt_dyn_string_writer_destroy(FmtAllocator allocator, FmtWriter *writer); #endif // FMT_DYN_INTERFACE // ================================================================================================= // Stack-allocated writers (they don't survive getting out of the outer scope) // ================================================================================================= // https://en.cppreference.com/w/c/language/compound_literal // > The unnamed object to which the compound literal evaluates has static storage duration if the // > compound literal occurs at file scope and automatic storage duration if the compound literal // > occurs at block scope (in which case the object's lifetime ends at the end of the enclosing // > block). // // Also: // https://x.com/mono_morphic/status/1869303586502397994 #define FMT_FILE_WRITER(file_) (FmtWriter) { \ .data = &(FmtFileWriter) { \ .file = (file_), \ .buffer = { \ .data = (u8[FMT_BUFFER_SIZE]){}, \ .size = 0, \ .capacity = FMT_BUFFER_SIZE, \ }, \ }, \ .write = fmt_file_writer_write, \ .flush = fmt_file_writer_flush, \ } #define FMT_CONSOLE_WRITER(console_type) (FmtWriter) { \ .data = &(FmtConsoleWriter) { \ .file = fmt_console_handle(console_type), \ .buffer = { \ .data = (u8[FMT_BUFFER_SIZE]){}, \ .size = 0, \ .capacity = FMT_BUFFER_SIZE, \ }, \ }, \ .write = fmt_file_writer_write, \ .flush = fmt_file_writer_flush, \ } #define FMT_STDOUT_WRITER() FMT_CONSOLE_WRITER(FMT_CONSOLE_STDOUT) #define FMT_STDERR_WRITER() FMT_CONSOLE_WRITER(FMT_CONSOLE_STDERR) #define FMT_STRING_WRITER(string, string_capacity) (FmtWriter) { \ .data = &(FmtStringWriter) { \ .data = (u8 *) (string), \ .size = 0, \ .capacity = (string_capacity), \ }, \ .write = fmt_string_writer_write, \ .flush = fmt_string_writer_flush, \ } // ================================================================================================= // Functions for writing primitive values // ================================================================================================= typedef u32 FmtCodePoint; FMT_API isize fmt_write_char(FmtWriter *writer, char value); FMT_API isize fmt_write_char_repeat(FmtWriter *writer, isize repeat_count, char value); FMT_API isize fmt_write_code_point(FmtWriter *writer, FmtCodePoint code_point); FMT_API isize fmt_write_sv(FmtWriter *writer, u8 const *data, isize size); FMT_API isize fmt_write_u16_sv(FmtWriter *writer, u16 const *data, isize size); FMT_API isize fmt_write_string(FmtWriter *writer, char const *data); FMT_API isize fmt_write_u16_string(FmtWriter *writer, u16 const *data); FMT_API isize fmt_write_bool(FmtWriter *writer, bool value); typedef struct { u64 unsigned_value; isize size; bool is_negative; } FmtIntValue; typedef struct { u64 base; bool make_uppercase; bool include_plus; isize precision; } FmtIntParams; FMT_API isize fmt_write_int(FmtWriter *writer, FmtIntValue const *value, FmtIntParams const *params); typedef enum { FMT_ALIGN_RIGHT, FMT_ALIGN_CENTER, FMT_ALIGN_LEFT, } FormatAlignment; FMT_API isize fmt_left_padding_amount(isize content_width, isize padding, FormatAlignment alignment); FMT_API isize fmt_right_padding_amount(isize content_width, isize padding, FormatAlignment alignment); // ================================================================================================= // Format arguments // ================================================================================================= typedef enum { FORMAT_ARG_INT, FORMAT_ARG_BOOL, FORMAT_ARG_CHAR, FORMAT_ARG_CODE_POINT, FORMAT_ARG_STRING_VIEW, FORMAT_ARG_U16_STRING_VIEW, FORMAT_ARG_STRING, FORMAT_ARG_U16_STRING, FORMAT_ARG_POINTER, } FormatArgKind; #define FMT_PADDING_PARAMS \ isize pad; \ char padder; \ FormatAlignment align typedef struct { FMT_PADDING_PARAMS; } FormatArgPaddingParams; typedef struct { FMT_PADDING_PARAMS; u64 value; isize size; bool is_negative; isize precision; bool plus; bool uppercase; bool hex; bool octal; bool bin; } FormatArgInt; typedef struct { FMT_PADDING_PARAMS; bool value; } FormatArgBool; typedef struct { FMT_PADDING_PARAMS; char value; } FormatArgChar; typedef struct { FMT_PADDING_PARAMS; FmtCodePoint value; } FormatArgCodePoint; typedef struct { FMT_PADDING_PARAMS; char const *value; isize max_width; } FormatArgString; typedef struct { FMT_PADDING_PARAMS; u16 const *value; isize max_width; } FormatArgU16String; typedef struct { FMT_PADDING_PARAMS; char const *value; isize size; isize max_width; } FormatArgStringView; typedef struct { FMT_PADDING_PARAMS; u16 const *value; isize size; isize max_width; } FormatArgU16StringView; typedef isize (*FmtPointerWriteFunction)(FmtWriter *writer, void const *pointer); typedef struct { FMT_PADDING_PARAMS; void const *value; FmtPointerWriteFunction fmt; } FormatArgPointer; typedef struct { FormatArgKind kind; char const *name; union { FormatArgPaddingParams padding_params; FormatArgInt int_arg; FormatArgBool bool_arg; FormatArgChar char_arg; FormatArgCodePoint code_point_arg; FormatArgString string_arg; FormatArgU16String u16_string_arg; FormatArgStringView string_view_arg; FormatArgU16StringView u16_string_view_arg; FormatArgPointer pointer_arg; } as; } FormatArg; #define FMT(type, value, ...) FMT_##type(type, NULL, value, __VA_ARGS__) #define FMT_KEY(type, name, value, ...) FMT_##type(type, #name, value, __VA_ARGS__) #define FMT_PLAIN_VALUE(type, type_enum_id, type_union_id, name_, value_, ...) \ (FormatArg) { \ .kind = FORMAT_ARG_##type_enum_id, \ .name = (name_), \ .as.type_union_id##_arg = { \ .value = (value_), \ __VA_ARGS__ \ } \ } FMT_API u64 fmt__i64_abs_as_u64(i64 value); #define FMT_INT(type, name_, value_, ...) \ (FormatArg) { \ .kind = FORMAT_ARG_INT, \ .name = (name_), \ .as.int_arg = { \ .value = (value_) >= 0 ? (u64) (value_) : fmt__i64_abs_as_u64((i64) (value_)), \ .size = sizeof(type), \ .is_negative = (value_) < 0, \ __VA_ARGS__ \ }, \ } #define FMT_bool(type, name, value, ...) \ FMT_PLAIN_VALUE(type, BOOL, bool, name, value, __VA_ARGS__) #define FMT_char(type, name, value, ...) \ FMT_PLAIN_VALUE(type, CHAR, char, name, value, __VA_ARGS__) #define FMT_CodePoint(type, name, value, ...) \ FMT_PLAIN_VALUE(FmtCodePoint, CODE_POINT, code_point, name, value, __VA_ARGS__) #define FMT_String(type, name, value, ...) \ FMT_PLAIN_VALUE(char const *, STRING, string, name, value, __VA_ARGS__) #define FMT_U16String(type, name, value, ...) \ FMT_PLAIN_VALUE(u16 const *, U16_STRING, u16_string, name, value, __VA_ARGS__) #define FMT_StringView(type, name, value, ...) \ FMT_PLAIN_VALUE(char const *, STRING_VIEW, string_view, name, value, __VA_ARGS__) #define FMT_U16StringView(type, name, value, ...) \ FMT_PLAIN_VALUE(u16 const *, U16_STRING_VIEW, u16_string_view, name, value, __VA_ARGS__) #define FMT_Pointer(type, name, value, ...) \ FMT_PLAIN_VALUE(void *, POINTER, pointer, name, value, __VA_ARGS__) // By default MSVC doesn't like "#define FMT_u8(...) FMT_INT(__VA_ARGS__)", unless you specify some // weird flags (-Zc:preprocessor). #define FMT_u8(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_i8(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_u16(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_i16(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_u32(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_i32(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_u64(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_i64(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_usize(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) #define FMT_isize(type, name, value, ...) FMT_INT(type, name, value, __VA_ARGS__) // ================================================================================================= // Format arguments writer function // ================================================================================================= // +1 dummy format arg in the array and then -1 afterwards are there to top MSVC from complaining // about sizeof returning 0 (C4034 warning), when there are no format args. #define FMT_FORMAT_ARGS_COUNT(...) \ ((isize) sizeof((FormatArg[]) {(FormatArg) {}, __VA_ARGS__}) / sizeof(FormatArg) - 1) FMT_API isize fmt_write_arg(FmtWriter *writer, FormatArg const *format_arg); FMT_API isize fmt_write_args( FmtWriter *writer, char const *format, FormatArg const *format_args, isize format_args_count ); // I don't really like that this macro pastes a list of format arguments twice into the // preprocessed source code, but I think in C99 there is no way to avoid this. // // One solution is to save a list of format args into a local variable and then compute the number // of args using this variable, but then I'm not going to be able to "return" the number of bytes // written into the writer from this macro. // // Another solution is to use a sentinel-terminated list of format args, but then there comes the // problem of appending a sentinel element to an empty __VA_ARGS__ list, which is impossible to // solve without something like __VA_OPT__. (Ugly solutions like this one // https://stackoverflow.com/a/11172679 are out of the question.) #define FMT_WRITE(writer, format, ...) \ fmt_write_args( \ writer, \ format, \ (FormatArg[]) {__VA_ARGS__}, \ FMT_FORMAT_ARGS_COUNT(__VA_ARGS__) \ ) // ================================================================================================= // Helpers for quickly writing and immediately flushing // ================================================================================================= FMT_API isize fmt_console_write_args( FmtConsoleOutputType output_type, char const *format, FormatArg const *format_args, isize format_args_count ); #define FMT_STDOUT(format, ...) \ fmt_console_write_args( \ FMT_CONSOLE_STDOUT, \ format, \ (FormatArg[]) {__VA_ARGS__}, \ FMT_FORMAT_ARGS_COUNT(__VA_ARGS__) \ ) #define FMT_STDERR(format, ...) \ fmt_console_write_args( \ FMT_CONSOLE_STDERR, \ format, \ (FormatArg[]) {__VA_ARGS__}, \ FMT_FORMAT_ARGS_COUNT(__VA_ARGS__) \ ) FMT_API isize fmt_string_write_args( char *string, isize capacity, char const *format, FormatArg const *format_args, isize format_args_count ); #define FMT_STRING(string, capacity, format, ...) \ fmt_string_write_args( \ string, \ capacity, \ format, \ (FormatArg[]) {__VA_ARGS__}, \ FMT_FORMAT_ARGS_COUNT(__VA_ARGS__) \ ) FMT_API isize fmt_file_write_args( FmtFile file, char const *format, FormatArg const *format_args, isize format_args_count ); #define FMT_FILE(file, format, ...) \ fmt_file_write_args( \ file, \ format, \ (FormatArg[]) {__VA_ARGS__}, \ FMT_FORMAT_ARGS_COUNT(__VA_ARGS__) \ ) #ifdef __cplusplus } #endif #endif // FMT_H // ================================================================================================= // Implementation section // ================================================================================================= #ifdef FMT_IMPLEMENTATION #undef FMT_IMPLEMENTATION #ifdef __cplusplus #define restrict #endif // It's probably a bad behavior on behalf of a single-header library to leak these unprefixed // #defines into the user's source code, but I just hate the idea of having to prefix simple types // like bool or u64, or constants like NULL or I64_MAX. // // It makes sense to prefix private functions, since in case of a name colision you can't do much, // because of the ODR. On the other hand, when you have colliding #defines, you can detect the // presence of some alien #defines with an #ifdef and then #undef and replace them with your own // implementations. // // I was contemplating doing something like this: // // #ifndef NULL // #define NULL ((void *) 0) // #define FMT_PRIVATE_NULL // #endif // // ... // // #ifdef FMT_PRIVATE_NULL // #undef NULL // #endif // // But it adds way too much complexity, it's even worse than having to include the prefixes. #ifndef NULL #define NULL ((void *) 0) #endif #ifndef UNUSED #define UNUSED(x) ((void) (x)) #endif #ifndef USIZE_MAX #define USIZE_MAX (~((usize) 0)) #endif #ifndef ISIZE_MAX #define ISIZE_MAX ((isize) (USIZE_MAX >> 1)) #endif #ifndef U64_MAX #define U64_MAX (~((u64) 0)) #endif #ifndef I64_MAX #define I64_MAX ((i64) (U64_MAX >> 1)) #define I64_MIN (-I64_MAX - 1) #endif // ================================================================================================= // OS layer // ================================================================================================= #ifdef FMT_USE_DEFAULT_FILE_INTERFACE #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include <Windows.h> #endif #ifdef linux #include <errno.h> #include <unistd.h> #endif #endif #ifdef FMT_USE_DEFAULT_ALLOCATOR #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include <Windows.h> #endif #ifdef linux #include <stdlib.h> #endif #endif // ================================================================================================= // Windows // ================================================================================================= #if defined(FMT_USE_DEFAULT_FILE_INTERFACE) && defined(_WIN32) static FmtFile fmt__win32_console_handle(FmtConsoleOutputType console_type) { FmtFile handle; switch (console_type) { case FMT_CONSOLE_STDOUT: { handle = GetStdHandle(STD_OUTPUT_HANDLE); } break; case FMT_CONSOLE_STDERR: { handle = GetStdHandle(STD_ERROR_HANDLE); } break; default: { handle = INVALID_HANDLE_VALUE; } break; } if (handle == INVALID_HANDLE_VALUE) { ExitProcess(1); } SetConsoleOutputCP(CP_UTF8); return handle; } static void fmt__file_write(FmtFile file, u8 const *data, isize size) { u8 const *data_iter = data; u8 const *data_end = data + size; while (data_iter < data_end) { DWORD bytes_written; BOOL write_result = WriteFile(file, data_iter, data_end - data_iter, &bytes_written, NULL); if (!write_result) { break; } data_iter += bytes_written; } } #endif // defined(FMT_USE_DEFAULT_FILE_INTERFACE) && defined(_WIN32) // ================================================================================================= // Linux // ================================================================================================= #if defined(FMT_USE_DEFAULT_FILE_INTERFACE) && defined(linux) static void fmt__file_write(FmtFile file, u8 const *data, isize size) { u8 const *data_iter = data; u8 const *data_end = data + size; while (data_iter < data_end) { isize write_result = write(file, data_iter, data_end - data_iter); if (write_result == -1) { if (errno == EINTR) { continue; } break; } data_iter += write_result; } } #endif // defined(FMT_USE_DEFAULT_FILE_INTERFACE) && defined(linux) // ================================================================================================= // Functions for working with memory // ================================================================================================= static void *fmt__nostd_memcpy( void *restrict dest_void, void const *restrict source_void, usize size ) { u8 const *source_iter = (u8 const *) source_void; u8 *dest_iter = (u8 *) dest_void; u8 *dest_end = (u8 *) dest_void + size; while (dest_iter < dest_end) { *dest_iter = *source_iter; source_iter += 1; dest_iter += 1; } return dest_void; } static void *fmt__nostd_memmove(void *dest_void, void const *source_void, usize size) { u8 const *source = (u8 const *) source_void; u8 *dest = (u8 *) dest_void; if (source == dest) { return dest_void; } if (dest < source) { u8 const *source_iter = source; u8 *dest_iter = dest; u8 *dest_end = dest + size; while (dest_iter < dest_end) { *dest_iter = *source_iter; source_iter += 1; dest_iter += 1; } } else { u8 const *source_iter = source + size - 1; u8 *dest_iter = dest + size - 1; while (dest_iter >= dest) { *dest_iter = *source_iter; source_iter -= 1; dest_iter -= 1; } } return dest_void; } // ================================================================================================= // Integers-related functions // ================================================================================================= FMT_API u64 fmt__i64_abs_as_u64(i64 self) { if (self >= 0) { return (u64) self; } else if (self != I64_MIN) { return (u64) (-self); } else { return (u64) 1 << 63; } } static isize fmt__isize_min(isize self, isize other) { return self < other ? self : other; } static isize fmt__isize_max(isize self, isize other) { return self > other ? self : other; } // ================================================================================================= // Unicode // ================================================================================================= #define FMT_MAX_CODE_POINT ((FmtCodePoint) 0x10ffff) #define FMT_REPLACEMENT_CHARACTER_CP ((FmtCodePoint) 0xfffd) // ================================================================================================= // UTF-8 // ================================================================================================= static isize fmt__code_point_utf8_size(FmtCodePoint code_point) { if (code_point < 0x80) { return 1; } else if (code_point < 0x800) { return 2; } else if (code_point < 0x10000) { return 3; } else if (code_point <= FMT_MAX_CODE_POINT) { return 4; } else { return fmt__code_point_utf8_size(FMT_REPLACEMENT_CHARACTER_CP); } } static isize fmt__code_point_to_utf8(FmtCodePoint code_point, u8 *data) { if (code_point > FMT_MAX_CODE_POINT) { return fmt__code_point_to_utf8(FMT_REPLACEMENT_CHARACTER_CP, data); } isize utf8_size = fmt__code_point_utf8_size(code_point); if (utf8_size == 1) { data[0] = (u8) code_point; } else if (utf8_size == 2) { data[0] = 0xc0 | (u8) ((code_point >> 6) & (FmtCodePoint) 0x1f); data[1] = 0x80 | (u8) ((code_point >> 0) & (FmtCodePoint) 0x3f); } else if (utf8_size == 3) { data[0] = 0xe0 | (u8) ((code_point >> 12) & (FmtCodePoint) 0x0f); data[1] = 0x80 | (u8) ((code_point >> 6) & (FmtCodePoint) 0x3f); data[2] = 0x80 | (u8) ((code_point >> 0) & (FmtCodePoint) 0x3f); } else { data[0] = 0xf0 | (u8) ((code_point >> 18) & (FmtCodePoint) 0x07); data[1] = 0x80 | (u8) ((code_point >> 12) & (FmtCodePoint) 0x3f); data[2] = 0x80 | (u8) ((code_point >> 6) & (FmtCodePoint) 0x3f); data[3] = 0x80 | (u8) ((code_point >> 0) & (FmtCodePoint) 0x3f); } return utf8_size; } static isize fmt__utf8_size(u8 first_byte) { if ((first_byte & 0x80) == 0) { return 1; } else if ((first_byte & 0xe0) == 0xc0) { return 2; } else if ((first_byte & 0xf0) == 0xe0) { return 3; } else if ((first_byte & 0xf8) == 0xf0) { return 4; } else { // If this function is used to iterate a string, keep going over invalid bytes. return 1; } } static bool fmt__utf8_is_start(u8 byte) { return (byte & 0x80) == 0 || (byte & 0xe0) == 0xc0 || (byte & 0xf0) == 0xe0 || (byte & 0xf8) == 0xf0; } // ================================================================================================= // UTF-16 // ================================================================================================= static isize fmt__utf16_size(u16 first_piece) { if (first_piece < 0xd800 || first_piece > 0xdfff) { return 1; } else if (first_piece < 0xdc00) { return 2; } else { // If this function is used to iterate a string, keep going over invalid pieces. return 1; } } static bool fmt__utf16_is_start(u16 piece) { return piece < 0xdc00 || piece > 0xdfff; } static bool fmt__utf16_is_high_surrogate(u16 piece) { return 0xd800 <= piece && piece < 0xdc00; } static bool fmt__utf16_is_low_surrogate(u16 piece) { return 0xdc00 <= piece && piece < 0xe000; } // ================================================================================================= // UTF-8 String[View] // ================================================================================================= static isize fmt__string_size(char const *string) { u8 const *string_iter = (u8 const *) string; while (*string_iter != 0) { string_iter += 1; } return string_iter - (u8 const *) string; } typedef struct { u8 const *data; isize size; } FmtStringView; static bool fmt__sv_equals(FmtStringView self, FmtStringView other) { if (self.size != other.size) { return false; } u8 const *self_iter = self.data; u8 const *self_end = self.data + self.size; u8 const *other_iter = other.data; while (self_iter < self_end) { if (*self_iter != *other_iter) { return false; } self_iter += 1; other_iter += 1; } return true; } static isize fmt__sv_chars_count(FmtStringView string_view) { isize chars_count = 0; u8 const *string_iter = string_view.data; u8 const *string_end = string_view.data + string_view.size; while (string_iter < string_end) { string_iter += fmt__utf8_size(*string_iter); chars_count += 1; } return chars_count; } static FmtStringView fmt__sv_chop_chars(FmtStringView *string_view, isize chars_count) { u8 const *string_iter = string_view->data; u8 const *string_end = string_view->data + string_view->size; if (string_iter < string_end) { isize chars_left = chars_count; while (chars_left > 0) { isize next_char_size = fmt__utf8_size(*string_iter); if (string_iter + next_char_size > string_end) { break; } string_iter += next_char_size; chars_left -= 1; } } FmtStringView chopped_string = { .data = string_view->data, .size = string_iter - string_view->data, }; string_view->data = string_iter; string_view->size = string_end - string_iter; return chopped_string; } static FmtStringView fmt__sv_from_string(char const *string) { FmtStringView string_view = { .data = (u8 const *) string, .size = fmt__string_size(string), }; return string_view; } // ================================================================================================= // UTF-16 String[View] // ================================================================================================= static isize fmt__u16_string_size(u16 const *string) { u16 const *string_iter = string; while (*string_iter != 0) { string_iter += 1; } return string_iter - string; } typedef struct { u16 const *data; isize size; } FmtU16StringView; static isize fmt__u16_sv_chars_count(FmtU16StringView string_view) { isize chars_count = 0; u16 const *string_iter = string_view.data; u16 const *string_end = string_view.data + string_view.size; while (string_iter < string_end) { string_iter += fmt__utf16_size(*string_iter); chars_count += 1; } return chars_count; } static FmtU16StringView fmt__u16_sv_chop_chars(FmtU16StringView *string_view, isize chars_count) { u16 const *string_iter = string_view->data; u16 const *string_end = string_view->data + string_view->size; if (string_iter < string_end) { isize chars_left = chars_count; while (chars_left > 0) { isize next_char_size = fmt__utf16_size(*string_iter); if (string_iter + next_char_size > string_end) { break; } string_iter += next_char_size; chars_left -= 1; } } FmtU16StringView chopped_string = { .data = string_view->data, .size = string_iter - string_view->data, }; string_view->data = string_iter; string_view->size = string_end - string_iter; return chopped_string; } static FmtU16StringView fmt__u16_sv_from_string(u16 const *string) { FmtU16StringView string_view = { .data = string, .size = fmt__u16_string_size(string), }; return string_view; } static bool fmt__u16_sv_chop_code_point(FmtU16StringView *string_view, FmtCodePoint *code_point) { u16 const *string_iter = string_view->data; u16 const *string_end = string_view->data + string_view->size; if (string_iter == string_end) { return false; } // Skip one u16 piece, if it is invalid. if (!fmt__utf16_is_start(*string_iter)) { *code_point = FMT_REPLACEMENT_CHARACTER_CP; string_iter += 1; string_view->data = string_iter; string_view->size = string_end - string_iter; return true; } // Exit, if the string ends abruptly. isize utf16_size = fmt__utf16_size(*string_iter); if (string_iter + utf16_size > string_end) { return false; } if (utf16_size == 1) { *code_point = (FmtCodePoint) *string_iter; } else if (fmt__utf16_is_low_surrogate(*(string_iter + 1))) { FmtCodePoint high_part = (*string_iter & 0x03ff) << 10; FmtCodePoint low_part = *(string_iter + 1) & 0x03ff; *code_point = (high_part | low_part) + 0x10000; } else { *code_point = FMT_REPLACEMENT_CHARACTER_CP; } string_iter += utf16_size; string_view->data = string_iter; string_view->size = string_end - string_iter; return true; } static isize fmt__u16_sv_utf8_size(FmtU16StringView string) { isize utf8_size = 0; FmtCodePoint next_code_point; while (fmt__u16_sv_chop_code_point(&string, &next_code_point)) { utf8_size += fmt__code_point_utf8_size(next_code_point); } return utf8_size; } static FmtStringView fmt__u16_sv_chop_into_u8_buffer( FmtU16StringView *string_view, u8 *dest, isize dest_size ) { u8 *dest_iter = dest; u8 *dest_end = dest + dest_size; FmtCodePoint next_cp; while (dest_end - dest_iter >= 4 && fmt__u16_sv_chop_code_point(string_view, &next_cp)) { dest_iter += fmt__code_point_to_utf8(next_cp, dest_iter); } FmtStringView result = { .data = dest, .size = dest_iter - dest, }; return result; } // ================================================================================================= // Logging // ================================================================================================= static void fmt__log_error(char const *message) { u8 buffer_data[FMT_BUFFER_SIZE]; FmtConsoleWriter writer_data = { fmt_console_handle(FMT_CONSOLE_STDERR), {buffer_data, 0, FMT_BUFFER_SIZE}, }; FmtWriter writer = {&writer_data, fmt_file_writer_write, fmt_file_writer_flush}; fmt_write_string(&writer, "[ERROR] "); fmt_write_string(&writer, message); fmt_write_char(&writer, '\n'); fmt_flush(&writer); } // ================================================================================================= // Buffer helper // ================================================================================================= static isize fmt_buffer_write(FmtBuffer *buffer, u8 const *data, isize size) { u8 *buffer_begin = buffer->data + buffer->size; u8 *buffer_end = buffer->data + buffer->capacity; isize bytes_to_write = fmt__isize_max(fmt__isize_min(size, buffer_end - buffer_begin), 0); FMT_MEMCPY(buffer_begin, data, bytes_to_write); buffer->size += bytes_to_write; return bytes_to_write; } static bool fmt_buffer_is_full(FmtBuffer *buffer) { return buffer->size == buffer->capacity; } static void fmt_buffer_reset(FmtBuffer *buffer) { buffer->size = 0; } // ================================================================================================= // Generic writer helpers // ================================================================================================= FMT_API void fmt_write(FmtWriter *writer, u8 const *data, isize size) { writer->write(writer->data, data, size); } FMT_API void fmt_flush(FmtWriter *writer) { writer->flush(writer->data); } // ================================================================================================= // Specific writers' implementations // ================================================================================================= FMT_API FmtFile fmt_console_handle(FmtConsoleOutputType console_type) { if (console_type == FMT_CONSOLE_STDOUT) { return FMT_FILE_STDOUT; } else { return FMT_FILE_STDERR; } } #ifdef FMT_FLUSH_AFTER_NEWLINES FMT_API void fmt_file_writer_flush(void *file_writer_void) { FmtFileWriter *file_writer = (FmtFileWriter *) file_writer_void; u8 const *buffer_iter = file_writer->buffer.data; u8 const *buffer_end = file_writer->buffer.data + file_writer->buffer.size; u8 const *line_begin = file_writer->buffer.data; // Write the buffer to the file line by line. while (buffer_iter < buffer_end) { if (*buffer_iter == '\n') { FMT_FILE_WRITE(file_writer->file, line_begin, buffer_iter - line_begin + 1); line_begin = buffer_iter + 1; } buffer_iter += 1; } if (buffer_iter - line_begin == file_writer->buffer.capacity) { // Buffer was not large enough to hold a whole line of text. FMT_FILE_WRITE(file_writer->file, file_writer->buffer.data, file_writer->buffer.size); fmt_buffer_reset(&file_writer->buffer); } else if (line_begin != buffer_end) { // There is an unfinished line at the end of the buffer. FMT_MEMMOVE(file_writer->buffer.data, line_begin, buffer_end - line_begin); file_writer->buffer.size = buffer_end - line_begin; } else { // We exhausted the whole buffer. fmt_buffer_reset(&file_writer->buffer); } } #else FMT_API void fmt_file_writer_flush(void *file_writer_void) { FmtFileWriter *file_writer = (FmtFileWriter *) file_writer_void; FMT_FILE_WRITE(file_writer->file, file_writer->buffer.data, file_writer->buffer.size); fmt_buffer_reset(&file_writer->buffer); } #endif // FMT_FLUSH_AFTER_NEWLINES FMT_API void fmt_file_writer_write(void *file_writer_void, u8 const *data, isize size) { FmtFileWriter *file_writer = (FmtFileWriter *) file_writer_void; u8 const *data_iter = data; u8 const *data_end = data + size; while (data_iter < data_end) { isize bytes_written = fmt_buffer_write(&file_writer->buffer, data_iter, data_end - data_iter); data_iter += bytes_written; if (fmt_buffer_is_full(&file_writer->buffer)) { fmt_file_writer_flush(file_writer); } } #ifdef FMT_FLUSH_AFTER_NEWLINES fmt_file_writer_flush(file_writer); #endif } FMT_API void fmt_string_writer_write(void *string_writer_void, u8 const *data, isize size) { FmtStringWriter *string_writer = (FmtStringWriter *) string_writer_void; fmt_buffer_write(string_writer, data, size); } FMT_API void fmt_string_writer_flush(void *string_writer_void) { UNUSED(string_writer_void); } // ================================================================================================= // Dynamically allocated writers // ================================================================================================= #ifdef FMT_DYN_INTERFACE FMT_API bool fmt_dyn_file_writer(FmtAllocator allocator, FmtFile file, FmtWriter *writer) { FmtFileWriter *writer_data = (FmtFileWriter *) FMT_ALLOC(allocator, sizeof(FmtFileWriter)); if (writer_data == NULL) { return false; } u8 *writer_buffer = (u8 *) FMT_ALLOC(allocator, FMT_BUFFER_SIZE); if (writer_buffer == NULL) { FMT_DEALLOC(allocator, writer_data); return false; } writer_data->file = file; writer_data->buffer.data = writer_buffer; writer_data->buffer.size = 0; writer_data->buffer.capacity = FMT_BUFFER_SIZE; writer->data = writer_data; writer->write = fmt_file_writer_write; writer->flush = fmt_file_writer_flush; return true; } FMT_API void fmt_dyn_file_writer_destroy(FmtAllocator allocator, FmtWriter *writer) { FmtFileWriter *writer_data = (FmtFileWriter *) writer->data; FMT_DEALLOC(allocator, writer_data->buffer.data); FMT_DEALLOC(allocator, writer_data); } FMT_API bool fmt_dyn_console_writer( FmtAllocator allocator, FmtConsoleOutputType console_type, FmtWriter *writer ) { return fmt_dyn_file_writer(allocator, fmt_console_handle(console_type), writer); } FMT_API void fmt_dyn_console_writer_destroy(FmtAllocator allocator, FmtWriter *writer) { fmt_dyn_file_writer_destroy(allocator, writer); } FMT_API bool fmt_dyn_string_writer( FmtAllocator allocator, char *string, isize capacity, FmtWriter *writer ) { FmtStringWriter *string_writer = (FmtStringWriter *) FMT_ALLOC(allocator, sizeof(FmtStringWriter)); if (string_writer == NULL) { return false; } string_writer->data = (u8 *) string; string_writer->capacity = capacity; writer->data = string_writer; writer->write = fmt_string_writer_write; writer->flush = fmt_string_writer_flush; return true; } FMT_API void fmt_dyn_string_writer_destroy(FmtAllocator allocator, FmtWriter *writer) { FMT_DEALLOC(allocator, writer->data); } #endif // FMT_DYN_INTERFACE // ================================================================================================= // Functions' implementations for writing primitive values // ================================================================================================= FMT_API isize fmt_write_char(FmtWriter *writer, char value) { isize data_size = sizeof(char); if (writer != NULL) { fmt_write(writer, (u8 const *) &value, data_size); } return data_size; } FMT_API isize fmt_write_char_repeat(FmtWriter *writer, isize repeat_count, char value) { isize data_size = repeat_count * sizeof(char); if (writer != NULL) { u8 buffer[512]; for (isize i = 0; i < fmt__isize_min(repeat_count, sizeof(buffer)); i += 1) { buffer[i] = value; } isize bytes_left = data_size; while (bytes_left > 0) { fmt_write(writer, buffer, fmt__isize_min(bytes_left, sizeof(buffer))); bytes_left -= sizeof(buffer); } } return data_size; } FMT_API isize fmt_write_code_point(FmtWriter *writer, FmtCodePoint code_point) { u8 data[4]; isize data_size = fmt__code_point_to_utf8(code_point, data); if (writer != NULL) { fmt_write(writer, data, data_size); } return data_size; } FMT_API isize fmt_write_sv(FmtWriter *writer, u8 const *data, isize size) { if (writer != NULL) { fmt_write(writer, (u8 const *) data, size); } return size; } FMT_API isize fmt_write_string(FmtWriter *writer, char const *string) { isize string_size = fmt__string_size(string); return fmt_write_sv(writer, (u8 const *) string, string_size); } FMT_API isize fmt_write_u16_sv(FmtWriter *writer, u16 const *data, isize size) { FmtU16StringView u16_string_view = { .data = data, .size = size, }; if (writer == NULL) { return fmt__u16_sv_utf8_size(u16_string_view); } u8 utf8_buffer[512]; isize bytes_written = 0; while (u16_string_view.size > 0) { FmtStringView string_view = fmt__u16_sv_chop_into_u8_buffer(&u16_string_view, utf8_buffer, sizeof(utf8_buffer)); bytes_written += fmt_write_sv(writer, string_view.data, string_view.size); } return bytes_written; } FMT_API isize fmt_write_u16_string(FmtWriter *writer, u16 const *string) { isize string_size = fmt__u16_string_size(string); return fmt_write_u16_sv(writer, string, string_size); } FMT_API isize fmt_write_bool(FmtWriter *writer, bool value) { if (value) { return fmt_write_string(writer, "true"); } else { return fmt_write_string(writer, "false"); } } FMT_API isize fmt_write_int(FmtWriter *writer, FmtIntValue const *value, FmtIntParams const *params) { if (params->base < 2 || params->base > 16) { fmt__log_error("Invalid integer base."); return 0; } u8 alphabet_lowercase[] = "0123456789abcdef", alphabet_uppercase[] = "0123456789ABCDEF"; u8 *alphabet = alphabet_lowercase; if (params->make_uppercase) { alphabet = alphabet_uppercase; } u64 unsigned_value = value->unsigned_value; bool print_as_negative = value->is_negative; if (params->base != 10 && print_as_negative) { unsigned_value = ~unsigned_value + 1; if (value->size < sizeof(u64)) { u64 smaller_int_mask = (((u64) 1) << (value->size * 8)) - 1; unsigned_value = unsigned_value & smaller_int_mask; } print_as_negative = false; } u8 string_buffer[sizeof(u64) * 8]; u8 *string_begin = string_buffer + sizeof(string_buffer); u8 *string_end = string_buffer + sizeof(string_buffer); if (unsigned_value == 0) { string_begin -= 1; *string_begin = alphabet[0]; } else { while (unsigned_value > 0) { string_begin -= 1; *string_begin = alphabet[unsigned_value % params->base]; unsigned_value /= params->base; } } isize bytes_written = 0; if (print_as_negative) { bytes_written += fmt_write_char(writer, '-'); } else if (params->include_plus && value->unsigned_value != 0) { bytes_written += fmt_write_char(writer, '+'); } bytes_written += fmt_write_char_repeat( writer, fmt__isize_max(0, params->precision - (string_end - string_begin)), '0' ); bytes_written += fmt_write_sv(writer, string_begin, string_end - string_begin); return bytes_written; } static isize fmt__write_int_arg(FmtWriter *writer, FormatArgInt const *arg) { FmtIntParams int_params = { .base = arg->bin ? (u64) 2 : arg->octal ? (u64) 8 : arg->hex ? (u64) 16 : (u64) 10, .make_uppercase = arg->uppercase, .include_plus = arg->plus, .precision = arg->precision, }; FmtIntValue int_value = { .unsigned_value = arg->value, .size = arg->size, .is_negative = arg->is_negative, }; return fmt_write_int(writer, &int_value, &int_params); } FMT_API isize fmt_left_padding_amount(isize content_width, isize padding, FormatAlignment alignment) { isize padding_amount = 0; if (alignment == FMT_ALIGN_RIGHT) { padding_amount = fmt__isize_max(padding - content_width, 0); } else if (alignment == FMT_ALIGN_CENTER) { isize total_padding_amount = fmt__isize_max(padding - content_width, 0); padding_amount = total_padding_amount / 2; } return padding_amount; } FMT_API isize fmt_right_padding_amount(isize content_width, isize padding, FormatAlignment alignment) { isize padding_amount = 0; if (alignment == FMT_ALIGN_LEFT) { padding_amount = fmt__isize_max(padding - content_width, 0); } else if (alignment == FMT_ALIGN_CENTER) { isize total_padding_amount = fmt__isize_max(padding - content_width, 0); padding_amount = total_padding_amount - total_padding_amount / 2; } return padding_amount; } // ================================================================================================= // Utility writer implementation for counting chars in formatted values // ================================================================================================= typedef struct { isize chars_count; } FmtCharsCounter; static void fmt__chars_counter_write(void *writer_data_void, u8 const *data, isize size) { u8 const *data_end = data + size; while (data < data_end && !fmt__utf8_is_start(*data)) { data += 1; } FmtCharsCounter *writer_data = (FmtCharsCounter *) writer_data_void; FmtStringView string_view = {data, data_end - data}; writer_data->chars_count += fmt__sv_chars_count(string_view); } static void fmt__chars_counter_flush(void *writer_data_void) { UNUSED(writer_data_void); } static isize fmt__arg_content_width(FormatArg const *fmt_arg) { FmtCharsCounter writer_data = { .chars_count = 0, }; FmtWriter writer = { .data = &writer_data, .write = fmt__chars_counter_write, .flush = fmt__chars_counter_flush, }; switch (fmt_arg->kind) { case FORMAT_ARG_INT: { FormatArgInt arg = fmt_arg->as.int_arg; fmt__write_int_arg(&writer, &arg); } break; case FORMAT_ARG_BOOL: { FormatArgBool arg = fmt_arg->as.bool_arg; if (arg.value) { return sizeof("true") - 1; } else { return sizeof("false") - 1; } } break; case FORMAT_ARG_CHAR: case FORMAT_ARG_CODE_POINT: { return 1; } break; case FORMAT_ARG_STRING: { FormatArgString arg = fmt_arg->as.string_arg; FmtStringView string_view = fmt__sv_from_string(arg.value); isize chars_count = fmt__sv_chars_count(string_view); if (arg.max_width == 0) { return chars_count; } else { return fmt__isize_min(chars_count, arg.max_width); } } break; case FORMAT_ARG_U16_STRING: { FormatArgU16String arg = fmt_arg->as.u16_string_arg; FmtU16StringView string_view = fmt__u16_sv_from_string(arg.value); isize chars_count = fmt__u16_sv_chars_count(string_view); if (arg.max_width == 0) { return chars_count; } else { return fmt__isize_min(chars_count, arg.max_width); } } break; case FORMAT_ARG_STRING_VIEW: { FormatArgStringView arg = fmt_arg->as.string_view_arg; FmtStringView string_view = {(u8 const *) arg.value, arg.size}; isize chars_count = fmt__sv_chars_count(string_view); if (arg.max_width == 0) { return chars_count; } else { return fmt__isize_min(chars_count, arg.max_width); } } break; case FORMAT_ARG_U16_STRING_VIEW: { FormatArgU16StringView arg = fmt_arg->as.u16_string_view_arg; FmtU16StringView string_view = {arg.value, arg.size}; isize chars_count = fmt__u16_sv_chars_count(string_view); if (arg.max_width == 0) { return chars_count; } else { return fmt__isize_min(chars_count, arg.max_width); } } break; case FORMAT_ARG_POINTER: { FormatArgPointer arg = fmt_arg->as.pointer_arg; arg.fmt(&writer, arg.value); } break; } return writer_data.chars_count; } // ================================================================================================= // Format arguments writer function // ================================================================================================= FMT_API isize fmt_write_arg(FmtWriter *writer, FormatArg const *format_arg) { isize bytes_written = 0; // All of the structs within the FormatArg union share a common sequence of padding parameters // in the begining, so it seems like this cast is not even a UB? // // https://stackoverflow.com/a/20752261 // > One special guarantee is made in order to simplify the use of unions: if a union contains // > several structures that share a common initial sequence (see below), and if the union // > object currently contains one of these structures, it is permitted to inspect the common // > initial part of any of them anywhere that a declaration of the completed type of the union // > is visible. Two structures share a common initial sequence if corresponding members have // > compatible types (and, for bit-fields, the same widths) for a sequence of one or more // > initial members. FormatArgPaddingParams padding_params = format_arg->as.padding_params; char padder = padding_params.padder == 0 ? ' ' : padding_params.padder; isize content_width = fmt__arg_content_width(format_arg); isize left_padding = fmt_left_padding_amount(content_width, padding_params.pad, padding_params.align); bytes_written += fmt_write_char_repeat(writer, left_padding, padder); switch (format_arg->kind) { case FORMAT_ARG_INT: { bytes_written += fmt__write_int_arg(writer, &format_arg->as.int_arg); } break; case FORMAT_ARG_BOOL: { bytes_written += fmt_write_bool(writer, format_arg->as.bool_arg.value); } break; case FORMAT_ARG_CHAR: { bytes_written += fmt_write_char(writer, format_arg->as.char_arg.value); } break; case FORMAT_ARG_CODE_POINT: { bytes_written += fmt_write_code_point(writer, format_arg->as.code_point_arg.value); } break; case FORMAT_ARG_STRING: { FormatArgString arg = format_arg->as.string_arg; FmtStringView string_view = fmt__sv_from_string(arg.value); if (arg.max_width != 0) { string_view = fmt__sv_chop_chars(&string_view, arg.max_width); } bytes_written += fmt_write_sv(writer, string_view.data, string_view.size); } break; case FORMAT_ARG_U16_STRING: { FormatArgU16String arg = format_arg->as.u16_string_arg; FmtU16StringView string_view = fmt__u16_sv_from_string(arg.value); if (arg.max_width != 0) { string_view = fmt__u16_sv_chop_chars(&string_view, arg.max_width); } bytes_written += fmt_write_u16_sv(writer, string_view.data, string_view.size); } break; case FORMAT_ARG_STRING_VIEW: { FormatArgStringView arg = format_arg->as.string_view_arg; FmtStringView string_view = {(u8 const *) arg.value, arg.size}; if (arg.max_width != 0) { string_view = fmt__sv_chop_chars(&string_view, arg.max_width); } bytes_written += fmt_write_sv(writer, string_view.data, string_view.size); } break; case FORMAT_ARG_U16_STRING_VIEW: { FormatArgU16StringView arg = format_arg->as.u16_string_view_arg; FmtU16StringView string_view = {arg.value, arg.size}; if (arg.max_width != 0) { string_view = fmt__u16_sv_chop_chars(&string_view, arg.max_width); } bytes_written += fmt_write_u16_sv(writer, string_view.data, string_view.size); } break; case FORMAT_ARG_POINTER: { FormatArgPointer arg = format_arg->as.pointer_arg; bytes_written += arg.fmt(writer, arg.value); } break; } isize right_padding = fmt_right_padding_amount(content_width, padding_params.pad, padding_params.align); bytes_written += fmt_write_char_repeat(writer, right_padding, padder); return bytes_written; } static FormatArg const *fmt__format_args_get_by_name( FormatArg const *haystack, isize haystack_size, FmtStringView needle ) { FormatArg const *haystack_iter = haystack; while (haystack_iter < haystack + haystack_size) { if (fmt__sv_equals(fmt__sv_from_string(haystack_iter->name), needle)) { return haystack_iter; } haystack_iter += 1; } return NULL; } FMT_API isize fmt_write_args( FmtWriter *writer, char const *format, FormatArg const *format_args, isize format_args_count ) { isize bytes_written = 0; FormatArg const *format_args_iter = format_args; FormatArg const *format_args_end = format_args + format_args_count; char const *format_iter = format; char const *format_begin = format; while (*format_iter != '\0') { if (*format_iter == '{' && *(format_iter + 1) == '\0') { fmt__log_error("An unpaired '{' encountered in the format string."); return bytes_written; } if (*format_iter == '}' && *(format_iter + 1) != '}') { fmt__log_error("An unescaped '}' encountered in the format string."); return bytes_written; } if (*format_iter == '{' && *(format_iter + 1) == '{' || *format_iter == '}' && *(format_iter + 1) == '}') { bytes_written += fmt_write_sv(writer, (u8 const *) format_begin, format_iter - format_begin); bytes_written += fmt_write_char(writer, *format_iter); format_iter += 2; format_begin = format_iter; continue; } if (*format_iter == '{') { bytes_written += fmt_write_sv(writer, (u8 const *) format_begin, format_iter - format_begin); format_iter += 1; FmtStringView arg_name; { char const *arg_name_begin = format_iter; while (*format_iter != '\0' && *format_iter != '}') { format_iter += 1; } if (*format_iter == '\0') { fmt__log_error("An unpaired '{' encountered in the format string."); return bytes_written; } arg_name.data = (u8 const *) arg_name_begin; arg_name.size = format_iter - arg_name_begin; // Skip the closing curly bracket format_iter += 1; format_begin = format_iter; } FormatArg const *current_format_arg; if (arg_name.size == 0) { while (format_args_iter < format_args_end) { if (format_args_iter->name == NULL) { break; } else { format_args_iter += 1; } } if (format_args_iter == format_args_end) { fmt__log_error("Not enough format arguments."); return 0; } current_format_arg = format_args_iter; format_args_iter += 1; } else { current_format_arg = fmt__format_args_get_by_name(format_args, format_args_count, arg_name); if (current_format_arg == NULL) { fmt__log_error("Format arg is not found by name."); return 0; } } bytes_written += fmt_write_arg(writer, current_format_arg); continue; } format_iter += 1; } if (format_begin < format_iter) { bytes_written += fmt_write_sv(writer, (u8 const *) format_begin, format_iter - format_begin); } return bytes_written; } FMT_API isize fmt_console_write_args( FmtConsoleOutputType console_type, char const *format, FormatArg const *format_args, isize format_args_count ) { u8 buffer_data[FMT_BUFFER_SIZE]; FmtConsoleWriter writer_data = { fmt_console_handle(console_type), {buffer_data, 0, FMT_BUFFER_SIZE}, }; FmtWriter writer = {&writer_data, fmt_file_writer_write, fmt_file_writer_flush}; isize bytes_written = fmt_write_args(&writer, format, format_args, format_args_count); fmt_flush(&writer); return bytes_written; } FMT_API isize fmt_string_write_args( char *string, isize capacity, char const *format, FormatArg const *format_args, isize format_args_count ) { if (string == NULL) { return fmt_write_args(NULL, format, format_args, format_args_count); } FmtStringWriter writer_data = {(u8 *) string, 0, capacity}; FmtWriter writer = {&writer_data, fmt_string_writer_write, fmt_string_writer_flush}; isize bytes_written = fmt_write_args(&writer, format, format_args, format_args_count); fmt_flush(&writer); return bytes_written; } FMT_API isize fmt_file_write_args( FmtFile file, const char *format, const FormatArg *format_args, isize format_args_count ) { u8 buffer_data[FMT_BUFFER_SIZE]; FmtConsoleWriter writer_data = {file, {buffer_data, 0, FMT_BUFFER_SIZE}}; FmtWriter writer = {&writer_data, fmt_file_writer_write, fmt_file_writer_flush}; isize bytes_written = fmt_write_args(&writer, format, format_args, format_args_count); fmt_flush(&writer); return bytes_written; } #ifdef __cplusplus #undef restrict #endif #endif // FMT_IMPLEMENTATION
Become a Patron
Sponsor on GitHub
Donate via PayPal
Compiler Explorer Shop
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
Statistics
Changelog
Version tree