| ... | ... | @@ -607,11 +607,15 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { |
| 607 | 607 | /// any of the bytes in `split_bytes`. |
| 608 | 608 | /// split(" abc def ghi ", " ") |
| 609 | 609 | /// Will return slices for "abc", "def", "ghi", null, in that order. |
| 610 | /// If `split_bytes` does not exist in buffer, |
| 611 | /// the iterator will return `buffer`, null, in that order. |
| 610 | 612 | pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { |
| 611 | 613 | return SplitIterator{ |
| 612 | 614 | .index = 0, |
| 613 | 615 | .buffer = buffer, |
| 614 | 616 | .split_bytes = split_bytes, |
| 617 | .glob = true, |
| 618 | .spun = false, |
| 615 | 619 | }; |
| 616 | 620 | } |
| 617 | 621 | |
| ... | ... | @@ -621,6 +625,76 @@ test "mem.split" { |
| 621 | 625 | assert(eql(u8, it.next().?, "def")); |
| 622 | 626 | assert(eql(u8, it.next().?, "ghi")); |
| 623 | 627 | assert(it.next() == null); |
| 628 | |
| 629 | it = split("..\\bob", "\\"); |
| 630 | assert(eql(u8, it.next().?, "..")); |
| 631 | assert(eql(u8, "..", "..\\bob"[0..it.index])); |
| 632 | assert(eql(u8, it.next().?, "bob")); |
| 633 | assert(it.next() == null); |
| 634 | |
| 635 | it = split("//a/b", "/"); |
| 636 | assert(eql(u8, it.next().?, "a")); |
| 637 | assert(eql(u8, it.next().?, "b")); |
| 638 | assert(eql(u8, "//a/b", "//a/b"[0..it.index])); |
| 639 | assert(it.next() == null); |
| 640 | |
| 641 | it = split("|", "|"); |
| 642 | assert(it.next() == null); |
| 643 | |
| 644 | it = split("", "|"); |
| 645 | assert(eql(u8, it.next().?, "")); |
| 646 | assert(it.next() == null); |
| 647 | |
| 648 | it = split("hello", ""); |
| 649 | assert(eql(u8, it.next().?, "hello")); |
| 650 | assert(it.next() == null); |
| 651 | |
| 652 | it = split("hello", " "); |
| 653 | assert(eql(u8, it.next().?, "hello")); |
| 654 | assert(it.next() == null); |
| 655 | } |
| 656 | |
| 657 | /// Returns an iterator that iterates over the slices of `buffer` that |
| 658 | /// seperates by bytes in `delimiter`. |
| 659 | /// separate("abc|def||ghi", "|") |
| 660 | /// Will return slices for "abc", "def", "", "ghi", null, in that order. |
| 661 | /// If `delimiter` does not exist in buffer, |
| 662 | /// the iterator will return `buffer`, null, in that order. |
| 663 | pub fn separate(buffer: []const u8, delimiter: []const u8) SplitIterator { |
| 664 | return SplitIterator{ |
| 665 | .index = 0, |
| 666 | .buffer = buffer, |
| 667 | .split_bytes = delimiter, |
| 668 | .glob = false, |
| 669 | .spun = false, |
| 670 | }; |
| 671 | } |
| 672 | |
| 673 | test "mem.separate" { |
| 674 | var it = separate("abc|def||ghi", "|"); |
| 675 | assert(eql(u8, it.next().?, "abc")); |
| 676 | assert(eql(u8, it.next().?, "def")); |
| 677 | assert(eql(u8, it.next().?, "")); |
| 678 | assert(eql(u8, it.next().?, "ghi")); |
| 679 | assert(it.next() == null); |
| 680 | |
| 681 | it = separate("", "|"); |
| 682 | assert(eql(u8, it.next().?, "")); |
| 683 | assert(it.next() == null); |
| 684 | |
| 685 | it = separate("|", "|"); |
| 686 | assert(eql(u8, it.next().?, "")); |
| 687 | assert(eql(u8, it.next().?, "")); |
| 688 | assert(it.next() == null); |
| 689 | |
| 690 | it = separate("hello", ""); |
| 691 | assert(eql(u8, it.next().?, "hello")); |
| 692 | assert(it.next() == null); |
| 693 | |
| 694 | it = separate("hello", " "); |
| 695 | assert(eql(u8, it.next().?, "hello")); |
| 696 | assert(it.next() == null); |
| 697 | |
| 624 | 698 | } |
| 625 | 699 | |
| 626 | 700 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| ... | ... | @@ -645,20 +719,32 @@ pub const SplitIterator = struct { |
| 645 | 719 | buffer: []const u8, |
| 646 | 720 | split_bytes: []const u8, |
| 647 | 721 | index: usize, |
| 722 | glob: bool, |
| 723 | spun: bool, |
| 648 | 724 | |
| 725 | /// Iterates and returns null or optionally a slice the next split segment |
| 649 | 726 | pub fn next(self: *SplitIterator) ?[]const u8 { |
| 650 | | // move to beginning of token |
| 651 | | while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} |
| 652 | | const start = self.index; |
| 653 | | if (start == self.buffer.len) { |
| 654 | | return null; |
| 727 | if (self.spun) { |
| 728 | if (self.index + 1 > self.buffer.len) return null; |
| 729 | self.index += 1; |
| 730 | } |
| 731 | |
| 732 | self.spun = true; |
| 733 | |
| 734 | if (self.glob) { |
| 735 | while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} |
| 655 | 736 | } |
| 656 | 737 | |
| 657 | | // move to end of token |
| 658 | | while (self.index < self.buffer.len and !self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} |
| 659 | | const end = self.index; |
| 738 | var cursor = self.index; |
| 739 | while (cursor < self.buffer.len and !self.isSplitByte(self.buffer[cursor])) : (cursor += 1) {} |
| 740 | |
| 741 | defer self.index = cursor; |
| 742 | |
| 743 | if (cursor == self.buffer.len) { |
| 744 | return if (self.glob and self.index == cursor and self.index > 0) null else self.buffer[self.index..]; |
| 745 | } |
| 660 | 746 | |
| 661 | | return self.buffer[start..end]; |
| 747 | return self.buffer[self.index..cursor]; |
| 662 | 748 | } |
| 663 | 749 | |
| 664 | 750 | /// Returns a slice of the remaining bytes. Does not affect iterator state. |