authorgravatar for jay@jayschwa.netJay Petacat <jay@jayschwa.net> 2026-01-11 01:26:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-13 06:19:14+01:00
logd86e8b7795a5b5c2403c6a07dc818bc1d8a5cda4
tree229faf2f7183a1453f1fa1ef21cadeb70a6f3d48
parent044ba3e0b020c317e8934d0d8271a512a9deeff8

std.mem.sliceTo: Return slice with sentinel from unbounded pointers

Commit dec1163fbb removed sentinels from the returned slice for C pointers. Since C pointers have no bounds, we know that it'll keep scanning until it finds `end` (or crash trying). The same is also true of many-item pointers without a sentinel (e.g. `[*]T`), so I added support for those too.

1 files changed, 21 insertions(+), 7 deletions(-)

lib/std/mem.zig+21-7
......@@ -913,8 +913,9 @@ fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type {
913913 .pointer => |ptr_info| {
914914 const Elem = std.meta.Elem(T);
915915 const have_sentinel: bool = switch (ptr_info.size) {
916 .one, .slice, .many => if (std.meta.sentinel(T)) |s| s == end else false,
917 .c => false,
916 .one, .slice => if (std.meta.sentinel(T)) |s| s == end else false,
917 .many => if (std.meta.sentinel(T)) |s| s == end else true,
918 .c => true,
918919 };
919920 return @Pointer(.slice, .{
920921 .@"const" = ptr_info.is_const,
......@@ -929,11 +930,14 @@ fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type {
929930 @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));
930931}
931932
932/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice and iterates searching for
933/// the first occurrence of `end`, returning the scanned slice.
934/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned.
935/// If the pointer type is sentinel terminated and `end` matches that terminator, the
936/// resulting slice is also sentinel terminated.
933/// Takes a pointer to an array, a many-item pointer, or a slice, and returns a
934/// slice of the items up to the first occurrence of `end`.
935/// If `end` is not found, the resulting slice will include all items up to the
936/// input's length or sentinel.
937/// If the pointer type is unbounded (no length or sentinel), `end` will be the
938/// sentinel for the resulting slice.
939/// If the pointer type is sentinel-terminated by `end`, the resulting slice
940/// will also be sentinel-terminated by `end`.
937941/// Pointer properties such as mutability and alignment are preserved.
938942/// C pointers are assumed to be non-null.
939943pub fn sliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) {
......@@ -961,8 +965,15 @@ test sliceTo {
961965 try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3));
962966 try testing.expectEqualSlices(u16, array[0..2], sliceTo(array[0..3], 3));
963967
968 const many_ptr: [*]u16 = &array;
969 try testing.expectEqualSlices(u16, array[0..2], sliceTo(many_ptr, 3));
970 try testing.expectEqual([:3]u16, @TypeOf(sliceTo(many_ptr, 3)));
971
964972 const sentinel_ptr = @as([*:5]u16, @ptrCast(&array));
965973 try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_ptr, 3));
974 try testing.expectEqual([]u16, @TypeOf(sliceTo(sentinel_ptr, 3)));
975 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 5));
976 try testing.expectEqual([:5]u16, @TypeOf(sliceTo(sentinel_ptr, 5)));
966977 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99));
967978
968979 const optional_sentinel_ptr = @as(?[*:5]u16, @ptrCast(&array));
......@@ -971,6 +982,7 @@ test sliceTo {
971982
972983 const c_ptr = @as([*c]u16, &array);
973984 try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3));
985 try testing.expectEqual([:3]u16, @TypeOf(sliceTo(c_ptr, 3)));
974986
975987 const slice: []u16 = &array;
976988 try testing.expectEqualSlices(u16, array[0..2], sliceTo(slice, 3));
......@@ -1015,6 +1027,8 @@ fn lenSliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) usize {
10151027 var i: usize = 0;
10161028 while (ptr[i] != end and ptr[i] != s) i += 1;
10171029 return i;
1030 } else {
1031 return findSentinel(ptr_info.child, end, @ptrCast(ptr));
10181032 },
10191033 .c => {
10201034 assert(ptr != null);