lib/wayland/src/stream/storage.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const sys = @import("sys");
3 const wayland = @import("../root.zig");
4
5 const wire = wayland.wire;
6
7 pub const default_byte_count: usize = wire.maximum_message_size;
8 pub const default_descriptor_count: usize = sys.ancillary.maximum_descriptors;
9
10 pub const Limits = struct {
11 inbound_byte_count: usize = default_byte_count,
12 inbound_descriptor_count: usize = default_descriptor_count,
13 outbound_byte_count: usize = default_byte_count,
14 outbound_descriptor_count: usize = default_descriptor_count,
15 };
16
17 pub const CapacityError = error{
18 InboundByteStorageTooSmall,
19 OutboundByteStorageTooSmall,
20 CapacityOverflow,
21 };
22
23 pub const Capacity = struct {
24 inbound_byte_count: usize,
25 inbound_descriptor_count: usize,
26 inbound_descriptor_bytes: usize,
27 inbound_storage_bytes: usize,
28 outbound_byte_count: usize,
29 outbound_descriptor_count: usize,
30 outbound_descriptor_bytes: usize,
31 outbound_storage_bytes: usize,
32 total_requested_bytes: usize,
33
34 pub fn derive(limits: Limits) CapacityError!Capacity {
35 if (limits.inbound_byte_count < wire.header_size) {
36 return error.InboundByteStorageTooSmall;
37 }
38 if (limits.outbound_byte_count < wire.header_size) {
39 return error.OutboundByteStorageTooSmall;
40 }
41 const inbound_descriptor_bytes = std.math.mul(
42 usize,
43 limits.inbound_descriptor_count,
44 @sizeOf(sys.fd.Descriptor),
45 ) catch return error.CapacityOverflow;
46 const inbound_storage_bytes = std.math.add(
47 usize,
48 limits.inbound_byte_count,
49 inbound_descriptor_bytes,
50 ) catch return error.CapacityOverflow;
51 const outbound_descriptor_bytes = std.math.mul(
52 usize,
53 limits.outbound_descriptor_count,
54 @sizeOf(sys.fd.Descriptor),
55 ) catch return error.CapacityOverflow;
56 const outbound_storage_bytes = std.math.add(
57 usize,
58 limits.outbound_byte_count,
59 outbound_descriptor_bytes,
60 ) catch return error.CapacityOverflow;
61 const total_requested_bytes = std.math.add(
62 usize,
63 inbound_storage_bytes,
64 outbound_storage_bytes,
65 ) catch return error.CapacityOverflow;
66 return .{
67 .inbound_byte_count = limits.inbound_byte_count,
68 .inbound_descriptor_count = limits.inbound_descriptor_count,
69 .inbound_descriptor_bytes = inbound_descriptor_bytes,
70 .inbound_storage_bytes = inbound_storage_bytes,
71 .outbound_byte_count = limits.outbound_byte_count,
72 .outbound_descriptor_count = limits.outbound_descriptor_count,
73 .outbound_descriptor_bytes = outbound_descriptor_bytes,
74 .outbound_storage_bytes = outbound_storage_bytes,
75 .total_requested_bytes = total_requested_bytes,
76 };
77 }
78 };
79
80 pub const StorageError = error{
81 InboundByteCapacityExceeded,
82 InboundDescriptorCapacityExceeded,
83 OutboundByteCapacityExceeded,
84 OutboundDescriptorCapacityExceeded,
85 };
86
87 pub const Status = struct {
88 inbound_byte_capacity_rejection_count: u64 = 0,
89 inbound_descriptor_capacity_rejection_count: u64 = 0,
90 outbound_byte_capacity_rejection_count: u64 = 0,
91 outbound_descriptor_capacity_rejection_count: u64 = 0,
92 };
93
94 pub fn compactSlice(
95 comptime T: type,
96 items: []T,
97 count: *usize,
98 offset: *usize,
99 ) void {
100 std.debug.assert(offset.* <= count.*);
101 std.debug.assert(count.* <= items.len);
102 if (offset.* == 0) return;
103 const remaining = count.* - offset.*;
104 std.mem.copyForwards(T, items[0..remaining], items[offset.*..count.*]);
105 count.* = remaining;
106 offset.* = 0;
107 }
108
109 test "transport capacity derives four exact storage regions" {
110 const capacity = try Capacity.derive(.{
111 .inbound_byte_count = 13,
112 .inbound_descriptor_count = 2,
113 .outbound_byte_count = 17,
114 .outbound_descriptor_count = 3,
115 });
116 const descriptor_size = @sizeOf(sys.fd.Descriptor);
117 try std.testing.expectEqual(@as(usize, 13), capacity.inbound_byte_count);
118 try std.testing.expectEqual(@as(usize, 2), capacity.inbound_descriptor_count);
119 try std.testing.expectEqual(2 * descriptor_size, capacity.inbound_descriptor_bytes);
120 try std.testing.expectEqual(13 + 2 * descriptor_size, capacity.inbound_storage_bytes);
121 try std.testing.expectEqual(@as(usize, 17), capacity.outbound_byte_count);
122 try std.testing.expectEqual(@as(usize, 3), capacity.outbound_descriptor_count);
123 try std.testing.expectEqual(3 * descriptor_size, capacity.outbound_descriptor_bytes);
124 try std.testing.expectEqual(17 + 3 * descriptor_size, capacity.outbound_storage_bytes);
125 try std.testing.expectEqual(30 + 5 * descriptor_size, capacity.total_requested_bytes);
126 }
127
128 test "transport defaults cover one maximum message and ancillary batch" {
129 const capacity = try Capacity.derive(.{});
130 try std.testing.expectEqual(
131 @as(usize, wire.maximum_message_size),
132 capacity.inbound_byte_count,
133 );
134 try std.testing.expectEqual(
135 @as(usize, sys.ancillary.maximum_descriptors),
136 capacity.inbound_descriptor_count,
137 );
138 try std.testing.expectEqual(
139 @as(usize, wire.maximum_message_size),
140 capacity.outbound_byte_count,
141 );
142 try std.testing.expectEqual(
143 @as(usize, sys.ancillary.maximum_descriptors),
144 capacity.outbound_descriptor_count,
145 );
146 }
147
148 test "transport capacity rejects unusable and overflowing limits" {
149 try std.testing.expectError(error.InboundByteStorageTooSmall, Capacity.derive(.{
150 .inbound_byte_count = wire.header_size - 1,
151 }));
152 try std.testing.expectError(error.OutboundByteStorageTooSmall, Capacity.derive(.{
153 .outbound_byte_count = wire.header_size - 1,
154 }));
155 try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
156 .inbound_descriptor_count = std.math.maxInt(usize),
157 }));
158 try std.testing.expectError(error.CapacityOverflow, Capacity.derive(.{
159 .inbound_byte_count = std.math.maxInt(usize),
160 }));
161 }