Commit 28a6c136e9

Andrew Kelley <andrew@ziglang.org>
2020-03-20 00:30:09
revert std.mem.span to prefer len over sentinel; add spanZ
1 parent dc04e97
Changed files (1)
lib
lib/std/mem.zig
@@ -496,14 +496,14 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
     return true;
 }
 
-/// Deprecated. Use `span`.
+/// Deprecated. Use `spanZ`.
 pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {
-    return ptr[0..len(ptr) :0];
+    return ptr[0..lenZ(ptr) :0];
 }
 
-/// Deprecated. Use `span`.
+/// Deprecated. Use `spanZ`.
 pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
-    return ptr[0..len(ptr) :0];
+    return ptr[0..lenZ(ptr) :0];
 }
 
 /// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
@@ -548,6 +548,9 @@ test "Span" {
 /// returns a slice. If there is a sentinel on the input type, there will be a
 /// sentinel on the output type. The constness of the output type matches
 /// the constness of the input type.
+///
+/// When there is both a sentinel and an array length or slice length, the
+/// length value is used instead of the sentinel.
 pub fn span(ptr: var) Span(@TypeOf(ptr)) {
     const Result = Span(@TypeOf(ptr));
     const l = len(ptr);
@@ -565,11 +568,74 @@ test "span" {
     testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
 }
 
+/// Same as `span`, except when there is both a sentinel and an array
+/// length or slice length, scans the memory for the sentinel value
+/// rather than using the length.
+pub fn spanZ(ptr: var) Span(@TypeOf(ptr)) {
+    const Result = Span(@TypeOf(ptr));
+    const l = lenZ(ptr);
+    if (@typeInfo(Result).Pointer.sentinel) |s| {
+        return ptr[0..l :s];
+    } else {
+        return ptr[0..l];
+    }
+}
+
+test "spanZ" {
+    var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
+    const ptr = @as([*:3]u16, array[0..2 :3]);
+    testing.expect(eql(u16, spanZ(ptr), &[_]u16{ 1, 2 }));
+    testing.expect(eql(u16, spanZ(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
+}
+
+/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
+/// or a slice, and returns the length.
+/// In the case of a sentinel-terminated array, it uses the array length.
+/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
+pub fn len(ptr: var) usize {
+    return switch (@typeInfo(@TypeOf(ptr))) {
+        .Array => |info| info.len,
+        .Pointer => |info| switch (info.size) {
+            .One => switch (@typeInfo(info.child)) {
+                .Array => ptr.len,
+                else => @compileError("invalid type given to std.mem.len"),
+            },
+            .Many => if (info.sentinel) |sentinel|
+                indexOfSentinel(info.child, sentinel, ptr)
+            else
+                @compileError("length of pointer with no sentinel"),
+            .C => indexOfSentinel(info.child, 0, ptr),
+            .Slice => ptr.len,
+        },
+        else => @compileError("invalid type given to std.mem.len"),
+    };
+}
+
+test "len" {
+    testing.expect(len("aoeu") == 4);
+
+    {
+        var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
+        testing.expect(len(&array) == 5);
+        testing.expect(len(array[0..3]) == 3);
+        array[2] = 0;
+        const ptr = @as([*:0]u16, array[0..2 :0]);
+        testing.expect(len(ptr) == 2);
+    }
+    {
+        var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
+        testing.expect(len(&array) == 5);
+        array[2] = 0;
+        testing.expect(len(&array) == 5);
+    }
+}
+
 /// Takes a pointer to an array, an array, a sentinel-terminated pointer,
 /// or a slice, and returns the length.
 /// In the case of a sentinel-terminated array, it scans the array
 /// for a sentinel and uses that for the length, rather than using the array length.
-pub fn len(ptr: var) usize {
+/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
+pub fn lenZ(ptr: var) usize {
     return switch (@typeInfo(@TypeOf(ptr))) {
         .Array => |info| if (info.sentinel) |sentinel|
             indexOfSentinel(info.child, sentinel, &ptr)
@@ -581,35 +647,38 @@ pub fn len(ptr: var) usize {
                     indexOfSentinel(x.child, sentinel, ptr)
                 else
                     ptr.len,
-                else => @compileError("invalid type given to std.mem.length"),
+                else => @compileError("invalid type given to std.mem.lenZ"),
             },
             .Many => if (info.sentinel) |sentinel|
                 indexOfSentinel(info.child, sentinel, ptr)
             else
                 @compileError("length of pointer with no sentinel"),
             .C => indexOfSentinel(info.child, 0, ptr),
-            .Slice => ptr.len,
+            .Slice => if (info.sentinel) |sentinel|
+                indexOfSentinel(info.child, sentinel, ptr.ptr)
+            else
+                ptr.len,
         },
-        else => @compileError("invalid type given to std.mem.length"),
+        else => @compileError("invalid type given to std.mem.lenZ"),
     };
 }
 
-test "len" {
-    testing.expect(len("aoeu") == 4);
+test "lenZ" {
+    testing.expect(lenZ("aoeu") == 4);
 
     {
         var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
-        testing.expect(len(&array) == 5);
-        testing.expect(len(array[0..3]) == 3);
+        testing.expect(lenZ(&array) == 5);
+        testing.expect(lenZ(array[0..3]) == 3);
         array[2] = 0;
         const ptr = @as([*:0]u16, array[0..2 :0]);
-        testing.expect(len(ptr) == 2);
+        testing.expect(lenZ(ptr) == 2);
     }
     {
         var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
-        testing.expect(len(&array) == 5);
+        testing.expect(lenZ(&array) == 5);
         array[2] = 0;
-        testing.expect(len(&array) == 2);
+        testing.expect(lenZ(&array) == 2);
     }
 }