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

JuliaLang / julia / #38163

07 Aug 2025 12:47PM UTC coverage: 69.322% (+43.6%) from 25.688%
#38163

push

local

web-flow
Fix precompiling when there's no manifest (#59212)

1 of 29 new or added lines in 2 files covered. (3.45%)

2699 existing lines in 92 files now uncovered.

42030 of 60630 relevant lines covered (69.32%)

6207804.78 hits per line

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

72.55
/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
341✔
31
        i ≤ j || return new(s, 0, 0)
412✔
32
        @boundscheck begin
282✔
33
            checkbounds(s, i:j)
282✔
34
            @inbounds isvalid(s, i) || string_index_err(s, i)
282✔
35
            @inbounds isvalid(s, j) || string_index_err(s, j)
282✔
36
        end
37
        return new(s, i-1, nextind(s,j)-i)
282✔
38
    end
39
    function SubString{T}(s::T, i::Int, j::Int, ::Val{:noshift}) where T<:AbstractString
7✔
40
        @boundscheck if !(i == j == 0)
7✔
41
            si, sj = i + 1, prevind(s, j + i + 1)
7✔
42
            @inbounds isvalid(s, si) || string_index_err(s, si)
7✔
43
            @inbounds isvalid(s, sj) || string_index_err(s, sj)
7✔
44
        end
45
        new(s, i, j)
7✔
46
    end
47
end
48

49
@propagate_inbounds SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j)
241,304✔
50
@propagate_inbounds SubString(s::T, i::Int, j::Int, v::Val{:noshift}) where {T<:AbstractString} = SubString{T}(s, i, j, v)
1,412✔
51
@propagate_inbounds SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) = SubString(s, Int(i), Int(j))
25,144✔
52
@propagate_inbounds SubString(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, first(r), last(r))
197,030✔
53

54
@propagate_inbounds function SubString(s::SubString, i::Int, j::Int)
82✔
55
    @boundscheck i ≤ j && checkbounds(s, i:j)
10,293✔
56
    SubString(s.string, s.offset+i, s.offset+j)
10,281✔
57
end
58

59
SubString(s::AbstractString) = SubString(s, 1, lastindex(s)::Int)
6,203✔
60
SubString{T}(s::T) where {T<:AbstractString} = SubString{T}(s, 1, lastindex(s)::Int)
226✔
61

62
@propagate_inbounds view(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, r)
74,257✔
63
@propagate_inbounds maybeview(s::AbstractString, r::AbstractUnitRange{<:Integer}) = view(s, r)
73,469✔
64
@propagate_inbounds maybeview(s::AbstractString, args...) = getindex(s, args...)
6✔
65

66
convert(::Type{SubString{S}}, s::AbstractString) where {S<:AbstractString} =
61✔
67
    SubString(convert(S, s))::SubString{S}
68
convert(::Type{T}, s::T) where {T<:SubString} = s
2✔
69

70
# Regex match allows only Union{String, SubString{String}} so define conversion to this type
71
convert(::Type{Union{String, SubString{String}}}, s::String) = s
×
72
convert(::Type{Union{String, SubString{String}}}, s::SubString{String}) = s
×
73
convert(::Type{Union{String, SubString{String}}}, s::AbstractString) = convert(String, s)::String
×
74

75
function String(s::SubString{String})
76
    parent = s.string
996,995✔
77
    copy = GC.@preserve parent unsafe_string(pointer(parent, s.offset+1), s.ncodeunits)
996,995✔
78
    return copy
991,877✔
79
end
80

81
ncodeunits(s::SubString) = s.ncodeunits
21,113,558✔
82
codeunit(s::SubString) = codeunit(s.string)::CodeunitType
×
83
length(s::SubString) = length(s.string, s.offset+1, s.offset+s.ncodeunits)
×
84

85
function codeunit(s::SubString, i::Integer)
86
    @boundscheck checkbounds(s, i)
73,115✔
87
    @inbounds return codeunit(s.string, s.offset + i)
73,115✔
88
end
89

90
function iterate(s::SubString, i::Integer=firstindex(s))
313✔
91
    i == ncodeunits(s)+1 && return nothing
1,213,492✔
92
    @boundscheck checkbounds(s, i)
448,491✔
93
    y = iterate(s.string, s.offset + i)
895,754✔
94
    y === nothing && return nothing
448,487✔
95
    c, i = y::Tuple{AbstractChar,Int}
448,152✔
96
    return c, i - s.offset
448,487✔
97
end
98

99
function getindex(s::SubString, i::Integer)
318✔
100
    @boundscheck checkbounds(s, i)
73,860✔
101
    @inbounds return getindex(s.string, s.offset + i)
146,414✔
102
end
103

104
isascii(ss::SubString{String}) = isascii(codeunits(ss))
×
105

106
function isvalid(s::SubString, i::Integer)
78,485✔
107
    ib = true
1,164✔
108
    @boundscheck ib = checkbounds(Bool, s, i)
13,360,954✔
109
    @inbounds return ib && isvalid(s.string, s.offset + i)::Bool
26,643,236✔
110
end
111

112
thisind(s::SubString{String}, i::Int) = _thisind_str(s, i)
120,654✔
113
nextind(s::SubString{String}, i::Int) = _nextind_str(s, i)
9,033✔
114

115
parent(s::SubString) = s.string
1✔
116
parentindices(s::SubString) = (s.offset + 1 : thisind(s.string, s.offset + s.ncodeunits),)
1✔
117

118
function ==(a::Union{String, SubString{String}}, b::Union{String, SubString{String}})
119
    sizeof(a) == sizeof(b) && _memcmp(a, b) == 0
2,122,350✔
120
end
121

122
function cmp(a::SubString{String}, b::SubString{String})
×
123
    c = _memcmp(a, b)
×
124
    return c < 0 ? -1 : c > 0 ? +1 : cmp(sizeof(a), sizeof(b))
×
125
end
126

127
# don't make unnecessary copies when passing substrings to C functions
128
cconvert(::Type{Ptr{UInt8}}, s::SubString{String}) = s
×
129
cconvert(::Type{Ptr{Int8}}, s::SubString{String}) = s
×
130

131
function unsafe_convert(::Type{Ptr{R}}, s::SubString{String}) where R<:Union{Int8, UInt8}
132
    convert(Ptr{R}, pointer(s.string)) + s.offset
1,093,306✔
133
end
134

135
pointer(x::SubString{String}) = pointer(x.string) + x.offset
561,853✔
136
pointer(x::SubString{String}, i::Integer) = pointer(x.string) + x.offset + (i-1)
×
137

138
hash(data::SubString{String}, h::UInt) =
488,168✔
139
    GC.@preserve data hash_bytes(pointer(data), sizeof(data), UInt64(h), HASH_SECRET) % UInt
140

141
_isannotated(::SubString{T}) where {T} = _isannotated(T)
×
142

143
"""
144
    reverse(s::AbstractString)::AbstractString
145

146
Reverses a string. Technically, this function reverses the codepoints in a string and its
147
main utility is for reversed-order string processing, especially for reversed
148
regular-expression searches. See also [`reverseind`](@ref) to convert indices in `s` to
149
indices in `reverse(s)` and vice-versa, and `graphemes` from module `Unicode` to
150
operate on user-visible "characters" (graphemes) rather than codepoints.
151
See also [`Iterators.reverse`](@ref) for
152
reverse-order iteration without making a copy. Custom string types must implement the
153
`reverse` function themselves and should typically return a string with the same type
154
and encoding. If they return a string with a different encoding, they must also override
155
`reverseind` for that string type to satisfy `s[reverseind(s,i)] == reverse(s)[i]`.
156

157
# Examples
158
```jldoctest
159
julia> reverse("JuliaLang")
160
"gnaLailuJ"
161
```
162

163
!!! note
164
    The examples below may be rendered differently on different systems.
165
    The comments indicate how they're supposed to be rendered
166

167
Combining characters can lead to surprising results:
168

169
```jldoctest
170
julia> reverse("ax̂e") # hat is above x in the input, above e in the output
171
"êxa"
172

173
julia> using Unicode
174

175
julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes; hat is above x in both in- and output
176
"ex̂a"
177
```
178
"""
179
function reverse(s::Union{String,SubString{String}})::String
×
180
    # Read characters forwards from `s` and write backwards to `out`
181
    out = _string_n(sizeof(s))
×
182
    offs = sizeof(s) + 1
×
183
    for c in s
×
184
        offs -= ncodeunits(c)
×
185
        __unsafe_string!(out, c, offs)
×
186
    end
×
187
    return out
×
188
end
189

UNCOV
190
string(a::String)            = String(a)
×
191
string(a::SubString{String}) = String(a)
2,741✔
192

193
function Symbol(s::SubString{String})
194
    return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), s, sizeof(s))
174✔
195
end
196

197
@inline function __unsafe_string!(out, c::Char, offs::Integer) # out is a (new) String (or StringVector)
198
    x = bswap(reinterpret(UInt32, c))
82,613✔
199
    n = ncodeunits(c)
82,613✔
200
    GC.@preserve out begin
82,613✔
201
        unsafe_store!(pointer(out, offs), x % UInt8)
82,613✔
202
        n == 1 && return n
82,613✔
203
        x >>= 8
76✔
204
        unsafe_store!(pointer(out, offs+1), x % UInt8)
76✔
205
        n == 2 && return n
76✔
206
        x >>= 8
57✔
207
        unsafe_store!(pointer(out, offs+2), x % UInt8)
57✔
208
        n == 3 && return n
57✔
209
        x >>= 8
17✔
210
        unsafe_store!(pointer(out, offs+3), x % UInt8)
17✔
211
    end
212
    return n
17✔
213
end
214

UNCOV
215
@assume_effects :nothrow @inline function __unsafe_string!(out, s::String, offs::Integer)
×
UNCOV
216
    n = sizeof(s)
×
UNCOV
217
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n)
×
UNCOV
218
    return n
×
219
end
220

UNCOV
221
@inline function __unsafe_string!(out, s::SubString{String}, offs::Integer)
×
UNCOV
222
    n = sizeof(s)
×
UNCOV
223
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n)
×
UNCOV
224
    return n
×
225
end
226

UNCOV
227
@assume_effects :nothrow @inline function __unsafe_string!(out, s::Symbol, offs::Integer)
×
UNCOV
228
    n = sizeof(s)
×
UNCOV
229
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), unsafe_convert(Ptr{UInt8},s), n)
×
UNCOV
230
    return n
×
231
end
232

233
# nothrow needed here because for v in a can't prove the indexing is inbounds.
234
@assume_effects :foldable :nothrow string(a::Union{Char, String, Symbol}...) = _string(a...)
2,654,761✔
235

236
string(a::Union{Char, String, SubString{String}, Symbol}...) = _string(a...)
914,484✔
237

238
function _string(a::Union{Char, String, SubString{String}, Symbol}...)
239
    n = 0
125✔
240
    for v in a
125✔
241
        # 4 types is too many for automatic Union-splitting, so we split manually
242
        # and allow one specializable call site per concrete type
243
        if v isa Char
125✔
244
            n += ncodeunits(v)
125✔
UNCOV
245
        elseif v isa String
×
UNCOV
246
            n += sizeof(v)
×
UNCOV
247
        elseif v isa SubString{String}
×
UNCOV
248
            n += sizeof(v)
×
249
        else
UNCOV
250
            n += sizeof(v::Symbol)
×
251
        end
252
    end
125✔
253
    out = _string_n(n)
125✔
254
    offs = 1
125✔
255
    for v in a
125✔
256
        if v isa Char
125✔
257
            offs += __unsafe_string!(out, v, offs)
125✔
UNCOV
258
        elseif v isa String || v isa SubString{String}
×
UNCOV
259
            offs += __unsafe_string!(out, v, offs)
×
260
        else
UNCOV
261
            offs += __unsafe_string!(out, v::Symbol, offs)
×
262
        end
263
    end
125✔
264
    return out
125✔
265
end
266

267
# don't assume effects for general integers since we cannot know their implementation
268
# not nothrow because r<0 throws
269
@assume_effects :foldable repeat(s::String, r::BitInteger) = @invoke repeat(s::String, r::Integer)
2,009,767✔
270

271
function repeat(s::Union{String, SubString{String}}, r::Integer)
45✔
272
    r < 0 && throw(ArgumentError("can't repeat a string $r times"))
155✔
273
    r = UInt(r)::UInt
156✔
274
    r == 0 && return ""
155✔
275
    r == 1 && return String(s)
127✔
276
    n = sizeof(s)
20✔
277
    out = _string_n(n*r)
20✔
278
    if n == 1 # common case: repeating a single-byte string
20✔
279
        @inbounds b = codeunit(s, 1)
10✔
280
        memset(unsafe_convert(Ptr{UInt8}, out), b, r)
10✔
281
    else
282
        for i = 0:r-1
10✔
283
            GC.@preserve s out unsafe_copyto!(pointer(out, i*n+1), pointer(s), n)
329✔
284
        end
628✔
285
    end
286
    return out
20✔
287
end
288

289
function filter(f, s::Union{String, SubString{String}})
3✔
290
    out = StringVector(sizeof(s))
3✔
291
    offset = 1
3✔
292
    for c in s
6✔
293
        if f(c)
131✔
294
            offset += __unsafe_string!(out, c, offset)
75✔
295
        end
296
    end
170✔
297
    resize!(out, offset-1)
3✔
298
    sizehint!(out, offset-1)
3✔
299
    return String(out)
3✔
300
end
301

302
getindex(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, r)
260✔
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