How does streamUntilDelimiter work?

I was trying to read input from stdin where I stumbled upon this behavior and don’t know what’s going on. Consider the following code:

fn read_input() ![]const u8 {
    const input_string = ".open\n";
    var input_fbs = std.io.fixedBufferStream(input_string);
    const reader = input_fbs.reader();

    var output: [input_string.len]u8 = undefined;
    var output_fbs = std.io.fixedBufferStream(&output);
    const writer = output_fbs.writer();

    while (true) {
        reader.streamUntilDelimiter(writer, '\n', input_fbs.buffer.len) catch |err| switch (err) {
            error.EndOfStream => {
                break;
            },
            else => |e| return e,
        };

        var line = output_fbs.getWritten();
        std.debug.print("============== {any}\n", .{line}); // { 110, 105, 116, 105, 110 }
    }

    std.debug.print("============== {any}\n", .{output}); // { 110, 105, 116, 105, 110, 170 } Where is 170 coming from?

    return &output;
}

The variable output certainly has the string from reader but I’m not sure where 170 at the end is coming from. Any sort of hint would be helpful :sweat_smile:

Also, for some reason, if I replace the 2nd argument in streamUntilDelimiter from '\n' to "\n", the error message kept point to the 3rd argument instead. I don’t know if that’s intentional but certainly had my head spinning for a while.

  • The output buffer is not initialized
  • It’s large enough to contain .open\n (i.e. the string you’re reading from the input, plus an extra byte for the \n delimiter which isn’t getting copied)
  • That last byte is never overwritten, so when you print it out you get some random value.

While we’re on this topic, do not return a pointer to a buffer which lives on the stack. Treat that buffer as ephemeral as it will not live past the function execution lifetime. As an alternative, you can either allocate memory for the output buffer, or let the caller provide a memory location to write to instead.

2 Likes

Right! That helped me solve it. Thanks!

or let the caller provide a memory location to write to instead.

I chose to go this route.