| ... | @@ -39,8 +39,8 @@ pub fn isSep(byte: u8) bool { | ... | @@ -39,8 +39,8 @@ pub fn isSep(byte: u8) bool { |
| 39 | | 39 | |
| 40 | /// This is different from mem.join in that the separator will not be repeated if | 40 | /// This is different from mem.join in that the separator will not be repeated if |
| 41 | /// it is found at the end or beginning of a pair of consecutive paths. | 41 | /// it is found at the end or beginning of a pair of consecutive paths. |
| 42 | fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, paths: []const []const u8) ![]u8 { | 42 | fn joinSepMaybeZ(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 { |
| 43 | if (paths.len == 0) return &[0]u8{}; | 43 | if (paths.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{}; |
| 44 | | 44 | |
| 45 | const total_len = blk: { | 45 | const total_len = blk: { |
| 46 | var sum: usize = paths[0].len; | 46 | var sum: usize = paths[0].len; |
| ... | @@ -53,6 +53,7 @@ fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, pat | ... | @@ -53,6 +53,7 @@ fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, pat |
| 53 | sum += @boolToInt(!prev_sep and !this_sep); | 53 | sum += @boolToInt(!prev_sep and !this_sep); |
| 54 | sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len; | 54 | sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len; |
| 55 | } | 55 | } |
| | 56 | if (zero) sum += 1; |
| 56 | break :blk sum; | 57 | break :blk sum; |
| 57 | }; | 58 | }; |
| 58 | | 59 | |
| ... | @@ -76,6 +77,8 @@ fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, pat | ... | @@ -76,6 +77,8 @@ fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, pat |
| 76 | buf_index += adjusted_path.len; | 77 | buf_index += adjusted_path.len; |
| 77 | } | 78 | } |
| 78 | | 79 | |
| | 80 | if (zero) buf[buf.len - 1] = 0; |
| | 81 | |
| 79 | // No need for shrink since buf is exactly the correct size. | 82 | // No need for shrink since buf is exactly the correct size. |
| 80 | return buf; | 83 | return buf; |
| 81 | } | 84 | } |
| ... | @@ -83,60 +86,73 @@ fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, pat | ... | @@ -83,60 +86,73 @@ fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, pat |
| 83 | /// Naively combines a series of paths with the native path seperator. | 86 | /// Naively combines a series of paths with the native path seperator. |
| 84 | /// Allocates memory for the result, which must be freed by the caller. | 87 | /// Allocates memory for the result, which must be freed by the caller. |
| 85 | pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 { | 88 | pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 86 | return joinSep(allocator, sep, isSep, paths); | 89 | return joinSepMaybeZ(allocator, sep, isSep, paths, false); |
| | 90 | } |
| | 91 | |
| | 92 | /// Naively combines a series of paths with the native path seperator and null terminator. |
| | 93 | /// Allocates memory for the result, which must be freed by the caller. |
| | 94 | pub fn joinZ(allocator: *Allocator, paths: []const []const u8) ![:0]u8 { |
| | 95 | const out = joinSepMaybeZ(allocator, sep, isSep, paths, true); |
| | 96 | return out[0 .. out.len - 1 :0]; |
| 87 | } | 97 | } |
| 88 | | 98 | |
| 89 | fn testJoinWindows(paths: []const []const u8, expected: []const u8) void { | 99 | fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: bool) void { |
| 90 | const windowsIsSep = struct { | 100 | const windowsIsSep = struct { |
| 91 | fn isSep(byte: u8) bool { | 101 | fn isSep(byte: u8) bool { |
| 92 | return byte == '/' or byte == '\\'; | 102 | return byte == '/' or byte == '\\'; |
| 93 | } | 103 | } |
| 94 | }.isSep; | 104 | }.isSep; |
| 95 | const actual = joinSep(testing.allocator, sep_windows, windowsIsSep, paths) catch @panic("fail"); | 105 | const actual = joinSepMaybeZ(testing.allocator, sep_windows, windowsIsSep, paths, zero) catch @panic("fail"); |
| 96 | defer testing.allocator.free(actual); | 106 | defer testing.allocator.free(actual); |
| 97 | testing.expectEqualSlices(u8, expected, actual); | 107 | testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual); |
| 98 | } | 108 | } |
| 99 | | 109 | |
| 100 | fn testJoinPosix(paths: []const []const u8, expected: []const u8) void { | 110 | fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bool) void { |
| 101 | const posixIsSep = struct { | 111 | const posixIsSep = struct { |
| 102 | fn isSep(byte: u8) bool { | 112 | fn isSep(byte: u8) bool { |
| 103 | return byte == '/'; | 113 | return byte == '/'; |
| 104 | } | 114 | } |
| 105 | }.isSep; | 115 | }.isSep; |
| 106 | const actual = joinSep(testing.allocator, sep_posix, posixIsSep, paths) catch @panic("fail"); | 116 | const actual = joinSepMaybeZ(testing.allocator, sep_posix, posixIsSep, paths, zero) catch @panic("fail"); |
| 107 | defer testing.allocator.free(actual); | 117 | defer testing.allocator.free(actual); |
| 108 | testing.expectEqualSlices(u8, expected, actual); | 118 | testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual); |
| 109 | } | 119 | } |
| 110 | | 120 | |
| 111 | test "join" { | 121 | test "join" { |
| 112 | testJoinWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); | 122 | for (&[_]bool{ false, true }) |zero| { |
| 113 | testJoinWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); | 123 | testJoinMaybeZWindows(&[_][]const u8{}, "", zero); |
| 114 | testJoinWindows(&[_][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c"); | 124 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero); |
| 115 | | 125 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero); |
| 116 | testJoinWindows(&[_][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c"); | 126 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c", zero); |
| 117 | testJoinWindows(&[_][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c"); | 127 | |
| 118 | | 128 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c", zero); |
| 119 | testJoinWindows( | 129 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c", zero); |
| 120 | &[_][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" }, | 130 | |
| 121 | "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig", | 131 | testJoinMaybeZWindows( |
| 122 | ); | 132 | &[_][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" }, |
| 123 | | 133 | "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig", |
| 124 | testJoinWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c"); | 134 | zero, |
| 125 | testJoinWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c"); | 135 | ); |
| 126 | | 136 | |
| 127 | testJoinPosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); | 137 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c", zero); |
| 128 | testJoinPosix(&[_][]const u8{ "/a/b/", "c" }, "/a/b/c"); | 138 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c", zero); |
| 129 | | 139 | |
| 130 | testJoinPosix(&[_][]const u8{ "/", "a", "b/", "c" }, "/a/b/c"); | 140 | testJoinMaybeZPosix(&[_][]const u8{}, "", zero); |
| 131 | testJoinPosix(&[_][]const u8{ "/a/", "b/", "c" }, "/a/b/c"); | 141 | testJoinMaybeZPosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c", zero); |
| 132 | | 142 | testJoinMaybeZPosix(&[_][]const u8{ "/a/b/", "c" }, "/a/b/c", zero); |
| 133 | testJoinPosix( | 143 | |
| 134 | &[_][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" }, | 144 | testJoinMaybeZPosix(&[_][]const u8{ "/", "a", "b/", "c" }, "/a/b/c", zero); |
| 135 | "/home/andy/dev/zig/build/lib/zig/std/io.zig", | 145 | testJoinMaybeZPosix(&[_][]const u8{ "/a/", "b/", "c" }, "/a/b/c", zero); |
| 136 | ); | 146 | |
| 137 | | 147 | testJoinMaybeZPosix( |
| 138 | testJoinPosix(&[_][]const u8{ "a", "/c" }, "a/c"); | 148 | &[_][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" }, |
| 139 | testJoinPosix(&[_][]const u8{ "a/", "/c" }, "a/c"); | 149 | "/home/andy/dev/zig/build/lib/zig/std/io.zig", |
| | 150 | zero, |
| | 151 | ); |
| | 152 | |
| | 153 | testJoinMaybeZPosix(&[_][]const u8{ "a", "/c" }, "a/c", zero); |
| | 154 | testJoinMaybeZPosix(&[_][]const u8{ "a/", "/c" }, "a/c", zero); |
| | 155 | } |
| 140 | } | 156 | } |
| 141 | | 157 | |
| 142 | pub const isAbsoluteC = @compileError("deprecated: renamed to isAbsoluteZ"); | 158 | pub const isAbsoluteC = @compileError("deprecated: renamed to isAbsoluteZ"); |