diff --git a/benchmark/bench_collectors.jl b/benchmark/bench_collectors.jl index b74fb9af..b5815fd4 100644 --- a/benchmark/bench_collectors.jl +++ b/benchmark/bench_collectors.jl @@ -1,6 +1,6 @@ module BenchCollectors -using BangBang: collector, finish!, push!! +using BangBang: Empty, collector, finish!, push!! using BenchmarkTools: @benchmarkable, BenchmarkGroup using SplittablesBase: amount @@ -55,11 +55,9 @@ end end end -const vec0 = Union{}[] +collect_collector(src) = finish!(_foldl(push!!, collector(), src)) -collect_collector(src) = finish!(_foldl(push!!, collector(vec0), src)) - -collect_vector(src) = _foldl(push!!, vec0, src) +collect_vector(src) = _foldl(push!!, Empty(Vector), src) collect_collector_nonexpanding(src) = finish!(_foldl(push!!, collector(resize!(Union{}[], amount(src)), Val(true)), src)) diff --git a/src/NoBang/base.jl b/src/NoBang/base.jl index eee2179d..505093ef 100644 --- a/src/NoBang/base.jl +++ b/src/NoBang/base.jl @@ -147,6 +147,8 @@ function _setindex(d0::AbstractDict, v, k) return d end +resize(xs::AbstractVector, n::Integer) = similar(xs, (n,)) + setproperty(value, name, x) = setproperties(value, NamedTuple{(name,)}((x,))) materialize(::Any, x) = Broadcast.materialize(x) diff --git a/src/NoBang/emptycontainers.jl b/src/NoBang/emptycontainers.jl index 77032d85..4f4d4f1c 100644 --- a/src/NoBang/emptycontainers.jl +++ b/src/NoBang/emptycontainers.jl @@ -73,6 +73,7 @@ append(::Empty{T}, x) where T = T(x) append(e::Empty, ::Empty) = e _empty(x::Empty) = x +resize(::Empty{T}, n::Integer) where {T <: AbstractVector} = similar(T, (n,)) _union(::Empty{T}, x) where {T} = unique!!(T(x)) _union(e::Empty, ::Empty) = e @@ -83,5 +84,18 @@ Base.get(::Empty, _, default) = default Base.IteratorSize(::Type{<:Empty}) = Base.HasLength() Base.IteratorEltype(::Type{<:Empty}) = Base.HasEltype() Base.eltype(::Type{<:Empty}) = Union{} +Base.eltype(::Type{<:Empty{<:AbstractVector{T}}}) where {T} = T Base.length(::Empty) = 0 Base.iterate(::Empty) = nothing + +Base.firstindex(::Empty{<:Vector}) = 1 +Base.lastindex(::Empty{<:Vector}) = 0 +Base.similar(e::Empty) = similar(e, eltype(e), (0,)) +Base.similar(e::Empty, dims::Int...) = similar(e, eltype(e), dims) +Base.similar(e::Empty, dims::Tuple{Vararg{Int}}) = similar(e, eltype(e), dims) +Base.similar(e::Empty, T::Type) = similar(e, T, (0,)) +Base.similar(e::Empty, T::Type, dims::Int...) = similar(e, T, dims) +Base.similar(::Empty{<:Vector}, T::Type, dims::Tuple{Vararg{Int}}) = + similar(Vector{T}, dims) +Base.similar(::Empty{Vector{T}}, dims::Tuple{Vararg{Int}}) where {T} = + similar(Vector{T}, dims) diff --git a/src/base.jl b/src/base.jl index 43d804dc..79cbecf1 100644 --- a/src/base.jl +++ b/src/base.jl @@ -375,6 +375,15 @@ possible(::typeof(_setindex!), ::C, ::V, ::K) where {C <: AbstractDict, V, K} = promote_type(keytype(C), K) <: keytype(C) && promote_type(valtype(C), V) <: valtype(C) +""" + resize!!(vector::AbstractVector, n::Integer) -> vector′ +""" +resize!!(xs::Union{AbstractVector,Empty{<:AbstractVector}}, n::Integer) = + implements(resize!, xs) ? resize!(xs, n) : NoBang.resize(xs, n) + +pure(::typeof(resize!)) = NoBang.resize +_asbb(::typeof(resize!)) = resize!! + """ setproperties!!(value, patch::NamedTuple) setproperties!!(value; patch...) diff --git a/src/collectors.jl b/src/collectors.jl index bb594759..ebf796c9 100644 --- a/src/collectors.jl +++ b/src/collectors.jl @@ -42,7 +42,7 @@ julia> finish!(append!!(collector(Vector{Float64}(undef, 10), Val(true)), [1, 2, collector collector(data::AbstractArray, ::Val{false} = Val(false)) = SafeCollector(data) collector(data::AbstractArray, ::Val{true}) = UnsafeCollector(data) -collector(ElType::Type = Union{}) = collector(ElType[]) +collector(ElType::Type = Union{}) = SafeCollector(Empty(Vector{ElType}), 1) abstract type AbstractCollector end @@ -79,10 +79,12 @@ _next_length(data, src) = max(4, length(data) + length(src), length(data) * 2) i = c.i if possible(_append!, data, src) if c isa UnsafeCollector + data′ = data elseif i + length(src) - 1 > lastindex(data) - resize!(data, _next_length(data, src)) + data′ = resize!!(data, _next_length(data, src)) + else + data′ = data end - data′ = data else T = promote_type(eltype(data), eltype(src)) if i + length(src) - 1 > lastindex(data) @@ -109,10 +111,13 @@ end ::Base.IteratorEltype, ) if c isa UnsafeCollector + data′′ = c.data elseif c.i + length(src) - 1 > lastindex(c.data) - resize!(c.data, _next_length(c.data, src)) + data′′ = resize!!(c.data, _next_length(c.data, src)) + else + data′′ = c.data end - data, i = foldl(src, init = (c.data, c.i)) do (data, i), v + data, i = foldl(src, init = (data′′, c.i)) do (data, i), v Base.@_inline_meta T = promote_type(eltype(data), eltype(v)) if T <: eltype(data) @@ -145,7 +150,7 @@ Extract the `data` collected in the collector `c`. See [`collector`](@ref). """ -finish!(c::AbstractCollector) = resize!(c.data, c.i - firstindex(c.data)) +finish!(c::AbstractCollector) = resize!!(c.data, c.i - firstindex(c.data)) # Final length is `(c.i - 1) - (firstindex(c.data) - 1)` where the # first `- 1` is because `c.i` is the index for the next element and # the second `- 1` is for turning the index to offset. diff --git a/src/core.jl b/src/core.jl index 4d48e041..7cc4d237 100644 --- a/src/core.jl +++ b/src/core.jl @@ -60,6 +60,12 @@ implements(::Mutator, ::Type{<:ImmutableContainer}) = false implements(::Mutator, ::Type{<:MaybeMutableContainer}) = true implements(::Mutator, ::Type{<:AbstractString}) = false +implements(::typeof(resize!), ::Type{<:AbstractVector}) = true +implements( + ::typeof(resize!), + ::Type{<:Union{SubArray{<:Any,1},Base.ReshapedArray{<:Any,1}}}, +) = false + Base.@pure ismutablestruct(T::DataType) = T.mutable implements(::typeof(setproperty!), T::DataType) = ismutablestruct(T) implements(::typeof(setproperty!), ::Type{<:NamedTuple}) = false diff --git a/test/test_empty_proxy.jl b/test/test_empty_proxy.jl index c5e79e5c..fc607542 100644 --- a/test/test_empty_proxy.jl +++ b/test/test_empty_proxy.jl @@ -2,10 +2,36 @@ module TestEmptyProxy include("preamble.jl") +==ₜₗ(_, _) = false +==ₜₗ(x::T, y::T) where {T} = length(x) == length(y) + @testset begin @test collect(Empty(Vector)) == Union{}[] @test collect(Empty(Dict)) == Union{}[] @test collect(Empty(Set)) == Union{}[] end +@testset "similar" begin + @test similar(Empty(Vector)) ==ₜ Union{}[] + @test similar(Empty(Vector{Union{}})) ==ₜ Union{}[] + @test similar(Empty(Vector{Int})) ==ₜ Int[] + @test similar(Empty(Vector), Int) ==ₜ Int[] + @test similar(Empty(Vector{Union{}}), Int) ==ₜ Int[] + @test similar(Empty(Vector{Int}), Symbol) ==ₜ Symbol[] + + @test similar(Empty(Vector), 3) ==ₜₗ resize!(Union{}[], 3) + @test similar(Empty(Vector{Union{}}), 3) ==ₜₗ resize!(Union{}[], 3) + @test similar(Empty(Vector{Int}), 3) ==ₜₗ resize!(Int[], 3) + @test similar(Empty(Vector), Int, 3) ==ₜₗ resize!(Int[], 3) + @test similar(Empty(Vector{Union{}}), Int, 3) ==ₜₗ resize!(Int[], 3) + @test similar(Empty(Vector{Int}), Symbol, 3) ==ₜₗ resize!(Symbol[], 3) + + @test similar(Empty(Vector), (3,)) ==ₜₗ resize!(Union{}[], 3) + @test similar(Empty(Vector{Union{}}), (3,)) ==ₜₗ resize!(Union{}[], 3) + @test similar(Empty(Vector{Int}), (3,)) ==ₜₗ resize!(Int[], 3) + @test similar(Empty(Vector), Int, (3,)) ==ₜₗ resize!(Int[], 3) + @test similar(Empty(Vector{Union{}}), Int, (3,)) ==ₜₗ resize!(Int[], 3) + @test similar(Empty(Vector{Int}), Symbol, (3,)) ==ₜₗ resize!(Symbol[], 3) +end + end # module