diff --git a/lib/std/debug/Coverage.zig b/lib/std/debug/Coverage.zig index 58e600dc6370..06d82689a191 100644 --- a/lib/std/debug/Coverage.zig +++ b/lib/std/debug/Coverage.zig @@ -40,9 +40,9 @@ pub const String = enum(u32) { pub fn eql(self: @This(), a: String, b: String, b_index: usize) bool { _ = b_index; - const a_slice = span(self.string_bytes[@intFromEnum(a)..]); - const b_slice = span(self.string_bytes[@intFromEnum(b)..]); - return std.mem.eql(u8, a_slice, b_slice); + const a_ptr: [*:0]const u8 = @ptrCast(&self.string_bytes[@intFromEnum(a)]); + const b_ptr: [*:0]const u8 = @ptrCast(&self.string_bytes[@intFromEnum(b)]); + return std.mem.sentinel_terminated.eql(u8, 0, a_ptr, b_ptr); } pub fn hash(self: @This(), a: String) u32 { @@ -55,8 +55,8 @@ pub const String = enum(u32) { pub fn eql(self: @This(), a_slice: []const u8, b: String, b_index: usize) bool { _ = b_index; - const b_slice = span(self.string_bytes[@intFromEnum(b)..]); - return std.mem.eql(u8, a_slice, b_slice); + const b_ptr: [*:0]const u8 = @ptrCast(&self.string_bytes[@intFromEnum(b)]); + return std.mem.sentinel_terminated.eqlSlice(u8, 0, b_ptr, a_slice); } pub fn hash(self: @This(), a: []const u8) u32 { _ = self; @@ -97,9 +97,9 @@ pub const File = extern struct { pub fn eql(self: MapContext, a: File, b: File, b_index: usize) bool { _ = b_index; if (a.directory_index != b.directory_index) return false; - const a_basename = span(self.string_bytes[@intFromEnum(a.basename)..]); - const b_basename = span(self.string_bytes[@intFromEnum(b.basename)..]); - return std.mem.eql(u8, a_basename, b_basename); + const a_basename_ptr: [*:0]const u8 = @ptrCast(&self.string_bytes[@intFromEnum(a.basename)]); + const b_basename_ptr: [*:0]const u8 = @ptrCast(&self.string_bytes[@intFromEnum(b.basename)]); + return std.mem.sentinel_terminated.eql(u8, 0, a_basename_ptr, b_basename_ptr); } }; @@ -119,8 +119,8 @@ pub const File = extern struct { pub fn eql(self: @This(), a: Entry, b: File, b_index: usize) bool { _ = b_index; if (a.directory_index != b.directory_index) return false; - const b_basename = span(self.string_bytes[@intFromEnum(b.basename)..]); - return std.mem.eql(u8, a.basename, b_basename); + const b_basename_ptr: [*:0]const u8 = @ptrCast(&self.string_bytes[@intFromEnum(b.basename)]); + return std.mem.sentinel_terminated.eqlSlice(u8, 0, b_basename_ptr, a.basename); } }; }; diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 3b72a2b57989..5a244fd6a6c2 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -18,6 +18,8 @@ pub const byte_size_in_bits = 8; pub const Allocator = @import("mem/Allocator.zig"); +pub const sentinel_terminated = @import("mem/sentinel_terminated.zig"); + /// Stored as a power-of-two. pub const Alignment = enum(math.Log2Int(usize)) { @"1" = 0, @@ -640,9 +642,7 @@ pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order { /// Compares two many-item pointers with NUL-termination lexicographically. pub fn orderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T) math.Order { - var i: usize = 0; - while (lhs[i] == rhs[i] and lhs[i] != 0) : (i += 1) {} - return math.order(lhs[i], rhs[i]); + return sentinel_terminated.order(T, 0, lhs, rhs); } test order { @@ -651,6 +651,7 @@ test order { try testing.expect(order(u8, "abc", "abc0") == .lt); try testing.expect(order(u8, "", "") == .eq); try testing.expect(order(u8, "", "a") == .lt); + try testing.expect(order(i8, &.{ -1, -2 }, &.{ -1, -2, -3 }) == .lt); } test orderZ { @@ -659,6 +660,7 @@ test orderZ { try testing.expect(orderZ(u8, "abc", "abc0") == .lt); try testing.expect(orderZ(u8, "", "") == .eq); try testing.expect(orderZ(u8, "", "a") == .lt); + try testing.expect(order(i8, &.{ -1, -2 }, &.{ -1, -2, -3 }) == .lt); } /// Returns true if lhs < rhs, false otherwise @@ -4842,3 +4844,7 @@ test "read/write(Var)PackedInt" { } } } + +test { + _ = sentinel_terminated; +} diff --git a/lib/std/mem/sentinel_terminated.zig b/lib/std/mem/sentinel_terminated.zig new file mode 100644 index 000000000000..a3d7ce0cb1d5 --- /dev/null +++ b/lib/std/mem/sentinel_terminated.zig @@ -0,0 +1,145 @@ +const std = @import("../std.zig"); +const debug = std.debug; +const assert = debug.assert; +const math = std.math; +const mem = std.mem; +const testing = std.testing; + +/// Returns true if and only if the pointers have the same length and all elements +/// compare true using equality operator. +pub fn eql(comptime T: type, comptime sentinel: T, a: [*:sentinel]const T, b: [*:sentinel]const T) bool { + if (a == b) return true; + + var i: usize = 0; + while (a[i] == b[i]) : (i += 1) { + if (a[i] == sentinel) return true; + } + return false; +} + +test eql { + try testing.expect(eql(u8, 0, "abcd", "abcd")); + try testing.expect(!eql(u8, 0, "abcdef", "abZdef")); + try testing.expect(!eql(u8, 0, "abcdefg", "abcdef")); + + try testing.expect(eql(u16, 1, ([5]u16{ 5, 6, 7, 8, 1 })[0..4 :1].ptr, ([5]u16{ 5, 6, 7, 8, 1 })[0..4 :1].ptr)); + try testing.expect(!eql(u16, 1, ([7]u16{ 5, 6, 7, 8, 9, 10, 1 })[0..6 :1].ptr, ([7]u16{ 5, 6, 17, 8, 9, 10, 1 })[0..6 :1].ptr)); + try testing.expect(!eql(u16, 1, ([8]u16{ 5, 6, 7, 8, 9, 10, 11, 1 })[0..7 :1].ptr, ([7]u16{ 5, 6, 7, 8, 9, 10, 1 })[0..6 :1].ptr)); +} + +/// Returns true if and only if the pointer and slice have the same length and all elements +/// compare true using equality operator. +pub fn eqlSlice(comptime T: type, comptime sentinel: T, a: [*:sentinel]const T, b: []const T) bool { + for (b, 0..) |b_elem, i| { + if (a[i] == sentinel or a[i] != b_elem) return false; + } + return a[b.len] == sentinel; +} + +test eqlSlice { + try testing.expect(eqlSlice(u8, 0, "abcd", "abcd")); + try testing.expect(!eqlSlice(u8, 0, "abcdef", "abZdef")); + try testing.expect(!eqlSlice(u8, 0, "abcdefg", "abcdef")); + + try testing.expect(eqlSlice(u16, 1, ([5]u16{ 5, 6, 7, 8, 1 })[0..4 :1].ptr, &[4]u16{ 5, 6, 7, 8 })); + try testing.expect(!eqlSlice(u16, 1, ([7]u16{ 5, 6, 7, 8, 9, 10, 1 })[0..6 :1].ptr, &[6]u16{ 5, 6, 17, 8, 9, 10 })); + try testing.expect(!eqlSlice(u16, 1, ([8]u16{ 5, 6, 7, 8, 9, 10, 11, 1 })[0..7 :1].ptr, &[6]u16{ 5, 6, 7, 8, 9, 10 })); +} + +/// Returns true if all elements in the pointer are equal to the scalar value provided +pub fn allEqual(comptime T: type, comptime sentinel: T, ptr: [*:sentinel]const T, scalar: T) bool { + var i: usize = 0; + while (ptr[i] != sentinel) : (i += 1) { + if (ptr[i] != scalar) return false; + } + return true; +} + +test allEqual { + try testing.expect(allEqual(u8, 0, "aaaa", 'a')); + try testing.expect(!allEqual(u8, 0, "abaa", 'a')); + try testing.expect(allEqual(u8, 0, "", 'a')); +} + +/// Returns the smallest number in a pointer. O(n). +/// `ptr` must have at least one element before a sentinel. +pub fn min(comptime T: type, comptime sentinel: T, ptr: [*:sentinel]const T) T { + assert(ptr[0] != sentinel); + var best = ptr[0]; + var i: usize = 1; + while (ptr[i] != sentinel) : (i += 1) { + best = @min(best, ptr[i]); + } + return best; +} + +test min { + try testing.expectEqual(min(u8, 0, "abcdefg"), 'a'); + try testing.expectEqual(min(u8, 0, "bcdefga"), 'a'); + try testing.expectEqual(min(u8, 0, "a"), 'a'); +} + +/// Returns the largest number in a pointer. O(n). +/// `ptr` must have at least one element before a sentinel. +pub fn max(comptime T: type, comptime sentinel: T, ptr: [*:sentinel]const T) T { + assert(ptr[0] != sentinel); + var best = ptr[0]; + var i: usize = 1; + while (ptr[i] != sentinel) : (i += 1) { + best = @max(best, ptr[i]); + } + return best; +} + +test max { + try testing.expectEqual(max(u8, 0, "abcdefg"), 'g'); + try testing.expectEqual(max(u8, 0, "gabcdef"), 'g'); + try testing.expectEqual(max(u8, 0, "g"), 'g'); +} + +/// Compares two pointers of numbers lexicographically. O(n). +pub fn order(comptime T: type, comptime sentinel: T, lhs: [*:sentinel]const T, rhs: [*:sentinel]const T) math.Order { + var i: usize = 0; + while (lhs[i] == rhs[i] and lhs[i] != sentinel) : (i += 1) {} + return if (lhs[i] == sentinel) if (rhs[i] == sentinel) .eq else .lt else if (rhs[i] == sentinel) .gt else math.order(lhs[i], rhs[i]); +} + +test order { + try testing.expect(order(u8, 0, "abcd", "bee") == .lt); + try testing.expect(order(u8, 0, "abc", "abc") == .eq); + try testing.expect(order(u8, 0, "abc", "abc0") == .lt); + try testing.expect(order(u8, 0, "", "") == .eq); + try testing.expect(order(u8, 0, "", "a") == .lt); + + try testing.expect(order(u16, 1, ([_]u16{ 2, 3, 4, 5, 1 })[0..4 :1].ptr, ([_]u16{ 2, 3, 4, 5, 1 })[0..4 :1].ptr) == .eq); + try testing.expect(order(u16, 1, ([_]u16{ 2, 3, 4, 1 })[0..3 :1].ptr, ([_]u16{ 2, 3, 4, 5, 1 })[0..4 :1].ptr) == .lt); + try testing.expect(order(u16, 1, ([_]u16{ 3, 4, 5, 6, 1 })[0..4 :1].ptr, ([_]u16{ 2, 3, 4, 5, 1 })[0..4 :1].ptr) == .gt); + try testing.expect(order(u16, 1, ([_]u16{1})[0..0 :1].ptr, ([_]u16{1})[0..0 :1].ptr) == .eq); + try testing.expect(order(u16, 1, ([_]u16{1})[0..0 :1].ptr, ([_]u16{ 2, 1 })[0..1 :1].ptr) == .lt); + + try testing.expect(order(i8, 0, ([_]i8{ -1, -2, 0 })[0..2 :0].ptr, ([_]i8{ -1, -2, -3, 0 })[0..3 :0].ptr) == .lt); +} + +/// Returns true if lhs < rhs, false otherwise +pub fn lessThan(comptime T: type, comptime sentinel: T, lhs: [*:sentinel]const T, rhs: [*:sentinel]const T) bool { + return order(T, sentinel, lhs, rhs) == .lt; +} + +test lessThan { + try testing.expect(lessThan(u8, 0, "abcd", "bee")); + try testing.expect(!lessThan(u8, 0, "abc", "abc")); + try testing.expect(lessThan(u8, 0, "abc", "abc0")); + try testing.expect(!lessThan(u8, 0, "", "")); + try testing.expect(lessThan(u8, 0, "", "a")); +} + +/// Takes a sentinel-terminated pointer and iterates over the memory to find the +/// sentinel and determine the length. +/// `[*c]` pointers are assumed to be non-null and 0-terminated. +pub const len = mem.len; + +/// Takes a sentinel-terminated pointer and returns a slice, iterating over the +/// memory to find the sentinel and determine the length. +/// Pointer attributes such as const are preserved. +/// `[*c]` pointers are assumed to be non-null and 0-terminated. +pub const span = mem.span; diff --git a/lib/std/process.zig b/lib/std/process.zig index 58d16eef1db5..b2d2d01ca1ee 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -2017,7 +2017,7 @@ test createNullDelimitedEnvMap { "XCURSOR_SIZE=24", }) |target| { for (environ) |variable| { - if (mem.eql(u8, mem.span(variable orelse continue), target)) break; + if (mem.sentinel_terminated.eqlSlice(u8, 0, variable orelse continue, target)) break; } else { try testing.expect(false); // Environment variable not found } diff --git a/src/main.zig b/src/main.zig index 9349899a561a..ee32da667f6b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6290,11 +6290,9 @@ fn detectNativeCpuWithLLVM( var result = std.Target.Cpu.baseline(arch, builtin.os); if (llvm_cpu_name_z) |cpu_name_z| { - const llvm_cpu_name = mem.span(cpu_name_z); - for (arch.allCpuModels()) |model| { const this_llvm_name = model.llvm_name orelse continue; - if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) { + if (mem.sentinel_terminated.eqlSlice(u8, 0, cpu_name_z, this_llvm_name)) { // Here we use the non-dependencies-populated set, // so that subtracting features later in this function // affect the prepopulated set.