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

JuliaLang / julia / #37483

pending completion
#37483

push

local

web-flow
Type-assert the value type in getindex(::AbstractDict, key) (#49115)

Closes https://github.com/JuliaCollections/OrderedCollections.jl/issues/101

2 of 2 new or added lines in 1 file covered. (100.0%)

72523 of 82770 relevant lines covered (87.62%)

34287845.75 hits per line

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

90.45
/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
6,170,232✔
31
        i ≤ j || return new(s, 0, 0)
6,970,324✔
32
        @boundscheck begin
5,370,129✔
33
            checkbounds(s, i:j)
5,370,146✔
34
            @inbounds isvalid(s, i) || string_index_err(s, i)
5,370,110✔
35
            @inbounds isvalid(s, j) || string_index_err(s, j)
5,370,116✔
36
        end
37
        return new(s, i-1, nextind(s,j)-i)
5,370,096✔
38
    end
39
end
40

41
@propagate_inbounds SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j)
6,170,278✔
42
@propagate_inbounds SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) = SubString(s, Int(i), Int(j))
2,890,491✔
43
@propagate_inbounds SubString(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, first(r), last(r))
74,048✔
44

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

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

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

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

81
function iterate(s::SubString, i::Integer=firstindex(s))
526,478✔
82
    i == ncodeunits(s)+1 && return nothing
6,223,696✔
83
    @boundscheck checkbounds(s, i)
3,134,604✔
84
    y = iterate(s.string, s.offset + i)
5,974,961✔
85
    y === nothing && return nothing
3,134,600✔
86
    c, i = y::Tuple{AbstractChar,Int}
3,134,396✔
87
    return c, i - s.offset
3,134,600✔
88
end
89

90
function getindex(s::SubString, i::Integer)
500,460✔
91
    @boundscheck checkbounds(s, i)
500,462✔
92
    @inbounds return getindex(s.string, s.offset + i)
500,458✔
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,946,321✔
100
    @inbounds return ib && isvalid(s.string, s.offset + i)::Bool
24,948,304✔
101
end
102

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

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

109
thisind(s::SubString{String}, i::Int) = _thisind_str(s, i)
597,040✔
110
nextind(s::SubString{String}, i::Int) = _nextind_str(s, i)
514,407✔
111

112
function ==(a::Union{String, SubString{String}}, b::Union{String, SubString{String}})
8,091✔
113
    sizeof(a) == sizeof(b) && _memcmp(a, b) == 0
15,337,010✔
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
364,056✔
127
end
128

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

132
function hash(s::SubString{String}, h::UInt)
284✔
133
    h += memhash_seed
284✔
134
    ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h
339✔
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)
45,977✔
185
string(a::SubString{String}) = String(a)
93✔
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)
263✔
192
    x = bswap(reinterpret(UInt32, c))
1,778,813✔
193
    n = ncodeunits(c)
1,894,269✔
194
    GC.@preserve out begin
1,778,813✔
195
        unsafe_store!(pointer(out, offs), x % UInt8)
1,778,813✔
196
        n == 1 && return n
1,778,813✔
197
        x >>= 8
58,237✔
198
        unsafe_store!(pointer(out, offs+1), x % UInt8)
58,237✔
199
        n == 2 && return n
58,237✔
200
        x >>= 8
54,566✔
201
        unsafe_store!(pointer(out, offs+2), x % UInt8)
54,566✔
202
        n == 3 && return n
54,566✔
203
        x >>= 8
2,653✔
204
        unsafe_store!(pointer(out, offs+3), x % UInt8)
2,653✔
205
    end
206
    return n
2,653✔
207
end
208

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

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

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

227
# nothrow needed here because for v in a can't prove the indexing is inbounds.
228
@assume_effects :foldable :nothrow function string(a::Union{Char, String, Symbol}...)
145,093✔
229
    _string(a...)
5,834,238✔
230
end
231

232
function string(a::Union{Char, String, SubString{String}, Symbol}...)
8,507✔
233
    _string(a...)
316,263✔
234
end
235

236
function _string(a::Union{Char, String, SubString{String}, Symbol}...)
6,150,249✔
237
    n = 0
×
238
    for v in a
6,150,249✔
239
        # 4 types is too many for automatic Union-splitting, so we split manually
240
        # and allow one specializable call site per concrete type
241
        if v isa Char
1,054,264✔
242
            n += ncodeunits(v)
250,711✔
243
        elseif v isa String
1,020,659✔
244
            n += sizeof(v)
12,697,872✔
245
        elseif v isa SubString{String}
336,874✔
246
            n += sizeof(v)
321,179✔
247
        else
248
            n += sizeof(v::Symbol)
15,711✔
249
        end
250
    end
19,115,211✔
251
    out = _string_n(n)
6,150,249✔
252
    offs = 1
×
253
    for v in a
6,150,249✔
254
        if v isa Char
1,054,264✔
255
            offs += __unsafe_string!(out, v, offs)
198,738✔
256
        elseif v isa String || v isa SubString{String}
1,357,533✔
257
            offs += __unsafe_string!(out, v, offs)
13,019,051✔
258
        else
259
            offs += __unsafe_string!(out, v::Symbol, offs)
15,711✔
260
        end
261
    end
19,115,211✔
262
    return out
6,150,249✔
263
end
264

265
# don't assume effects for general integers since we cannot know their implementation
266
# not nothrow because r<0 throws
267
@assume_effects :foldable function repeat(s::String, r::BitInteger)
212,176✔
268
    @invoke repeat(s, r::Integer)
959,403✔
269
end
270

271
function repeat(s::Union{String, SubString{String}}, r::Integer)
959,384✔
272
    r < 0 && throw(ArgumentError("can't repeat a string $r times"))
959,384✔
273
    r == 0 && return ""
959,376✔
274
    r == 1 && return String(s)
850,354✔
275
    n = sizeof(s)
713,863✔
276
    out = _string_n(n*r)
713,863✔
277
    if n == 1 # common case: repeating a single-byte string
713,863✔
278
        @inbounds b = codeunit(s, 1)
672,373✔
279
        ccall(:memset, Ptr{Cvoid}, (Ptr{UInt8}, Cint, Csize_t), out, b, r)
672,373✔
280
    else
281
        for i = 0:r-1
82,980✔
282
            GC.@preserve s out unsafe_copyto!(pointer(out, i*n+1), pointer(s), n)
2,064,981✔
283
        end
2,064,981✔
284
    end
285
    return out
713,863✔
286
end
287

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

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