mulle-sprintf is a fast, extensible C formatting library for projects that need more than a basic sprintf . It supports standard stdarg and mulle_vararg calls, custom conversion handlers, UTF-8/UTF-16/UTF-32 strings, allocator-aware buffers, integer and floating-point formatting, and convenient sprintf / snprintf / asprintf -style APIs. Designed for the Mulle ecosystem but useful on its own, it combines familiar C formatting with a modular architecture, focused performance optimizations, and clear extension points for application-specific types and output formats.
The extensibility is used in MulleObjCStandardFoundation to add object conversion (%@) and to print BOOL values as 'YES', 'NO' (%bd).
Floating point conversion is done by mulle-dtostr,
but fallback to C library FP can be used with NO_MULLE__DTOSTR.
long double arguments are cast to double before formatting.
Release benchmark, 100,000 iterations on linux:
| Case | mulle | glibc | Ratio |
|---|---|---|---|
int %d |
6.438 ms | 4.211 ms | 1.53x |
int %d x3 |
12.698 ms | 10.798 ms | 1.18x |
int %d x16 |
59.206 ms | 51.998 ms | 1.14x |
int %d x24 |
88.304 ms | 76.374 ms | 1.16x |
long lit + %d |
9.549 ms | 3.038 ms | 3.14x |
literal %% + long tail + %d |
14.429 ms | 6.441 ms | 2.24x |
hex %x |
6.635 ms | 4.354 ms | 1.52x |
hex %x x8 |
32.187 ms | 27.136 ms | 1.19x |
oct %o |
6.714 ms | 4.399 ms | 1.53x |
oct %o x8 |
33.519 ms | 26.976 ms | 1.24x |
mixed 8 conversions |
53.597 ms | 51.750 ms | 1.04x |
float %.6f x3 |
46.727 ms | 52.136 ms | 0.90x |
float %.6f x16 |
226.419 ms | 286.144 ms | 0.79x |
float %.6f x24 |
349.513 ms | 424.260 ms | 0.82x |
string %s + int x2 |
9.919 ms | 7.170 ms | 1.38x |
string %s x8 |
23.267 ms | 18.597 ms | 1.25x |
string %s x24 |
71.808 ms | 52.602 ms | 1.37x |
pad mix (12 conversions) |
88.649 ms | 101.189 ms | 0.88x |
| Release Version | Release Notes | AI Documentation |
|---|---|---|
| RELEASENOTES | DeepWiki for mulle-sprintf |
| File | Description |
|---|---|
mulle_sprintf |
The various sprintf like functions |
| Function | Description |
|---|---|
mulle_sprintf |
sprintf replacement |
mulle_snprintf |
snprintf replacement |
mulle_asprintf |
asprintf replacement |
mulle_buffer_sprintf |
sprintf into mulle_buffer |
mulle_allocator_asprintf |
asprintf with custom allocator |
For more detailed information on each characte consult a sprintf man page.
| Character | Description |
|---|---|
| Β (SPC) | A blank should be left before a positive number. |
0 |
The value should be zero padded. |
# |
The value should be converted to an "alternate form". |
- |
The converted value is to be left adjusted on the field boundary. |
+ |
A sign (+ or -) should always be placed before a number. |
' |
Use thousands' grouping characters (UNUSED) |
b |
BOOL, print as YES or NO (for Objective-C) |
| Characters | Description |
|---|---|
h |
short (or utf16) |
hh |
char (or utf8) |
j |
intmax_t |
l |
long (or utf32) |
ll |
long long |
L |
long double (FP only) |
q |
int64. |
t |
ptrdiff_t |
z |
size_t |
| Character | Description |
|---|---|
i |
int as decimal |
d |
int as decimal |
D |
long int (compatibility) |
u |
unsigned int |
o |
int as octal |
x |
int as lowercase hex |
U |
unsigned long int (compatibility) |
O |
unsigned long int as octal (compatibility) |
X |
unsigned long int as hex (compatibility) |
Used modifiers: all except L
Uppercase conversion specifiers output value such as 0e-20 or nan as uppercase 0E-20 or NAN.
| Character | Description |
|---|---|
a |
double as hex [-]0xh.hhhhp+-d |
e |
double as [-]d.ddde+-dd |
f |
double as [-]ddd.ddd with lowercase for inf/nan |
g |
double in e or f |
A |
double as hex [-]0Xh.hhhhP+-d |
E |
double as [-]d.dddE+-dd |
F |
double as [-]d.ddde+-dd with uppercase for INF/NAN |
G |
double in E or F |
Used modifiers: L.
The actual conversion is done with the C-library sprintf function. This is
contrast with the other conversions, which are not using the C library.
For portability across platforms -nan and -0.0 will not be printed with the
leading minus sign.
| Character | Description |
|---|---|
c |
single character |
C |
wide character (utf16 with h, utf32 with l or no modifier) |
n |
return conversion |
p |
void * as hex with 0x prefix |
s |
char * as utf8 (alternate form #s: escaped for C String) |
S |
wide string (utf16 with h, utf32 with l or no modifier, utf8 with hh) |
Used modifiers: hl
mulle-sprintf provides two sets of character and string format specifiers:
These follow the C standard and use platform-dependent wide character types:
%c - Character:
%c:char(single byte character)%lc:wint_t(wide character, platform-dependent encoding)
%s - String:
%s:char *(null-terminated UTF-8/ASCII string)%ls:wchar_t *(wide string, platform-dependent encoding)- Windows: UTF-16 (wchar_t is 16-bit)
- Linux/Unix: UTF-32 (wchar_t is 32-bit)
Use these when working with C standard library functions or platform APIs that expect wchar_t.
These provide explicit UTF encoding control, avoiding platform-dependent wchar_t:
%C - Wide Character:
%Cor%lC:uint32_t(UTF-32 codepoint, explicit 32-bit)%hC:uint16_t(UTF-16 code unit, explicit 16-bit)
%S - Wide String:
%Sor%lS:uint32_t *(UTF-32 string, explicit 32-bit per character)%hS:uint16_t *(UTF-16 string, explicit 16-bit per character)%hhS:char *(UTF-8 string, same as%s)
Use these when you need guaranteed UTF encoding regardless of platform.
Key Differences:
| Format | Type | Encoding | Platform Dependent? |
|---|---|---|---|
%ls |
wchar_t * |
UTF-16 (Windows) / UTF-32 (Unix) | Yes |
%lS |
uint32_t * |
UTF-32 always | No |
%lc |
wint_t |
UTF-16 (Windows) / UTF-32 (Unix) | Yes |
%lC |
uint32_t |
UTF-32 always | No |
Example:
// Platform-dependent (C standard)
wchar_t *wstr = L"Hello";
mulle_sprintf(buf, "%ls", wstr); // Uses wchar_t encoding
// Platform-independent (explicit UTF-32)
uint32_t utf32_str[] = {'H', 'e', 'l', 'l', 'o', 0};
mulle_sprintf(buf, "%lS", utf32_str); // Always UTF-32
mulle_sprintf(buf, "%S", utf32_str); // Same as %lSmulle-sprintf provides stdlib-compatible functions that work out of the box:
char buf[ 32];
mulle_snprintf( buf, sizeof( buf), "%d", 1848);char *str;
mulle_asprintf( &str, "%s %d", "VfL", 1848);
mulle_free( str);mulle_buffer_do( buffer)
{
mulle_buffer_sprintf( buffer, "%d", 1848);
}mulle-sprintf is a component of the mulle-core library. So in your code include the mulle-core umbrella header:
#include <mulle-core/mulle-core.h>git submodule add https://github.com/mulle-core/mulle-core.git mulle-coreAdd this to your CMakeLists.txt:
add_subdirectory( mulle-core)
target_link_libraries( ${PROJECT_NAME} PRIVATE mulle-core)mulle-sde add github:mulle-core/mulle-coreclib install --out src mulle-core/mulle-sprintfAppend src to your include path (e.g. add -isystem src to your CFLAGS)
and compile all the sources that were downloaded.
Use mulle-sde to build and install mulle-sprintf and all dependencies:
mulle-sde install --prefix /usr/local \
https://github.com/mulle-core/mulle-sprintf/archive/latest.tar.gzInstall the requirements:
| Requirements | Description |
|---|---|
| mulle-buffer | |
| mulle-utf | π€ UTF8-16-32 analysis and manipulation library |
| mulle-vararg | βͺ Access variable arguments in struct layout fashion in C |
| mulle-thread | π Cross-platform thread/mutex/tss/atomic operations in C |
| mulle-dtostr | π§Ά Double to string conversion |
Download the latest tar or zip archive and unpack it.
Install mulle-sprintf into /usr/local with cmake:
PREFIX_DIR="/usr/local"
cmake -B build \
-DMULLE_SDK_PATH="${PREFIX_DIR}" \
-DCMAKE_INSTALL_PREFIX="${PREFIX_DIR}" \
-DCMAKE_PREFIX_PATH="${PREFIX_DIR}" \
-DCMAKE_BUILD_TYPE=Release &&
cmake --build build --config Release &&
cmake --install build --config ReleaseNat! for Mulle kybernetiK