Library / SDK
cppfastio/fast_io avatar
cppfastio/fast_io

fast_io: a freestanding C++20 input and output library built on direct system calls

Freestanding fast input/output for C++20

855 stars69 forksC++NOASSERTION

At a glance

What is it?
fast_io is a header-only C++20 library that replaces the standard streams and C standard I/O with direct system calls, adding native file mapping, Unicode transcoding, hash functions and binary serialisation along the way. It demands a compiler with concepts support, and its licence is a custom permissive one you have to read.
Who is it for?
fast_io fits a C++20 developer whose program is bound by input and output cost, such as a parser, a log pipeline or a competitive programming entry, and who can accept a concepts-capable compiler and a vendored header. It does not fit a team that needs to stay inside the standard library, that has to build on a toolchain without concepts support, or that wants a licence it already recognises.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library actually replaces

fast_io is described as a freestanding input and output library for C++20 whose primary purpose is to supersede the widely used iostream and cstdio libraries. It is header-only, so integration is a matter of including one header, and it is distributed under the permissive Anti-Tivo License, which the README says simplifies integration into any project. Usage mandates a C++20 compiler with support for concepts.

The smallest complete program prints to the C standard output stream:

cpp
#include <fast_io.h>

int main() 
{
	::fast_io::io::print("Hello, fast_io world!\n");
	::fast_io::io::print(::fast_io::c_stdout(), "Equivalent to the function above\n");
}

The design principle underneath is stated as the first feature: using system calls directly, bypassing intermediary layers for enhanced efficiency. That is the whole reason the library exists. Instead of building a FILE stream, a stream buffer and a locale layer, you call the operating system.

The library also interops with both older mechanisms rather than blocking them. It facilitates interoperation with cstdio and iostream, and it implements RAII for C FILE pointers, POSIX file descriptors and win32 and NT handles, so a handle you own is closed correctly when it goes out of scope.

One thing to note about the repository itself: the original fast_io repository has been archived due to accumulated git data, and past commits were moved to a Bitbucket archive dated 2022-01-23. The current tree is the working one, with historical commits accessible rather than living in the same history.

Reading from file descriptors and handles directly

The scan side mirrors the print side. Reading an integer from the C standard input looks like this:

cpp
#include <fast_io.h>

int main() 
{
	int i;
	::fast_io::io::scan(i);
	// Equivalent to the function above
	::fast_io::io::scan(::fast_io::c_stdin(), i);
}

The second form passes the C stream explicitly, which is how you retarget the same call at a different handle.

The native output path goes further, all the way to the raw handle. The README spells out what that means per platform: on non-Windows systems it is the file descriptor 1, on win32 it is GetStdHandle(STD_OUTPUT_HANDLE), and on NT it is RtlGetCurrentPeb()->ProcessParameters->StandardOutput.

cpp
#include <fast_io.h>

int main() 
{
	::fast_io::io::print(::fast_io::out(), "Hello, fast_io world!\n");
}

The out() version is documented as unbuffered, which is the point of it: it writes to the native handle without the layers in between.

There is a broader Native Handle Interface that exposes the internal implementation of FILE pointers and of the C++ streams, and the library explicitly says it abstains from using traits_type and EOF. That last detail matters for anyone porting code: the usual idioms for detecting end of file do not apply here, and you have to work in the library's terms instead of the standard ones.

Memory mapped files that satisfy a contiguous range

File loading is a first class feature rather than an add-on. The loader hands back an object that satisfies std::ranges::contiguous_range:

cpp
#include <fast_io.h>

int main() 
{
	::fast_io::native_file_loader file_data{"text.txt"};
	// file_data satisfies ::std::ranges::contiguous_range
}

That single property is what makes the loader usable with the standard range algorithms. A contiguous range is one whose elements are laid out in memory with no indirection, so a mapped file is something you can span, search or feed to a range adaptor without copying it into a string first.

The fallback is documented too: on platforms that do not support memory mapping, native_file_loader reads files into allocated space instead. The interface stays the same, which is the useful part, since a program written against the loader compiles and behaves the same way on a platform without mapping support.

The library also supports memory mapping as a general feature, and the examples directory has dedicated folders for file I/O and filesystem work, alongside others for legacy interoperation, OpenSSL, Qt, network, i18n, LLVM integration and an operating system kernel driver sample, which tells you how far the handle layer reaches.

Unicode codecs, formats and the deliberate lack of printf

The library embraces Unicode with UTF-8, UTF-16 and UTF-32, and adds codecvt support for GB18030 and UTF-EBCDIC, with the stated reason that these are not properly addressed by the standard library. POSIX iconv is supported for transcoding, and there is a separate i18n repository that houses the i18n source files.

Formatting is a place where the library deliberately departs from convention. It employs static I/O manipulators in lieu of format strings, and it supplies basic, Lua, Python and similar formatting for print and scan while omitting C and C++ formatting, and the reason given is security concerns. That is an unusual and defensible trade: the format string is the injection risk, and a static manipulator cannot carry one.

Error handling is consistent, with exceptions as the sole error reporting mechanism, excluding std::error_code, std::system_error and redundant bounds checking. That means a failed write throws rather than returning a status, and it means the library is not usable in a noexcept-only path without catching.

The library also minimises misuse potential unlike std::endl, which is worth noting because the usual performance trap in C++ output code is exactly that flush.

Other capabilities in the feature list include binary serialisation for trivially copyable types and standard containers, zlib compression and decompression, a round-trip floating-point algorithm, optional locale support, filesystem capabilities, and hash support spanning intrinsic SHA-1, HMAC-SHA1, SHA-256, HMAC-SHA256, SHA-512 and HMAC-SHA512 alongside the non-cryptographic Jenkins Hash. The examples directory has dedicated folders for sha, random, match and status work, which is a good map of those areas.

The benchmark table and how to read it

The README publishes a benchmark for I/O of ten million integers, with the objective stated as writing ten million integers from 0 to 10M to a file and reading them back from the same file. All benchmark details sit in benchmarks/0000.10m_size_t/unit.

The headline comparison on Windows is between stdio, fstream, fstream with the rdbuf.sputc trick, and fast_io's own buffer types. In the published table stdio's fprintf and fscanf take 2.412987s for output and 5.607791s for input, fstream takes 0.462012s and 1.192s, the rdbuf.sputc trick takes 0.33895s and 1.170173s, and fast_io's i/obuf_file takes 0.04903s and 0.080996s. The mutex variant, which is thread safe, takes 0.146064s and 0.113155s, and a locale imbued variant takes 0.065988s and 0.086012s.

Two caveats matter more than the numbers. First, the column set is Windows-specific and the platforms named are MinGW-W64 GCC 11.0.0 and MSVCRT with libstdc++, so this is one toolchain family, not a general result. Second, the README notes that libstdc++'s BUFSIZ was adjusted to 1048576 because of MinGW-W64's poor performance with the default 512 bytes, and that GCC has since raised it to 4096 by itself.

That second point is the one to carry away. The comparison between a buffer that is 1MB and one that is 512 bytes is largely a comparison of buffer sizes, and the project's own note shows how much of the gap a stock compiler can close on its own. The library's own FAQ makes a similar point about the name, saying that fast does not strictly denote the absolute fastest I/O library available, but rather that it consistently surpasses the performance of iostream and cstdio.

Where fast_io is the wrong choice

The most obvious objection is that it is not standard. A header-only library with a custom licence, hosted on a GitHub repository whose licence GitHub cannot classify, with a Chinese translation of the licence alongside it, is a different procurement conversation from including something from the standard library or from a package manager with a familiar licence. Reading license.txt before you vendor it is not optional.

The compiler requirement is the second constraint. Concepts support in C++20 narrows the set of toolchains you can use, and the library supports freestanding mode, which is a sign of ambition rather than of compatibility. If your build matrix includes an older compiler, this is a hard stop rather than a tuning problem.

The scope question is the third. The feature list runs far beyond input and output: OpenSSL BIO integration, Qt QFile support, MFC CFile support, a graphics debugging I/O mode with an optional GUI, hash algorithms, compression, filesystem work, an LLVM example and a kernel driver example. Each of those is a surface area with its own maintenance behaviour, and a project that only wants fast printing is taking on a much larger header with a much larger set of dependencies and platform-specific code paths.

There is also the stated plan for after C++20: module support, coroutine support for asynchronous I/O, and a refactoring following the integration of zero-overhead deterministic exceptions into the standard. That is a road map for a library still finding its final shape, which is normal for a young project and worth accounting for in a long-lived codebase.

The alternative is honest and simple: a buffered FILE stream with a large buffer set at startup, or std::to_chars and std::from_chars for numeric conversion, both of which are standard and land in C++17. For most programs the gap fast_io closes is small, and it is the benchmark table above, on your platform and your data, that decides whether it is worth the dependency.

Editorial conclusion

fast_io fits a C++20 developer whose program is bound by input and output cost, such as a parser, a log pipeline or a competitive programming entry, and who can accept a concepts-capable compiler and a vendored header. It does not fit a team that needs to stay inside the standard library, that has to build on a toolchain without concepts support, or that wants a licence it already recognises. Before adopting it, read license.txt yourself, since the licence field on the repository is classified as a custom one and a separate Chinese translation sits alongside it, confirm your compiler and platform are in the supported list in support.md, and benchmark against your own buffers rather than trusting the published table, which was produced on MinGW-W64 and MSVCRT setups.

Frequently asked questions

What is fast_io and what does it replace?

fast_io is a header-only C++20 input and output library whose stated purpose is to supersede iostream and cstdio by using system calls directly and bypassing intermediary layers. It requires a C++20 compiler with concepts support and is distributed under the Anti-Tivo License.

How do I print to the native stdout with fast_io?

Call print with the native output handle, which is the file descriptor 1 on non-Windows systems, GetStdHandle(STD_OUTPUT_HANDLE) on win32 and the process parameters standard output on NT. The out() form is unbuffered, and the README notes the original repository was archived with past commits kept in a Bitbucket archive.

Does fast_io support memory mapped files?

Yes. The native_file_loader reads a file and the resulting object satisfies std::ranges::contiguous_range, so range algorithms can work on it directly. On platforms without memory mapping support, the loader reads the file into allocated space instead and keeps the same interface.

How does fast_io handle formatting and errors?

It uses static I/O manipulators instead of format strings, and supplies basic, Lua and Python style formatting while omitting C and C++ formatting for security reasons. Errors are reported through exceptions as the sole mechanism, with std::error_code, std::system_error and redundant bounds checking excluded.

What platforms and compilers does fast_io support?

The README points to support.md for a comprehensive list of supported elements and platform-specific usage instructions, and offers a discord server and a QQ group for questions. It also names a licensing note worth checking, since the licence field on the repository is classified as a custom one with a Chinese translation alongside license.txt.

Official sources

  1. cppfastio/fast_io on GitHub
  2. Issues
  3. Project website
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cppfastio-fast-io.svg)](https://hysenlabs.com/projects/cppfastio-fast-io)

Was this useful?