Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions lib/std/debug/Coverage.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
};

Expand All @@ -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);
}
};
};
Expand Down
12 changes: 9 additions & 3 deletions lib/std/mem.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -4842,3 +4844,7 @@ test "read/write(Var)PackedInt" {
}
}
}

test {
_ = sentinel_terminated;
}
145 changes: 145 additions & 0 deletions lib/std/mem/sentinel_terminated.zig
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion lib/std/process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 1 addition & 3 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down