Skip to content

Commit

Permalink
Merge branch 'main' into resolve-ambiguities-without-autoformat
Browse files Browse the repository at this point in the history
Required manual merge of runtests, as the newly included
BufferedVectors' tests were added there, but must now be in the
SentinelArraysTests module.
  • Loading branch information
KeithWM committed Jun 3, 2023
2 parents 9ae27d9 + 6ec2e5b commit b6af311
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "SentinelArrays"
uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c"
authors = ["Jacob Quinn <[email protected]>"]
version = "1.3.18"
version = "1.4.0"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
63 changes: 63 additions & 0 deletions src/BufferedVectors.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module BufferedVectors

export BufferedVector, skip_element!, shiftleft!, unsafe_push!
mutable struct BufferedVector{T} <: AbstractArray{T,1}
elements::Vector{T}
occupied::Int
end
BufferedVector{T}() where {T} = BufferedVector(T[], 0)
BufferedVector(v::Vector{T}) where {T} = BufferedVector{T}(v, length(v))

Base.length(x::BufferedVector) = x.occupied
Base.size(x::BufferedVector) = (x.occupied,)
Base.@propagate_inbounds function Base.last(x::BufferedVector)
return x.elements[x.occupied]
end
@inline function Base.first(x::BufferedVector)
@boundscheck((x.occupied > 0) || Base.throw_boundserror(x, 1))
return @inbounds(x.elements[1])
end
@inline function Base.getindex(x::BufferedVector, i::Int)
@boundscheck((x.occupied >= i && i > 0) || Base.throw_boundserror(x, i))
return @inbounds(x.elements[i])
end
Base.ndims(x::BufferedVector) = 1
Base.empty!(x::BufferedVector) = (x.occupied = 0; x)
Base.isempty(x::BufferedVector) = x.occupied == 0
Base.IndexStyle(::BufferedVector) = Base.IndexLinear()
Base.IteratorSize(::BufferedVector) = Base.HasLength()
Base.IteratorEltype(::BufferedVector) = Base.HasEltype()
Base.collect(x::BufferedVector{T}) where {T} = length(x) > 0 ? @inbounds(x.elements[1:length(x)]) : T[]
Base.eltype(::BufferedVector{T}) where T = T
@inline Base.push!(buffer::T, x::S) where {T,S} = push!(buffer, convert(T, x))
@inline function Base.push!(buffer::BufferedVector{T}, x::T) where {T}
if length(buffer.elements) == buffer.occupied
Base._growend!(buffer.elements, _grow_by(T))
end
buffer.occupied += 1
@inbounds buffer.elements[buffer.occupied] = x
end
_grow_by(::Type) = 16
_grow_by(::Type{T}) where {T<:Union{Bool,UInt8}} = 64

@inline unsafe_push!(buffer::BufferedVector{T}, x::S) where {T,S} = unsafe_push!(buffer, convert(T, x))
@inline function unsafe_push!(buffer::BufferedVector{T}, x::T) where {T}
buffer.occupied += 1
@inbounds buffer.elements[buffer.occupied] = x
end
Base.ensureroom(x::BufferedVector, n) = ((length(x.elements) < n) && Base._growend!(x.elements, n - length(x.elements)); return nothing)
skip_element!(x::BufferedVector) = x.occupied += 1
function shiftleft!(x::BufferedVector, n)
n < 0 && throw(ArgumentError("n must be >= 0"))
n == 0 && return
len = length(x)
if n >= len
empty!(x)
return nothing
end
unsafe_copyto!(x.elements, 1, x.elements, 1 + n, len - n + 1)
x.occupied -= n
return nothing
end

end # module
3 changes: 3 additions & 0 deletions src/SentinelArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module SentinelArrays
using Dates, Random

export SentinelArray, SentinelMatrix, SentinelVector, SentinelCollisionError, ChainedVector, MissingVector
export BufferedVector, skip_element!, shiftleft!, unsafe_push!

"""
SentinelArray(A::AbstractArray, sentinel, value)
Expand Down Expand Up @@ -447,6 +448,8 @@ end

include("chainedvector.jl")
include("missingvector.jl")
include("BufferedVectors.jl")
using .BufferedVectors

include("precompile.jl")
_precompile_()
Expand Down
95 changes: 95 additions & 0 deletions test/BufferedVectors.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Test
using SentinelArrays
using SentinelArrays: BufferedVectors


@noinline unsafe(bv, i) = @inbounds bv[i]

@testset "BufferedVectors" begin
bv = BufferedVector{Int}()
@test eltype(bv) == Int
@test bv == Int[]
@test bv.elements == Int[]
@test bv.occupied == 0
@test length(bv) == 0
@test length(bv.elements) == 0
@test isempty(bv)
@test_throws BoundsError bv[1]
@test_throws BoundsError first(bv)
@test_throws BoundsError last(bv)
@test collect(bv) == Int[]

Base.ensureroom(bv, 2)
@test length(bv.elements) == 2
@test bv.occupied == 0
@test length(bv) == 0
@test_throws BoundsError first(bv)

bv = BufferedVector{Int}([1,2,3], 3)
@test bv == [1,2,3]
@test bv.elements[1:3] == [1,2,3]
@test bv.occupied == 3
@test length(bv) == 3
@test size(bv) == (3,)
@test collect(bv) == [1,2,3]
@test first(bv) == 1
@test bv[1] == 1
@test bv[2] == 2
@test bv[3] == 3
@test last(bv) == 3
@test_throws BoundsError bv[0]
@test_throws BoundsError bv[4]
@test !isempty(bv)

empty!(bv)
@test bv == Int[]
@test isempty(bv)

push!(bv, 1)
@test bv == [1]
@assert length(bv) == 1
skip_element!(bv)
@test length(bv) == 2

bv = BufferedVector{Int}()
push!(bv, 1)
@test length(bv.elements) == BufferedVectors._grow_by(Int)
@assert BufferedVectors._grow_by(Int) > 0
@test_throws BoundsError bv[2]
unsafe_push!(bv, 42)
@test bv == [1, 42]

code = """
using SentinelArrays
using Test
bv = BufferedVector{Int}(Int[1, 2], 0)
# `unsafe` is actually safe here, it's just accessing undef values
@noinline unsafe(bv, i) = @inbounds bv[i]
@test unsafe(bv, 2) == bv.elements[2]
"""
cmd = `$(Base.julia_cmd()) --startup-file=no --project=. --check-bounds=auto -e $code`
@test success(pipeline(cmd; stdout=stdout, stderr=stderr))

bv = BufferedVector{Int32}()
push!(bv, Int32(1))
@test length(bv.elements) == BufferedVectors._grow_by(Int32)
@assert BufferedVectors._grow_by(Int32) > 0

bv = BufferedVector{Int}([1,2,3], 3)
shiftleft!(bv, 1)
@test bv == [2,3]

bv = BufferedVector{Int}([1,2,3], 3)
shiftleft!(bv, 2)
@test bv == [3]

bv = BufferedVector{Int}([1,2,3], 3)
shiftleft!(bv, 3)
@test bv == []

bv = BufferedVector{Int}([1,2,3], 3)
shiftleft!(bv, 4)
@test bv == []

@test_throws ArgumentError shiftleft!(bv, -1)
end
1 change: 1 addition & 0 deletions test/SentinelArrayTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ using SentinelArrays
include("sentinelarrays.jl")
include("missingvector.jl")
include("chainedvector.jl")
include("BufferedVectors.jl")

end
4 changes: 2 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using ReTest
susing ReTest

using SentinelArrays

include("SentinelArrayTests.jl")

retest(SentinelArrays, SentinelArrayTests)
retest(SentinelArrays, SentinelArrayTests)

0 comments on commit b6af311

Please sign in to comment.