From 972cab5bb027dce3d4f700e381bec3e1724c9504 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 24 Mar 2025 07:12:23 -0800 Subject: [PATCH] std: add bit_set.findLastSet() (#22411) --- lib/std/bit_set.zig | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/lib/std/bit_set.zig b/lib/std/bit_set.zig index 7ff97cfa9d..3c8f3cf723 100644 --- a/lib/std/bit_set.zig +++ b/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());