Ch12.3: The ibuf_white_hole_engine
What is ibuf_white_hole_engine?
::fast_io::ibuf_white_hole_engine is a cryptographically secure random number
engine that reads directly from the operating system's entropy source. The name "white hole"
is a metaphor: unlike a black hole that absorbs everything, a white hole emits
random bytes.
This engine satisfies the C++ UniformRandomBitGenerator requirement, which
means it works seamlessly with all distributions from <random>, such as
::std::uniform_int_distribution, ::std::bernoulli_distribution,
and others.
Key Features
-
Buffered I/O: Reads 8 KB at a time from the OS entropy source, amortizing
syscall overhead. Most calls to generate random numbers are just a pointer bump and
memcpy— no syscall. - Secure memory clearing: The internal buffer is securely wiped when the engine is destroyed, protecting cryptographic key material.
-
Automatic platform selection: Uses the best available entropy source for
your platform (
getrandom(),/dev/urandom,RtlGenRandom, etc.). -
Standards-conforming: Works with any C++ distribution from
<random>. - Thread-safe: Each engine instance is independent. Multiple threads can use separate engine instances without synchronization.
Basic Usage
#include <fast_io.h>
#include <fast_io_device.h>
#include <random>
int main() {
using namespace ::fast_io::io;
// Create a cryptographically secure random engine
::fast_io::ibuf_white_hole_engine eng;
// Use with uniform_int_distribution
::std::uniform_int_distribution<::std::uint32_t> dist(1, 100);
// Generate random numbers
for (::std::size_t i{}; i != 10; ++i) {
println("Random number in [1, 100]: ", dist(eng));
}
// Use with bernoulli_distribution for true/false
::std::bernoulli_distribution coin_flip(0.5);
for (::std::size_t i{}; i != 5; ++i) {
bool heads = coin_flip(eng);
println("Coin flip: ", ::fast_io::mnp::os_c_str(heads ? "heads" : "tails"));
}
}
Related Topics
The ibuf_white_hole_engine also works with ::std::shuffle for
cryptographically secure container shuffling. See
Ch12.5: Shuffling with std::shuffle for details.
How It Works
The engine is implemented as a type alias:
using ibuf_white_hole_engine = basic_white_hole_engine<ibuf_white_hole>;
Where ibuf_white_hole is an input-buffered wrapper around the platform-specific
random source. The buffer mode includes buffer_mode::secure_clear, which ensures
the buffer is wiped when destroyed.
The operator()() method reads sizeof(::std::size_t) bytes from the
buffered source and returns them as a ::std::size_t. Most calls are served from
the buffer without a syscall.
Reading Raw Bytes with ibuf_white_hole
If you need raw random bytes (for example, to create cryptographic keys or tokens), you can
use ::fast_io::ibuf_white_hole directly as an input stream:
#include <fast_io.h>
#include <fast_io_device.h>
#include <array>
int main() {
using namespace ::fast_io::io;
// Create a buffered white hole (input stream)
::fast_io::ibuf_white_hole rng;
// Read 32 random bytes
::fast_io::array<::std::byte, 32> buffer;
::fast_io::operations::read_all(rng, buffer.data(), buffer.data() + buffer.size());
// Print as hex
println("Random bytes (hex):");
for (::std::size_t i{}; i != buffer.size(); ++i) {
print(::fast_io::mnp::hex(::fast_io::mnp::upper(buffer[i])), " ");
if ((i + 1) % 16 == 0) {
print("\n");
}
}
// Use for generating a UUID (16 bytes)
::fast_io::array<::std::byte, 16> uuid_bytes;
::fast_io::operations::read_all(rng, uuid_bytes.data(), uuid_bytes.data() + uuid_bytes.size());
println("\nUUID bytes:");
for (::std::size_t i{}; i != uuid_bytes.size(); ++i) {
print(::fast_io::mnp::hex(uuid_bytes[i]));
if (i == 3 || i == 5 || i == 7 || i == 9) {
print('-');
}
}
print("\n");
}
ibuf_white_hole is the underlying input stream type. It provides the same
buffered, cryptographically secure random bytes as the engine, but through the standard
I/O interface (read_all, read_some, etc.).
One-Time Use: native_white_hole
If you only need to generate random bytes once (such as creating a single secret key or
token), use ::fast_io::native_white_hole instead of
ibuf_white_hole. The native_white_hole is unbuffered and makes
direct system calls, which is more efficient when you won't reuse the stream.
#include <fast_io.h>
#include <fast_io_device.h>
#include <array>
int main() {
using namespace ::fast_io::io;
// Generate a single API key (32 bytes)
::fast_io::native_white_hole rng;
::fast_io::array<::std::byte, 32> api_key;
::fast_io::operations::read_all(rng, api_key.data(), api_key.data() + api_key.size());
// Guard ensures api_key is securely cleared when it goes out of scope
::fast_io::secure_clear_guard guard(api_key.data(), api_key.size());
println("Generated API key (32 bytes)");
// No need to keep rng around - we only used it once
// The unbuffered approach is more efficient for one-time use
// api_key will be securely cleared automatically when guard is destroyed
}
::fast_io::secure_clear_guard to
automatically wipe sensitive data like API keys, tokens, or cryptographic keys from memory
when they go out of scope. This RAII guard prevents secrets from leaking through memory
dumps, swap files, or debugging tools, even if exceptions occur.
When to use native_white_hole vs ibuf_white_hole:
-
Use
native_white_holefor one-time generation of secrets, keys, or tokens where you won't reuse the stream. No buffering overhead. -
Use
ibuf_white_holeoribuf_white_hole_enginewhen you need multiple random values over time. The 8KB buffer amortizes syscall overhead.
When to Use
Use ibuf_white_hole_engine when:
- You need cryptographically secure random numbers
- Generating cryptographic keys, tokens, or session IDs
- Salting passwords
- Any security-sensitive application
- You don't need reproducible randomness
For reproducible randomness (debugging, testing, simulations), see Ch12.4: Seeding Reproducible Engines.
Key Takeaways
-
ibuf_white_hole_engineis a cryptographically secure, buffered random engine that automatically uses the best platform-specific entropy source. - It satisfies the UniformRandomBitGenerator requirement and works directly with all C++ distributions.
- The 8 KB buffer amortizes syscall overhead, making it fast despite reading from the OS.
- The buffer is securely cleared on destruction, protecting sensitive data.
- Use it for security-sensitive applications where unpredictability is required.