| ... | @@ -1707,23 +1707,32 @@ pub fn split(comptime T: type, buffer: []const T, delimiter: []const T) SplitIte | ... | @@ -1707,23 +1707,32 @@ pub fn split(comptime T: type, buffer: []const T, delimiter: []const T) SplitIte |
| 1707 | | 1707 | |
| 1708 | test "split" { | 1708 | test "split" { |
| 1709 | var it = split(u8, "abc|def||ghi", "|"); | 1709 | var it = split(u8, "abc|def||ghi", "|"); |
| 1710 | try testing.expect(eql(u8, it.next().?, "abc")); | 1710 | try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); |
| 1711 | try testing.expect(eql(u8, it.next().?, "def")); | 1711 | try testing.expectEqualSlices(u8, it.next().?, "abc"); |
| 1712 | try testing.expect(eql(u8, it.next().?, "")); | 1712 | |
| 1713 | try testing.expect(eql(u8, it.next().?, "ghi")); | 1713 | try testing.expectEqualSlices(u8, it.rest(), "def||ghi"); |
| | 1714 | try testing.expectEqualSlices(u8, it.next().?, "def"); |
| | 1715 | |
| | 1716 | try testing.expectEqualSlices(u8, it.rest(), "|ghi"); |
| | 1717 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 1718 | |
| | 1719 | try testing.expectEqualSlices(u8, it.rest(), "ghi"); |
| | 1720 | try testing.expectEqualSlices(u8, it.next().?, "ghi"); |
| | 1721 | |
| | 1722 | try testing.expectEqualSlices(u8, it.rest(), ""); |
| 1714 | try testing.expect(it.next() == null); | 1723 | try testing.expect(it.next() == null); |
| 1715 | | 1724 | |
| 1716 | it = split(u8, "", "|"); | 1725 | it = split(u8, "", "|"); |
| 1717 | try testing.expect(eql(u8, it.next().?, "")); | 1726 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| 1718 | try testing.expect(it.next() == null); | 1727 | try testing.expect(it.next() == null); |
| 1719 | | 1728 | |
| 1720 | it = split(u8, "|", "|"); | 1729 | it = split(u8, "|", "|"); |
| 1721 | try testing.expect(eql(u8, it.next().?, "")); | 1730 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| 1722 | try testing.expect(eql(u8, it.next().?, "")); | 1731 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| 1723 | try testing.expect(it.next() == null); | 1732 | try testing.expect(it.next() == null); |
| 1724 | | 1733 | |
| 1725 | it = split(u8, "hello", " "); | 1734 | it = split(u8, "hello", " "); |
| 1726 | try testing.expect(eql(u8, it.next().?, "hello")); | 1735 | try testing.expectEqualSlices(u8, it.next().?, "hello"); |
| 1727 | try testing.expect(it.next() == null); | 1736 | try testing.expect(it.next() == null); |
| 1728 | | 1737 | |
| 1729 | var it16 = split( | 1738 | var it16 = split( |
| ... | @@ -1731,17 +1740,18 @@ test "split" { | ... | @@ -1731,17 +1740,18 @@ test "split" { |
| 1731 | std.unicode.utf8ToUtf16LeStringLiteral("hello"), | 1740 | std.unicode.utf8ToUtf16LeStringLiteral("hello"), |
| 1732 | std.unicode.utf8ToUtf16LeStringLiteral(" "), | 1741 | std.unicode.utf8ToUtf16LeStringLiteral(" "), |
| 1733 | ); | 1742 | ); |
| 1734 | try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello"))); | 1743 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello")); |
| 1735 | try testing.expect(it16.next() == null); | 1744 | try testing.expect(it16.next() == null); |
| 1736 | } | 1745 | } |
| 1737 | | 1746 | |
| 1738 | test "split (multibyte)" { | 1747 | test "split (multibyte)" { |
| 1739 | var it = split(u8, "a, b ,, c, d, e", ", "); | 1748 | var it = split(u8, "a, b ,, c, d, e", ", "); |
| 1740 | try testing.expect(eql(u8, it.next().?, "a")); | 1749 | try testing.expectEqualSlices(u8, it.next().?, "a"); |
| 1741 | try testing.expect(eql(u8, it.next().?, "b ,")); | 1750 | try testing.expectEqualSlices(u8, it.rest(), "b ,, c, d, e"); |
| 1742 | try testing.expect(eql(u8, it.next().?, "c")); | 1751 | try testing.expectEqualSlices(u8, it.next().?, "b ,"); |
| 1743 | try testing.expect(eql(u8, it.next().?, "d")); | 1752 | try testing.expectEqualSlices(u8, it.next().?, "c"); |
| 1744 | try testing.expect(eql(u8, it.next().?, "e")); | 1753 | try testing.expectEqualSlices(u8, it.next().?, "d"); |
| | 1754 | try testing.expectEqualSlices(u8, it.next().?, "e"); |
| 1745 | try testing.expect(it.next() == null); | 1755 | try testing.expect(it.next() == null); |
| 1746 | | 1756 | |
| 1747 | var it16 = split( | 1757 | var it16 = split( |
| ... | @@ -1749,11 +1759,99 @@ test "split (multibyte)" { | ... | @@ -1749,11 +1759,99 @@ test "split (multibyte)" { |
| 1749 | std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), | 1759 | std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), |
| 1750 | std.unicode.utf8ToUtf16LeStringLiteral(", "), | 1760 | std.unicode.utf8ToUtf16LeStringLiteral(", "), |
| 1751 | ); | 1761 | ); |
| 1752 | try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a"))); | 1762 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")); |
| 1753 | try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,"))); | 1763 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); |
| 1754 | try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c"))); | 1764 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); |
| 1755 | try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d"))); | 1765 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); |
| 1756 | try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e"))); | 1766 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")); |
| | 1767 | try testing.expect(it16.next() == null); |
| | 1768 | } |
| | 1769 | |
| | 1770 | /// Returns an iterator that iterates backwards over the slices of `buffer` |
| | 1771 | /// that are separated by bytes in `delimiter`. |
| | 1772 | /// splitBackwards(u8, "abc|def||ghi", "|") |
| | 1773 | /// will return slices for "ghi", "", "def", "abc", null, in that order. |
| | 1774 | /// If `delimiter` does not exist in buffer, |
| | 1775 | /// the iterator will return `buffer`, null, in that order. |
| | 1776 | /// The delimiter length must not be zero. |
| | 1777 | pub fn splitBackwards(comptime T: type, buffer: []const T, delimiter: []const T) SplitBackwardsIterator(T) { |
| | 1778 | assert(delimiter.len != 0); |
| | 1779 | return SplitBackwardsIterator(T){ |
| | 1780 | .index = buffer.len, |
| | 1781 | .buffer = buffer, |
| | 1782 | .delimiter = delimiter, |
| | 1783 | }; |
| | 1784 | } |
| | 1785 | |
| | 1786 | test "splitBackwards" { |
| | 1787 | var it = splitBackwards(u8, "abc|def||ghi", "|"); |
| | 1788 | try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); |
| | 1789 | try testing.expectEqualSlices(u8, it.next().?, "ghi"); |
| | 1790 | |
| | 1791 | try testing.expectEqualSlices(u8, it.rest(), "abc|def|"); |
| | 1792 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 1793 | |
| | 1794 | try testing.expectEqualSlices(u8, it.rest(), "abc|def"); |
| | 1795 | try testing.expectEqualSlices(u8, it.next().?, "def"); |
| | 1796 | |
| | 1797 | try testing.expectEqualSlices(u8, it.rest(), "abc"); |
| | 1798 | try testing.expectEqualSlices(u8, it.next().?, "abc"); |
| | 1799 | |
| | 1800 | try testing.expectEqualSlices(u8, it.rest(), ""); |
| | 1801 | try testing.expect(it.next() == null); |
| | 1802 | |
| | 1803 | it = splitBackwards(u8, "", "|"); |
| | 1804 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 1805 | try testing.expect(it.next() == null); |
| | 1806 | |
| | 1807 | it = splitBackwards(u8, "|", "|"); |
| | 1808 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 1809 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 1810 | try testing.expect(it.next() == null); |
| | 1811 | |
| | 1812 | it = splitBackwards(u8, "hello", " "); |
| | 1813 | try testing.expectEqualSlices(u8, it.next().?, "hello"); |
| | 1814 | try testing.expect(it.next() == null); |
| | 1815 | |
| | 1816 | var it16 = splitBackwards( |
| | 1817 | u16, |
| | 1818 | std.unicode.utf8ToUtf16LeStringLiteral("hello"), |
| | 1819 | std.unicode.utf8ToUtf16LeStringLiteral(" "), |
| | 1820 | ); |
| | 1821 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello")); |
| | 1822 | try testing.expect(it16.next() == null); |
| | 1823 | } |
| | 1824 | |
| | 1825 | test "splitBackwards (multibyte)" { |
| | 1826 | var it = splitBackwards(u8, "a, b ,, c, d, e", ", "); |
| | 1827 | try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d, e"); |
| | 1828 | try testing.expectEqualSlices(u8, it.next().?, "e"); |
| | 1829 | |
| | 1830 | try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d"); |
| | 1831 | try testing.expectEqualSlices(u8, it.next().?, "d"); |
| | 1832 | |
| | 1833 | try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c"); |
| | 1834 | try testing.expectEqualSlices(u8, it.next().?, "c"); |
| | 1835 | |
| | 1836 | try testing.expectEqualSlices(u8, it.rest(), "a, b ,"); |
| | 1837 | try testing.expectEqualSlices(u8, it.next().?, "b ,"); |
| | 1838 | |
| | 1839 | try testing.expectEqualSlices(u8, it.rest(), "a"); |
| | 1840 | try testing.expectEqualSlices(u8, it.next().?, "a"); |
| | 1841 | |
| | 1842 | try testing.expectEqualSlices(u8, it.rest(), ""); |
| | 1843 | try testing.expect(it.next() == null); |
| | 1844 | |
| | 1845 | var it16 = splitBackwards( |
| | 1846 | u16, |
| | 1847 | std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), |
| | 1848 | std.unicode.utf8ToUtf16LeStringLiteral(", "), |
| | 1849 | ); |
| | 1850 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")); |
| | 1851 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); |
| | 1852 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); |
| | 1853 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); |
| | 1854 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")); |
| 1757 | try testing.expect(it16.next() == null); | 1855 | try testing.expect(it16.next() == null); |
| 1758 | } | 1856 | } |
| 1759 | | 1857 | |
| ... | @@ -1862,6 +1960,35 @@ pub fn SplitIterator(comptime T: type) type { | ... | @@ -1862,6 +1960,35 @@ pub fn SplitIterator(comptime T: type) type { |
| 1862 | }; | 1960 | }; |
| 1863 | } | 1961 | } |
| 1864 | | 1962 | |
| | 1963 | pub fn SplitBackwardsIterator(comptime T: type) type { |
| | 1964 | return struct { |
| | 1965 | buffer: []const T, |
| | 1966 | index: ?usize, |
| | 1967 | delimiter: []const T, |
| | 1968 | |
| | 1969 | const Self = @This(); |
| | 1970 | |
| | 1971 | /// Returns a slice of the next field, or null if splitting is complete. |
| | 1972 | pub fn next(self: *Self) ?[]const T { |
| | 1973 | const end = self.index orelse return null; |
| | 1974 | const start = if (lastIndexOf(T, self.buffer[0..end], self.delimiter)) |delim_start| blk: { |
| | 1975 | self.index = delim_start; |
| | 1976 | break :blk delim_start + self.delimiter.len; |
| | 1977 | } else blk: { |
| | 1978 | self.index = null; |
| | 1979 | break :blk 0; |
| | 1980 | }; |
| | 1981 | return self.buffer[start..end]; |
| | 1982 | } |
| | 1983 | |
| | 1984 | /// Returns a slice of the remaining bytes. Does not affect iterator state. |
| | 1985 | pub fn rest(self: Self) []const T { |
| | 1986 | const end = self.index orelse 0; |
| | 1987 | return self.buffer[0..end]; |
| | 1988 | } |
| | 1989 | }; |
| | 1990 | } |
| | 1991 | |
| 1865 | /// Naively combines a series of slices with a separator. | 1992 | /// Naively combines a series of slices with a separator. |
| 1866 | /// Allocates memory for the result, which must be freed by the caller. | 1993 | /// Allocates memory for the result, which must be freed by the caller. |
| 1867 | pub fn join(allocator: Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { | 1994 | pub fn join(allocator: Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { |