diff --git a/src/array/datavaluevector.jl b/src/array/datavaluevector.jl index c8fbe3a..1e32a42 100644 --- a/src/array/datavaluevector.jl +++ b/src/array/datavaluevector.jl @@ -52,7 +52,7 @@ null then this method inserts a null entry at the beginning of `X`. Returns `X`. """ function Base.pushfirst!(X::DataValueVector, v::DataValue) if isna(v) - ccall(:jl_array_grow_beg, Nothing, (Any, UInt), X.values, 1) + Base._growbeg!(X.values, 1) pushfirst!(X.isna, true) else pushfirst!(X.values, v.value) @@ -200,8 +200,8 @@ collection to `prepend!`. function Base.prepend!(X::DataValueVector, items::AbstractVector) old_length = length(X) nitems = length(items) - ccall(:jl_array_grow_beg, Nothing, (Any, UInt), X.values, nitems) - ccall(:jl_array_grow_beg, Nothing, (Any, UInt), X.isna, nitems) + Base._growbeg!(X.values, nitems) + Base._growbeg!(X.isna, nitems) if X === items copyto!(X, 1, items, nitems + 1, nitems) else @@ -210,6 +210,10 @@ function Base.prepend!(X::DataValueVector, items::AbstractVector) return X end +# `pushfirst!(X, a, b, c)` reaches us as `prepend!(X, (a, b, c))`. Base's fallback for that +# only handles `Vector`, so collect any other iterable into one first. +Base.prepend!(X::DataValueVector, items) = prepend!(X, [x for x in items]) + """ sizehint!(X::DataValueVector, newsz::Integer) @@ -246,11 +250,11 @@ end """ reverse!(X::DataValueVector, [s], [n]) -Modify `X` by reversing the first `n` elements starting at index `s` -(inclusive). If unspecified, `s` and `n` will default to `1` and `length(X)`, +Modify `X` by reversing the elements from index `s` to index `n` (both +inclusive). If unspecified, `s` and `n` will default to `1` and `length(X)`, respectively. """ -function Base.reverse!(X::DataValueVector, s=1, n=length(X)) +function Base.reverse!(X::DataValueVector, s::Integer=1, n::Integer=length(X)) if isbitstype(eltype(X)) || !any(isna, X) reverse!(X.values, s, n) reverse!(X.isna, s, n) @@ -276,11 +280,11 @@ end """ reverse(X::DataValueVector, [s], [n]) -Return a copy of `X` with the first `n` elements starting at index `s` -(inclusive) reversed. If unspecified, `s` and `n` will default to `1` and +Return a copy of `X` with the elements from index `s` to index `n` (both +inclusive) reversed. If unspecified, `s` and `n` will default to `1` and `length(X)`, respectively. """ -function Base.reverse(X::DataValueVector, s=1, n=length(X)) +function Base.reverse(X::DataValueVector, s::Integer=1, n::Integer=length(X)) return reverse!(copy(X), s, n) end diff --git a/src/scalar/core.jl b/src/scalar/core.jl index 30c47f9..acb4305 100644 --- a/src/scalar/core.jl +++ b/src/scalar/core.jl @@ -44,6 +44,10 @@ Base.convert(::Type{Missing}, ::DataValue{Union{}}) = missing Base.promote_rule(::Type{DataValue{S}}, ::Type{T}) where {S,T} = DataValue{promote_type(S, T)} Base.promote_rule(::Type{DataValue{T}}, ::Type{Any}) where {T} = DataValue{Any} +# Base promotes anything against `Any` to `Any` (see the `>:Union{Missing,Nothing}` +# rule in base/missing.jl). Without the mirror image of the rule above, the two +# directions disagree and `promote_type(Any, DataValue{T})` recurses forever. +Base.promote_rule(::Type{Any}, ::Type{DataValue{T}}) where {T} = DataValue{Any} Base.promote_rule(::Type{DataValue{Union{}}}, ::Type{Any}) = DataValue{Any} Base.promote_rule(::Type{Any}, ::Type{DataValue{Union{}}}) = DataValue{Any} Base.promote_rule(::Type{DataValue{S}}, ::Type{DataValue{T}}) where {S,T} = DataValue{promote_type(S, T)} diff --git a/test/array/test_reduce.jl b/test/array/test_reduce.jl index 103aec5..d3aa11f 100644 --- a/test/array/test_reduce.jl +++ b/test/array/test_reduce.jl @@ -8,7 +8,9 @@ DataValue(5 * x.value)) for N in (10, 2050) - A = rand(N) + # Integer-valued, so every partial sum is exact: Base reduces `X.values` with + # `@simd`, which reassociates, while the DataValueArray reduction cannot. + A = Float64.(rand(1:1000, N)) M = rand(Bool, N) i = rand(1:N) M[i] = true