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