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

JuliaLang / julia / #37487

pending completion
#37487

push

local

web-flow
Subtype: Add a fastpath for nested constructor with identity name. (#49159)

* Subtype: Add a fastpath for nested constructor with identity name.

* Update src/subtype.c

---------

Co-authored-by: Jameson Nash <vtjnash@gmail.com>

71468 of 82918 relevant lines covered (86.19%)

30575088.21 hits per line

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

88.96
/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
5,986,117✔
31
        i ≤ j || return new(s, 0, 0)
6,720,392✔
32
        @boundscheck begin
5,251,831✔
33
            checkbounds(s, i:j)
5,251,848✔
34
            @inbounds isvalid(s, i) || string_index_err(s, i)
5,251,812✔
35
            @inbounds isvalid(s, j) || string_index_err(s, j)
5,251,818✔
36
        end
37
        return new(s, i-1, nextind(s,j)-i)
5,251,798✔
38
    end
39
end
40

41
@propagate_inbounds SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j)
5,986,163✔
42
@propagate_inbounds SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) = SubString(s, Int(i), Int(j))
2,763,204✔
43
@propagate_inbounds SubString(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, first(r), last(r))
74,742✔
44

45
@propagate_inbounds function SubString(s::SubString, i::Int, j::Int)
389✔
46
    @boundscheck i ≤ j && checkbounds(s, i:j)
138,485✔
47
    SubString(s.string, s.offset+i, s.offset+j)
138,474✔
48
end
49

50
SubString(s::AbstractString) = SubString(s, 1, lastindex(s)::Int)
657✔
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
77✔
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})
68,472✔
67
    parent = s.string
485,248✔
68
    copy = GC.@preserve parent unsafe_string(pointer(parent, s.offset+1), s.ncodeunits)
485,248✔
69
    return copy
485,248✔
70
end
71

72
ncodeunits(s::SubString) = s.ncodeunits
24,318,627✔
73
codeunit(s::SubString) = codeunit(s.string)::CodeunitType
×
74
length(s::SubString) = length(s.string, s.offset+1, s.offset+s.ncodeunits)
159,951✔
75

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

81
function iterate(s::SubString, i::Integer=firstindex(s))
526,478✔
82
    i == ncodeunits(s)+1 && return nothing
6,217,849✔
83
    @boundscheck checkbounds(s, i)
3,111,266✔
84
    y = iterate(s.string, s.offset + i)
5,928,242✔
85
    y === nothing && return nothing
3,111,262✔
86
    c, i = y::Tuple{AbstractChar,Int}
3,111,058✔
87
    return c, i - s.offset
3,111,262✔
88
end
89

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

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

97
function isvalid(s::SubString, i::Integer)
70,695✔
98
    ib = true
233✔
99
    @boundscheck ib = checkbounds(Bool, s, i)
24,880,261✔
100
    @inbounds return ib && isvalid(s.string, s.offset + i)::Bool
24,882,244✔
101
end
102

103
byte_string_classify(s::SubString{String}) =
106✔
104
    ccall(:u8_isvalid, Int32, (Ptr{UInt8}, Int), s, sizeof(s))
105

106
isvalid(::Type{String}, s::SubString{String}) = byte_string_classify(s) ≠ 0
106✔
107
isvalid(s::SubString{String}) = isvalid(String, s)
106✔
108

109
thisind(s::SubString{String}, i::Int) = _thisind_str(s, i)
598,520✔
110
nextind(s::SubString{String}, i::Int) = _nextind_str(s, i)
485,830✔
111

112
function ==(a::Union{String, SubString{String}}, b::Union{String, SubString{String}})
167,705✔
113
    sizeof(a) == sizeof(b) && _memcmp(a, b) == 0
14,892,502✔
114
end
115

116
function cmp(a::SubString{String}, b::SubString{String})
×
117
    c = _memcmp(a, b)
×
118
    return c < 0 ? -1 : c > 0 ? +1 : cmp(sizeof(a), sizeof(b))
×
119
end
120

121
# don't make unnecessary copies when passing substrings to C functions
122
cconvert(::Type{Ptr{UInt8}}, s::SubString{String}) = s
×
123
cconvert(::Type{Ptr{Int8}}, s::SubString{String}) = s
2✔
124

125
function unsafe_convert(::Type{Ptr{R}}, s::SubString{String}) where R<:Union{Int8, UInt8}
189✔
126
    convert(Ptr{R}, pointer(s.string)) + s.offset
460,585✔
127
end
128

129
pointer(x::SubString{String}) = pointer(x.string) + x.offset
2,986,912✔
130
pointer(x::SubString{String}, i::Integer) = pointer(x.string) + x.offset + (i-1)
64✔
131

132
function hash(s::SubString{String}, h::UInt)
×
133
    h += memhash_seed
×
134
    ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h
32,709✔
135
end
136

137
"""
138
    reverse(s::AbstractString) -> AbstractString
139

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

151
# Examples
152
```jldoctest
153
julia> reverse("JuliaLang")
154
"gnaLailuJ"
155
```
156

157
!!! note
158
    The examples below may be rendered differently on different systems.
159
    The comments indicate how they're supposed to be rendered
160

161
Combining characters can lead to surprising results:
162

163
```jldoctest
164
julia> reverse("ax̂e") # hat is above x in the input, above e in the output
165
"êxa"
166

167
julia> using Unicode
168

169
julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes; hat is above x in both in- and output
170
"ex̂a"
171
```
172
"""
173
function reverse(s::Union{String,SubString{String}})::String
1,054✔
174
    # Read characters forwards from `s` and write backwards to `out`
175
    out = _string_n(sizeof(s))
1,054✔
176
    offs = sizeof(s) + 1
1,054✔
177
    for c in s
1,984✔
178
        offs -= ncodeunits(c)
42,481✔
179
        __unsafe_string!(out, c, offs)
40,015✔
180
    end
74,841✔
181
    return out
1,054✔
182
end
183

184
string(a::String)            = String(a)
46,133✔
185
string(a::SubString{String}) = String(a)
95✔
186

187
function Symbol(s::SubString{String})
116✔
188
    return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), s, sizeof(s))
410✔
189
end
190

191
@inline function __unsafe_string!(out, c::Char, offs::Integer) # out is a (new) String (or StringVector)
265✔
192
    x = bswap(reinterpret(UInt32, c))
1,854,108✔
193
    n = ncodeunits(c)
1,969,550✔
194
    GC.@preserve out begin
1,854,108✔
195
        unsafe_store!(pointer(out, offs), x % UInt8)
1,854,108✔
196
        n == 1 && return n
1,854,108✔
197
        x >>= 8
58,233✔
198
        unsafe_store!(pointer(out, offs+1), x % UInt8)
58,233✔
199
        n == 2 && return n
58,233✔
200
        x >>= 8
54,557✔
201
        unsafe_store!(pointer(out, offs+2), x % UInt8)
54,557✔
202
        n == 3 && return n
54,557✔
203
        x >>= 8
2,652✔
204
        unsafe_store!(pointer(out, offs+3), x % UInt8)
2,652✔
205
    end
206
    return n
2,652✔
207
end
208

209
@assume_effects :nothrow @inline function __unsafe_string!(out, s::String, offs::Integer)
×
210
    n = sizeof(s)
12,598,162✔
211
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n)
12,598,162✔
212
    return n
12,598,162✔
213
end
214

215
@inline function __unsafe_string!(out, s::SubString{String}, offs::Integer)
×
216
    n = sizeof(s)
346,996✔
217
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n)
346,996✔
218
    return n
346,996✔
219
end
220

221
@assume_effects :nothrow @inline function __unsafe_string!(out, s::Symbol, offs::Integer)
×
222
    n = sizeof(s)
15,601✔
223
    GC.@preserve s out unsafe_copyto!(pointer(out, offs), unsafe_convert(Ptr{UInt8},s), n)
15,601✔
224
    return n
15,601✔
225
end
226

227
# nothrow needed here because for v in a can't prove the indexing is inbounds.
228
@assume_effects :foldable :nothrow string(a::Union{Char, String, Symbol}...) = _string(a...)
5,736,164✔
229

230
string(a::Union{Char, String, SubString{String}, Symbol}...) = _string(a...)
342,627✔
231

232
function _string(a::Union{Char, String, SubString{String}, Symbol}...)
6,078,539✔
233
    n = 0
×
234
    for v in a
6,078,539✔
235
        # 4 types is too many for automatic Union-splitting, so we split manually
236
        # and allow one specializable call site per concrete type
237
        if v isa Char
1,159,943✔
238
            n += ncodeunits(v)
316,585✔
239
        elseif v isa String
1,127,732✔
240
            n += sizeof(v)
12,598,162✔
241
        elseif v isa SubString{String}
362,581✔
242
            n += sizeof(v)
346,996✔
243
        else
244
            n += sizeof(v::Symbol)
15,601✔
245
        end
246
    end
18,900,793✔
247
    out = _string_n(n)
6,078,539✔
248
    offs = 1
×
249
    for v in a
6,078,539✔
250
        if v isa Char
1,159,943✔
251
            offs += __unsafe_string!(out, v, offs)
264,622✔
252
        elseif v isa String || v isa SubString{String}
1,490,313✔
253
            offs += __unsafe_string!(out, v, offs)
12,945,158✔
254
        else
255
            offs += __unsafe_string!(out, v::Symbol, offs)
15,601✔
256
        end
257
    end
18,900,793✔
258
    return out
6,078,539✔
259
end
260

261
# don't assume effects for general integers since we cannot know their implementation
262
# not nothrow because r<0 throws
263
@assume_effects :foldable repeat(s::String, r::BitInteger) = @invoke repeat(s::String, r::Integer)
960,277✔
264

265
function repeat(s::Union{String, SubString{String}}, r::Integer)
960,258✔
266
    r < 0 && throw(ArgumentError("can't repeat a string $r times"))
960,258✔
267
    r == 0 && return ""
960,250✔
268
    r == 1 && return String(s)
851,613✔
269
    n = sizeof(s)
715,263✔
270
    out = _string_n(n*r)
715,263✔
271
    if n == 1 # common case: repeating a single-byte string
715,263✔
272
        @inbounds b = codeunit(s, 1)
673,773✔
273
        ccall(:memset, Ptr{Cvoid}, (Ptr{UInt8}, Cint, Csize_t), out, b, r)
673,773✔
274
    else
275
        for i = 0:r-1
82,980✔
276
            GC.@preserve s out unsafe_copyto!(pointer(out, i*n+1), pointer(s), n)
2,064,981✔
277
        end
2,064,981✔
278
    end
279
    return out
715,263✔
280
end
281

282
function filter(f, s::Union{String, SubString{String}})
42✔
283
    out = StringVector(sizeof(s))
42✔
284
    offset = 1
3✔
285
    for c in s
84✔
286
        if f(c)
844✔
287
            offset += __unsafe_string!(out, c, offset)
663✔
288
        end
289
    end
1,637✔
290
    resize!(out, offset-1)
84✔
291
    sizehint!(out, offset-1)
42✔
292
    return String(out)
42✔
293
end
294

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