Commit cb5a6be41a

John Simon <john@whatisaph.one>
2023-07-03 19:58:03
Smaller memory footprint for BoundedArray (#16299)
Store BoundedArray's length using the smallest possible integer Co-authored-by: zooster <r00ster91@proton.me>
1 parent 28ad74e
Changed files (1)
lib/std/bounded_array.zig
@@ -39,14 +39,16 @@ pub fn BoundedArrayAligned(
 ) type {
     return struct {
         const Self = @This();
+        const Len = std.math.IntFittingRange(0, buffer_capacity);
+
         buffer: [buffer_capacity]T align(alignment) = undefined,
-        len: usize = 0,
+        len: Len = 0,
 
         /// Set the actual length of the slice.
         /// Returns error.Overflow if it exceeds the length of the backing array.
         pub fn init(len: usize) error{Overflow}!Self {
             if (len > buffer_capacity) return error.Overflow;
-            return Self{ .len = len };
+            return Self{ .len = @intCast(len) };
         }
 
         /// View the internal array as a slice whose size was previously set.
@@ -67,7 +69,7 @@ pub fn BoundedArrayAligned(
         /// Does not initialize added items if any.
         pub fn resize(self: *Self, len: usize) error{Overflow}!void {
             if (len > buffer_capacity) return error.Overflow;
-            self.len = len;
+            self.len = @intCast(len);
         }
 
         /// Copy the content of an existing slice.
@@ -163,7 +165,7 @@ pub fn BoundedArrayAligned(
         /// This operation is O(N).
         pub fn insertSlice(self: *Self, i: usize, items: []const T) error{Overflow}!void {
             try self.ensureUnusedCapacity(items.len);
-            self.len += items.len;
+            self.len = @intCast(self.len + items.len);
             mem.copyBackwards(T, self.slice()[i + items.len .. self.len], self.constSlice()[i .. self.len - items.len]);
             @memcpy(self.slice()[i..][0..items.len], items);
         }
@@ -193,7 +195,7 @@ pub fn BoundedArrayAligned(
                 for (self.constSlice()[after_range..], 0..) |item, i| {
                     self.slice()[after_subrange..][i] = item;
                 }
-                self.len -= len - new_items.len;
+                self.len = @intCast(self.len - len + new_items.len);
             }
         }
 
@@ -244,7 +246,7 @@ pub fn BoundedArrayAligned(
         /// enough to store the new items.
         pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
             const old_len = self.len;
-            self.len += items.len;
+            self.len = @intCast(self.len + items.len);
             @memcpy(self.slice()[old_len..][0..items.len], items);
         }
 
@@ -260,8 +262,8 @@ pub fn BoundedArrayAligned(
         /// Asserts the capacity is enough.
         pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
             const old_len = self.len;
-            self.len += n;
-            assert(self.len <= buffer_capacity);
+            assert(self.len + n <= buffer_capacity);
+            self.len = @intCast(self.len + n);
             @memset(self.slice()[old_len..self.len], value);
         }
 
@@ -387,6 +389,18 @@ test "BoundedArray" {
     try testing.expectEqualStrings(s, a.constSlice());
 }
 
+test "BoundedArray sizeOf" {
+    // Just sanity check size on one CPU
+    if (@import("builtin").cpu.arch != .x86_64)
+        return;
+
+    try testing.expectEqual(@sizeOf(BoundedArray(u8, 3)), 4);
+
+    // `len` is the minimum required size to hold the maximum capacity
+    try testing.expectEqual(@TypeOf(@as(BoundedArray(u8, 15), undefined).len), u4);
+    try testing.expectEqual(@TypeOf(@as(BoundedArray(u8, 16), undefined).len), u5);
+}
+
 test "BoundedArrayAligned" {
     var a = try BoundedArrayAligned(u8, 16, 4).init(0);
     try a.append(0);