@@ -36,18 +36,67 @@ struct SubString{T<:AbstractString} <: AbstractString
3636 end
3737 return new (s, i- 1 , nextind (s,j)- i)
3838 end
39- function SubString {T} (s:: T , i:: Int , j:: Int , :: Val{:noshift} ) where T<: AbstractString
40- @boundscheck if ! (i == j == 0 )
41- si, sj = i + 1 , prevind (s, j + i + 1 )
42- @inbounds isvalid (s, si) || string_index_err (s, si)
43- @inbounds isvalid (s, sj) || string_index_err (s, sj)
44- end
45- new (s, i, j)
39+ # We don't expose this, because the exposed constructor needs to avoid constructing
40+ # a SubString{SubString{T}} when passed a substring.
41+ global function _unsafe_substring (s:: T , offset:: Int , ncodeunits:: Int ) where {T <: AbstractString }
42+ new {T} (s, offset, ncodeunits)
43+ end
44+ end
45+
46+ function check_codeunit_bounds (s:: AbstractString , first_index:: Int , n_codeunits:: Int )
47+ last_index = first_index + n_codeunits - 1
48+ bad_index = if first_index < 1
49+ first_index
50+ elseif last_index > ncodeunits (s)
51+ last_index
52+ else
53+ return nothing
4654 end
55+ throw (BoundsError (s, bad_index))
56+ end
57+
58+ """
59+ unsafe_substring(s::AbstractString, first_index::Int, n_codeunits::Int)::SubString{typeof(s)}
60+ unsafe_substring(s::SubString{S}, first_index::Int, n_codeunits::Int)::SubString{S}
61+
62+ Create a substring of `s` spanning the codeunits `first_index:(first_index + n_codeunits - 1)`.
63+
64+ If `first_index` < 1, or `first_index + n_codeunits - 1 > ncodeunits(s)`, throw a `BoundsError`.
65+
66+ This function does check bounds, but does not validate that the arguments corresponds to valid
67+ start and end indices in `s`, and so the resulting substring may contain truncated characters.
68+ The presence of truncated characters is safe and well-defined for `String` and `SubString{String}`,
69+ but may not be permitted for custom subtypes of `AbstractString`.
70+
71+ # Examples
72+ ```jldoctest
73+ julia> s = "Hello, Bjørn!";
74+
75+ julia> ss = unsafe_substring(s, 3, 10)
76+ "lo, Bjørn"
77+
78+ julia> typeof(ss)
79+ SubString{String}
80+
81+ julia> ss2 = unsafe_substring(ss, 2, 6)
82+ "o, Bj\\ xc3"
83+
84+ julia> typeof(ss2)
85+ SubString{String}
86+ ```
87+ """
88+ function unsafe_substring (s:: AbstractString , first_index:: Int , n_codeunits:: Int )
89+ @boundscheck @inline checkbounds (codeunits (s), first_index: (first_index + n_codeunits - 1 ))
90+ return _unsafe_substring (s, first_index - 1 , n_codeunits)
91+ end
92+
93+ function unsafe_substring (s:: SubString , first_index:: Int , n_codeunits:: Int )
94+ @boundscheck @inline check_codeunit_bounds (s, first_index, n_codeunits)
95+ string = s. string
96+ return _unsafe_substring (string, first_index + s. offset - 1 , n_codeunits)
4797end
4898
4999@propagate_inbounds SubString (s:: T , i:: Int , j:: Int ) where {T<: AbstractString } = SubString {T} (s, i, j)
50- @propagate_inbounds SubString (s:: T , i:: Int , j:: Int , v:: Val{:noshift} ) where {T<: AbstractString } = SubString {T} (s, i, j, v)
51100@propagate_inbounds SubString (s:: AbstractString , i:: Integer , j:: Integer = lastindex (s)) = SubString (s, Int (i):: Int , Int (j):: Int )
52101@propagate_inbounds SubString (s:: AbstractString , r:: AbstractUnitRange{<:Integer} ) = SubString (s, first (r), last (r))
53102
56105 SubString (s. string, s. offset+ i, s. offset+ j)
57106end
58107
59- SubString (s:: AbstractString ) = SubString (s, 1 , lastindex (s):: Int )
60- SubString {T} (s:: T ) where {T<: AbstractString } = SubString {T} (s, 1 , lastindex (s):: Int )
108+ SubString (s:: AbstractString ) = @inbounds unsafe_substring (s, 1 , Int (ncodeunits (s)):: Int )
109+ SubString {T} (s:: T ) where {T<: AbstractString } = SubString (s)
110+ SubString (s:: SubString ) = s
61111
62112@propagate_inbounds view (s:: AbstractString , r:: AbstractUnitRange{<:Integer} ) = SubString (s, r)
63113@propagate_inbounds maybeview (s:: AbstractString , r:: AbstractUnitRange{<:Integer} ) = view (s, r)
0 commit comments