lib/xkb/src/compose/workspace/capacity.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 
  3 pub const max_include_depth: usize = 5;
  4 pub const max_file_bytes: usize = 16 * 1024 * 1024;
  5 pub const max_registry_bytes: usize = 8 * 1024 * 1024;
  6 pub const max_path_bytes: usize = 4096;
  7 pub const file_line_slot_count: usize = max_include_depth + 1;
  8 pub const registry_line_slot_index: usize = file_line_slot_count;
  9 pub const line_slot_count: usize = file_line_slot_count + 1;
 10 pub const file_path_slot_count: usize = max_include_depth + 1;
 11 pub const auxiliary_path_slot_count: usize = 2;
 12 pub const path_slot_count: usize = file_path_slot_count + auxiliary_path_slot_count;
 13 
 14 pub const Limits = struct {
 15     line_bytes: usize,
 16     path_bytes: usize,
 17 };
 18 
 19 pub const default_limits = Limits{
 20     .line_bytes = 256,
 21     .path_bytes = max_path_bytes,
 22 };
 23 
 24 pub const DeriveError = error{
 25     LineByteLimitZero,
 26     LineByteLimitExceeded,
 27     PathByteLimitZero,
 28     PathByteLimitExceeded,
 29     CapacityOverflow,
 30 };
 31 
 32 pub const Capacity = struct {
 33     limits: Limits,
 34     line_bytes_per_slot: usize,
 35     line_slot_count: usize,
 36     line_offset: usize,
 37     line_storage_bytes: usize,
 38     path_slot_count: usize,
 39     path_offset: usize,
 40     path_storage_bytes: usize,
 41     storage_bytes: usize,
 42 
 43     pub fn derive(limits: Limits) DeriveError!Capacity {
 44         if (limits.line_bytes == 0) return error.LineByteLimitZero;
 45         if (limits.line_bytes > max_file_bytes) return error.LineByteLimitExceeded;
 46         if (limits.path_bytes == 0) return error.PathByteLimitZero;
 47         if (limits.path_bytes > max_path_bytes) return error.PathByteLimitExceeded;
 48         const line_bytes_per_slot = try added(limits.line_bytes, 1);
 49         const line_storage_bytes = try multiplied(line_slot_count, line_bytes_per_slot);
 50         const path_storage_bytes = try multiplied(path_slot_count, limits.path_bytes);
 51         return .{
 52             .limits = limits,
 53             .line_bytes_per_slot = line_bytes_per_slot,
 54             .line_slot_count = line_slot_count,
 55             .line_offset = 0,
 56             .line_storage_bytes = line_storage_bytes,
 57             .path_slot_count = path_slot_count,
 58             .path_offset = line_storage_bytes,
 59             .path_storage_bytes = path_storage_bytes,
 60             .storage_bytes = try added(line_storage_bytes, path_storage_bytes),
 61         };
 62     }
 63 };
 64 
 65 fn added(left: usize, right: usize) DeriveError!usize {
 66     return std.math.add(usize, left, right) catch error.CapacityOverflow;
 67 }
 68 
 69 fn multiplied(left: usize, right: usize) DeriveError!usize {
 70     return std.math.mul(usize, left, right) catch error.CapacityOverflow;
 71 }
 72 
 73 fn modelCapacity(limits: Limits) DeriveError!Capacity {
 74     if (limits.line_bytes == 0) return error.LineByteLimitZero;
 75     if (limits.line_bytes > max_file_bytes) return error.LineByteLimitExceeded;
 76     if (limits.path_bytes == 0) return error.PathByteLimitZero;
 77     if (limits.path_bytes > max_path_bytes) return error.PathByteLimitExceeded;
 78     const line_bytes_per_slot = @as(u128, limits.line_bytes) + 1;
 79     const line_storage_bytes = @as(u128, line_slot_count) * line_bytes_per_slot;
 80     const path_storage_bytes = @as(u128, path_slot_count) * limits.path_bytes;
 81     const storage_bytes = line_storage_bytes + path_storage_bytes;
 82     const values = [_]u128{ line_bytes_per_slot, line_storage_bytes, path_storage_bytes, storage_bytes };
 83     for (values) |value| {
 84         if (value > std.math.maxInt(usize)) return error.CapacityOverflow;
 85     }
 86     return .{
 87         .limits = limits,
 88         .line_bytes_per_slot = @intCast(line_bytes_per_slot),
 89         .line_slot_count = line_slot_count,
 90         .line_offset = 0,
 91         .line_storage_bytes = @intCast(line_storage_bytes),
 92         .path_slot_count = path_slot_count,
 93         .path_offset = @intCast(line_storage_bytes),
 94         .path_storage_bytes = @intCast(path_storage_bytes),
 95         .storage_bytes = @intCast(storage_bytes),
 96     };
 97 }
 98 
 99 test "Compose scratch capacity matches an independent slot model" {
100     comptime {
101         @stardustClaim(
102             @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "xkb_compose_scratch_capacity"),
103             null,
104             null,
105             null,
106             null,
107             null,
108             null,
109         );
110     }
111 
112     const capacity = try Capacity.derive(default_limits);
113     try std.testing.expectEqual(try modelCapacity(default_limits), capacity);
114     try std.testing.expectEqual(@as(usize, 34_567), capacity.storage_bytes);
115     try std.testing.expectEqual(@as(usize, 7), capacity.line_slot_count);
116     try std.testing.expectEqual(@as(usize, 8), capacity.path_slot_count);
117 }
118 
119 test "Compose scratch capacity rejects invalid semantic limits" {
120     try std.testing.expectError(
121         error.LineByteLimitZero,
122         Capacity.derive(.{ .line_bytes = 0, .path_bytes = 1 }),
123     );
124     try std.testing.expectError(
125         error.LineByteLimitExceeded,
126         Capacity.derive(.{ .line_bytes = max_file_bytes + 1, .path_bytes = 1 }),
127     );
128     try std.testing.expectError(
129         error.PathByteLimitZero,
130         Capacity.derive(.{ .line_bytes = 1, .path_bytes = 0 }),
131     );
132     try std.testing.expectError(
133         error.PathByteLimitExceeded,
134         Capacity.derive(.{ .line_bytes = 1, .path_bytes = max_path_bytes + 1 }),
135     );
136 }