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