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 }