| ... | ... | @@ -856,65 +856,31 @@ pub const TokenIterator = struct { |
| 856 | 856 | } |
| 857 | 857 | }; |
| 858 | 858 | |
| 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 | | } |
| 870 | | } |
| 871 | | |
| 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; |
| 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]; |
| 885 | 875 | } |
| 886 | 876 | |
| 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 | | } |
| 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]; |
| 916 | 882 | } |
| 917 | | } |
| 883 | }; |
| 918 | 884 | |
| 919 | 885 | /// Naively combines a series of slices with a separator. |
| 920 | 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 | 897 | const buf = try allocator.alloc(u8, total_len); |
| 932 | 898 | errdefer allocator.free(buf); |
| 933 | 899 | |
| 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; |
| 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; |
| 944 | 907 | } |
| 945 | 908 | |
| 946 | 909 | // No need for shrink since buf is exactly the correct size. |
| 947 | 910 | return buf; |
| 948 | 911 | } |
| 949 | 912 | |
| 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")); |
| 913 | test "mem.join" { |
| 914 | var buf: [1024]u8 = undefined; |
| 915 | const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; |
| 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 | 921 | test "testStringEquality" { |