Skip to content

Commit

Permalink
lower-level replace(...) hooks in latest Julia (#21)
Browse files Browse the repository at this point in the history
* lower-level replace(...) hooks in latest Julia

* multiple replacements require julia 1.7
  • Loading branch information
stevengj authored Jul 5, 2023
1 parent c311bc1 commit 9389f74
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 39 deletions.
93 changes: 54 additions & 39 deletions src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 10 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ 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."
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
@test cmp("foobar","bar") == cmp(s,"bar") == -cmp("bar",s) == cmp(s,StringView("bar"))
@test s == StringView("foobar") == "foobar" == s == "foobar" != StringView("bar")
Expand Down

0 comments on commit 9389f74

Please sign in to comment.