| ... | ... | @@ -692,11 +692,15 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { |
| 692 | 692 | /// any of the bytes in `split_bytes`. |
| 693 | 693 | /// split(" abc def ghi ", " ") |
| 694 | 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 | 697 | pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { |
| 696 | 698 | return SplitIterator{ |
| 697 | 699 | .index = 0, |
| 698 | 700 | .buffer = buffer, |
| 699 | 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 | 710 | assert(eql(u8, it.next().?, "def")); |
| 707 | 711 | assert(eql(u8, it.next().?, "ghi")); |
| 708 | 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 | 804 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| ... | ... | @@ -730,20 +823,32 @@ pub const SplitIterator = struct { |
| 730 | 823 | buffer: []const u8, |
| 731 | 824 | split_bytes: []const u8, |
| 732 | 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 | 830 | pub fn next(self: *SplitIterator) ?[]const u8 { |
| 735 | | // move to beginning of token |
| 736 | | while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} |
| 737 | | const start = self.index; |
| 738 | | if (start == self.buffer.len) { |
| 739 | | return null; |
| 831 | if (self.spun) { |
| 832 | if (self.index + 1 > self.buffer.len) return null; |
| 833 | self.index += 1; |
| 740 | 834 | } |
| 741 | 835 | |
| 742 | | // move to end of token |
| 743 | | while (self.index < self.buffer.len and !self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} |
| 744 | | const end = self.index; |
| 836 | self.spun = true; |
| 837 | |
| 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 | 854 | /// Returns a slice of the remaining bytes. Does not affect iterator state. |