Skip to content

Commit 820c08b

Browse files
authored
fix #45825, BitArray methods assuming 1-indexing of AbstractArray (#45835)
1 parent e1739aa commit 820c08b

File tree

3 files changed

+70
-28
lines changed

3 files changed

+70
-28
lines changed

base/abstractarray.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,10 @@ julia> y
10351035
"""
10361036
function copyto!(dest::AbstractArray, src::AbstractArray)
10371037
isempty(src) && return dest
1038+
if dest isa BitArray
1039+
# avoid ambiguities with other copyto!(::AbstractArray, ::SourceArray) methods
1040+
return _copyto_bitarray!(dest, src)
1041+
end
10381042
src′ = unalias(dest, src)
10391043
copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′)
10401044
end
@@ -1139,10 +1143,10 @@ function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::A
11391143
return B
11401144
end
11411145

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

1145-
checkaxs(axes(dest), axes(src))
1148+
function copyto_axcheck!(dest, src)
1149+
_checkaxs(axes(dest), axes(src))
11461150
copyto!(dest, src)
11471151
end
11481152

base/bitarray.jl

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -501,40 +501,42 @@ function Array{T,N}(B::BitArray{N}) where {T,N}
501501
end
502502

503503
BitArray(A::AbstractArray{<:Any,N}) where {N} = BitArray{N}(A)
504+
504505
function BitArray{N}(A::AbstractArray{T,N}) where N where T
505506
B = BitArray(undef, convert(Dims{N}, size(A)::Dims{N}))
506-
Bc = B.chunks
507-
l = length(B)
507+
_checkaxs(axes(B), axes(A))
508+
_copyto_bitarray!(B, A)
509+
return B::BitArray{N}
510+
end
511+
512+
function _copyto_bitarray!(B::BitArray, A::AbstractArray)
513+
l = length(A)
508514
l == 0 && return B
509-
ind = 1
515+
l > length(B) && throw(BoundsError(B, length(B)+1))
516+
Bc = B.chunks
517+
nc = num_bit_chunks(l)
518+
Ai = first(eachindex(A))
510519
@inbounds begin
511-
for i = 1:length(Bc)-1
520+
for i = 1:nc-1
512521
c = UInt64(0)
513522
for j = 0:63
514-
c |= (UInt64(convert(Bool, A[ind])::Bool) << j)
515-
ind += 1
523+
c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
524+
Ai = nextind(A, Ai)
516525
end
517526
Bc[i] = c
518527
end
519528
c = UInt64(0)
520-
for j = 0:_mod64(l-1)
521-
c |= (UInt64(convert(Bool, A[ind])::Bool) << j)
522-
ind += 1
529+
tail = _mod64(l - 1) + 1
530+
for j = 0:tail-1
531+
c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
532+
Ai = nextind(A, Ai)
523533
end
524-
Bc[end] = c
534+
msk = _msk_end(tail)
535+
Bc[nc] = (c & msk) | (Bc[nc] & ~msk)
525536
end
526537
return B
527538
end
528539

529-
function BitArray{N}(A::Array{Bool,N}) where N
530-
B = BitArray(undef, size(A))
531-
Bc = B.chunks
532-
l = length(B)
533-
l == 0 && return B
534-
copy_to_bitarray_chunks!(Bc, 1, A, 1, l)
535-
return B::BitArray{N}
536-
end
537-
538540
reinterpret(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) where {N} = reinterpret(B, dims)
539541
reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims)
540542

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

724-
c = 1
726+
Xi = first(eachindex(X))
727+
lastXi = last(eachindex(X))
725728
for i = 1:lc
726729
@inbounds Imsk = Ic[i]
727730
@inbounds C = Bc[i]
728731
u = UInt64(1)
729732
for j = 1:(i < lc ? 64 : last_chunk_len)
730733
if Imsk & u != 0
731-
lx < c && throw_setindex_mismatch(X, c)
732-
@inbounds x = convert(Bool, X[c])
734+
Xi > lastXi && throw_setindex_mismatch(X, count(I))
735+
@inbounds x = convert(Bool, X[Xi])
733736
C = ifelse(x, C | u, C & ~u)
734-
c += 1
737+
Xi = nextind(X, Xi)
735738
end
736739
u <<= 1
737740
end
738741
@inbounds Bc[i] = C
739742
end
740-
if length(X) != c-1
741-
throw_setindex_mismatch(X, c-1)
743+
if Xi != nextind(X, lastXi)
744+
throw_setindex_mismatch(X, count(I))
742745
end
743746
return B
744747
end

test/bitarray.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,3 +1787,38 @@ end
17871787
@test all(bitarray[rangein, rangeout] .== true)
17881788
end
17891789
end
1790+
1791+
# issue #45825
1792+
1793+
isdefined(Main, :OffsetArrays) || @eval Main include("testhelpers/OffsetArrays.jl")
1794+
using .Main.OffsetArrays
1795+
1796+
let all_false = OffsetArray(falses(2001), -1000:1000)
1797+
@test !any(==(true), all_false)
1798+
# should be run with --check-bounds=yes
1799+
@test_throws DimensionMismatch BitArray(all_false)
1800+
all_false = OffsetArray(falses(2001), 1:2001)
1801+
@test !any(==(true), BitArray(all_false))
1802+
all_false = OffsetArray(falses(100, 100), 0:99, -1:98)
1803+
@test !any(==(true), all_false)
1804+
@test_throws DimensionMismatch BitArray(all_false)
1805+
all_false = OffsetArray(falses(100, 100), 1:100, 1:100)
1806+
@test !any(==(true), all_false)
1807+
end
1808+
let a = falses(1000),
1809+
msk = BitArray(rand(Bool, 1000)),
1810+
n = count(msk),
1811+
b = OffsetArray(rand(Bool, n), (-n÷2):(n÷2)-iseven(n))
1812+
a[msk] = b
1813+
@test a[msk] == collect(b)
1814+
a = falses(100, 100)
1815+
msk = BitArray(rand(Bool, 100, 100))
1816+
n = count(msk)
1817+
b = OffsetArray(rand(Bool, 1, n), 1:1, (-n÷2):(n÷2)-iseven(n))
1818+
a[msk] = b
1819+
@test a[msk] == vec(collect(b))
1820+
end
1821+
let b = trues(10)
1822+
copyto!(b, view([0,0,0], :))
1823+
@test b == [0,0,0,1,1,1,1,1,1,1]
1824+
end

0 commit comments

Comments
 (0)