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 }