stylix

July 28, 2026, 5:29pm 1

So basically i have a file with the size of 1200 in bytes.

 ❯ stat -c %s input.txt
1200

I tried initialize buffer with a size of 1200 bytes and always get error OutOfMemory() when running the code below? I tried using binary search method to find the find the smallest buffer size that it doesn’t cause OOM and its 1928?

Can someone explain how this happen? Am i misunderstand something, or am I missing something obvious?

const std = @import("std");
const Io = std.Io;

pub fn main(init: std.process.Init) !void {
    const io = init.io;
    var buffer: [1928]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buffer);
    const allocator = fba.allocator();
    
    const data = try Io.Dir.cwd().readFileAlloc(
        io,
        "input.txt",
        allocator,
        .unlimited,
    );
    defer allocator.free(data);
    std.debug.print("Read {} bytes\n", .{data.len});
    //Output: Read 1200 bytes
}

pasta July 28, 2026, 5:39pm 2

The specifics of why this is the case are going to be visible in the FBA and file reader implementations, so it’s worth looking in there. It could be allocation headers, alignment, maybe some fragmentation caused by readFileAlloc needing intermediate allocations? (the following post actually explains it better!)

Regardless it’s probably more useful to say that this isn’t really a good way to use readFileAlloc - just give it a normal gpa.

Either that or use a fixed writer to stream the bytes into a buffer, or similar.

Bobvan July 28, 2026, 5:41pm 3

The reason is simple, readFileAlloc() does not querry the file size when reading.
It just initializes arraylist and then streams the data into it. However this arraylist grows independant to the file size (it will overgrow it).

Here is the growing logic for arraylist:

/// Called when memory growth is necessary. Returns a capacity larger than
/// minimum that grows super-linearly.
pub fn growCapacity(minimum: usize) usize {
    if (@sizeOf(T) == 0) return math.maxInt(usize);
    const init_capacity: comptime_int = @max(1, std.atomic.cache_line / @sizeOf(T));
    return minimum +| (minimum / 2 + init_capacity);
}

If you need to have the allocation consume exactly the file size memory, querry the file size, allocate buffer of that size and then read into it.

If you need anything more explained, feel free to ask.
Robert :blush:

stylix July 28, 2026, 5:53pm 4

I know, im just new to zig and trying to do everything with allocator (honestly im still not fully understand it) so i just tried to play around with allocator as much as i could. Even after i read the official docs of zig (Memory: How to choose an allocator), i dont even know which allocator should i choose​:face_with_bags_under_eyes:

stylix July 28, 2026, 5:54pm 5

Thanks man, so that’s why. Much appreciated

Maybe that’s an optimisation worth a PR.

After all I don’t really see the point of not doing that since that way one could read in one read syscall instead of multiple smaller ones.

sc68cal July 28, 2026, 7:14pm 8

So, as I understand it, the link that @kalapalo provided contains a section on using readFile where you can allocate a single buffer that’s the same size as the file itself. I think the point is that using the readFileAlloc uses an ArrayList which needs more memory than just the file contents since it has some additional features compared to a raw buffer. The OOM crash is because there is a FixedBufferAllocator being used which does not have enough memory available for the ArrayList and the actual file contents