diff --git a/core/os/path.odin b/core/os/path.odin index d3525b504..7ee95c1c2 100644 --- a/core/os/path.odin +++ b/core/os/path.odin @@ -29,22 +29,39 @@ with the `new_sep` rune. *Allocates Using Provided Allocator* */ replace_path_separators :: proc(path: string, new_sep: rune, allocator: runtime.Allocator) -> (new_path: string, err: Error) { - buf := make([]u8, len(path), allocator) or_return + length := len(path) + rep_b, rep_w := utf8.encode_rune(new_sep) + + byte_oriented := rep_w == 1 - i: int for r in path { - replacement := r if r == '/' || r == '\\' { - replacement = new_sep + length += rep_w - 1 } + if r > rune(0x7f) { + byte_oriented = false + } + } - if replacement <= rune(0x7F) { - buf[i] = u8(replacement) - i += 1 - } else { - b, w := utf8.encode_rune(r) - copy(buf[i:], b[:w]) - i += w + buf := make([]u8, length, allocator) or_return + + if byte_oriented { + // Neither replacement rune or any other rune in the path takes up more than 1 byte + str := transmute([]byte)path + #no_bounds_check for b, i in str { + buf[i] = u8(new_sep) if b == '/' || b == '\\' else b + } + } else { + i: int + for r in path { + if r == '/' || r == '\\' { + copy(buf[i:], rep_b[:rep_w]) + i += rep_w + } else { + r_b, r_w := utf8.encode_rune(r) + copy(buf[i:], r_b[:r_w]) + i += r_w + } } } return string(buf), nil diff --git a/tests/core/os/path.odin b/tests/core/os/path.odin index cdfaed56f..19fe15049 100644 --- a/tests/core/os/path.odin +++ b/tests/core/os/path.odin @@ -99,6 +99,17 @@ test_clean_path :: proc(t: ^testing.T) { } } +@(test) +replace_path_separators :: proc(t: ^testing.T) { + P :: "W:/Odin/core/os/path.odin" + + p1, _ := os.replace_path_separators(P, '๐Ÿ˜€', context.temp_allocator) + testing.expect_value(t, p1, "W:๐Ÿ˜€Odin๐Ÿ˜€core๐Ÿ˜€os๐Ÿ˜€path.odin") + + p2, _ := os.replace_path_separators(P, '|', context.temp_allocator) + testing.expect_value(t, p2, "W:|Odin|core|os|path.odin") +} + @(test) test_is_absolute_path :: proc(t: ^testing.T) { when ODIN_OS == .Windows {