lib/acp/src/reader/capacity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 /// A caller chooses the longest line the reader must accept, so the reader's buffer size follows.
4 /// The structure holds one limit: the most bytes a line may hold before its newline. The package
5 /// exports it as `acp.ReaderLimits`, `reader.Limits` and `Storage.Limits`. `reader.default_limits`
6 /// sets 2 MiB.
7 pub const Limits = struct {
8 /// The most bytes a line may hold before its newline. Zero is allowed and accepts only empty
9 /// lines.
10 message_bytes: usize,
11 };
12
13 pub const DeriveError = error{CapacityOverflow};
14
15 /// A caller learns the buffer size before any buffer exists, for example to set aside the reader's
16 /// share of one larger region of memory. The structure holds the line limit and the buffer size it
17 /// needs. `derive` builds it. Another program in the repository adds the buffer size into the size
18 /// of one region it allocates up front. The package exports it as `acp.ReaderCapacity`.
19 pub const Capacity = struct {
20 /// The line limit, copied from `Limits`.
21 message_bytes: usize,
22 /// The buffer size: the line limit plus one byte, for the newline after a longest line. The
23 /// value is always above the line limit and above zero.
24 storage_bytes: usize,
25
26 /// A caller works out the buffer size for a line limit and learns early when the limit is too
27 /// large. The function returns the line limit with its buffer size. The call fails with
28 /// `error.CapacityOverflow` when the limit plus one does not fit in a `usize`. The call
29 /// allocates nothing.
30 pub fn derive(limits: Limits) DeriveError!Capacity {
31 const storage_bytes = std.math.add(usize, limits.message_bytes, 1) catch
32 return error.CapacityOverflow;
33 std.debug.assert(storage_bytes > limits.message_bytes);
34 std.debug.assert(storage_bytes != 0);
35 return .{
36 .message_bytes = limits.message_bytes,
37 .storage_bytes = storage_bytes,
38 };
39 }
40 };
41
42 test "ACP reader capacity matches independent byte arithmetic" {
43 comptime {
44 @stardustClaim(
45 @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "acp_reader_capacity"),
46 null,
47 null,
48 null,
49 null,
50 null,
51 null,
52 );
53 }
54
55 try std.testing.expectEqual(
56 Capacity{ .message_bytes = 0, .storage_bytes = 1 },
57 try Capacity.derive(.{ .message_bytes = 0 }),
58 );
59 try std.testing.expectEqual(
60 Capacity{ .message_bytes = 31, .storage_bytes = 32 },
61 try Capacity.derive(.{ .message_bytes = 31 }),
62 );
63 try std.testing.expectError(
64 error.CapacityOverflow,
65 Capacity.derive(.{ .message_bytes = std.math.maxInt(usize) }),
66 );
67 }