| ... | @@ -856,65 +856,31 @@ pub const TokenIterator = struct { | ... | @@ -856,65 +856,31 @@ pub const TokenIterator = struct { |
| 856 | } | 856 | } |
| 857 | }; | 857 | }; |
| 858 | | 858 | |
| 859 | /// Naively combines a series of strings with a separator. | 859 | pub const SplitIterator = struct { |
| 860 | /// Allocates memory for the result, which must be freed by the caller. | 860 | buffer: []const u8, |
| 861 | pub fn join(allocator: *Allocator, sep: u8, strings: []const []const u8) ![]u8 { | 861 | index: ?usize, |
| 862 | assert(strings.len >= 1); | 862 | delimiter: []const u8, |
| 863 | var total_strings_len: usize = strings.len; // 1 sep per string | 863 | |
| 864 | { | 864 | /// Returns a slice of the next field, or null if splitting is complete. |
| 865 | var string_i: usize = 0; | 865 | pub fn next(self: *SplitIterator) ?[]const u8 { |
| 866 | while (string_i < strings.len) : (string_i += 1) { | 866 | const start = self.index orelse return null; |
| 867 | const arg = ([]const u8)(strings[string_i]); | 867 | const end = if (indexOfPos(u8, self.buffer, start, self.delimiter)) |delim_start| blk: { |
| 868 | total_strings_len += arg.len; | 868 | self.index = delim_start + self.delimiter.len; |
| 869 | } | 869 | break :blk delim_start; |
| 870 | } | 870 | } else blk: { |
| 871 | | 871 | self.index = null; |
| 872 | const buf = try allocator.alloc(u8, total_strings_len); | 872 | break :blk self.buffer.len; |
| 873 | errdefer allocator.free(buf); | 873 | }; |
| 874 | | 874 | return self.buffer[start..end]; |
| 875 | var buf_index: usize = 0; | | |
| 876 | var string_i: usize = 0; | | |
| 877 | while (true) { | | |
| 878 | const arg = ([]const u8)(strings[string_i]); | | |
| 879 | string_i += 1; | | |
| 880 | copy(u8, buf[buf_index..], arg); | | |
| 881 | buf_index += arg.len; | | |
| 882 | if (string_i >= strings.len) break; | | |
| 883 | buf[buf_index] = sep; | | |
| 884 | buf_index += 1; | | |
| 885 | } | 875 | } |
| 886 | | 876 | |
| 887 | return allocator.shrink(u8, buf, buf_index); | 877 | /// Returns a slice of the remaining bytes. Does not affect iterator state. |
| 888 | } | 878 | pub fn rest(self: SplitIterator) []const u8 { |
| 889 | | 879 | const end = self.buffer.len; |
| 890 | test "mem.join" { | 880 | const start = self.index orelse end; |
| 891 | var str: []u8 = try join(debug.global_allocator, ',', [][]const u8{ "a", "b", "c" }); | 881 | return self.buffer[start..end]; |
| 892 | errdefer debug.global_allocator.free(str); | | |
| 893 | assert(eql(u8, str, "a,b,c")); | | |
| 894 | debug.global_allocator.free(str); | | |
| 895 | | | |
| 896 | str = try join(debug.global_allocator, ',', [][]const u8{"a"}); | | |
| 897 | assert(eql(u8, str, "a")); | | |
| 898 | debug.global_allocator.free(str); | | |
| 899 | | | |
| 900 | str = try join(debug.global_allocator, ',', [][]const u8{ "a", ([]const u8)(""), "b", ([]const u8)(""), "c" }); | | |
| 901 | assert(eql(u8, str, "a,,b,,c")); | | |
| 902 | debug.global_allocator.free(str); | | |
| 903 | } | | |
| 904 | | | |
| 905 | /// Naively combines a series of strings with a separator inline. | | |
| 906 | /// Allocates memory for the result, which must be freed by the caller. | | |
| 907 | pub fn joinInline(allocator: *Allocator, sep: u8, strings: ...) ![]u8 { | | |
| 908 | comptime assert(strings.len >= 1); | | |
| 909 | var total_strings_len: usize = strings.len; // 1 sep per string | | |
| 910 | { | | |
| 911 | comptime var string_i = 0; | | |
| 912 | inline while (string_i < strings.len) : (string_i += 1) { | | |
| 913 | const arg = ([]const u8)(strings[string_i]); | | |
| 914 | total_strings_len += arg.len; | | |
| 915 | } | | |
| 916 | } | 882 | } |
| 917 | } | 883 | }; |
| 918 | | 884 | |
| 919 | /// Naively combines a series of slices with a separator. | 885 | /// Naively combines a series of slices with a separator. |
| 920 | /// Allocates memory for the result, which must be freed by the caller. | 886 | /// Allocates memory for the result, which must be freed by the caller. |
| ... | @@ -931,26 +897,25 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons | ... | @@ -931,26 +897,25 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons |
| 931 | const buf = try allocator.alloc(u8, total_len); | 897 | const buf = try allocator.alloc(u8, total_len); |
| 932 | errdefer allocator.free(buf); | 898 | errdefer allocator.free(buf); |
| 933 | | 899 | |
| 934 | var buf_index: usize = 0; | 900 | copy(u8, buf, slices[0]); |
| 935 | comptime var string_i = 0; | 901 | var buf_index: usize = slices[0].len; |
| 936 | inline while (true) { | 902 | for (slices[1..]) |slice| { |
| 937 | const arg = ([]const u8)(strings[string_i]); | 903 | copy(u8, buf[buf_index..], separator); |
| 938 | string_i += 1; | 904 | buf_index += separator.len; |
| 939 | copy(u8, buf[buf_index..], arg); | 905 | copy(u8, buf[buf_index..], slice); |
| 940 | buf_index += arg.len; | 906 | buf_index += slice.len; |
| 941 | if (string_i >= strings.len) break; | | |
| 942 | buf[buf_index] = sep; | | |
| 943 | buf_index += 1; | | |
| 944 | } | 907 | } |
| 945 | | 908 | |
| 946 | // No need for shrink since buf is exactly the correct size. | 909 | // No need for shrink since buf is exactly the correct size. |
| 947 | return buf; | 910 | return buf; |
| 948 | } | 911 | } |
| 949 | | 912 | |
| 950 | test "mem.joinInline" { | 913 | test "mem.join" { |
| 951 | assert(eql(u8, try joinInline(debug.global_allocator, ',', "a", "b", "c"), "a,b,c")); | 914 | var buf: [1024]u8 = undefined; |
| 952 | assert(eql(u8, try joinInline(debug.global_allocator, ',', "a", "b", ([]const u8)(""), "c"), "a,b,,c")); | 915 | const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; |
| 953 | assert(eql(u8, try joinInline(debug.global_allocator, ',', "a"), "a")); | 916 | assert(eql(u8, try join(a, ",", [][]const u8{ "a", "b", "c" }), "a,b,c")); |
| | 917 | assert(eql(u8, try join(a, ",", [][]const u8{"a"}), "a")); |
| | 918 | assert(eql(u8, try join(a, ",", [][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c")); |
| 954 | } | 919 | } |
| 955 | | 920 | |
| 956 | test "testStringEquality" { | 921 | test "testStringEquality" { |