Skip to content

Commit a1b29b5

Browse files
committed
Improve repeat character performance
1 parent fc9d1f8 commit a1b29b5

File tree

4 files changed

+137
-11
lines changed

4 files changed

+137
-11
lines changed

src/core.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ end
135135
@_inline_meta()
136136
T(get_codeunit(str, pos)), pos + 1
137137
end
138-
@propagate_inbounds function iterate(str::MaybeSub{T}, pos::Int=firstindex(str)
138+
@propagate_inbounds function iterate(str::MaybeSub{T}, pos::Integer=firstindex(str)
139139
)::Union{Nothing,Tuple{eltype(T),Int}} where {T<:Str}
140140
@_inline_meta()
141141
pos > ncodeunits(str) && return nothing

src/support.jl

+94-10
Original file line numberDiff line numberDiff line change
@@ -698,16 +698,6 @@ last(str::Str, n::Integer) = str[max(1, prevind(str, ncodeunits(str)+1, n)):end
698698

699699
const Chrs = @static V6_COMPAT ? Union{Char,AbstractChar} : Chr
700700

701-
function repeat(ch::CP, cnt::Integer) where {CP <: Chrs}
702-
C = codepoint_cse(CP)
703-
cnt > 1 && return Str(C, _repeat(EncodingStyle(C), C, codepoint(ch), cnt))
704-
cnt == 1 && return _convert(C, codepoint(ch))
705-
cnt == 0 && return empty_str(C)
706-
repeaterr(cnt)
707-
end
708-
709-
(^)(ch::CP, cnt::Integer) where {CP <: Chrs} = repeat(ch, cnt)
710-
711701
# low level mem support functions
712702

713703
const HAS_WMEM = !(@static V6_COMPAT ? is_windows() : Sys.iswindows())
@@ -812,6 +802,39 @@ _memcpy(a::Ptr{T}, b::Ptr{T}, len) where {T<:OthChr} =
812802
end
813803
end
814804

805+
@inline _aligned_set(pnt::Ptr{UInt8}, ch::UInt8, cnt) = _memset(pnt, ch, cnt)
806+
807+
@inline function _aligned_set(pnt::Ptr{UInt16}, ch::UInt16, cnt)
808+
val = ch%UInt64
809+
val |= (val<<16) | (val<<32) | (val<<48)
810+
p64 = reinterpret(Ptr{UInt64}, pnt)
811+
@inbounds for i = 1:((cnt + 3)>>2)
812+
unsafe_store!(p64, val, i)
813+
end
814+
#=
815+
fin = p64 + (((cnt + 3)>>2)<<3)
816+
while p64 < fin
817+
unsafe_store!(p64, val)
818+
p64 += 8
819+
end
820+
=#
821+
end
822+
823+
@inline function _aligned_set(pnt::Ptr{UInt32}, ch::UInt32, cnt)
824+
val = ((ch%UInt64)<<32) | ch
825+
p64 = reinterpret(Ptr{UInt64}, pnt)
826+
@inbounds for i = 1:((cnt + 1)>>1)
827+
unsafe_store!(p64, val, i)
828+
end
829+
#=
830+
fin = p64 + (((cnt + 1)>>1)<<3)
831+
while p64 < fin
832+
unsafe_store!(p64, val)
833+
p64 += 8
834+
end
835+
=#
836+
end
837+
815838
@inline function _repeat_chr(::Type{T}, ch, cnt) where {T<:CodeUnitTypes}
816839
#println("_repeat_chr($T, $ch, $cnt)")
817840
buf, pnt = _allocate(T, cnt)
@@ -877,6 +900,67 @@ end
877900

878901
(^)(str::T, cnt::Integer) where {T<:Str} = repeat(str, cnt)
879902

903+
function repeat(ch::CP, cnt::Integer) where {CP <: Chrs}
904+
C = codepoint_cse(CP)
905+
cnt > 1 && return Str(C, _repeat(EncodingStyle(C), C, codepoint(ch), cnt))
906+
cnt == 1 && return _convert(C, codepoint(ch))
907+
cnt == 0 && return empty_str(C)
908+
repeaterr(cnt)
909+
end
910+
911+
(^)(ch::CP, cnt::Integer) where {CP <: Chrs} = repeat(ch, cnt)
912+
913+
#=
914+
function _repeat(::Type{CS}, ch::C, cnt::Integer) where {CS<:CSE,C<:Union{ASCIIChr,LatinChr}}
915+
cnt == 0 && return empty_str(CS)
916+
cnt < 0 && repeaterr(cnt)
917+
buf, pnt = _allocate(UInt8, cnt)
918+
cnt == 1 ? set_codeunit!(pnt, ch%UInt8) : _memset(pnt, ch%UInt8, cnt)
919+
Str(CS, buf)
920+
end
921+
922+
function _repeat(::Type{CS}, ch::C, cnt::Integer) where {CS<:CSE,C<:Union{UCS2Chr,UTF32Chr}}
923+
cnt == 0 && return empty_str(CS)
924+
cnt < 0 && repeaterr(cnt)
925+
CU = codeunit(CS)
926+
buf, pnt = _allocate(CU, cnt)
927+
cnt == 1 ? set_codeunit!(pnt, ch%CU) : _aligned_set(pnt, ch%CU, cnt)
928+
Str(CS, buf)
929+
end
930+
931+
repeat(ch::ASCIIChr, cnt::Integer) = _repeat(ASCIICSE, ch, cnt)
932+
repeat(ch::LatinChr, cnt::Integer) = _repeat(LatinCSE, ch, cnt)
933+
repeat(ch::UCS2Chr, cnt::Integer) = _repeat(UCS2CSE, ch, cnt)
934+
repeat(ch::UTF32Chr, cnt::Integer) = _repeat(UTF32CSE, ch, cnt)
935+
=#
936+
937+
function repeat(ch::C, cnt::Integer) where {C<:Union{ASCIIChr,LatinChr,_LatinChr}}
938+
cnt == 0 && return empty_str(ASCIICSE)
939+
cnt < 0 && repeaterr(cnt)
940+
cu = ch%UInt8
941+
buf, pnt = _allocate(UInt8, cnt)
942+
_memset(pnt, cu, cnt)
943+
Str((C == ASCIIChr || cu <= 0x7f) ? ASCIICSE : (C == _LatinChr ? _LatinCSE : LatinCSE), buf)
944+
end
945+
946+
function repeat(ch::C, cnt::Integer) where {C<:Union{UCS2Chr,UTF32Chr}}
947+
cnt == 0 && return empty_str(ASCIICSE)
948+
cnt < 0 && repeaterr(cnt)
949+
if ch%UInt32 <= 0xff
950+
buf, pnt = _allocate(UInt8, cnt)
951+
cnt == 1 && set_codepoint!(pnt, ch%UInt8) : _memset(pnt, ch%UInt8, cnt)
952+
Str(ifelse(ch%UInt8 <= 0x7f, ASCIICSE, LatinCSE), buf)
953+
elseif C == UCS2Chr || ch%UInt32 <= 0xffff
954+
buf, pnt = _allocate(UInt16, cnt)
955+
cnt == 1 && set_codepoint!(pnt, ch%UInt16) : _aligned_set(pnt, ch%UInt16, cnt)
956+
Str(UCS2CSE, buf)
957+
else
958+
buf, pnt = _allocate(UInt32, cnt)
959+
cnt == 1 && set_codepoint!(pnt, ch%UInt32) : _aligned_set(pnt, ch%UInt32, cnt)
960+
Str(UTF32CSE, buf)
961+
end
962+
end
963+
880964
# Definitions for C compatible strings, that don't allow embedded
881965
# '\0', and which are terminated by a '\0'
882966

src/utf16.jl

+20
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ function _nextcpfun(::MultiCU, ::Type{UTF16CSE}, pnt)
154154
: (ch%UInt32, pnt + 2))
155155
end
156156

157+
#=
157158
@propagate_inbounds function _iterate(::MultiCU, ::Type{T}, str::MS_UTF16, pos::Int) where {T}
158159
@preserve str begin
159160
pnt = bytoff(pointer(str), pos)
@@ -164,6 +165,25 @@ end
164165
end
165166
end
166167
168+
@propagate_inbounds function iterate(str::MS_UTF16)
169+
@_inline_meta()
170+
ncodeunits(str) == 0 && return nothing
171+
ch = get_codeunit(str)
172+
(is_surrogate_lead(ch)
173+
? (UTF32Chr(get_supplementary(ch, get_codeunit(str, 2))), 3)
174+
: (UTF32Chr(ch), 2))
175+
end
176+
=#
177+
178+
@propagate_inbounds function iterate(str::MS_UTF16, pos::Integer=1)
179+
#@_inline_meta()
180+
pos > ncodeunits(str) && return nothing
181+
@boundscheck pos <= 0 && boundserr(str, pos)
182+
ch = get_codeunit(str, pos)
183+
is_surrogate_lead(ch) || return UTF32Chr(ch), pos+1
184+
(UTF32Chr(get_supplementary(ch, get_codeunit(str, pos+1))), pos+2)
185+
end
186+
167187
@propagate_inbounds function _next(::MultiCU, ::Type{T}, str::MS_UTF16, pos::Int) where {T}
168188
@boundscheck pos <= ncodeunits(str) || boundserr(str, pos)
169189
_iterate(MultiCU(), T, str, pos)

src/utf8.jl

+22
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ function _nextcpfun(::MultiCU, ::Type{UTF8CSE}, pnt)
330330
end
331331
end
332332

333+
#=
333334
@propagate_inbounds function _iterate(::MultiCU, ::Type{T}, str::MS_UTF8,
334335
pos::Int) where {T<:Chr}
335336
@preserve str begin
@@ -348,6 +349,27 @@ end
348349
end
349350
end
350351
end
352+
=#
353+
354+
@propagate_inbounds function iterate(str::MS_UTF8, pos::Integer=1)
355+
pos > ncodeunits(str) && return nothing
356+
@boundscheck pos <= 0 && boundserr(str, pos)
357+
@preserve str begin
358+
pnt = pointer(str) + pos - 1
359+
ch = get_codeunit(pnt)
360+
if ch < 0x80
361+
UTF32Chr(ch), pos + 1
362+
elseif ch < 0xc0
363+
index_error(str, pos)
364+
elseif ch < 0xe0
365+
UTF32Chr(get_utf8_2byte(pnt + 1, ch)), pos + 2
366+
elseif ch < 0xf0
367+
UTF32Chr(get_utf8_3byte(pnt + 2, ch)), pos + 3
368+
else
369+
UTF32Chr(get_utf8_4byte(pnt + 3, ch)), pos + 4
370+
end
371+
end
372+
end
351373

352374
_iterate(::MultiCU, ::Type{T}, str::Str{RawUTF8CSE}, pos::Int) where {T} =
353375
iterate(str.data, pos)

0 commit comments

Comments
 (0)