| ... | @@ -692,11 +692,15 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { | ... | @@ -692,11 +692,15 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { |
| 692 | /// any of the bytes in `split_bytes`. | 692 | /// any of the bytes in `split_bytes`. |
| 693 | /// split(" abc def ghi ", " ") | 693 | /// split(" abc def ghi ", " ") |
| 694 | /// Will return slices for "abc", "def", "ghi", null, in that order. | 694 | /// Will return slices for "abc", "def", "ghi", null, in that order. |
| | 695 | /// If `split_bytes` does not exist in buffer, |
| | 696 | /// the iterator will return `buffer`, null, in that order. |
| 695 | pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { | 697 | pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { |
| 696 | return SplitIterator{ | 698 | return SplitIterator{ |
| 697 | .index = 0, | 699 | .index = 0, |
| 698 | .buffer = buffer, | 700 | .buffer = buffer, |
| 699 | .split_bytes = split_bytes, | 701 | .split_bytes = split_bytes, |
| | 702 | .glob = true, |
| | 703 | .spun = false, |
| 700 | }; | 704 | }; |
| 701 | } | 705 | } |
| 702 | | 706 | |
| ... | @@ -706,6 +710,95 @@ test "mem.split" { | ... | @@ -706,6 +710,95 @@ test "mem.split" { |
| 706 | assert(eql(u8, it.next().?, "def")); | 710 | assert(eql(u8, it.next().?, "def")); |
| 707 | assert(eql(u8, it.next().?, "ghi")); | 711 | assert(eql(u8, it.next().?, "ghi")); |
| 708 | assert(it.next() == null); | 712 | assert(it.next() == null); |
| | 713 | |
| | 714 | it = split("..\\bob", "\\"); |
| | 715 | assert(eql(u8, it.next().?, "..")); |
| | 716 | assert(eql(u8, "..", "..\\bob"[0..it.index])); |
| | 717 | assert(eql(u8, it.next().?, "bob")); |
| | 718 | assert(it.next() == null); |
| | 719 | |
| | 720 | it = split("//a/b", "/"); |
| | 721 | assert(eql(u8, it.next().?, "a")); |
| | 722 | assert(eql(u8, it.next().?, "b")); |
| | 723 | assert(eql(u8, "//a/b", "//a/b"[0..it.index])); |
| | 724 | assert(it.next() == null); |
| | 725 | |
| | 726 | it = split("|", "|"); |
| | 727 | assert(it.next() == null); |
| | 728 | |
| | 729 | it = split("", "|"); |
| | 730 | assert(eql(u8, it.next().?, "")); |
| | 731 | assert(it.next() == null); |
| | 732 | |
| | 733 | it = split("hello", ""); |
| | 734 | assert(eql(u8, it.next().?, "hello")); |
| | 735 | assert(it.next() == null); |
| | 736 | |
| | 737 | it = split("hello", " "); |
| | 738 | assert(eql(u8, it.next().?, "hello")); |
| | 739 | assert(it.next() == null); |
| | 740 | } |
| | 741 | |
| | 742 | test "mem.split (multibyte)" { |
| | 743 | var it = split("a|b,c/d e", " /,|"); |
| | 744 | assert(eql(u8, it.next().?, "a")); |
| | 745 | assert(eql(u8, it.next().?, "b")); |
| | 746 | assert(eql(u8, it.next().?, "c")); |
| | 747 | assert(eql(u8, it.next().?, "d")); |
| | 748 | assert(eql(u8, it.next().?, "e")); |
| | 749 | assert(it.next() == null); |
| | 750 | } |
| | 751 | |
| | 752 | /// Returns an iterator that iterates over the slices of `buffer` that |
| | 753 | /// seperates by bytes in `delimiter`. |
| | 754 | /// separate("abc|def||ghi", "|") |
| | 755 | /// Will return slices for "abc", "def", "", "ghi", null, in that order. |
| | 756 | /// If `delimiter` does not exist in buffer, |
| | 757 | /// the iterator will return `buffer`, null, in that order. |
| | 758 | pub fn separate(buffer: []const u8, delimiter: []const u8) SplitIterator { |
| | 759 | return SplitIterator{ |
| | 760 | .index = 0, |
| | 761 | .buffer = buffer, |
| | 762 | .split_bytes = delimiter, |
| | 763 | .glob = false, |
| | 764 | .spun = false, |
| | 765 | }; |
| | 766 | } |
| | 767 | |
| | 768 | test "mem.separate" { |
| | 769 | var it = separate("abc|def||ghi", "|"); |
| | 770 | assert(eql(u8, it.next().?, "abc")); |
| | 771 | assert(eql(u8, it.next().?, "def")); |
| | 772 | assert(eql(u8, it.next().?, "")); |
| | 773 | assert(eql(u8, it.next().?, "ghi")); |
| | 774 | assert(it.next() == null); |
| | 775 | |
| | 776 | it = separate("", "|"); |
| | 777 | assert(eql(u8, it.next().?, "")); |
| | 778 | assert(it.next() == null); |
| | 779 | |
| | 780 | it = separate("|", "|"); |
| | 781 | assert(eql(u8, it.next().?, "")); |
| | 782 | assert(eql(u8, it.next().?, "")); |
| | 783 | assert(it.next() == null); |
| | 784 | |
| | 785 | it = separate("hello", ""); |
| | 786 | assert(eql(u8, it.next().?, "hello")); |
| | 787 | assert(it.next() == null); |
| | 788 | |
| | 789 | it = separate("hello", " "); |
| | 790 | assert(eql(u8, it.next().?, "hello")); |
| | 791 | assert(it.next() == null); |
| | 792 | } |
| | 793 | |
| | 794 | test "mem.separate (multibyte)" { |
| | 795 | var it = separate("a|b,c/d e", " /,|"); |
| | 796 | assert(eql(u8, it.next().?, "a")); |
| | 797 | assert(eql(u8, it.next().?, "b")); |
| | 798 | assert(eql(u8, it.next().?, "c")); |
| | 799 | assert(eql(u8, it.next().?, "d")); |
| | 800 | assert(eql(u8, it.next().?, "e")); |
| | 801 | assert(it.next() == null); |
| 709 | } | 802 | } |
| 710 | | 803 | |
| 711 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { | 804 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| ... | @@ -730,20 +823,32 @@ pub const SplitIterator = struct { | ... | @@ -730,20 +823,32 @@ pub const SplitIterator = struct { |
| 730 | buffer: []const u8, | 823 | buffer: []const u8, |
| 731 | split_bytes: []const u8, | 824 | split_bytes: []const u8, |
| 732 | index: usize, | 825 | index: usize, |
| | 826 | glob: bool, |
| | 827 | spun: bool, |
| 733 | | 828 | |
| | 829 | /// Iterates and returns null or optionally a slice the next split segment |
| 734 | pub fn next(self: *SplitIterator) ?[]const u8 { | 830 | pub fn next(self: *SplitIterator) ?[]const u8 { |
| 735 | // move to beginning of token | 831 | if (self.spun) { |
| 736 | while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} | 832 | if (self.index + 1 > self.buffer.len) return null; |
| 737 | const start = self.index; | 833 | self.index += 1; |
| 738 | if (start == self.buffer.len) { | | |
| 739 | return null; | | |
| 740 | } | 834 | } |
| 741 | | 835 | |
| 742 | // move to end of token | 836 | self.spun = true; |
| 743 | while (self.index < self.buffer.len and !self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} | 837 | |
| 744 | const end = self.index; | 838 | if (self.glob) { |
| | 839 | while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} |
| | 840 | } |
| | 841 | |
| | 842 | var cursor = self.index; |
| | 843 | while (cursor < self.buffer.len and !self.isSplitByte(self.buffer[cursor])) : (cursor += 1) {} |
| | 844 | |
| | 845 | defer self.index = cursor; |
| | 846 | |
| | 847 | if (cursor == self.buffer.len) { |
| | 848 | return if (self.glob and self.index == cursor and self.index > 0) null else self.buffer[self.index..]; |
| | 849 | } |
| 745 | | 850 | |
| 746 | return self.buffer[start..end]; | 851 | return self.buffer[self.index..cursor]; |
| 747 | } | 852 | } |
| 748 | | 853 | |
| 749 | /// Returns a slice of the remaining bytes. Does not affect iterator state. | 854 | /// Returns a slice of the remaining bytes. Does not affect iterator state. |