Skip to content

Commit

Permalink
fix #45825, BitArray methods assuming 1-indexing of AbstractArray
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Jun 30, 2022
1 parent 84bf42a commit 8c780d6
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 28 deletions.
10 changes: 7 additions & 3 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,10 @@ julia> y
"""
function copyto!(dest::AbstractArray, src::AbstractArray)
isempty(src) && return dest
if dest isa BitArray
# avoid ambiguities with other copyto!(::AbstractArray, ::SourceArray) methods
return _copyto_bitarray!(dest, src)
end
src′ = unalias(dest, src)
copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′)
end
Expand Down Expand Up @@ -1139,10 +1143,10 @@ function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::A
return B
end

function copyto_axcheck!(dest, src)
@noinline checkaxs(axd, axs) = axd == axs || throw(DimensionMismatch("axes must agree, got $axd and $axs"))
@noinline _checkaxs(axd, axs) = axd == axs || throw(DimensionMismatch("axes must agree, got $axd and $axs"))

checkaxs(axes(dest), axes(src))
function copyto_axcheck!(dest, src)
_checkaxs(axes(dest), axes(src))
copyto!(dest, src)
end

Expand Down
53 changes: 28 additions & 25 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -501,40 +501,42 @@ function Array{T,N}(B::BitArray{N}) where {T,N}
end

BitArray(A::AbstractArray{<:Any,N}) where {N} = BitArray{N}(A)

function BitArray{N}(A::AbstractArray{T,N}) where N where T
B = BitArray(undef, convert(Dims{N}, size(A)::Dims{N}))
Bc = B.chunks
l = length(B)
_checkaxs(axes(B), axes(A))
_copyto_bitarray!(B, A)
return B::BitArray{N}
end

function _copyto_bitarray!(B::BitArray, A::AbstractArray)
l = length(A)
l == 0 && return B
ind = 1
l > length(B) && throw(BoundsError(B, length(B)+1))
Bc = B.chunks
nc = num_bit_chunks(l)
Ai = first(eachindex(A))
@inbounds begin
for i = 1:length(Bc)-1
for i = 1:nc-1
c = UInt64(0)
for j = 0:63
c |= (UInt64(convert(Bool, A[ind])::Bool) << j)
ind += 1
c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
Ai = nextind(A, Ai)
end
Bc[i] = c
end
c = UInt64(0)
for j = 0:_mod64(l-1)
c |= (UInt64(convert(Bool, A[ind])::Bool) << j)
ind += 1
tail = _mod64(l - 1) + 1
for j = 0:tail-1
c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
Ai = nextind(A, Ai)
end
Bc[end] = c
msk = _msk_end(tail)
Bc[nc] = (c & msk) | (Bc[nc] & ~msk)
end
return B
end

function BitArray{N}(A::Array{Bool,N}) where N
B = BitArray(undef, size(A))
Bc = B.chunks
l = length(B)
l == 0 && return B
copy_to_bitarray_chunks!(Bc, 1, A, 1, l)
return B::BitArray{N}
end

reinterpret(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) where {N} = reinterpret(B, dims)
reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims)

Expand Down Expand Up @@ -721,24 +723,25 @@ function _unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray)
lx = length(X)
last_chunk_len = _mod64(length(B)-1)+1

c = 1
Xi = first(eachindex(X))
lastXi = last(eachindex(X))
for i = 1:lc
@inbounds Imsk = Ic[i]
@inbounds C = Bc[i]
u = UInt64(1)
for j = 1:(i < lc ? 64 : last_chunk_len)
if Imsk & u != 0
lx < c && throw_setindex_mismatch(X, c)
@inbounds x = convert(Bool, X[c])
Xi > lastXi && throw_setindex_mismatch(X, count(I))
@inbounds x = convert(Bool, X[Xi])
C = ifelse(x, C | u, C & ~u)
c += 1
Xi = nextind(X, Xi)
end
u <<= 1
end
@inbounds Bc[i] = C
end
if length(X) != c-1
throw_setindex_mismatch(X, c-1)
if Xi != nextind(X, lastXi)
throw_setindex_mismatch(X, count(I))
end
return B
end
Expand Down
35 changes: 35 additions & 0 deletions test/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1787,3 +1787,38 @@ end
@test all(bitarray[rangein, rangeout] .== true)
end
end

# issue #45825

isdefined(Main, :OffsetArrays) || @eval Main include("testhelpers/OffsetArrays.jl")
using .Main.OffsetArrays

let all_false = OffsetArray(falses(2001), -1000:1000)
@test !any(==(true), all_false)
# should be run with --check-bounds=yes
@test_throws DimensionMismatch BitArray(all_false)
all_false = OffsetArray(falses(2001), 1:2001)
@test !any(==(true), BitArray(all_false))
all_false = OffsetArray(falses(100, 100), 0:99, -1:98)
@test !any(==(true), all_false)
@test_throws DimensionMismatch BitArray(all_false)
all_false = OffsetArray(falses(100, 100), 1:100, 1:100)
@test !any(==(true), all_false)
end
let a = falses(1000),
msk = BitArray(rand(Bool, 1000)),
n = count(msk),
b = OffsetArray(rand(Bool, n), (-n÷2):(n÷2)-iseven(n))
a[msk] = b
@test a[msk] == collect(b)
a = falses(100, 100)
msk = BitArray(rand(Bool, 100, 100))
n = count(msk)
b = OffsetArray(rand(Bool, 1, n), 1:1, (-n÷2):(n÷2)-iseven(n))
a[msk] = b
@test a[msk] == vec(collect(b))
end
let b = trues(10)
copyto!(b, view([0,0,0], :))
@test b == [0,0,0,1,1,1,1,1,1,1]
end

0 comments on commit 8c780d6

Please sign in to comment.