lib/wayland/src/runtime/creation.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const wayland = @import("../root.zig");
3
4 pub const Creation = struct {
5 id: u32,
6 interface: *const wayland.protocol.schema.Interface,
7 version: u32,
8 };
9
10 pub const maximum_event_creation_count = maximumEventCreationCount(
11 &wayland.protocol.interfaces,
12 );
13
14 pub const Limits = struct {
15 event_creation_count: usize = maximum_event_creation_count,
16 };
17
18 pub const CapacityError = error{
19 EventCreationStorageTooLarge,
20 CapacityOverflow,
21 };
22
23 pub const Capacity = struct {
24 event_creation_count: usize,
25 event_creation_bytes: usize,
26 total_requested_bytes: usize,
27
28 pub fn derive(limits: Limits) CapacityError!Capacity {
29 const event_creation_bytes = std.math.mul(
30 usize,
31 limits.event_creation_count,
32 @sizeOf(Creation),
33 ) catch return error.CapacityOverflow;
34 if (limits.event_creation_count > maximum_event_creation_count) {
35 return error.EventCreationStorageTooLarge;
36 }
37 return .{
38 .event_creation_count = limits.event_creation_count,
39 .event_creation_bytes = event_creation_bytes,
40 .total_requested_bytes = event_creation_bytes,
41 };
42 }
43 };
44
45 pub const StorageError = error{EventCreationCapacityExceeded};
46
47 pub const Status = struct {
48 event_creation_capacity_rejection_count: u64 = 0,
49 };
50
51 pub const default_capacity = Capacity.derive(.{}) catch unreachable;
52
53 pub const Storage = struct {
54 session_allocator: std.mem.Allocator,
55 creations: []Creation,
56 count: usize = 0,
57 capacity_rejection_count: u64 = 0,
58
59 pub fn init(
60 session_allocator: std.mem.Allocator,
61 capacity: Capacity,
62 ) std.mem.Allocator.Error!Storage {
63 return .{
64 .session_allocator = session_allocator,
65 .creations = try session_allocator.alloc(
66 Creation,
67 capacity.event_creation_count,
68 ),
69 };
70 }
71
72 pub fn deinit(self: *Storage) void {
73 self.assertValid();
74 if (self.creations.len != 0) self.session_allocator.free(self.creations);
75 self.* = undefined;
76 }
77
78 pub fn begin(self: *Storage, required_count: usize) StorageError!void {
79 self.assertValid();
80 self.count = 0;
81 if (required_count > self.creations.len) {
82 self.capacity_rejection_count +|= 1;
83 return error.EventCreationCapacityExceeded;
84 }
85 }
86
87 pub fn appendAssumeCapacity(self: *Storage, creation: Creation) void {
88 self.assertValid();
89 std.debug.assert(self.count < self.creations.len);
90 self.creations[self.count] = creation;
91 self.count += 1;
92 }
93
94 pub fn reset(self: *Storage) void {
95 self.assertValid();
96 self.count = 0;
97 }
98
99 pub fn items(self: *const Storage) []const Creation {
100 self.assertValid();
101 return self.creations[0..self.count];
102 }
103
104 pub fn status(self: *const Storage) Status {
105 self.assertValid();
106 return .{
107 .event_creation_capacity_rejection_count = self.capacity_rejection_count,
108 };
109 }
110
111 fn assertValid(self: *const Storage) void {
112 std.debug.assert(self.count <= self.creations.len);
113 }
114 };
115
116 fn maximumEventCreationCount(
117 interfaces: []const wayland.protocol.schema.Interface,
118 ) usize {
119 var maximum: usize = 0;
120 for (interfaces) |interface| {
121 for (interface.events) |event| {
122 maximum = @max(maximum, event.newIdCount());
123 }
124 }
125 return maximum;
126 }
127
128 test "generated events derive one exact creation slot" {
129 try std.testing.expectEqual(@as(usize, 1), maximum_event_creation_count);
130 for (wayland.protocol.interfaces) |interface| {
131 for (interface.events) |event| {
132 try std.testing.expect(event.newIdCount() <= maximum_event_creation_count);
133 }
134 }
135 try std.testing.expectEqual(
136 @as(usize, 1),
137 wayland.protocol.core.wl_data_device.events.data_offer.newIdCount(),
138 );
139 try std.testing.expectEqual(@as(usize, 1), default_capacity.event_creation_count);
140 try std.testing.expectEqual(
141 @sizeOf(Creation),
142 default_capacity.event_creation_bytes,
143 );
144 try std.testing.expectEqual(
145 default_capacity.event_creation_bytes,
146 default_capacity.total_requested_bytes,
147 );
148 try std.testing.expectError(
149 error.EventCreationStorageTooLarge,
150 Capacity.derive(.{
151 .event_creation_count = maximum_event_creation_count + 1,
152 }),
153 );
154 try std.testing.expectError(
155 error.CapacityOverflow,
156 Capacity.derive(.{ .event_creation_count = std.math.maxInt(usize) }),
157 );
158 }
159
160 test "event creation storage acquires its region before use" {
161 var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{
162 .fail_index = 0,
163 });
164 try std.testing.expectError(
165 error.OutOfMemory,
166 Storage.init(failing.allocator(), default_capacity),
167 );
168 failing = std.testing.FailingAllocator.init(std.testing.allocator, .{
169 .fail_index = 1,
170 });
171 var storage = try Storage.init(failing.allocator(), default_capacity);
172 defer storage.deinit();
173 try std.testing.expectEqual(@as(usize, 1), failing.allocations);
174 }
175
176 test "event creation max plus one clears staging and saturates status" {
177 var storage = try Storage.init(std.testing.allocator, default_capacity);
178 defer storage.deinit();
179 const creation: Creation = .{
180 .id = wayland.ids.first_server_id,
181 .interface = &wayland.protocol.core.wl_data_offer.metadata,
182 .version = 1,
183 };
184 const pointer = storage.creations.ptr;
185 try storage.begin(1);
186 storage.appendAssumeCapacity(creation);
187 try std.testing.expectEqualSlices(Creation, &.{creation}, storage.items());
188
189 try std.testing.expectError(error.EventCreationCapacityExceeded, storage.begin(2));
190 try std.testing.expectEqual(@as(usize, 0), storage.items().len);
191 try std.testing.expectEqual(pointer, storage.creations.ptr);
192 try std.testing.expectEqual(
193 @as(u64, 1),
194 storage.status().event_creation_capacity_rejection_count,
195 );
196
197 storage.capacity_rejection_count = std.math.maxInt(u64);
198 try std.testing.expectError(error.EventCreationCapacityExceeded, storage.begin(2));
199 try std.testing.expectEqual(
200 std.math.maxInt(u64),
201 storage.status().event_creation_capacity_rejection_count,
202 );
203 }