Ch11.4: I/O Operations

Overview

All low-level I/O operations live in the ::fast_io::operations namespace. These are the raw building blocks that higher-level functions like ::fast_io::io::println and ::fast_io::read are built on. You rarely need to call them directly, but understanding them is important for performance-critical code and for writing custom devices.

1. read_some / read_all

read_some attempts to read into a contiguous buffer from a device. It may read fewer bytes than requested and returns a pointer past the last byte read. This mirrors the semantics of POSIX read() — a short read is not an error (it may indicate EOF or a non-blocking device).

read_all loops internally until the buffer is completely filled or EOF/error occurs. This is what you usually want when reading a known amount of data.


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::ibuf_file ibf{"input.txt"};

    char buffer[1024];
    char* first{buffer};
    char* last{buffer + sizeof(buffer)};

    // read_some: may do a partial read.
    // Returns pointer past the last byte read.
    char* next = ::fast_io::operations::read_some(ibf, first, last);

    // Process what was read
    ::std::size_t bytes_read = static_cast<::std::size_t>(next - first);
    println("Read ", bytes_read, " bytes");

    // Read more with read_all (loops until buffer is full or EOF)
    ::fast_io::operations::read_all(ibf, next, last);
}

2. read_some_bytes / read_all_bytes

Identical to read_some / read_all but operate on ::std::byte* instead of char*. Use these for binary data.


#include <fast_io.h>
#include <fast_io_device.h>
#include <cstddef>

int main() {
    ::fast_io::ibuf_file ibf{"binary.bin"};

    ::std::byte data[256];

    ::std::byte* first{data};
    ::std::byte* last{data + 256uz};

    // Read all bytes (loops until buffer is full or EOF)
    ::fast_io::operations::read_all_bytes(ibf, first, last);
}

3. pread_some / pread_all

Positional reads — read at a given file offset without changing the file’s current position. This mirrors POSIX pread(). Useful for concurrent readers that must not disturb each other’s file position.

The signature takes an extra offset parameter (in bytes from the start of the file).


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::ibuf_file ibf{"data.bin"};

    char block0[4];
    char block1[4];

    // Read 4 bytes from offset 1024 without moving the file position
    ::fast_io::operations::pread_all(ibf, block1, block1 + 4uz, 1024uz);

    // Read 4 bytes from offset 0; file position is still 0
    ::fast_io::operations::pread_all(ibf, block0, block0 + 4uz, 0uz);
}

4. pread_some_bytes / pread_all_bytes

Same as pread_some / pread_all but accept ::std::byte*. Use for binary positional reads.


#include <fast_io.h>
#include <fast_io_device.h>
#include <cstddef>

int main() {
    ::fast_io::ibuf_file ibf{"binary.bin"};

    ::std::byte header[4];

    // Read PNG header from offset 0
    ::fast_io::operations::pread_all_bytes(ibf, header, header + 4uz, 0uz);
}

5. scatter_read_some / scatter_read_all

Scatter (gather) reads read into multiple non-contiguous buffers in a single syscall — equivalent to POSIX readv(). This avoids copying data into a temporary contiguous buffer and then distributing it.

The argument is a span of ::fast_io::io_scatter_t, each describing one base/length pair.


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::ibuf_file ibf{"scatter.txt"};

    char part1[10];
    char part2[10];
    char part3[10];

    ::fast_io::io_scatter_t const scatters[]{
        {part1, sizeof(part1)},
        {part2, sizeof(part2)},
        {part3, sizeof(part3)},
    };

    // One syscall reads into all three buffers
    ::fast_io::operations::scatter_read_all(ibf, scatters, scatters + 3uz);
}

6. scatter_pread_some / scatter_pread_all

Combines scatter reads with positional reads — reads into multiple buffers from a given offset without changing the file position. This is the scatter equivalent of preadv().


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::ibuf_file ibf{"scatter_pos.bin"};

    char part1[3];
    char part2[3];

    ::fast_io::io_scatter_t const scatters[]{
        {part1, 3uz},
        {part2, 3uz},
    };

    // Read 6 bytes from offset 512 into two buffers, file position unchanged
    ::fast_io::operations::scatter_pread_all(ibf, scatters, scatters + 2uz, 512uz);
}

8. write_some / write_all

write_some attempts to write a contiguous buffer to a device. It may write fewer bytes than requested and returns a pointer to the first unwritten character. This mirrors the semantics of POSIX write() — a short write is not an error.

write_all loops internally until every byte has been written or a hard error occurs. This is what you usually want.


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::obuf_file obf{"output.txt"};

    char const message[] = "Hello, fast_io operations!\n";
    char const* first{message};
    char const* last{message + sizeof(message) - 1}; // exclude null terminator

    // write_some: may do a partial write.
    // Returns pointer to first unwritten char.
    char const* next = ::fast_io::operations::write_some(obf, first, last);

    // Write the rest with write_all (loops until done)
    ::fast_io::operations::write_all(obf, next, last);
}

9. write_some_bytes / write_all_bytes

Identical to write_some / write_all but operate on ::std::byte const* instead of char const*. Use these for binary data.


#include <fast_io.h>
#include <fast_io_device.h>
#include <cstddef>

int main() {
    ::fast_io::obuf_file obf{"binary.bin"};

    ::std::byte const data[]{
        ::std::byte{0xDE}, ::std::byte{0xAD},
        ::std::byte{0xBE}, ::std::byte{0xEF}
    };

    ::std::byte const* first{data};
    ::std::byte const* last{data + 4uz};

    // Write all bytes (loops until complete)
    ::fast_io::operations::write_all_bytes(obf, first, last);
}

10. pwrite_some / pwrite_all

Positional writes — write at a given file offset without changing the file’s current position. This mirrors POSIX pwrite(). Useful for concurrent writers that must not disturb each other’s file position.

The signature takes an extra offset parameter (in bytes from the start of the file).


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::obuf_file obf{"data.bin",
        ::fast_io::open_mode::out | ::fast_io::open_mode::trunc};

    char const block0[] = "AAAA";
    char const block1[] = "BBBB";

    // Write "BBBB" at offset 1024 without moving the file position
    ::fast_io::operations::pwrite_all(obf, block1, block1 + 4uz, 1024uz);

    // Write "AAAA" at offset 0; file position is still 0
    ::fast_io::operations::pwrite_all(obf, block0, block0 + 4uz, 0uz);
}

11. pwrite_some_bytes / pwrite_all_bytes

Same as pwrite_some / pwrite_all but accept ::std::byte const*. Use for binary positional writes.


#include <fast_io.h>
#include <fast_io_device.h>
#include <cstddef>

int main() {
    ::fast_io::obuf_file obf{"binary.bin",
        ::fast_io::open_mode::out | ::fast_io::open_mode::trunc};

    ::std::byte const header[]{
        ::std::byte{0x89}, ::std::byte{0x50},
        ::std::byte{0x4E}, ::std::byte{0x47}
    };

    ::fast_io::operations::pwrite_all_bytes(obf, header, header + 4uz, 0uz);
}

12. scatter_write_some / scatter_write_all

Scatter (gather) writes write multiple non-contiguous buffers in a single syscall — equivalent to POSIX writev(). This avoids copying scattered data into a temporary contiguous buffer.

The argument is a span of ::fast_io::io_scatter_t, each describing one base/length pair.


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::obuf_file obf{"scatter.txt"};

    char const part1[] = "Hello, ";
    char const part2[] = "scatter ";
    char const part3[] = "write!\n";

    ::fast_io::io_scatter_t const scatters[]{
        {part1, sizeof(part1) - 1uz},
        {part2, sizeof(part2) - 1uz},
        {part3, sizeof(part3) - 1uz},
    };

    // One syscall writes all three parts
    ::fast_io::operations::scatter_write_all(obf, scatters, scatters + 3uz);
}

13. scatter_pwrite_some / scatter_pwrite_all

Combines scatter writes with positional writes — writes multiple buffers at a given offset without changing the file position. This is the scatter equivalent of pwritev().


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::obuf_file obf{"scatter_pos.bin",
        ::fast_io::open_mode::out | ::fast_io::open_mode::trunc};

    char const part1[] = "AAA";
    char const part2[] = "BBB";

    ::fast_io::io_scatter_t const scatters[]{
        {part1, 3uz},
        {part2, 3uz},
    };

    // Write "AAABBB" at offset 512, file position unchanged
    ::fast_io::operations::scatter_pwrite_all(obf, scatters, scatters + 2uz, 512uz);
}

14. char_put

Writes a single character. All write variants also have a char_put counterpart for emitting one character at a time.


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    ::fast_io::obuf_file obf{"chars.txt"};

    ::fast_io::operations::char_put(obf, 'H');
    ::fast_io::operations::char_put(obf, 'i');
    ::fast_io::operations::char_put(obf, '\n');
}

For buffered devices (obuf_file), repeated char_put calls are cheap — they just write into the user-space buffer. For unbuffered devices (native_file), each char_put is a syscall — extremely slow. See Ch11.5.

15. Complete Reference Table

Operation Pointer Type Read/Write Positional? Scatter? Loops?
read_some char* Read No No No (partial OK)
read_all char* Read No No Yes
read_some_bytes ::std::byte* Read No No No (partial OK)
read_all_bytes ::std::byte* Read No No Yes
pread_some char* Read Yes No No (partial OK)
pread_all char* Read Yes No Yes
pread_some_bytes ::std::byte* Read Yes No No (partial OK)
pread_all_bytes ::std::byte* Read Yes No Yes
scatter_read_some char (scatter) Read No Yes No (partial OK)
scatter_read_all char (scatter) Read No Yes Yes
scatter_pread_some char (scatter) Read Yes Yes No (partial OK)
scatter_pread_all char (scatter) Read Yes Yes Yes
write_some char const* Write No No No (partial OK)
write_all char const* Write No No Yes
write_some_bytes ::std::byte const* Write No No No (partial OK)
write_all_bytes ::std::byte const* Write No No Yes
pwrite_some char const* Write Yes No No (partial OK)
pwrite_all char const* Write Yes No Yes
pwrite_some_bytes ::std::byte const* Write Yes No No (partial OK)
pwrite_all_bytes ::std::byte const* Write Yes No Yes
scatter_write_some char (scatter) Write No Yes No (partial OK)
scatter_write_all char (scatter) Write No Yes Yes
scatter_pwrite_some char (scatter) Write Yes Yes No (partial OK)
scatter_pwrite_all char (scatter) Write Yes Yes Yes
char_put single char Write No No N/A

Key takeaways