Skip to content

Commit fd81415

Browse files
committed
Add new API blockappend! for possibly-non-copying append!
1 parent 0f2d150 commit fd81415

File tree

4 files changed

+94
-53
lines changed

4 files changed

+94
-53
lines changed

docs/src/lib/public.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ BlockArray
5050
undef_blocks
5151
UndefBlocksInitializer
5252
mortar
53+
blockappend!
5354
Base.append!
5455
Base.push!
5556
Base.pushfirst!

src/BlockArrays.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export undef_blocks, undef, findblock, findblockindex
1616

1717
export khatri_rao
1818

19+
export blockappend!
20+
1921
import Base: @propagate_inbounds, Array, to_indices, to_index,
2022
unsafe_indices, first, last, size, length, unsafe_length,
2123
unsafe_convert,

src/blockdeque.jl

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,35 @@
11
"""
2-
append!(dest::BlockVector, sources...; alias = false)
2+
blockappend!(dest::BlockVector, sources...) -> dest
33
4-
Append items from `sources` to the last block of `dest`. If `alias = true`,
5-
append the blocks in `sources` as-is if they are compatible with the internal
6-
block array type of `dest`. Importantly, this means that mutating `sources`
7-
afterwards alters the items in `dest` and it may even break the invariance
8-
of `dest` if the length of `sources` are changed. The elements may be copied
9-
even if `alias = true` if the corresponding implementation does not exist.
4+
Append blocks from `sources` to `dest`. The number of blocks in `dest` are
5+
increased by `sum(blocklength, sources)`.
106
11-
The blocks in `dest` must not alias with `sources` or components of them.
12-
For example, the result of `append!(x, x)` is undefined.
13-
"""
14-
Base.append!(dest::BlockVector, sources...; alias::Bool = false) =
15-
foldl((d, s) -> append!(d, s; alias = alias), sources; init = dest)
16-
17-
function Base.append!(dest::BlockVector, src; alias::Bool = false)
18-
if _blocktype(dest) === _blocktype(src) && alias
19-
return blockappend!(dest, src)
20-
else
21-
return append_copy!(dest, src)
22-
end
23-
end
24-
25-
_blocktype(::Any) = nothing
26-
_blocktype(::T) where {T<:AbstractArray} = T
27-
_blocktype(::BlockArray{<:Any,<:Any,<:AbstractArray{T}}) where {T<:AbstractArray} = T
28-
_blocktype(::PseudoBlockArray{<:Any,<:Any,T}) where {T<:AbstractArray} = T
7+
This function avoids copying the elements of the blocks in `sources` when
8+
these blocks are compatible with `dest`. Importantly, this means that
9+
mutating `sources` afterwards alters the items in `dest` and it may even
10+
break the invariance of `dest` if the length of `sources` are changed.
2911
12+
The blocks in `dest` must not alias with `sources` or components of them.
13+
For example, the result of `blockappend!(x, x)` is undefined.
14+
15+
# Examples
16+
```jldoctest
17+
julia> using BlockArrays
18+
19+
julia> blockappend!(mortar([[1], [2, 3]]), mortar([[4, 5]]))
20+
3-blocked 5-element BlockArray{Int64,1}:
21+
1
22+
23+
2
24+
3
25+
26+
4
27+
5
28+
```
3029
"""
31-
blockappend!(dest::BlockVector, src)
30+
blockappend!(dest::BlockVector, s1, s2, sources...) =
31+
foldl(blockappend!, (s1, s2, sources...); init = dest)
3232

33-
Append blocks from `src` to `dest`. When `src` is a vector of the same type
34-
as the blocks of `dest`, or a `PseudoBlockVector` with the same underlying
35-
array type, a single vector block is appended to `dest`.
36-
"""
3733
function blockappend!(dest::BlockVector{<:Any,T}, src::BlockVector{<:Any,T}) where {T}
3834
isempty(src) && return dest
3935
append!(dest.blocks, src.blocks)
@@ -42,21 +38,60 @@ function blockappend!(dest::BlockVector{<:Any,T}, src::BlockVector{<:Any,T}) whe
4238
return dest
4339
end
4440

45-
blockappend!(
41+
function blockappend!(
4642
dest::BlockVector{<:Any,<:AbstractArray{T}},
4743
src::PseudoBlockVector{<:Any,T},
48-
) where {T} = blockappend!(dest, src.blocks)
44+
) where {T}
45+
if blocklength(src) == 1
46+
return blockappend!(dest, src.blocks)
47+
else
48+
return blockappend_fallback!(dest, src)
49+
end
50+
end
4951

50-
function blockappend!(dest::BlockVector{<:Any,<:AbstractArray{T}}, src::T) where {T}
52+
function blockappend!(
53+
dest::BlockVector{<:Any,<:AbstractArray{T}},
54+
src::T,
55+
) where {T<:AbstractVector}
5156
isempty(src) && return dest
5257
push!(dest.blocks, src)
5358
push!(dest.axes[1].lasts, last(dest.axes[1]) + length(src))
5459
return dest
5560
end
5661

57-
append_copy!(dest::BlockVector, src) = _append_copy!(dest, Base.IteratorSize(src), src)
62+
blockappend!(dest::BlockVector{<:Any,<:Any}, src::AbstractVector) =
63+
blockappend_fallback!(dest, src)
64+
65+
blockappend_fallback!(dest::BlockVector{<:Any,<:AbstractArray{T}}, src) where {T} =
66+
blockappend!(dest, mortar([convert(T, @view src[b]) for b in blockaxes(src, 1)]))
67+
68+
"""
69+
append!(dest::BlockVector, sources...)
70+
71+
Append items from `sources` to the last block of `dest`.
72+
73+
The blocks in `dest` must not alias with `sources` or components of them.
74+
For example, the result of `append!(x, x)` is undefined.
75+
76+
# Examples
77+
```jldoctest
78+
julia> using BlockArrays
79+
80+
julia> append!(mortar([[1], [2, 3]]), mortar([[4], [5]]))
81+
2-blocked 5-element BlockArray{Int64,1}:
82+
1
83+
84+
2
85+
3
86+
4
87+
5
88+
```
89+
"""
90+
Base.append!(dest::BlockVector, sources...) = foldl(append!, sources; init = dest)
91+
92+
Base.append!(dest::BlockVector, src) = append_itr!(dest, Base.IteratorSize(src), src)
5893

59-
function _append_copy!(dest::BlockVector, ::Union{Base.HasShape,Base.HasLength}, src)
94+
function append_itr!(dest::BlockVector, ::Union{Base.HasShape,Base.HasLength}, src)
6095
block = dest.blocks[end]
6196
li = lastindex(block)
6297
resize!(block, length(block) + length(src))
@@ -72,7 +107,7 @@ function _append_copy!(dest::BlockVector, ::Union{Base.HasShape,Base.HasLength},
72107
return dest
73108
end
74109

75-
function _append_copy!(dest::BlockVector, ::Base.SizeUnknown, src)
110+
function append_itr!(dest::BlockVector, ::Base.SizeUnknown, src)
76111
block = dest.blocks[end]
77112
# Equivalent to `n = 0; for x in src; ...; end` but (maybe) faster:
78113
n = foldl(src, init = 0) do n, x

test/test_blockdeque.jl

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
using BlockArrays, Test
22

3-
@testset "append!(::BlockVector, vector)" begin
4-
@testset for alias in [false, true],
5-
compatible in [false, true],
6-
srctype in [:BlockVector, :PseudoBlockVector, :Vector]
3+
@testset "blockappend!(::BlockVector, _)" begin
4+
@testset for compatible in [false, true],
5+
srctype in [:BlockVector, :PseudoBlockVector, :PseudoBlockVector2, :Vector]
76

87
dest = mortar([[1, 2, 3], [4, 5]])
98

@@ -16,46 +15,50 @@ using BlockArrays, Test
1615
if srctype === :BlockVector
1716
src = mortar([T[6, 7], T[8, 9]])
1817
elseif srctype === :PseudoBlockVector
19-
src = PseudoBlockVector(T[6:9;], [3, 1])
18+
src = PseudoBlockVector(T[6:9;], [4])
19+
elseif srctype === :PseudoBlockVector2
20+
src = PseudoBlockVector(T[6:9;], [2, 2])
2021
elseif srctype === :Vector
2122
src = T[6:9;]
2223
else
2324
error("Unknown srctype = $srctype")
2425
end
2526

26-
@test append!(dest, src; alias = alias) === dest
27+
@test blockappend!(dest, src) === dest
2728
@test dest == 1:9
2829

2930
@test dest[Block(1)] == [1, 2, 3]
30-
if alias && compatible
31-
@test dest[Block(2)] == [4, 5]
32-
if srctype === :BlockVector
33-
@test dest[Block(3)] == [6, 7]
34-
@test dest[Block(4)] == [8, 9]
35-
else
36-
@test dest[Block(3)] == [6, 7, 8, 9]
37-
end
31+
@test dest[Block(2)] == [4, 5]
32+
if blocklength(src) == 2
33+
@test dest[Block(3)] == [6, 7]
34+
@test dest[Block(4)] == [8, 9]
35+
elseif blocklength(src) == 1
36+
@test dest[Block(3)] == 6:9
3837
else
39-
@test dest[Block(2)] == 4:9
38+
error("Unexpected: blocklength(src) = ", blocklength(src))
4039
end
4140

4241
src[1] = 666
43-
if alias && compatible
42+
if compatible && srctype !== :PseudoBlockVector2
4443
@test dest[6] == 666
4544
else
4645
@test dest[6] == 6
4746
end
4847
end
4948
end
5049

51-
@testset "append!(::BlockVector, iterator)" begin
50+
@testset "append!(::BlockVector, _)" begin
5251
@testset "$label" for (label, itr) in [
52+
"UnitRange" => 6:9,
53+
"BlockVector" => mortar([[6, 7], [8, 9]]),
5354
"with length" => (x + 0 for x in 6:9),
5455
"no length" => (x for x in 6:9 if x > 0),
5556
]
5657
dest = mortar([[1, 2, 3], [4, 5]])
5758
@test append!(dest, itr) === dest
5859
@test dest == 1:9
60+
@test dest[Block(1)] == [1, 2, 3]
61+
@test dest[Block(2)] == 4:9
5962
end
6063
end
6164

0 commit comments

Comments
 (0)