Commit ef17af1270

Isaac Freund <ifreund@ifreund.xyz>
2020-07-08 23:07:44
std: add mem.joinZ
currently the only options are doing a second allocation and copying or implementing this yourself.
1 parent 5b570bc
Changed files (1)
lib
lib/std/mem.zig
@@ -1674,12 +1674,23 @@ pub const SplitIterator = struct {
 /// Naively combines a series of slices with a separator.
 /// Allocates memory for the result, which must be freed by the caller.
 pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
+    return joinMaybeZ(allocator, separator, slices, false);
+}
+
+/// Naively combines a series of slices with a separator and null terminator.
+/// Allocates memory for the result, which must be freed by the caller.
+pub fn joinZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![:0]u8 {
+    const out = try joinMaybeZ(allocator, separator, slices, true);
+    return out[0 .. out.len - 1 :0];
+}
+
+fn joinMaybeZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8, zero: bool) ![]u8 {
     if (slices.len == 0) return &[0]u8{};
 
     const total_len = blk: {
         var sum: usize = separator.len * (slices.len - 1);
-        for (slices) |slice|
-            sum += slice.len;
+        for (slices) |slice| sum += slice.len;
+        if (zero) sum += 1;
         break :blk sum;
     };
 
@@ -1695,6 +1706,8 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons
         buf_index += slice.len;
     }
 
+    if (zero) buf[buf.len - 1] = 0;
+
     // No need for shrink since buf is exactly the correct size.
     return buf;
 }
@@ -1717,6 +1730,27 @@ test "mem.join" {
     }
 }
 
+test "mem.joinZ" {
+    {
+        const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" });
+        defer testing.allocator.free(str);
+        testing.expect(eql(u8, str, "a,b,c"));
+        testing.expectEqual(str[str.len], 0);
+    }
+    {
+        const str = try joinZ(testing.allocator, ",", &[_][]const u8{"a"});
+        defer testing.allocator.free(str);
+        testing.expect(eql(u8, str, "a"));
+        testing.expectEqual(str[str.len], 0);
+    }
+    {
+        const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" });
+        defer testing.allocator.free(str);
+        testing.expect(eql(u8, str, "a,,b,,c"));
+        testing.expectEqual(str[str.len], 0);
+    }
+}
+
 /// Copies each T from slices into a new slice that exactly holds all the elements.
 pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {
     if (slices.len == 0) return &[0]T{};