From 4c306e746ea5a4ae8b13c1b8fe30991645dd0ecd Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 5 Jul 2023 14:24:43 -0400 Subject: [PATCH 1/2] lower-level replace(...) hooks in latest Julia --- src/util.jl | 93 ++++++++++++++++++++++++++++-------------------- test/runtests.jl | 8 +++++ 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/util.jl b/src/util.jl index 0fccfe7..e6c7db8 100644 --- a/src/util.jl +++ b/src/util.jl @@ -36,49 +36,64 @@ function Base.chomp(s::StringViewAndSub) end end -Base.replace(str::DenseStringViewAndSub, pat_repl::Pair{<:AbstractChar}; count::Integer=typemax(Int)) = - replace(str, isequal(first(pat_repl)) => last(pat_repl); count=count) -Base.replace(str::DenseStringViewAndSub, pat_repl::Pair{<:Union{Tuple{Vararg{AbstractChar}}, - AbstractVector{<:AbstractChar},Set{<:AbstractChar}}}; - count::Integer=typemax(Int)) = - replace(str, in(first(pat_repl)) => last(pat_repl), count=count) +# support replace via JuliaLang/julia#48625 +if isdefined(Base, :_replace_) + Base.replace(io::IO, s::DenseStringViewAndSub, pat_f::Pair...; count=typemax(Int)) = + Base._replace_(io, s, pat_f, Int(count)) -import Base: _pat_replacer, _free_pat_replacer - -function Base.replace(str::DenseStringViewAndSub, pat_repl::Pair; count::Integer=typemax(Int)) - pattern, repl = pat_repl - count == 0 && return str - count < 0 && throw(DomainError(count, "`count` must be non-negative.")) - n = 1 - e = lastindex(str) - i = a = firstindex(str) - pattern = _pat_replacer(pattern) - r = something(findnext(pattern,str,i), 0) - j, k = first(r), last(r) - if j == 0 - _free_pat_replacer(pattern) - return str + function Base.replace(s::DenseStringViewAndSub, pat_f::Pair...; count=typemax(Int)) + # don't simply call Base._replace_(s, pat_f, Int(count)), + # to avoid type-instability for empty-replacements case: always return String + # (remove when #50424 is merged) + buf = IOBuffer(sizehint=floor(Int, 1.2sizeof(s))) + return String(take!(replace(buf, s, pat_f...; count=count))) end - out = IOBuffer(sizehint=floor(Int, 1.2sizeof(str))) - while j != 0 - if i == a || i <= k - GC.@preserve str unsafe_write(out, pointer(str, i), UInt(j-i)) - Base._replace(out, repl, str, r, pattern) +else + Base.replace(str::DenseStringViewAndSub, pat_repl::Pair{<:AbstractChar}; count::Integer=typemax(Int)) = + replace(str, isequal(first(pat_repl)) => last(pat_repl); count=count) + + Base.replace(str::DenseStringViewAndSub, pat_repl::Pair{<:Union{Tuple{Vararg{AbstractChar}}, + AbstractVector{<:AbstractChar},Set{<:AbstractChar}}}; + count::Integer=typemax(Int)) = + replace(str, in(first(pat_repl)) => last(pat_repl), count=count) + + import Base: _pat_replacer, _free_pat_replacer + + function Base.replace(str::DenseStringViewAndSub, pat_repl::Pair; count::Integer=typemax(Int)) + pattern, repl = pat_repl + count == 0 && return str + count < 0 && throw(DomainError(count, "`count` must be non-negative.")) + n = 1 + e = lastindex(str) + i = a = firstindex(str) + pattern = _pat_replacer(pattern) + r = something(findnext(pattern,str,i), 0) + j, k = first(r), last(r) + if j == 0 + _free_pat_replacer(pattern) + return str end - if k < j - i = j - j > e && break - k = nextind(str, j) - else - i = k = nextind(str, k) + out = IOBuffer(sizehint=floor(Int, 1.2sizeof(str))) + while j != 0 + if i == a || i <= k + GC.@preserve str unsafe_write(out, pointer(str, i), UInt(j-i)) + Base._replace(out, repl, str, r, pattern) + end + if k < j + i = j + j > e && break + k = nextind(str, j) + else + i = k = nextind(str, k) + end + r = something(findnext(pattern,str,k), 0) + r === 0:-1 || n == count && break + j, k = first(r), last(r) + n += 1 end - r = something(findnext(pattern,str,k), 0) - r === 0:-1 || n == count && break - j, k = first(r), last(r) - n += 1 + _free_pat_replacer(pattern) + write(out, SubString(str,i)) + String(take!(out)) end - _free_pat_replacer(pattern) - write(out, SubString(str,i)) - String(take!(out)) end diff --git a/test/runtests.jl b/test/runtests.jl index c84607f..c78ed05 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -140,6 +140,14 @@ end end end +@testset "replace" begin + @test replace(StringView(b"abcd"), r"[bc]?" => "^") == "^a^^d^" + @test replace(StringView(b"a"), 'a' => typeof) == "Char" + @test replace(StringView(b"The foxes."), r"fox(es)?" => s"bus\1") == "The buses." + @test replace(StringView(b"foobarbaz"), "oo" => "zz", "ar" => "zz", "z" => "m") == "fzzbzzbam" + @test replace(StringView(b"foobar"), 'o' => '0', "" => "") == "f00bar" +end + @testset "miscellaneous" begin @test cmp("foobar","bar") == cmp(s,"bar") == -cmp("bar",s) == cmp(s,StringView("bar")) @test s == StringView("foobar") == "foobar" == s == "foobar" != StringView("bar") From f0821dedeb9fee75629362d96b304439daf68b38 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 5 Jul 2023 14:31:50 -0400 Subject: [PATCH 2/2] multiple replacements require julia 1.7 --- test/runtests.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index c78ed05..c0eaeca 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -144,8 +144,10 @@ end @test replace(StringView(b"abcd"), r"[bc]?" => "^") == "^a^^d^" @test replace(StringView(b"a"), 'a' => typeof) == "Char" @test replace(StringView(b"The foxes."), r"fox(es)?" => s"bus\1") == "The buses." - @test replace(StringView(b"foobarbaz"), "oo" => "zz", "ar" => "zz", "z" => "m") == "fzzbzzbam" - @test replace(StringView(b"foobar"), 'o' => '0', "" => "") == "f00bar" + if VERSION ≥ v"1.7" # for multiple replacements + @test replace(StringView(b"foobarbaz"), "oo" => "zz", "ar" => "zz", "z" => "m") == "fzzbzzbam" + @test replace(StringView(b"foobar"), 'o' => '0', "" => "") == "f00bar" + end end @testset "miscellaneous" begin