| ... | ... | @@ -602,6 +602,7 @@ test "span" { |
| 602 | 602 | try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null))); |
| 603 | 603 | } |
| 604 | 604 | |
| 605 | /// Deprecated: use std.mem.span() or std.mem.sliceTo() |
| 605 | 606 | /// Same as `span`, except when there is both a sentinel and an array |
| 606 | 607 | /// length or slice length, scans the memory for the sentinel value |
| 607 | 608 | /// rather than using the length. |
| ... | ... | @@ -630,6 +631,192 @@ test "spanZ" { |
| 630 | 631 | try testing.expectEqual(@as(?[:0]u16, null), spanZ(@as(?[*:0]u16, null))); |
| 631 | 632 | } |
| 632 | 633 | |
| 634 | /// Helper for the return type of sliceTo() |
| 635 | fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type { |
| 636 | switch (@typeInfo(T)) { |
| 637 | .Optional => |optional_info| { |
| 638 | return ?SliceTo(optional_info.child, end); |
| 639 | }, |
| 640 | .Pointer => |ptr_info| { |
| 641 | var new_ptr_info = ptr_info; |
| 642 | new_ptr_info.size = .Slice; |
| 643 | switch (ptr_info.size) { |
| 644 | .One => switch (@typeInfo(ptr_info.child)) { |
| 645 | .Array => |array_info| { |
| 646 | new_ptr_info.child = array_info.child; |
| 647 | // The return type must only be sentinel terminated if we are guaranteed |
| 648 | // to find the value searched for, which is only the case if it matches |
| 649 | // the sentinel of the type passed. |
| 650 | if (array_info.sentinel) |sentinel| { |
| 651 | if (end == sentinel) { |
| 652 | new_ptr_info.sentinel = end; |
| 653 | } else { |
| 654 | new_ptr_info.sentinel = null; |
| 655 | } |
| 656 | } |
| 657 | }, |
| 658 | else => {}, |
| 659 | }, |
| 660 | .Many, .Slice => { |
| 661 | // The return type must only be sentinel terminated if we are guaranteed |
| 662 | // to find the value searched for, which is only the case if it matches |
| 663 | // the sentinel of the type passed. |
| 664 | if (ptr_info.sentinel) |sentinel| { |
| 665 | if (end == sentinel) { |
| 666 | new_ptr_info.sentinel = end; |
| 667 | } else { |
| 668 | new_ptr_info.sentinel = null; |
| 669 | } |
| 670 | } |
| 671 | }, |
| 672 | .C => { |
| 673 | new_ptr_info.sentinel = end; |
| 674 | // C pointers are always allowzero, but we don't want the return type to be. |
| 675 | assert(new_ptr_info.is_allowzero); |
| 676 | new_ptr_info.is_allowzero = false; |
| 677 | }, |
| 678 | } |
| 679 | return @Type(std.builtin.TypeInfo{ .Pointer = new_ptr_info }); |
| 680 | }, |
| 681 | else => {}, |
| 682 | } |
| 683 | @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T)); |
| 684 | } |
| 685 | |
| 686 | /// Takes a pointer to an array, an array, a sentinel-terminated pointer, or a slice and |
| 687 | /// iterates searching for the first occurrence of `end`, returning the scanned slice. |
| 688 | /// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned. |
| 689 | /// If the pointer type is sentinel terminated and `end` matches that terminator, the |
| 690 | /// resulting slice is also sentinel terminated. |
| 691 | /// Pointer properties such as mutability and alignment are preserved. |
| 692 | /// C pointers are assumed to be non-null. |
| 693 | pub fn sliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) { |
| 694 | if (@typeInfo(@TypeOf(ptr)) == .Optional) { |
| 695 | const non_null = ptr orelse return null; |
| 696 | return sliceTo(non_null, end); |
| 697 | } |
| 698 | const Result = SliceTo(@TypeOf(ptr), end); |
| 699 | const length = lenSliceTo(ptr, end); |
| 700 | if (@typeInfo(Result).Pointer.sentinel) |s| { |
| 701 | return ptr[0..length :s]; |
| 702 | } else { |
| 703 | return ptr[0..length]; |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | test "sliceTo" { |
| 708 | try testing.expectEqualSlices(u8, "aoeu", sliceTo("aoeu", 0)); |
| 709 | |
| 710 | { |
| 711 | var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; |
| 712 | try testing.expectEqualSlices(u16, &array, sliceTo(&array, 0)); |
| 713 | try testing.expectEqualSlices(u16, array[0..3], sliceTo(array[0..3], 0)); |
| 714 | try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3)); |
| 715 | try testing.expectEqualSlices(u16, array[0..2], sliceTo(array[0..3], 3)); |
| 716 | |
| 717 | const sentinel_ptr = @ptrCast([*:5]u16, &array); |
| 718 | try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_ptr, 3)); |
| 719 | try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99)); |
| 720 | |
| 721 | const optional_sentinel_ptr = @ptrCast(?[*:5]u16, &array); |
| 722 | try testing.expectEqualSlices(u16, array[0..2], sliceTo(optional_sentinel_ptr, 3).?); |
| 723 | try testing.expectEqualSlices(u16, array[0..4], sliceTo(optional_sentinel_ptr, 99).?); |
| 724 | |
| 725 | const c_ptr = @as([*c]u16, &array); |
| 726 | try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3)); |
| 727 | |
| 728 | const slice: []u16 = &array; |
| 729 | try testing.expectEqualSlices(u16, array[0..2], sliceTo(slice, 3)); |
| 730 | try testing.expectEqualSlices(u16, &array, sliceTo(slice, 99)); |
| 731 | |
| 732 | const sentinel_slice: [:5]u16 = array[0..4 :5]; |
| 733 | try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_slice, 3)); |
| 734 | try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_slice, 99)); |
| 735 | } |
| 736 | { |
| 737 | var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; |
| 738 | try testing.expectEqualSlices(u16, sentinel_array[0..2], sliceTo(&sentinel_array, 3)); |
| 739 | try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 0)); |
| 740 | try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 99)); |
| 741 | } |
| 742 | |
| 743 | try testing.expectEqual(@as(?[]u8, null), sliceTo(@as(?[]u8, null), 0)); |
| 744 | } |
| 745 | |
| 746 | /// Private helper for sliceTo(). If you want the length, use sliceTo(foo, x).len |
| 747 | fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize { |
| 748 | switch (@typeInfo(@TypeOf(ptr))) { |
| 749 | .Pointer => |ptr_info| switch (ptr_info.size) { |
| 750 | .One => switch (@typeInfo(ptr_info.child)) { |
| 751 | .Array => |array_info| { |
| 752 | if (array_info.sentinel) |sentinel| { |
| 753 | if (sentinel == end) { |
| 754 | return indexOfSentinel(array_info.child, end, ptr); |
| 755 | } |
| 756 | } |
| 757 | return indexOfScalar(array_info.child, ptr, end) orelse array_info.len; |
| 758 | }, |
| 759 | else => {}, |
| 760 | }, |
| 761 | .Many => if (ptr_info.sentinel) |sentinel| { |
| 762 | // We may be looking for something other than the sentinel, |
| 763 | // but iterating past the sentinel would be a bug so we need |
| 764 | // to check for both. |
| 765 | var i: usize = 0; |
| 766 | while (ptr[i] != end and ptr[i] != sentinel) i += 1; |
| 767 | return i; |
| 768 | }, |
| 769 | .C => { |
| 770 | assert(ptr != null); |
| 771 | return indexOfSentinel(ptr_info.child, end, ptr); |
| 772 | }, |
| 773 | .Slice => { |
| 774 | if (ptr_info.sentinel) |sentinel| { |
| 775 | if (sentinel == end) { |
| 776 | return indexOfSentinel(ptr_info.child, sentinel, ptr); |
| 777 | } |
| 778 | } |
| 779 | return indexOfScalar(ptr_info.child, ptr, end) orelse ptr.len; |
| 780 | }, |
| 781 | }, |
| 782 | else => {}, |
| 783 | } |
| 784 | @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(@TypeOf(ptr))); |
| 785 | } |
| 786 | |
| 787 | test "lenSliceTo" { |
| 788 | try testing.expect(lenSliceTo("aoeu", 0) == 4); |
| 789 | |
| 790 | { |
| 791 | var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; |
| 792 | try testing.expectEqual(@as(usize, 5), lenSliceTo(&array, 0)); |
| 793 | try testing.expectEqual(@as(usize, 3), lenSliceTo(array[0..3], 0)); |
| 794 | try testing.expectEqual(@as(usize, 2), lenSliceTo(&array, 3)); |
| 795 | try testing.expectEqual(@as(usize, 2), lenSliceTo(array[0..3], 3)); |
| 796 | |
| 797 | const sentinel_ptr = @ptrCast([*:5]u16, &array); |
| 798 | try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_ptr, 3)); |
| 799 | try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_ptr, 99)); |
| 800 | |
| 801 | const c_ptr = @as([*c]u16, &array); |
| 802 | try testing.expectEqual(@as(usize, 2), lenSliceTo(c_ptr, 3)); |
| 803 | |
| 804 | const slice: []u16 = &array; |
| 805 | try testing.expectEqual(@as(usize, 2), lenSliceTo(slice, 3)); |
| 806 | try testing.expectEqual(@as(usize, 5), lenSliceTo(slice, 99)); |
| 807 | |
| 808 | const sentinel_slice: [:5]u16 = array[0..4 :5]; |
| 809 | try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_slice, 3)); |
| 810 | try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_slice, 99)); |
| 811 | } |
| 812 | { |
| 813 | var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; |
| 814 | try testing.expectEqual(@as(usize, 2), lenSliceTo(&sentinel_array, 3)); |
| 815 | try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 0)); |
| 816 | try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 99)); |
| 817 | } |
| 818 | } |
| 819 | |
| 633 | 820 | /// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer, |
| 634 | 821 | /// a slice or a tuple, and returns the length. |
| 635 | 822 | /// In the case of a sentinel-terminated array, it uses the array length. |
| ... | ... | @@ -688,6 +875,7 @@ test "len" { |
| 688 | 875 | } |
| 689 | 876 | } |
| 690 | 877 | |
| 878 | /// Deprecated: use std.mem.len() or std.mem.sliceTo().len |
| 691 | 879 | /// Takes a pointer to an array, an array, a sentinel-terminated pointer, |
| 692 | 880 | /// or a slice, and returns the length. |
| 693 | 881 | /// In the case of a sentinel-terminated array, it scans the array |