Read slice of T from AnyReader

I’m trying to read a slice of T from a buffer of bytes without copying or allocating.
The length of the slice is encoded in the stream itself and not known at comptime.

As far as I can tell this is not possible with the reader interface in the std, but I can’t implement it myself because I can’t figure out how to read the current position of the reader context.

const std = @import("std");
const builtin = @import("builtin");

const content: [4]u32 = .{ 0x3, 0xfa, 0xfb, 0xfc };
const stream: *const [4 * 4]u8 = @ptrCast(&content);

pub fn main() !void {
    var fst = std.io.fixedBufferStream(stream);
    const vec = try readVector(u32, fst.reader().any(), stream);
    std.debug.print("{any}\n", .{vec});
}

fn readVector(T: type, rd: std.io.AnyReader, buf: []const u8) ![]const T {
    const length = try rd.readInt(u32, .little);
    const offset = rd.context.i; // can't use the underlying context because it's *anyopaque

    const p: [*]const T = @alignCast(@ptrCast(buf.ptr + offset));
    return p[0..length];
}

After thinking about it, it makes more sense that AnyReader doesn’t expose the current position. Maybe FixedBufferStream is more fit for the task

This is work around.
You can use std.io.StreamSource type to resolve this issue.

const std = @import("std");
const builtin = @import("builtin");

const content: [4]u32 = .{ 0x3, 0xfa, 0xfb, 0xfc };
const stream: *const [4 * 4]u8 = @ptrCast(&content);

pub fn main() !void {
    // Wrap `std.io.fixedBufferStream` with `std.io.StreamSource`
    var source = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(stream) };
    // Instead of `AnyReader`, It passes `std.io.StreamSource` instance
    const vec = try readVector(u32, &source, stream);
    std.debug.print("{any}\n", .{vec});
}

// I've changed `T` to `comptime T`
fn readVector(comptime T: type, source: *std.io.StreamSource, buf: []const u8) ![]const T {
    const rd = source.reader().any();
    const length = try rd.readInt(T, .little);
    
    // You can access to offset via `SeekableStream` in `std.io.StreamSource`
    const offset = try source.getPos();

    const p: [*]const T = @alignCast(@ptrCast(buf.ptr + offset));
    return p[0..length];
}

1 Like