From b3d23464d066a10e902b75349e99752f2a235e66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Sep 2026 16:15:33 -0700 Subject: [PATCH 1/3] std.mem: introduce copySentinel for when source memory region is sentinel-terminated and length is not yet known. --- lib/std/mem.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index d8b3872eb0be27f79fa66b3805f7cb567e57be10..24daa4a508f6fde819c5d558a5e3dd81f685c3b1 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -269,6 +269,21 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { } } +/// Copy `source` into `dest`. Asserts no overlap. Asserts `dest.len` greater or equal to source len. Returns +/// number of elements copied. Sentinel is not copied. +pub fn copySentinel(comptime T: type, comptime s: T, dest: []T, source: [*:s]const T) usize { + const i = findSentinel(T, s, source); + @memcpy(dest[0..i], source[0..i]); + return i; +} + +test copySentinel { + var dst: [11]u8 = @splat(0xff); + const n = copySentinel(u8, 0, &dst, "hello this\x00is my null-terminated string"); + try testing.expectEqualStrings("hello this\xff", &dst); + try testing.expectEqual(n, 10); +} + /// Generally, Zig users are encouraged to explicitly initialize all fields of a struct explicitly rather than using this function. /// However, it is recognized that there are sometimes use cases for initializing all fields to a "zero" value. For example, when /// interfacing with a C API where this practice is more common and relied upon. If you are performing code review and see this -- 2.54.0 From 41905730c036dd69e2d5d4ed8d87320c609b4531 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Sep 2026 16:17:28 -0700 Subject: [PATCH 2/3] std.mem: introduce lessThanZ complementing orderZ and lessThan --- lib/std/mem.zig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 24daa4a508f6fde819c5d558a5e3dd81f685c3b1..278e917df6213af76dbd1131d7acc872416fbe30 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -748,6 +748,19 @@ test lessThan { try testing.expect(lessThan(u8, "", "a")); } +/// Returns `true` if `lhs < rhs`; `false` otherwise, operating on null-terminated arrays. +pub fn lessThanZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T) bool { + return orderZ(T, lhs, rhs) == .lt; +} + +test lessThanZ { + try testing.expect(lessThanZ(u8, "abcd", "bee")); + try testing.expect(!lessThanZ(u8, "abc", "abc")); + try testing.expect(lessThanZ(u8, "abc", "abc0")); + try testing.expect(!lessThanZ(u8, "", "")); + try testing.expect(lessThanZ(u8, "", "a")); +} + const use_vectors = switch (builtin.zig_backend) { // These backends don't support vectors yet. .stage2_aarch64, -- 2.54.0 From 06d068bf7b84b8fd4e28edf3daffb3fca9079000 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Sep 2026 16:43:44 -0700 Subject: [PATCH 3/3] std.mem: introduce copySentinelInclusive --- lib/std/mem.zig | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 278e917df6213af76dbd1131d7acc872416fbe30..a679291dba1bcae6e79286ad3b7b199562f68695 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -269,8 +269,10 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { } } -/// Copy `source` into `dest`. Asserts no overlap. Asserts `dest.len` greater or equal to source len. Returns -/// number of elements copied. Sentinel is not copied. +/// Copy `source` into `dest`, excluding the sentinel. +/// +/// Asserts no overlap. Asserts `dest.len` greater or equal to source len. Returns number of elements copied, +/// not counting the sentinel. pub fn copySentinel(comptime T: type, comptime s: T, dest: []T, source: [*:s]const T) usize { const i = findSentinel(T, s, source); @memcpy(dest[0..i], source[0..i]); @@ -284,6 +286,22 @@ test copySentinel { try testing.expectEqual(n, 10); } +/// Copy `source` into `dest`, including the sentinel. +/// +/// Asserts no overlap. Asserts `dest.len` greater or equal to source len, including room for the sentinel. +/// Returns number of elements copied, including the sentinel. +pub fn copySentinelInclusive(comptime T: type, comptime s: T, dest: []T, source: [*:s]const T) usize { + const i = findSentinel(T, s, source) + 1; + @memcpy(dest[0..i], source[0..i]); + return i; +} + +test copySentinelInclusive { + var dst: [12]u8 = @splat(0xff); + const n = copySentinelInclusive(u8, 0, &dst, "hello this\x00is my null-terminated string"); + try testing.expectEqualStrings("hello this\x00\xff", &dst); + try testing.expectEqual(n, 11); +} /// Generally, Zig users are encouraged to explicitly initialize all fields of a struct explicitly rather than using this function. /// However, it is recognized that there are sometimes use cases for initializing all fields to a "zero" value. For example, when /// interfacing with a C API where this practice is more common and relied upon. If you are performing code review and see this -- 2.54.0