Skip to content

Commit feb1f68

Browse files
bounds checks on string length(s, i, j)
1 parent 937c3ad commit feb1f68

File tree

5 files changed

+40
-33
lines changed

5 files changed

+40
-33
lines changed

base/strings/basic.jl

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -313,15 +313,17 @@ isless(a::Symbol, b::Symbol) = cmp(a, b) < 0
313313
## character index arithmetic ##
314314

315315
"""
316-
length(s::AbstractString, lo::Integer=1, hi::Integer=ncodeunits(s)) -> Integer
316+
length(s::AbstractString) -> Int
317+
length(s::AbstractString, i::Integer, j::Integer) -> Int
317318
318-
The number of characters in string `s` from indices `lo` through `hi`. This is
319-
computed as the number of code unit indices from `lo` to `hi` which are valid
319+
The number of characters in string `s` from indices `i` through `j`. This is
320+
computed as the number of code unit indices from `i` to `j` which are valid
320321
character indices. Without only a single string argument, this computes the
321-
number of characters in the entire string. With `lo` and `hi` arguments it computes
322-
the number of indices between `lo` and `hi` inclusive that are valid indices in
323-
the string `s`. Note that the trailing character may include code units past `hi`
324-
and still be counted.
322+
number of characters in the entire string. With `i` and `j` arguments it
323+
computes the number of indices between `i` and `j` inclusive that are valid
324+
indices in the string `s`. In addition to in-bounds values, `i` may take the
325+
out-of-bounds value `ncodeunits(s) + 1` and `j` may take the out-of-bounds
326+
value `0`.
325327
326328
See also: [`isvalid`](@ref), [`ncodeunits`](@ref), [`endof`](@ref),
327329
[`thisind`](@ref), [`nextind`](@ref), [`prevind`](@ref)
@@ -332,18 +334,23 @@ julia> length("jμΛIα")
332334
5
333335
```
334336
"""
335-
function length(s::AbstractString, lo::Integer=1, hi::Integer=ncodeunits(s))
336-
lo hi || return 0
337-
z = ncodeunits(s)
338-
a = Int(max(1, min(z, lo)))
339-
b = Int(min(z, max(1, hi)))
340-
n = a - b
341-
for i = a:b
342-
n += isvalid(s, i)
337+
length(s::AbstractString) = @inbounds return length(s, 1, ncodeunits(s))
338+
339+
function length(s::AbstractString, i::Int, j::Int)
340+
@boundscheck begin
341+
0 < i ncodeunits(s)+1 || throw(BoundsError(s, i))
342+
0  j < ncodeunits(s)+1 || throw(BoundsError(s, j))
343343
end
344-
return n + hi - lo
344+
n = 0
345+
for k = i:j
346+
@inbounds n += isvalid(s, k)
347+
end
348+
return n
345349
end
346350

351+
@propagate_inbounds length(s::AbstractString, i::Integer, j::Integer) =
352+
length(s, Int(i), Int(j))
353+
347354
"""
348355
thisind(s::AbstractString, i::Integer) -> Int
349356

base/strings/string.jl

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -232,18 +232,16 @@ function getindex(s::String, r::UnitRange{Int})
232232
return ss
233233
end
234234

235-
function length(s::String, lo::Int, hi::Int)
236-
i, n = lo, hi
237-
c = max(0, hi - lo + 1)
235+
function length(s::String, i::Int, j::Int)
238236
@boundscheck begin
239-
z = ncodeunits(s)
240-
i = Int(max(1, min(z, lo)))
241-
n = Int(min(z, max(1, hi)))
237+
0 < i ncodeunits(s)+1 || throw(BoundsError(s, i))
238+
0  j < ncodeunits(s)+1 || throw(BoundsError(s, j))
242239
end
243-
i < n || return c
244-
@inbounds i, j = thisind(s, i), i
245-
c -= i < j
246-
_length(s, i, n, c)
240+
j < i && return 0
241+
c = j - i + 1
242+
@inbounds i, k = thisind(s, i), i
243+
c -= i < k
244+
_length(s, i, j, c)
247245
end
248246

249247
length(s::String) = _length(s, 1, ncodeunits(s), ncodeunits(s))

test/lineedit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function new_state()
1717
end
1818

1919
charseek(buf, i) = seek(buf, nextind(content(buf), 0, i+1)-1)
20-
charpos(buf, pos=position(buf)) = length(content(buf), 1, pos+1)-1
20+
charpos(buf, pos=position(buf)) = length(content(buf), 1, pos)
2121

2222
function transform!(f, s, i = -1) # i is char-based (not bytes) buffer position
2323
buf = buffer(s)

test/strings/basic.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,13 @@ end
9999
end
100100

101101
@testset "issue #7248" begin
102-
@test length("hello", 1, -1) == 0
102+
@test_throws BoundsError length("hello", 1, -1) == 0
103103
@test prevind("hello", 0, 1) == -1
104-
@test length("hellø", 1, -1) == 0
104+
@test_throws BoundsError length("hellø", 1, -1) == 0
105105
@test prevind("hellø", 0, 1) == -1
106-
@test length("hello", 1, 10) == 10
106+
@test_throws BoundsError length("hello", 1, 10) == 10
107107
@test nextind("hello", 0, 10) == 10
108-
@test length("hellø", 1, 10) == 9
108+
@test_throws BoundsError length("hellø", 1, 10) == 9
109109
@test nextind("hellø", 0, 10) == 11
110110
@test_throws BoundsError checkbounds("hello", 0)
111111
@test_throws BoundsError checkbounds("hello", 6)

test/strings/types.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,10 @@ let s = "Σx + βz - 2"
205205
end
206206

207207
let ss = SubString("hello", 1, 5)
208-
@test length(ss, 1, -1) == 0
209-
@test length(ss, 1, 10) == 10
208+
@test length(ss, 1, 0) == 0
209+
@test_throws BoundsError length(ss, 1, -1) == 0
210+
@test_throws BoundsError length(ss, 1, 6)
211+
@test_throws BoundsError length(ss, 1, 10)
210212
@test prevind(ss, 0, 1) == -1
211213
@test nextind(ss, 0, 10) == 10
212214
end

0 commit comments

Comments
 (0)