• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37525

pending completion
#37525

push

local

web-flow
NFC: some cleanup in gc.c (#49577)

71766 of 83435 relevant lines covered (86.01%)

34298284.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.08
/base/strings/substring.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
    SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s))
5
    SubString(s::AbstractString, r::UnitRange{<:Integer})
6

7
Like [`getindex`](@ref), but returns a view into the parent string `s`
8
within range `i:j` or `r` respectively instead of making a copy.
9

10
The [`@views`](@ref) macro converts any string slices `s[i:j]` into
11
substrings `SubString(s, i, j)` in a block of code.
12

13
# Examples
14
```jldoctest
15
julia> SubString("abc", 1, 2)
16
"ab"
17

18
julia> SubString("abc", 1:2)
19
"ab"
20

21
julia> SubString("abc", 2)
22
"bc"
23
```
24
"""
25
struct SubString{T<:AbstractString} <: AbstractString
26
    string::T
27
    offset::Int
28
    ncodeunits::Int
29

30
    function SubString{T}(s::T, i::Int, j::Int) where T<:AbstractString
7,034,864✔
31
        i ≤ j || return new(s, 0, 0)
7,835,495✔
32
        @boundscheck begin
6,234,231✔
33
            checkbounds(s, i:j)
6,234,233✔
34
            @inbounds isvalid(s, i) || string_index_err(s, i)
6,234,225✔
35
            @inbounds isvalid(s, j) || string_index_err(s, j)
6,234,225✔
36
        end
37
        return new(s, i-1, nextind(s,j)-i)
6,234,225✔
38
    end
39
end
40

41
@propagate_inbounds SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j)
7,034,953✔
42
@propagate_inbounds SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) = SubString(s, Int(i), Int(j))
3,421,248✔
43
@propagate_inbounds SubString(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, first(r), last(r))
98,084✔
44

45
@propagate_inbounds function SubString(s::SubString, i::Int, j::Int)
290✔
46
    @boundscheck i ≤ j && checkbounds(s, i:j)
141,421✔
47
    SubString(s.string, s.offset+i, s.offset+j)
141,421✔
48
end
49

50
SubString(s::AbstractString) = SubString(s, 1, lastindex(s)::Int)
345✔
51
SubString{T}(s::T) where {T<:AbstractString} = SubString{T}(s, 1, lastindex(s)::Int)
×
52

53
@propagate_inbounds view(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, r)
454✔
54
@propagate_inbounds maybeview(s::AbstractString, r::AbstractUnitRange{<:Integer}) = view(s, r)
3✔
55
@propagate_inbounds maybeview(s::AbstractString, args...) = getindex(s, args...)
6✔
56

57
convert(::Type{SubString{S}}, s::AbstractString) where {S<:AbstractString} =
60✔
58
    SubString(convert(S, s))::SubString{S}
59
convert(::Type{T}, s::T) where {T<:SubString} = s
2✔
60

61
# Regex match allows only Union{String, SubString{String}} so define conversion to this type
62
convert(::Type{Union{String, SubString{String}}}, s::String) = s
×
63
convert(::Type{Union{String, SubString{String}}}, s::SubString{String}) = s
×
64
convert(::Type{Union{String, SubString{String}}}, s::AbstractString) = convert(String, s)::String
×
65

66
function String(s::SubString{String})
67,219✔
67
    parent = s.string
728,369✔
68
    copy = GC.@preserve parent unsafe_string(pointer(parent, s.offset+1), s.ncodeunits)
728,369✔
69
    return copy
728,369✔
70
end
71

72
ncodeunits(s::SubString) = s.ncodeunits
23,789,694✔
73
codeunit(s::SubString) = codeunit(s.string)::CodeunitType
×
74
length(s::SubString) = length(s.string, s.offset+1, s.offset+s.ncodeunits)
160,723✔
75

76
function codeunit(s::SubString, i::Integer)
1✔
77
    @boundscheck checkbounds(s, i)
965,677✔
78
    @inbounds return codeunit(s.string, s.offset + i)
965,677✔
79
end
80

81
function iterate(s::SubString, i::Integer=firstindex(s))
525,840✔
82
    i == ncodeunits(s)+1 && return nothing
6,278,348✔
83
    @boundscheck checkbounds(s, i)
3,151,610✔
84
    y = iterate(s.string, s.offset + i)
6,010,255✔
85
    y === nothing && return nothing
3,151,610✔
86
    c, i = y::Tuple{AbstractChar,Int}
3,151,406✔
87
    return c, i - s.offset
3,151,610✔
88
end
89

90
function getindex(s::SubString, i::Integer)
484,464✔
91
    @boundscheck checkbounds(s, i)
484,464✔
92
    @inbounds return getindex(s.string, s.offset + i)
484,464✔
93
end
94

95
isascii(ss::SubString{String}) = isascii(codeunits(ss))
×
96

97
function isvalid(s::SubString, i::Integer)
75✔
98
    ib = true
73✔
99
    @boundscheck ib = checkbounds(Bool, s, i)
212,880✔
100
    @inbounds return ib && isvalid(s.string, s.offset + i)::Bool
212,880✔
101
end
102

103
thisind(s::SubString{String}, i::Int) = _thisind_str(s, i)
501,559✔
104
nextind(s::SubString{String}, i::Int) = _nextind_str(s, i)
466,092✔
105

106
function ==(a::Union{String, SubString{String}}, b::Union{String, SubString{String}})
128,050✔
107
    sizeof(a) == sizeof(b) && _memcmp(a, b) == 0
16,198,484✔
108
end
109

110
function cmp(a::SubString{String}, b::SubString{String})
×
111
    c = _memcmp(a, b)
×
112
    return c < 0 ? -1 : c > 0 ? +1 : cmp(sizeof(a), sizeof(b))
×
113
end
114

115
# don't make unnecessary copies when passing substrings to C functions
116
cconvert(::Type{Ptr{UInt8}}, s::SubString{String}) = s
×
117
cconvert(::Type{Ptr{Int8}}, s::SubString{String}) = s
×
118

119
function unsafe_convert(::Type{Ptr{R}}, s::SubString{String}) where R<:Union{Int8, UInt8}
59✔
120
    convert(Ptr{R}, pointer(s.string)) + s.offset
681,336✔
121
end
122

123
pointer(x::SubString{String}) = pointer(x.string) + x.offset
3,356,447✔
124
pointer(x::SubString{String}, i::Integer) = pointer(x.string) + x.offset + (i-1)
64✔
125

126
function hash(s::SubString{String}, h::UInt)
×
127
    h += memhash_seed
×
128
    ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h
132,489✔
129
end
130

131
"""
132
    reverse(s::AbstractString) -> AbstractString
133

134
Reverses a string. Technically, this function reverses the codepoints in a string and its
135
main utility is for reversed-order string processing, especially for reversed
136
regular-expression searches. See also [`reverseind`](@ref) to convert indices in `s` to
137
indices in `reverse(s)` and vice-versa, and `graphemes` from module `Unicode` to
138
operate on user-visible "characters" (graphemes) rather than codepoints.
139
See also [`Iterators.reverse`](@ref) for
140
reverse-order iteration without making a copy. Custom string types must implement the
141
`reverse` function themselves and should typically return a string with the same type
142
and encoding. If they return a string with a different encoding, they must also override
143
`reverseind` for that string type to satisfy `s[reverseind(s,i)] == reverse(s)[i]`.
144

145
# Examples
146
```jldoctest
147
julia> reverse("JuliaLang")
148
"gnaLailuJ"
149
```
150

151
!!! note
152
    The examples below may be rendered differently on different systems.
153
    The comments indicate how they're supposed to be rendered
154

155
Combining characters can lead to surprising results:
156

157
```jldoctest
158
julia> reverse("ax̂e") # hat is above x in the input, above e in the output
159
"êxa"
160

161
julia> using Unicode
162

163
julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes; hat is above x in both in- and output
164
"ex̂a"
165
```
166
"""
167
function reverse(s::Union{String,SubString{String}})::String
592✔
168
    # Read characters forwards from `s` and write backwards to `out`
169
    out = _string_n(sizeof(s))
592✔
170
    offs = sizeof(s) + 1
592✔
171
    for c in s
1,180✔
172
        offs -= ncodeunits(c)
35,027✔
173
        __unsafe_string!(out, c, offs)
35,021✔
174
    end
69,320✔
175
    return out
592✔
176
end
177

178
string(a::String)            = String(a)
46,366✔
179
string(a::SubString{String}) = String(a)
88✔
180

181
function Symbol(s::SubString{String})
116✔
182
    return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), s, sizeof(s))
10,488✔
183
end
184

185
@inline function __unsafe_string!(out, c::Char, offs::Integer) # out is a (new) String (or StringVector)
265✔
186
    x = bswap(reinterpret(UInt32, c))
1,842,433✔
187
    n = ncodeunits(c)
1,952,881✔
188
    GC.@preserve out begin
1,842,433✔
189
        unsafe_store!(pointer(out, offs), x % UInt8)
1,842,433✔
190
        n == 1 && return n
1,842,433✔
191
        x >>= 8
56,139✔
192
        unsafe_store!(pointer(out, offs+1), x % UInt8)
56,139✔
193
        n == 2 && return n
56,139✔
194
        x >>= 8
53,111✔
195
        unsafe_store!(pointer(out, offs+2), x % UInt8)
53,111✔
196
        n == 3 && return n
53,111✔
197
        x >>= 8
1,198✔
198
        unsafe_store!(pointer(out, offs+3), x % UInt8)
1,198✔
199
    end
200
    return n
1,198✔
201
end
202

203
@assume_effects :nothrow @inline function __unsafe_string!(out, s::String, offs::Integer)
×
204
    n = sizeof(s)
14,005,044✔
205
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n)
14,005,044✔
206
    return n
14,005,044✔
207
end
208

209
@inline function __unsafe_string!(out, s::SubString{String}, offs::Integer)
×
210
    n = sizeof(s)
561,263✔
211
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n)
561,263✔
212
    return n
561,263✔
213
end
214

215
@assume_effects :nothrow @inline function __unsafe_string!(out, s::Symbol, offs::Integer)
×
216
    n = sizeof(s)
15,885✔
217
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), unsafe_convert(Ptr{UInt8},s), n)
15,885✔
218
    return n
15,885✔
219
end
220

221
# nothrow needed here because for v in a can't prove the indexing is inbounds.
222
@assume_effects :foldable :nothrow string(a::Union{Char, String, Symbol}...) = _string(a...)
6,278,182✔
223

224
string(a::Union{Char, String, SubString{String}, Symbol}...) = _string(a...)
556,537✔
225

226
function _string(a::Union{Char, String, SubString{String}, Symbol}...)
6,834,456✔
227
    n = 0
×
228
    for v in a
6,834,456✔
229
        # 4 types is too many for automatic Union-splitting, so we split manually
230
        # and allow one specializable call site per concrete type
231
        if v isa Char
1,688,287✔
232
            n += ncodeunits(v)
316,506✔
233
        elseif v isa String
1,655,848✔
234
            n += sizeof(v)
14,005,044✔
235
        elseif v isa SubString{String}
577,136✔
236
            n += sizeof(v)
561,263✔
237
        else
238
            n += sizeof(v::Symbol)
15,885✔
239
        end
240
    end
21,277,821✔
241
    out = _string_n(n)
6,834,456✔
242
    offs = 1
×
243
    for v in a
6,834,456✔
244
        if v isa Char
1,688,287✔
245
            offs += __unsafe_string!(out, v, offs)
264,983✔
246
        elseif v isa String || v isa SubString{String}
2,232,984✔
247
            offs += __unsafe_string!(out, v, offs)
14,566,307✔
248
        else
249
            offs += __unsafe_string!(out, v::Symbol, offs)
15,885✔
250
        end
251
    end
21,277,821✔
252
    return out
6,834,456✔
253
end
254

255
# don't assume effects for general integers since we cannot know their implementation
256
# not nothrow because r<0 throws
257
@assume_effects :foldable repeat(s::String, r::BitInteger) = @invoke repeat(s::String, r::Integer)
964,705✔
258

259
function repeat(s::Union{String, SubString{String}}, r::Integer)
964,688✔
260
    r < 0 && throw(ArgumentError("can't repeat a string $r times"))
964,688✔
261
    r == 0 && return ""
964,680✔
262
    r == 1 && return String(s)
854,954✔
263
    n = sizeof(s)
718,200✔
264
    out = _string_n(n*r)
718,200✔
265
    if n == 1 # common case: repeating a single-byte string
718,200✔
266
        @inbounds b = codeunit(s, 1)
676,746✔
267
        ccall(:memset, Ptr{Cvoid}, (Ptr{UInt8}, Cint, Csize_t), out, b, r)
676,746✔
268
    else
269
        for i = 0:r-1
82,908✔
270
            GC.@preserve s out unsafe_copyto!(pointer(out, i*n+1), pointer(s), n)
2,047,751✔
271
        end
2,047,751✔
272
    end
273
    return out
718,200✔
274
end
275

276
function filter(f, s::Union{String, SubString{String}})
42✔
277
    out = StringVector(sizeof(s))
42✔
278
    offset = 1
3✔
279
    for c in s
84✔
280
        if f(c)
844✔
281
            offset += __unsafe_string!(out, c, offset)
663✔
282
        end
283
    end
1,637✔
284
    resize!(out, offset-1)
84✔
285
    sizehint!(out, offset-1)
42✔
286
    return String(out)
42✔
287
end
288

289
getindex(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, r)
2,709✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc