Skip to content

Commit 2924dc8

Browse files
committed
Implement BitArray construction from iterables
in particular, can now use generators. Fixes #3166
1 parent 90a3740 commit 2924dc8

File tree

3 files changed

+108
-8
lines changed

3 files changed

+108
-8
lines changed

base/bitarray.jl

+94
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,16 @@ function copy_to_bitarray_chunks!{T<:Real}(Bc::Vector{UInt64}, pos_d::Int, C::Ar
352352
end
353353
end
354354

355+
# auxiliary definitions used when filling a BitArray via a Vector{Bool} cache
356+
# (e.g. when constructing from an iterable, or in broadcast!)
357+
358+
const bitcache_chunks = 64 # this can be changed
359+
const bitcache_size = 64 * bitcache_chunks # do not change this
360+
361+
dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) =
362+
copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6))
363+
364+
355365
## custom iterator ##
356366
start(B::BitArray) = 0
357367
next(B::BitArray, i::Int) = (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1)
@@ -562,6 +572,90 @@ convert{T,N}(::Type{AbstractArray{T,N}}, B::BitArray{N}) = convert(Array{T,N}, B
562572
reinterpret{N}(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) = reinterpret(B, dims)
563573
reinterpret{N}(B::BitArray, dims::NTuple{N,Int}) = reshape(B, dims)
564574

575+
## Constructors from generic iterables ##
576+
577+
BitArray{T,N}(A::AbstractArray{T,N}) = convert(BitArray{N}, A)
578+
579+
BitArray(itr) = gen_bitarray(iteratorsize(itr), itr)
580+
581+
# generic constructor from an iterable without compile-time info
582+
# (we pass start(itr) explicitly to avoid a type-instability with filters)
583+
gen_bitarray(isz::IteratorSize, itr) = gen_bitarray_from_itr(itr, start(itr))
584+
585+
# generic iterable with known shape
586+
function gen_bitarray(::HasShape, itr)
587+
B = BitArray(size(itr))
588+
for (I,x) in zip(CartesianRange(indices(itr)), itr)
589+
B[I] = x
590+
end
591+
return B
592+
end
593+
594+
# generator with known shape or length
595+
function gen_bitarray(::HasShape, itr::Generator)
596+
B = BitArray(size(itr))
597+
return fill_bitarray_from_itr!(B, itr, start(itr))
598+
end
599+
function gen_bitarray(::HasLength, itr)
600+
n = length(itr)
601+
B = BitArray(n)
602+
return fill_bitarray_from_itr!(B, itr, start(itr))
603+
end
604+
605+
gen_bitarray(::IsInfinite, itr) = throw(ArgumentError("infinite-size iterable used in BitArray constructor"))
606+
607+
# The aux functions gen_bitarray_from_itr and fill_bitarray_from_itr! both
608+
# use a Vector{Bool} cache for performance reasons
609+
610+
function gen_bitarray_from_itr(itr, st)
611+
B = empty!(BitArray(bitcache_size))
612+
C = Vector{Bool}(bitcache_size)
613+
Bc = B.chunks
614+
ind = 1
615+
cind = 1
616+
while !done(itr, st)
617+
x, st = next(itr, st)
618+
@inbounds C[ind] = x
619+
ind += 1
620+
if ind > bitcache_size
621+
resize!(B, length(B) + bitcache_size)
622+
dumpbitcache(Bc, cind, C)
623+
cind += bitcache_chunks
624+
ind = 1
625+
end
626+
end
627+
if ind > 1
628+
@inbounds C[ind:bitcache_size] = false
629+
resize!(B, length(B) + ind - 1)
630+
dumpbitcache(Bc, cind, C)
631+
end
632+
return B
633+
end
634+
635+
function fill_bitarray_from_itr!(B::BitArray, itr, st)
636+
n = length(B)
637+
C = Vector{Bool}(bitcache_size)
638+
Bc = B.chunks
639+
ind = 1
640+
cind = 1
641+
while !done(itr, st)
642+
x, st = next(itr, st)
643+
@inbounds C[ind] = x
644+
ind += 1
645+
if ind > bitcache_size
646+
dumpbitcache(Bc, cind, C)
647+
cind += bitcache_chunks
648+
ind = 1
649+
end
650+
end
651+
if ind > 1
652+
@inbounds C[ind:bitcache_size] = false
653+
dumpbitcache(Bc, cind, C)
654+
end
655+
return B
656+
end
657+
658+
565659
## Indexing: getindex ##
566660

567661
@inline function unsafe_bitgetindex(Bc::Vector{UInt64}, i::Int)

base/broadcast.jl

+2-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
module Broadcast
44

55
using Base.Cartesian
6-
using Base: promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, tail, OneTo, to_shape
6+
using Base: promote_eltype_op, linearindices, tail, OneTo, to_shape,
7+
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache
78
import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, , .%, .<<, .>>, .^
89
import Base: broadcast
910
export broadcast!, bitbroadcast, dotview
@@ -119,13 +120,6 @@ map_newindexer(shape, ::Tuple{}) = (), ()
119120
(keep, keeps...), (Idefault, Idefaults...)
120121
end
121122

122-
# For output BitArrays
123-
const bitcache_chunks = 64 # this can be changed
124-
const bitcache_size = 64 * bitcache_chunks # do not change this
125-
126-
dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) =
127-
Base.copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6))
128-
129123
@inline _broadcast_getindex(A, I) = _broadcast_getindex(containertype(A), A, I)
130124
@inline _broadcast_getindex(::Type{Any}, A, I) = A
131125
@inline _broadcast_getindex(::Any, A, I) = A[I]

test/bitarray.jl

+12
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,18 @@ end
132132

133133
timesofar("utils")
134134

135+
## Constructors from iterables ##
136+
137+
for g in ((x%7==3 for x = 1:v1),
138+
(x%7==3 for x = 1:v1 if x>5),
139+
((x+y)%5==2 for x = 1:n1, y = 1:n2),
140+
((x+y+z+t)%5==2 for x = 1:s2, y = 1:s2, z = 1:s3, t = 1:s4),
141+
((x+y)%5==2 for x = 1:n1 for y = 1:n2))
142+
@test BitArray(g) == BitArray(collect(g))
143+
end
144+
145+
timesofar("constructors")
146+
135147
## Indexing ##
136148

137149
# 0d

0 commit comments

Comments
 (0)