# Read bytes from reader into a bounded array?

**URL:** <https://ziggit.dev/t/read-bytes-from-reader-into-a-bounded-array/5932>\
**Category:** Help\
**Tags:** standard-library\
**Created:** [September 8, 2024, 8:34pm UTC](https://ziggit.dev/t/read-bytes-from-reader-into-a-bounded-array/5932 "2024-09-08T20:34:43Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [September 8, 2024, 8:34pm UTC](https://ziggit.dev/t/read-bytes-from-reader-into-a-bounded-array/5932/1 "2024-09-08T20:34:43Z")

</div>

How can I read bytes from a reader into a bounded array?

Something like this:

```zig
pub const SDOServerNormal = struct {
    mbx_header: MailboxHeader,
    coe_header: CoEHeader,
    sdo_header: SDOHeaderServer,
    complete_size: u32,
    data: std.BoundedArray(u8, max_mailbox_size - 10),

    pub fn deserialize(buf: []const u8) !SDOServerNormal {
        var fbs = std.io.fixedBufferStream(buf);
        var reader = fbs.reader();
        const mbx_header = try nic.packFromECatReader(MailboxHeader, &reader);
        const coe_header = try nic.packFromECatReader(CoEHeader, &reader);
        const sdo_header = try nic.packFromECatReader(SDOHeaderServer, &reader);
        const complete_size = try nic.packFromECatReader(u32, &reader);

        if (mbx_header.length < 10) {
            return error.InvalidMbxHeaderLength;
        }
        const data_length: u16 = mbx_header.length -| 10;
        var data = try std.BoundedArray(u8, max_mailbox_size - 10).init(0);

        // read the next data_length bytes into the data bounded array
        // using the reader
        // data_length can be zero!

        return SDOServerNormal{
            .mbx_header = mbx_header,
            .coe_header = coe_header,
            .sdo_header = sdo_header,
            .complete_size = complete_size,
            .data = data,
        };
    }
};

```

---

<div class="post-metadata">

**Author:** ![squeek502](https://ziggit.dev/user_avatar/ziggit.dev/squeek502/32/409_2.png) [@squeek502](https://ziggit.dev/u/squeek502)\
**Post date:** [September 8, 2024, 11:23pm UTC](https://ziggit.dev/t/read-bytes-from-reader-into-a-bounded-array/5932/2 "2024-09-08T23:23:27Z")

</div>

Something like:

```zig
const data_writer = data.writer();
var i: u16 = 0;
while (i < data_len) {
    const max_chunk_len = 256;
    var chunk_buf: [max_chunk_len]u8 = undefined;
    const chunk_len = @min(data_len - i, max_chunk_len);
    const chunk = try reader.readAll(chunk_buf[0..chunk_len]);
    try data_writer.writeAll(chunk);
    i += chunk_len;
}

```

EDIT: Another option for the same sort of pattern is via [`pump`](https://ziglang.org/documentation/master/std/#std.fifo.LinearFifo.pump) from `std.fifo`:

```zig
// wrap the reader so that it returns EOF after data_len bytes
var limited_reader = std.io.limitedReader(reader, data_len);

// 256 is arbitrary, size it appropriately for your use-case
const FifoBuffer = std.fifo.LinearFifo(u8, .{ .Static = 256 });
var fifo = FifoBuffer.init();
try fifo.pump(limited_reader.reader(), writer);

```

(the `fifo` approach is what I use in resinator [here](https://github.com/squeek502/resinator/blob/9a6e50b0c0859e0dee5fd1871d93329e0e1194ef/src/compile.zig#L1251-L1257))

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [September 8, 2024, 11:52pm UTC](https://ziggit.dev/t/read-bytes-from-reader-into-a-bounded-array/5932/3 "2024-09-08T23:52:17Z")

</div>

```zig
var data: std.BoundedArray(u8, max_mailbox_size - 10) = 
  .{ .len = data_length };
// The buffer field is implicitly undefined.
try reader.readNoEof(data.slice());

```

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [September 9, 2024, 4:16am UTC](https://ziggit.dev/t/read-bytes-from-reader-into-a-bounded-array/5932/4 "2024-09-09T04:16:42Z")

</div>

Went with a variant of this but used the `init` method of the bounded array instead of modifying length directly (which provides an error on overflow).

```zig
var data = try std.BoundedArray(u8, data_max_size).init(data_length);
try reader.readNoEof(data.slice());

```

This was the full implementation:

```zig
pub const SDOServerNormal = struct {
    mbx_header: MailboxHeader,
    coe_header: CoEHeader,
    sdo_header: SDOHeaderServer,
    complete_size: u32,
    data: std.BoundedArray(u8, data_max_size),

    pub const data_max_size = max_mailbox_size - 16;

    pub fn deserialize(buf: []const u8) !SDOServerNormal {
        var fbs = std.io.fixedBufferStream(buf);
        const reader = fbs.reader();
        const mbx_header = try nic.packFromECatReader(MailboxHeader, reader);
        const coe_header = try nic.packFromECatReader(CoEHeader, reader);
        const sdo_header = try nic.packFromECatReader(SDOHeaderServer, reader);
        const complete_size = try nic.packFromECatReader(u32, reader);

        if (mbx_header.length < 10) {
            return error.InvalidMbxHeaderLength;
        }
        const data_length: u16 = mbx_header.length -| 10;
        var data = try std.BoundedArray(u8, data_max_size).init(data_length);
        try reader.readNoEof(data.slice());

        return SDOServerNormal{
            .mbx_header = mbx_header,
            .coe_header = coe_header,
            .sdo_header = sdo_header,
            .complete_size = complete_size,
            .data = data,
        };
    }

    pub fn serialize(self: *const SDOServerNormal, out: []u8) !usize {
        var fbs = std.io.fixedBufferStream(out);
        const writer = fbs.writer();
        try nic.eCatFromPackToWriter(self.mbx_header, writer);
        try nic.eCatFromPackToWriter(self.coe_header, writer);
        try nic.eCatFromPackToWriter(self.sdo_header, writer);
        try nic.eCatFromPackToWriter(self.complete_size, writer);
        try writer.writeAll(self.data.slice());
        return fbs.getWritten().len;
    }

    comptime {
        assert(data_max_size == max_mailbox_size -
            @divExact(@bitSizeOf(MailboxHeader), 8) -
            @divExact(@bitSizeOf(CoEHeader), 8) -
            @divExact(@bitSizeOf(SDOHeaderServer), 8) -
            @divExact(@bitSizeOf(u32), 8));
    }
};

test "serialize and deserialize sdo server normal" {
    const expected = SDOServerNormal{
        .mbx_header = .{
            .length = 14, // 4 bytes of payload
            .address = 0x0,
            .channel = 0,
            .priority = 0,
            .type = .CoE,
            .cnt = 2,
        },
        .coe_header = .{
            .number = 0,
            .service = .sdo_response,
        },
        .sdo_header = .{
            .size_indicator = true,
            .transfer_type = .normal,
            .data_set_size = .four_octets,
            .complete_access = false,
            .command = .initiate_upload_response,
            .index = 1234,
            .subindex = 0,
        },
        .complete_size = 12345,
        .data = try std.BoundedArray(u8, SDOServerNormal.data_max_size).fromSlice(&.{ 1, 2, 3, 4 }),
    };
    var bytes = std.mem.zeroes([max_mailbox_size]u8);
    const byte_size = try expected.serialize(&bytes);
    try std.testing.expectEqual(@as(usize, 6 + 2 + 12), byte_size);
    const actual = try SDOServerNormal.deserialize(&bytes);
    try std.testing.expectEqualDeep(expected, actual);
}

```
