Commit 972cab5bb0

Meghan Denny <hello@nektro.net>
2025-03-24 16:12:23
std: add bit_set.findLastSet() (#22411)
1 parent c1db72c
Changed files (1)
lib
lib/std/bit_set.zig
@@ -184,6 +184,14 @@ pub fn IntegerBitSet(comptime size: u16) type {
             return @ctz(mask);
         }
 
+        /// Finds the index of the last set bit.
+        /// If no bits are set, returns null.
+        pub fn findLastSet(self: Self) ?usize {
+            const mask = self.mask;
+            if (mask == 0) return null;
+            return bit_length - @clz(mask) - 1;
+        }
+
         /// Finds the index of the first set bit, and unsets it.
         /// If no bits are set, returns null.
         pub fn toggleFirstSet(self: *Self) ?usize {
@@ -542,6 +550,24 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {
             return offset + @ctz(mask);
         }
 
+        /// Finds the index of the last set bit.
+        /// If no bits are set, returns null.
+        pub fn findLastSet(self: Self) ?usize {
+            if (bit_length == 0) return null;
+            const bs = @bitSizeOf(MaskInt);
+            var len = bit_length / bs;
+            if (bit_length % bs != 0) len += 1;
+            var offset: usize = len * bs;
+            var idx: usize = len - 1;
+            while (self.masks[idx] == 0) : (idx -= 1) {
+                offset -= bs;
+                if (idx == 0) return null;
+            }
+            offset -= @clz(self.masks[idx]);
+            offset -= 1;
+            return offset;
+        }
+
         /// Finds the index of the first set bit, and unsets it.
         /// If no bits are set, returns null.
         pub fn toggleFirstSet(self: *Self) ?usize {
@@ -941,6 +967,24 @@ pub const DynamicBitSetUnmanaged = struct {
         return offset + @ctz(mask[0]);
     }
 
+    /// Finds the index of the last set bit.
+    /// If no bits are set, returns null.
+    pub fn findLastSet(self: Self) ?usize {
+        if (self.bit_length == 0) return null;
+        const bs = @bitSizeOf(MaskInt);
+        var len = self.bit_length / bs;
+        if (self.bit_length % bs != 0) len += 1;
+        var offset: usize = len * bs;
+        var idx: usize = len - 1;
+        while (self.masks[idx] == 0) : (idx -= 1) {
+            offset -= bs;
+            if (idx == 0) return null;
+        }
+        offset -= @clz(self.masks[idx]);
+        offset -= 1;
+        return offset;
+    }
+
     /// Finds the index of the first set bit, and unsets it.
     /// If no bits are set, returns null.
     pub fn toggleFirstSet(self: *Self) ?usize {
@@ -1160,6 +1204,12 @@ pub const DynamicBitSet = struct {
         return self.unmanaged.findFirstSet();
     }
 
+    /// Finds the index of the last set bit.
+    /// If no bits are set, returns null.
+    pub fn findLastSet(self: Self) ?usize {
+        return self.unmanaged.findLastSet();
+    }
+
     /// Finds the index of the first set bit, and unsets it.
     /// If no bits are set, returns null.
     pub fn toggleFirstSet(self: *Self) ?usize {
@@ -1514,8 +1564,10 @@ fn testBitSet(a: anytype, b: anytype, len: usize) !void {
         }
     }
     try testing.expectEqual(@as(?usize, null), a.findFirstSet());
+    try testing.expectEqual(@as(?usize, null), a.findLastSet());
     try testing.expectEqual(@as(?usize, null), a.toggleFirstSet());
     try testing.expectEqual(@as(?usize, null), a.findFirstSet());
+    try testing.expectEqual(@as(?usize, null), a.findLastSet());
     try testing.expectEqual(@as(?usize, null), a.toggleFirstSet());
     try testing.expectEqual(@as(usize, 0), a.count());