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 {...@@ -913,8 +913,9 @@ fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type {
913 .pointer => |ptr_info| {913 .pointer => |ptr_info| {
914 const Elem = std.meta.Elem(T);914 const Elem = std.meta.Elem(T);
915 const have_sentinel: bool = switch (ptr_info.size) {915 const have_sentinel: bool = switch (ptr_info.size) {
916 .one, .slice, .many => if (std.meta.sentinel(T)) |s| s == end else false,916 .one, .slice => if (std.meta.sentinel(T)) |s| s == end else false,
917 .c => false,917 .many => if (std.meta.sentinel(T)) |s| s == end else true,
918 .c => true,
918 };919 };
919 return @Pointer(.slice, .{920 return @Pointer(.slice, .{
920 .@"const" = ptr_info.is_const,921 .@"const" = ptr_info.is_const,
...@@ -929,11 +930,14 @@ fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type {...@@ -929,11 +930,14 @@ fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type {
929 @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));930 @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));
930}931}
931932
932/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice and iterates searching for933/// Takes a pointer to an array, a many-item pointer, or a slice, and returns a
933/// the first occurrence of `end`, returning the scanned slice.934/// slice of the items up to the first occurrence of `end`.
934/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned.935/// If `end` is not found, the resulting slice will include all items up to the
935/// If the pointer type is sentinel terminated and `end` matches that terminator, the936/// input's length or sentinel.
936/// resulting slice is also sentinel terminated.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`.
937/// Pointer properties such as mutability and alignment are preserved.941/// Pointer properties such as mutability and alignment are preserved.
938/// C pointers are assumed to be non-null.942/// C pointers are assumed to be non-null.
939pub fn sliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) {943pub fn sliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) {
...@@ -961,8 +965,15 @@ test sliceTo {...@@ -961,8 +965,15 @@ test sliceTo {
961 try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3));965 try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3));
962 try testing.expectEqualSlices(u16, array[0..2], sliceTo(array[0..3], 3));966 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
964 const sentinel_ptr = @as([*:5]u16, @ptrCast(&array));972 const sentinel_ptr = @as([*:5]u16, @ptrCast(&array));
965 try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_ptr, 3));973 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)));
966 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99));977 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99));
967978
968 const optional_sentinel_ptr = @as(?[*:5]u16, @ptrCast(&array));979 const optional_sentinel_ptr = @as(?[*:5]u16, @ptrCast(&array));
...@@ -971,6 +982,7 @@ test sliceTo {...@@ -971,6 +982,7 @@ test sliceTo {
971982
972 const c_ptr = @as([*c]u16, &array);983 const c_ptr = @as([*c]u16, &array);
973 try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3));984 try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3));
985 try testing.expectEqual([:3]u16, @TypeOf(sliceTo(c_ptr, 3)));
974986
975 const slice: []u16 = &array;987 const slice: []u16 = &array;
976 try testing.expectEqualSlices(u16, array[0..2], sliceTo(slice, 3));988 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 {...@@ -1015,6 +1027,8 @@ fn lenSliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) usize {
1015 var i: usize = 0;1027 var i: usize = 0;
1016 while (ptr[i] != end and ptr[i] != s) i += 1;1028 while (ptr[i] != end and ptr[i] != s) i += 1;
1017 return i;1029 return i;
1030 } else {
1031 return findSentinel(ptr_info.child, end, @ptrCast(ptr));
1018 },1032 },
1019 .c => {1033 .c => {
1020 assert(ptr != null);1034 assert(ptr != null);