Skip to content

Commit 5b26128

Browse files
committed
add new functions to std.mem and deprecate others
add std.mem.Span add std.mem.span add std.mem.length add std.mem.indexOfSentinel deprecate std.mem.len deprecate std.mem.toSlice deprecate std.mem.toSliceConst
1 parent 4505857 commit 5b26128

File tree

2 files changed

+118
-2
lines changed

2 files changed

+118
-2
lines changed

lib/std/builtin.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ pub const TypeInfo = union(enum) {
185185
child: type,
186186
is_allowzero: bool,
187187

188+
/// This field is an optional type.
188189
/// The type of the sentinel is the element type of the pointer, which is
189190
/// the value of the `child` field in this struct. However there is no way
190191
/// to refer to that type here, so we use `var`.
@@ -206,6 +207,7 @@ pub const TypeInfo = union(enum) {
206207
len: comptime_int,
207208
child: type,
208209

210+
/// This field is an optional type.
209211
/// The type of the sentinel is the element type of the array, which is
210212
/// the value of the `child` field in this struct. However there is no way
211213
/// to refer to that type here, so we use `var`.

lib/std/mem.zig

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,20 @@ pub fn zeroes(comptime T: type) T {
333333
}
334334
return array;
335335
},
336-
.Vector, .ErrorUnion, .ErrorSet, .Union, .Fn, .BoundFn, .Type, .NoReturn, .Undefined, .Opaque, .Frame, .AnyFrame, => {
337-
@compileError("Can't set a "++ @typeName(T) ++" to zero.");
336+
.Vector,
337+
.ErrorUnion,
338+
.ErrorSet,
339+
.Union,
340+
.Fn,
341+
.BoundFn,
342+
.Type,
343+
.NoReturn,
344+
.Undefined,
345+
.Opaque,
346+
.Frame,
347+
.AnyFrame,
348+
=> {
349+
@compileError("Can't set a " ++ @typeName(T) ++ " to zero.");
338350
},
339351
}
340352
}
@@ -470,20 +482,122 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
470482
return true;
471483
}
472484

485+
/// Deprecated. Use `length` or `indexOfSentinel`.
473486
pub fn len(comptime T: type, ptr: [*:0]const T) usize {
474487
var count: usize = 0;
475488
while (ptr[count] != 0) : (count += 1) {}
476489
return count;
477490
}
478491

492+
/// Deprecated. Use `span`.
479493
pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {
480494
return ptr[0..len(T, ptr) :0];
481495
}
482496

497+
/// Deprecated. Use `span`.
483498
pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
484499
return ptr[0..len(T, ptr) :0];
485500
}
486501

502+
/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
503+
/// returns a slice. If there is a sentinel on the input type, there will be a
504+
/// sentinel on the output type. The constness of the output type matches
505+
/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated.
506+
pub fn Span(comptime T: type) type {
507+
var ptr_info = @typeInfo(T).Pointer;
508+
switch (ptr_info.size) {
509+
.One => switch (@typeInfo(ptr_info.child)) {
510+
.Array => |info| {
511+
ptr_info.child = info.child;
512+
ptr_info.sentinel = info.sentinel;
513+
},
514+
else => @compileError("invalid type given to std.mem.Span"),
515+
},
516+
.C => {
517+
ptr_info.sentinel = 0;
518+
},
519+
.Many, .Slice => {},
520+
}
521+
ptr_info.size = .Slice;
522+
return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info });
523+
}
524+
525+
test "Span" {
526+
testing.expect(Span(*[5]u16) == []u16);
527+
testing.expect(Span(*const [5]u16) == []const u16);
528+
testing.expect(Span([]u16) == []u16);
529+
testing.expect(Span([]const u8) == []const u8);
530+
testing.expect(Span([:1]u16) == [:1]u16);
531+
testing.expect(Span([:1]const u8) == [:1]const u8);
532+
testing.expect(Span([*:1]u16) == [:1]u16);
533+
testing.expect(Span([*:1]const u8) == [:1]const u8);
534+
testing.expect(Span([*c]u16) == [:0]u16);
535+
testing.expect(Span([*c]const u8) == [:0]const u8);
536+
}
537+
538+
/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
539+
/// returns a slice. If there is a sentinel on the input type, there will be a
540+
/// sentinel on the output type. The constness of the output type matches
541+
/// the constness of the input type.
542+
pub fn span(ptr: var) Span(@TypeOf(ptr)) {
543+
const Result = Span(@TypeOf(ptr));
544+
const l = length(ptr);
545+
if (@typeInfo(Result).Pointer.sentinel) |s| {
546+
return ptr[0..l :s];
547+
} else {
548+
return ptr[0..l];
549+
}
550+
}
551+
552+
test "span" {
553+
var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
554+
const ptr = array[0..2 :3].ptr;
555+
testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));
556+
testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
557+
}
558+
559+
/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
560+
/// or a slice, and returns the length.
561+
pub fn length(ptr: var) usize {
562+
return switch (@typeInfo(@TypeOf(ptr))) {
563+
.Array => |info| info.len,
564+
.Pointer => |info| switch (info.size) {
565+
.One => switch (@typeInfo(info.child)) {
566+
.Array => |x| x.len,
567+
else => @compileError("invalid type given to std.mem.length"),
568+
},
569+
.Many => if (info.sentinel) |sentinel|
570+
indexOfSentinel(info.child, sentinel, ptr)
571+
else
572+
@compileError("length of pointer with no sentinel"),
573+
.C => indexOfSentinel(info.child, 0, ptr),
574+
.Slice => ptr.len,
575+
},
576+
else => @compileError("invalid type given to std.mem.length"),
577+
};
578+
}
579+
580+
test "length" {
581+
testing.expect(length("aoeu") == 4);
582+
583+
{
584+
var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
585+
testing.expect(length(&array) == 5);
586+
testing.expect(length(array[0..3]) == 3);
587+
array[2] = 0;
588+
const ptr = array[0..2 :0].ptr;
589+
testing.expect(length(ptr) == 2);
590+
}
591+
}
592+
593+
pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize {
594+
var i: usize = 0;
595+
while (ptr[i] != sentinel) {
596+
i += 1;
597+
}
598+
return i;
599+
}
600+
487601
/// Returns true if all elements in a slice are equal to the scalar value provided
488602
pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
489603
for (slice) |item| {

0 commit comments

Comments
 (0)