| ... | @@ -1674,12 +1674,23 @@ pub const SplitIterator = struct { | ... | @@ -1674,12 +1674,23 @@ pub const SplitIterator = struct { |
| 1674 | /// Naively combines a series of slices with a separator. | 1674 | /// Naively combines a series of slices with a separator. |
| 1675 | /// Allocates memory for the result, which must be freed by the caller. | 1675 | /// Allocates memory for the result, which must be freed by the caller. |
| 1676 | pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { | 1676 | pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { |
| | 1677 | return joinMaybeZ(allocator, separator, slices, false); |
| | 1678 | } |
| | 1679 | |
| | 1680 | /// Naively combines a series of slices with a separator and null terminator. |
| | 1681 | /// Allocates memory for the result, which must be freed by the caller. |
| | 1682 | pub fn joinZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![:0]u8 { |
| | 1683 | const out = try joinMaybeZ(allocator, separator, slices, true); |
| | 1684 | return out[0 .. out.len - 1 :0]; |
| | 1685 | } |
| | 1686 | |
| | 1687 | fn joinMaybeZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8, zero: bool) ![]u8 { |
| 1677 | if (slices.len == 0) return &[0]u8{}; | 1688 | if (slices.len == 0) return &[0]u8{}; |
| 1678 | | 1689 | |
| 1679 | const total_len = blk: { | 1690 | const total_len = blk: { |
| 1680 | var sum: usize = separator.len * (slices.len - 1); | 1691 | var sum: usize = separator.len * (slices.len - 1); |
| 1681 | for (slices) |slice| | 1692 | for (slices) |slice| sum += slice.len; |
| 1682 | sum += slice.len; | 1693 | if (zero) sum += 1; |
| 1683 | break :blk sum; | 1694 | break :blk sum; |
| 1684 | }; | 1695 | }; |
| 1685 | | 1696 | |
| ... | @@ -1695,6 +1706,8 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons | ... | @@ -1695,6 +1706,8 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons |
| 1695 | buf_index += slice.len; | 1706 | buf_index += slice.len; |
| 1696 | } | 1707 | } |
| 1697 | | 1708 | |
| | 1709 | if (zero) buf[buf.len - 1] = 0; |
| | 1710 | |
| 1698 | // No need for shrink since buf is exactly the correct size. | 1711 | // No need for shrink since buf is exactly the correct size. |
| 1699 | return buf; | 1712 | return buf; |
| 1700 | } | 1713 | } |
| ... | @@ -1717,6 +1730,27 @@ test "mem.join" { | ... | @@ -1717,6 +1730,27 @@ test "mem.join" { |
| 1717 | } | 1730 | } |
| 1718 | } | 1731 | } |
| 1719 | | 1732 | |
| | 1733 | test "mem.joinZ" { |
| | 1734 | { |
| | 1735 | const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" }); |
| | 1736 | defer testing.allocator.free(str); |
| | 1737 | testing.expect(eql(u8, str, "a,b,c")); |
| | 1738 | testing.expectEqual(str[str.len], 0); |
| | 1739 | } |
| | 1740 | { |
| | 1741 | const str = try joinZ(testing.allocator, ",", &[_][]const u8{"a"}); |
| | 1742 | defer testing.allocator.free(str); |
| | 1743 | testing.expect(eql(u8, str, "a")); |
| | 1744 | testing.expectEqual(str[str.len], 0); |
| | 1745 | } |
| | 1746 | { |
| | 1747 | const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" }); |
| | 1748 | defer testing.allocator.free(str); |
| | 1749 | testing.expect(eql(u8, str, "a,,b,,c")); |
| | 1750 | testing.expectEqual(str[str.len], 0); |
| | 1751 | } |
| | 1752 | } |
| | 1753 | |
| 1720 | /// Copies each T from slices into a new slice that exactly holds all the elements. | 1754 | /// Copies each T from slices into a new slice that exactly holds all the elements. |
| 1721 | pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T { | 1755 | pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T { |
| 1722 | if (slices.len == 0) return &[0]T{}; | 1756 | if (slices.len == 0) return &[0]T{}; |