From e40035e1c97ffa2adfc3547859259b2f47056356 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 15 Feb 2022 10:35:51 +0800 Subject: [PATCH 1/7] Replace `@inline @propagate_inbounds` with `@propagate_inbounds` --- base/reinterpretarray.jl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index 7dc6607285fd0..686ef53c152ca 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -336,7 +336,7 @@ axes(a::NonReshapedReinterpretArray{T,0}) where {T} = () elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T) unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent)) -@inline @propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S} +@propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S} if isprimitivetype(T) && isprimitivetype(S) reinterpret(T, a.parent[]) else @@ -344,14 +344,14 @@ unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = end end -@inline @propagate_inbounds getindex(a::ReinterpretArray) = a[firstindex(a)] +@propagate_inbounds getindex(a::ReinterpretArray) = a[firstindex(a)] -@inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S} +@propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S} check_readable(a) _getindex_ra(a, inds[1], tail(inds)) end -@inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S} +@propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S} check_readable(a) if isa(IndexStyle(a), IndexLinear) return _getindex_ra(a, i, ()) @@ -362,7 +362,7 @@ end isempty(inds) ? _getindex_ra(a, 1, ()) : _getindex_ra(a, inds[1], tail(inds)) end -@inline @propagate_inbounds function getindex(a::ReshapedReinterpretArray{T,N,S}, ind::SCartesianIndex2) where {T,N,S} +@propagate_inbounds function getindex(a::ReshapedReinterpretArray{T,N,S}, ind::SCartesianIndex2) where {T,N,S} check_readable(a) s = Ref{S}(a.parent[ind.j]) GC.@preserve s begin @@ -373,7 +373,7 @@ end @inline _memcpy!(dst, src, n) = ccall(:memcpy, Cvoid, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), dst, src, n) -@inline @propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} +@propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 if issingletontype(T) # singleton types @@ -429,7 +429,7 @@ end end end -@inline @propagate_inbounds function _getindex_ra(a::ReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} +@propagate_inbounds function _getindex_ra(a::ReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 if issingletontype(T) # singleton types @@ -476,7 +476,7 @@ end end end -@inline @propagate_inbounds function setindex!(a::NonReshapedReinterpretArray{T,0,S}, v) where {T,S} +@propagate_inbounds function setindex!(a::NonReshapedReinterpretArray{T,0,S}, v) where {T,S} if isprimitivetype(S) && isprimitivetype(T) a.parent[] = reinterpret(S, v) return a @@ -484,14 +484,14 @@ end setindex!(a, v, firstindex(a)) end -@inline @propagate_inbounds setindex!(a::ReinterpretArray, v) = setindex!(a, v, firstindex(a)) +@propagate_inbounds setindex!(a::ReinterpretArray, v) = setindex!(a, v, firstindex(a)) -@inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S} +@propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S} check_writable(a) _setindex_ra!(a, v, inds[1], tail(inds)) end -@inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S} +@propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S} check_writable(a) if isa(IndexStyle(a), IndexLinear) return _setindex_ra!(a, v, i, ()) @@ -500,7 +500,7 @@ end _setindex_ra!(a, v, inds[1], tail(inds)) end -@inline @propagate_inbounds function setindex!(a::ReshapedReinterpretArray{T,N,S}, v, ind::SCartesianIndex2) where {T,N,S} +@propagate_inbounds function setindex!(a::ReshapedReinterpretArray{T,N,S}, v, ind::SCartesianIndex2) where {T,N,S} check_writable(a) v = convert(T, v)::T s = Ref{S}(a.parent[ind.j]) @@ -512,7 +512,7 @@ end return a end -@inline @propagate_inbounds function _setindex_ra!(a::NonReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} +@propagate_inbounds function _setindex_ra!(a::NonReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} v = convert(T, v)::T # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 @@ -585,7 +585,7 @@ end return a end -@inline @propagate_inbounds function _setindex_ra!(a::ReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} +@propagate_inbounds function _setindex_ra!(a::ReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} v = convert(T, v)::T # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 From a6d13468d29abdc7f3e1d6efedaedb7a49a8d9e0 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 15 Feb 2022 10:32:33 +0800 Subject: [PATCH 2/7] Make `ReinterpretArray`'s indexing pure pointer based if its root parent isa `Array` and it is dense like. Also add missing `pointer` for `FasterContiguousSubArray` --- base/reinterpretarray.jl | 28 ++++++++++++++++++++++++++++ base/subarray.jl | 2 +- test/reinterpretarray.jl | 10 ++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index 686ef53c152ca..0fd36f94d4f0e 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -344,15 +344,22 @@ unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = end end +check_store(a::StridedReinterpretArray) = check_store(parent(a)) +check_store(a::FastContiguousSubArray) = check_store(parent(a)) +check_store(a::Array) = true +check_store(a::AbstractArray) = false + @propagate_inbounds getindex(a::ReinterpretArray) = a[firstindex(a)] @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S} check_readable(a) + check_store(a) && return _getindex_ptr(a, inds...) _getindex_ra(a, inds[1], tail(inds)) end @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S} check_readable(a) + check_store(a) && return _getindex_ptr(a, i) if isa(IndexStyle(a), IndexLinear) return _getindex_ra(a, i, ()) end @@ -373,6 +380,15 @@ end @inline _memcpy!(dst, src, n) = ccall(:memcpy, Cvoid, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), dst, src, n) +@inline function _getindex_ptr(a::ReinterpretArray{T}, inds...) where {T} + @boundscheck checkbounds(a, inds...) + li = _to_linear_index(a, inds...) + GC.@preserve a begin + p = pointer(a) + sizeof(T) * (li - 1) + return unsafe_load(p) + end +end + @propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 @@ -488,11 +504,13 @@ end @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S} check_writable(a) + check_store(a) && return _setindex_ptr!(a, v, inds...) _setindex_ra!(a, v, inds[1], tail(inds)) end @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S} check_writable(a) + check_store(a) && return _setindex_ptr!(a, v, i) if isa(IndexStyle(a), IndexLinear) return _setindex_ra!(a, v, i, ()) end @@ -512,6 +530,16 @@ end return a end +@inline function _setindex_ptr!(a::ReinterpretArray{T}, v, inds...) where {T} + @boundscheck checkbounds(a, inds...) + li = _to_linear_index(a, inds...) + GC.@preserve a begin + p = pointer(a) + sizeof(T) * (li - 1) + unsafe_store!(p, v) + end + return a +end + @propagate_inbounds function _setindex_ra!(a::NonReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} v = convert(T, v)::T # Make sure to match the scalar reinterpret if that is applicable diff --git a/base/subarray.jl b/base/subarray.jl index ff2408bb48534..ffeee0c85865c 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -432,10 +432,10 @@ find_extended_inds() = () function unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} return unsafe_convert(Ptr{T}, V.parent) + _memory_offset(V.parent, map(first, V.indices)...) end +unsafe_convert(::Type{Ptr{T}}, V::FastContiguousSubArray{T}) where {T} = unsafe_convert(Ptr{T}, V.parent) + V.offset1 * sizeof(T) pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) - function pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::AbstractCartesianIndex{N}) where {N} index = first_index(V) strds = strides(V) diff --git a/test/reinterpretarray.jl b/test/reinterpretarray.jl index e623b407f70a6..d914ce9f3105f 100644 --- a/test/reinterpretarray.jl +++ b/test/reinterpretarray.jl @@ -508,3 +508,13 @@ end @test setindex!(x, SomeSingleton(:), 3, 5) == x2 @test_throws MethodError x[2,4] = nothing end + +@testset "pointer for StridedArray" begin + a = rand(Float64, 251) + v = view(a, UInt(2):UInt(251)); + A = reshape(v, 25, 10); + @test A isa StridedArray && pointer(A) === pointer(a, 2) + Av = view(A, 1:20, 1:2) + @test Av isa StridedArray && pointer(Av) === pointer(a, 2) + @test Av * Av' isa Array +end From 6cc5d9ba61117580b25ccc81ba19e79f7e3959e4 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 29 Apr 2022 15:55:35 +0800 Subject: [PATCH 3/7] Make `StridedReinterpretArray`'s `getindex` effect-free --- base/pointer.jl | 5 ++++- test/reinterpretarray.jl | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/base/pointer.jl b/base/pointer.jl index 60db18f2ca855..6db9ebd815ad2 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -62,7 +62,10 @@ unsafe_convert(::Type{Ptr{Int8}}, s::String) = ccall(:jl_string_ptr, Ptr{Int8}, cconvert(::Type{Ptr{UInt8}}, s::AbstractString) = String(s) cconvert(::Type{Ptr{Int8}}, s::AbstractString) = String(s) -unsafe_convert(::Type{Ptr{T}}, a::Array{T}) where {T} = ccall(:jl_array_ptr, Ptr{T}, (Any,), a) +@eval function unsafe_convert(::Type{Ptr{T}}, a::Array{T}) where {T} + #@assume_effects :effect_free @ccall jl_array_ptr(a::Any)::Ptr{T} + $(Expr(:foreigncall, QuoteNode(:jl_array_ptr), :(Ptr{T}), :(Core.svec(Any)), 0, QuoteNode((:ccall, 0x02)), :a)) +end unsafe_convert(::Type{Ptr{S}}, a::AbstractArray{T}) where {S,T} = convert(Ptr{S}, unsafe_convert(Ptr{T}, a)) unsafe_convert(::Type{Ptr{T}}, a::AbstractArray{T}) where {T} = error("conversion to pointer not defined for $(typeof(a))") diff --git a/test/reinterpretarray.jl b/test/reinterpretarray.jl index d914ce9f3105f..93d4832afc594 100644 --- a/test/reinterpretarray.jl +++ b/test/reinterpretarray.jl @@ -518,3 +518,8 @@ end @test Av isa StridedArray && pointer(Av) === pointer(a, 2) @test Av * Av' isa Array end + +@testset "effect of StridedReinterpretArray's getindex" begin + eff = Base.infer_effects(getindex, Base.typesof(reinterpret(Int8, Int[1]), 1)) + @test Core.Compiler.is_effect_free(eff) +end From 75ee2e40ee3f1ffa17251473e8b4fcdf63f5e53e Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 15 Feb 2022 13:53:22 +0800 Subject: [PATCH 4/7] Extend test Since `getindex`/`setindex!` might be strided-based, we use `WrapperArray{T,N,<:Array}` to make sure the general fallback is tested thoroughly. --- test/reinterpretarray.jl | 220 +++++++++++++++---------- test/testhelpers/arrayindexingtypes.jl | 3 + 2 files changed, 132 insertions(+), 91 deletions(-) diff --git a/test/reinterpretarray.jl b/test/reinterpretarray.jl index 93d4832afc594..669d37ba9c58b 100644 --- a/test/reinterpretarray.jl +++ b/test/reinterpretarray.jl @@ -6,20 +6,28 @@ using .Main.OffsetArrays isdefined(Main, :TSlow) || @eval Main include("testhelpers/arrayindexingtypes.jl") using .Main: TSlow, WrapperArray +tslow(a::AbstractArray) = TSlow(a) +wrapper(a::AbstractArray) = WrapperArray(a) +fcviews(a::AbstractArray) = view(a, ntuple(Returns(:),ndims(a)-1)..., axes(a)[end]) +fcviews(a::AbstractArray{<:Any, 0}) = view(a) +tslow(t::Tuple) = map(tslow, t) +wrapper(t::Tuple) = map(wrapper, t) +fcviews(t::Tuple) = map(fcviews, t) + +test_many_wrappers(testf, A, wrappers) = foreach(w -> testf(w(A)), wrappers) +test_many_wrappers(testf, A) = test_many_wrappers(testf, A, (identity, tslow, wrapper, fcviews)) + A = Int64[1, 2, 3, 4] -As = TSlow(A) Ars = Int64[1 3; 2 4] -Arss = TSlow(Ars) B = Complex{Int64}[5+6im, 7+8im, 9+10im] -Bs = TSlow(B) Av = [Int32[1,2], Int32[3,4]] -for Ar in (Ars, Arss) +test_many_wrappers(Ars, (identity, tslow)) do Ar @test @inferred(ndims(reinterpret(reshape, Complex{Int64}, Ar))) == 1 @test @inferred(axes(reinterpret(reshape, Complex{Int64}, Ar))) === (Base.OneTo(2),) @test @inferred(size(reinterpret(reshape, Complex{Int64}, Ar))) == (2,) end -for _B in (B, Bs) +test_many_wrappers(B, (identity, tslow)) do _B @test @inferred(ndims(reinterpret(reshape, Int64, _B))) == 2 @test @inferred(axes(reinterpret(reshape, Int64, _B))) === (Base.OneTo(2), Base.OneTo(3)) @test @inferred(size(reinterpret(reshape, Int64, _B))) == (2, 3) @@ -42,24 +50,25 @@ end @test_throws ArgumentError("cannot reinterpret a zero-dimensional `UInt8` array to `UInt16` which is of a larger size") reinterpret(reshape, UInt16, reshape([0x01])) # getindex -for _A in (A, As) +test_many_wrappers(A) do _A @test reinterpret(Complex{Int64}, _A) == [1 + 2im, 3 + 4im] @test reinterpret(Float64, _A) == reinterpret.(Float64, A) @test reinterpret(reshape, Float64, _A) == reinterpret.(Float64, A) end -for Ar in (Ars, Arss) +test_many_wrappers(Ars) do Ar @test reinterpret(reshape, Complex{Int64}, Ar) == [1 + 2im, 3 + 4im] @test reinterpret(reshape, Float64, Ar) == reinterpret.(Float64, Ars) end -for _B in (B, Bs) +test_many_wrappers(B) do _B @test reinterpret(NTuple{3, Int64}, _B) == [(5,6,7),(8,9,10)] @test reinterpret(reshape, Int64, _B) == [5 7 9; 6 8 10] end # setindex -for (_A, Ar, _B) in ((A, Ars, B), (As, Arss, Bs)) - let Ac = copy(_A), Arsc = copy(Ar), Bc = copy(_B) +test_many_wrappers((A, Ars, B)) do (A, Ars, B) + _A, Ar, _B = deepcopy(A), deepcopy(Ars), deepcopy(B) + let Ac = deepcopy(_A), Arsc = deepcopy(Ar), Bc = deepcopy(_B) reinterpret(Complex{Int64}, Ac)[2] = -1 - 2im @test Ac == [1, 2, -1, -2] reinterpret(Complex{Int64}, Arsc)[2] = -1 - 2im @@ -94,50 +103,67 @@ for (_A, Ar, _B) in ((A, Ars, B), (As, Arss, Bs)) end end A3 = collect(reshape(1:18, 2, 3, 3)) -A3r = reinterpret(reshape, Complex{Int}, A3) -@test A3r[4] === A3r[1,2] === A3r[CartesianIndex(1, 2)] === 7+8im -A3r[2,3] = -8-15im -@test A3[1,2,3] == -8 -@test A3[2,2,3] == -15 -A3r[4] = 100+200im -@test A3[1,1,2] == 100 -@test A3[2,1,2] == 200 -A3r[CartesianIndex(1,2)] = 300+400im -@test A3[1,1,2] == 300 -@test A3[2,1,2] == 400 +test_many_wrappers(A3) do A3_ + A3 = deepcopy(A3_) + A3r = reinterpret(reshape, Complex{Int}, A3) + @test A3r[4] === A3r[1,2] === A3r[CartesianIndex(1, 2)] === 7+8im + A3r[2,3] = -8-15im + @test A3[1,2,3] == -8 + @test A3[2,2,3] == -15 + A3r[4] = 100+200im + @test A3[1,1,2] == 100 + @test A3[2,1,2] == 200 + A3r[CartesianIndex(1,2)] = 300+400im + @test A3[1,1,2] == 300 + @test A3[2,1,2] == 400 +end # same-size reinterpret where one of the types is non-primitive -let a = NTuple{4,UInt8}[(0x01,0x02,0x03,0x04)], ra = reinterpret(Float32, a) - @test ra[1] == reinterpret(Float32, 0x04030201) - @test setindex!(ra, 2.0) === ra - @test reinterpret(Float32, a)[1] == 2.0 +let a = NTuple{4,UInt8}[(0x01,0x02,0x03,0x04)] + test_many_wrappers(a, (identity, wrapper, fcviews)) do a_ + a = deepcopy(a_) + ra = reinterpret(Float32, a) + @test ra[1] == reinterpret(Float32, 0x04030201) + @test setindex!(ra, 2.0) === ra + @test reinterpret(Float32, a)[1] == 2.0 + end end -let a = NTuple{4,UInt8}[(0x01,0x02,0x03,0x04)], ra = reinterpret(reshape, Float32, a) - @test ra[1] == reinterpret(Float32, 0x04030201) - @test setindex!(ra, 2.0) === ra - @test reinterpret(reshape, Float32, a)[1] == 2.0 +let a = NTuple{4,UInt8}[(0x01,0x02,0x03,0x04)] + test_many_wrappers(a, (identity, wrapper, fcviews)) do a_ + a = deepcopy(a_) + ra = reinterpret(reshape, Float32, a) + @test ra[1] == reinterpret(Float32, 0x04030201) + @test setindex!(ra, 2.0) === ra + @test reinterpret(reshape, Float32, a)[1] == 2.0 + end end # Pass-through indexing B = Complex{Int64}[5+6im, 7+8im, 9+10im] -Br = reinterpret(reshape, Int64, B) -W = WrapperArray(Br) -for (b, w) in zip(5:10, W) - @test b == w -end -for (i, j) in zip(eachindex(W), 11:16) - W[i] = j +test_many_wrappers(B) do B_ + B = deepcopy(B_) + Br = reinterpret(reshape, Int64, B) + W = WrapperArray(Br) + for (b, w) in zip(5:10, W) + @test b == w + end + for (i, j) in zip(eachindex(W), 11:16) + W[i] = j + end + @test B[1] === Complex{Int64}(11+12im) + @test B[2] === Complex{Int64}(13+14im) + @test B[3] === Complex{Int64}(15+16im) end -@test B[1] === Complex{Int64}(11+12im) -@test B[2] === Complex{Int64}(13+14im) -@test B[3] === Complex{Int64}(15+16im) z3 = (0x00, 0x00, 0x00) Az = [z3 z3; z3 z3] -Azr = reinterpret(reshape, UInt8, Az) -W = WrapperArray(Azr) -copyto!(W, fill(0x01, 3, 2, 2)) -@test all(isequal((0x01, 0x01, 0x01)), Az) -@test eachindex(W, W) == eachindex(W) +test_many_wrappers(Az, (identity, wrapper)) do Az_ + Az = deepcopy(Az_) + Azr = reinterpret(reshape, UInt8, Az) + W = WrapperArray(Azr) + copyto!(W, fill(0x01, 3, 2, 2)) + @test all(isequal((0x01, 0x01, 0x01)), Az) + @test eachindex(W, W) == eachindex(W) +end # ensure that reinterpret arrays aren't erroneously classified as strided let A = reshape(1:20, 5, 4) @@ -169,7 +195,7 @@ function check_strides(A::AbstractArray) end @testset "strides for NonReshapedReinterpretArray" begin - A = Array{Int32}(reshape(1:88, 11, 8)) + A = WrapperArray(Array{Int32}(reshape(1:88, 11, 8))) for viewax2 in (1:8, 1:2:6, 7:-1:1, 5:-2:1, 2:3:8, 7:-6:1, 3:5:11) # dim1 is contiguous for T in (Int16, Float32) @@ -200,7 +226,7 @@ end end @testset "strides for ReshapedReinterpretArray" begin - A = Array{Int32}(reshape(1:192, 3, 8, 8)) + A = WrapperArray(Array{Int32}(reshape(1:192, 3, 8, 8))) for viewax1 in (1:8, 1:2:8, 8:-1:1, 8:-2:1), viewax2 in (1:2, 4:-1:1) for T in (Int16, Float32) @test check_strides(reinterpret(reshape, T, view(A, 1:2, viewax1, viewax2))) @@ -237,7 +263,8 @@ end end # IndexStyle -let a = fill(1.0, 5, 3) +test_many_wrappers(fill(1.0, 5, 3), (identity, wrapper)) do a_ + a = deepcopy(a_) r = reinterpret(Int64, a) @test @inferred(IndexStyle(r)) == IndexLinear() fill!(r, 2) @@ -290,14 +317,13 @@ let a = fill(1.0, 5, 3) @test setindex!(r, -5, goodinds...) === r @test r[goodinds...] == -5 end - - ar = [(1,2), (3,4)] +end +let ar = [(1,2), (3,4)] arr = reinterpret(reshape, Int, ar) @test @inferred(IndexStyle(arr)) == Base.IndexSCartesian2{2}() @test @inferred(eachindex(arr)) == Base.SCartesianIndices2{2}(Base.OneTo(2)) @test @inferred(eachindex(arr, arr)) == Base.SCartesianIndices2{2}(Base.OneTo(2)) end - # Error on reinterprets that would expose padding struct S1 a::Int8 @@ -311,11 +337,14 @@ end A1 = S1[S1(0, 0)] A2 = S2[S2(0, 0)] -@test reinterpret(S1, A2)[1] == S1(0, 0) -@test_throws Base.PaddingError (reinterpret(S1, A2)[1] = S2(1, 2)) -@test_throws Base.PaddingError reinterpret(S2, A1)[1] -reinterpret(S2, A1)[1] = S2(1, 2) -@test A1[1] == S1(1, 2) +test_many_wrappers((A1, A2), (identity, wrapper)) do (A1_, A2_) + A1, A2 = deepcopy(A1_), deepcopy(A2_) + @test reinterpret(S1, A2)[1] == S1(0, 0) + @test_throws Base.PaddingError (reinterpret(S1, A2)[1] = S2(1, 2)) + @test_throws Base.PaddingError reinterpret(S2, A1)[1] + reinterpret(S2, A1)[1] = S2(1, 2) + @test A1[1] == S1(1, 2) +end # Unconventional axes let a = [0.1 0.2; 0.3 0.4], at = reshape([(i,i+1) for i = 1:2:8], 2, 2) @@ -368,50 +397,59 @@ end # Test 0-dimensional Arrays A = zeros(UInt32) -B = reinterpret(Int32, A) -Brs = reinterpret(reshape,Int32, A) -C = reinterpret(Tuple{UInt32}, A) # non-primitive type -Crs = reinterpret(reshape, Tuple{UInt32}, A) # non-primitive type -@test size(B) == size(Brs) == size(C) == size(Crs) == () -@test axes(B) == axes(Brs) == axes(C) == axes(Crs) == () -@test setindex!(B, Int32(5)) === B -@test B[] === Int32(5) -@test Brs[] === Int32(5) -@test C[] === (UInt32(5),) -@test Crs[] === (UInt32(5),) -@test A[] === UInt32(5) -@test setindex!(Brs, Int32(12)) === Brs -@test A[] === UInt32(12) -@test setindex!(C, (UInt32(7),)) === C -@test A[] === UInt32(7) -@test setindex!(Crs, (UInt32(3),)) === Crs -@test A[] === UInt32(3) - - -a = [(1.0,2.0)] -af = @inferred(reinterpret(reshape, Float64, a)) -anew = @inferred(reinterpret(reshape, Tuple{Float64,Float64}, vec(af))) -@test anew[1] == a[1] -@test ndims(anew) == 0 +test_many_wrappers(A, (identity, wrapper)) do A_ + A = deepcopy(A_) + B = reinterpret(Int32, A) + Brs = reinterpret(reshape,Int32, A) + C = reinterpret(Tuple{UInt32}, A) # non-primitive type + Crs = reinterpret(reshape, Tuple{UInt32}, A) # non-primitive type + @test size(B) == size(Brs) == size(C) == size(Crs) == () + @test axes(B) == axes(Brs) == axes(C) == axes(Crs) == () + @test setindex!(B, Int32(5)) === B + @test B[] === Int32(5) + @test Brs[] === Int32(5) + @test C[] === (UInt32(5),) + @test Crs[] === (UInt32(5),) + @test A[] === UInt32(5) + @test setindex!(Brs, Int32(12)) === Brs + @test A[] === UInt32(12) + @test setindex!(C, (UInt32(7),)) === C + @test A[] === UInt32(7) + @test setindex!(Crs, (UInt32(3),)) === Crs + @test A[] === UInt32(3) +end + +test_many_wrappers([(1.0,2.0)], (identity, wrapper)) do a + af = @inferred(reinterpret(reshape, Float64, a)) + anew = @inferred(reinterpret(reshape, Tuple{Float64,Float64}, vec(af))) + @test anew[1] == a[1] + @test ndims(anew) == 0 +end # re-reinterpret a0 = reshape([0x22, 0x44, 0x88, 0xf0, 0x01, 0x02, 0x03, 0x04], 4, 2) -a = reinterpret(reshape, NTuple{4,UInt8}, a0) -@test a == [(0x22, 0x44, 0x88, 0xf0), (0x01, 0x02, 0x03, 0x04)] -@test reinterpret(UInt8, a) == [0x22, 0x44, 0x88, 0xf0, 0x01, 0x02, 0x03, 0x04] -@test reinterpret(reshape, UInt8, a) === a0 +test_many_wrappers(a0, (identity, wrapper)) do a0 + a = reinterpret(reshape, NTuple{4,UInt8}, a0) + @test a == [(0x22, 0x44, 0x88, 0xf0), (0x01, 0x02, 0x03, 0x04)] + @test reinterpret(UInt8, a) == [0x22, 0x44, 0x88, 0xf0, 0x01, 0x02, 0x03, 0x04] + @test reinterpret(reshape, UInt8, a) === a0 +end # reductions a = [(1,2,3), (4,5,6)] -ars = reinterpret(reshape, Int, a) -@test sum(ars) == 21 -@test sum(ars; dims=1) == [6 15] -@test sum(ars; dims=2) == reshape([5,7,9], (3, 1)) -@test sum(ars; dims=(1,2)) == reshape([21], (1, 1)) +test_many_wrappers(a, (identity, wrapper)) do a + ars = reinterpret(reshape, Int, a) + @test sum(ars) == 21 + @test sum(ars; dims=1) == [6 15] + @test sum(ars; dims=2) == reshape([5,7,9], (3, 1)) + @test sum(ars; dims=(1,2)) == reshape([21], (1, 1)) +end # also test large sizes for the pairwise algorithm a = [(k,k+1,k+2) for k = 1:3:4000] -ars = reinterpret(reshape, Int, a) -@test sum(ars) == 8010003 +test_many_wrappers(a, (identity, wrapper)) do a + ars = reinterpret(reshape, Int, a) + @test sum(ars) == 8010003 +end @testset "similar(::ReinterpretArray)" begin a = reinterpret(NTuple{2,Float64}, TSlow(rand(Float64, 4, 4))) diff --git a/test/testhelpers/arrayindexingtypes.jl b/test/testhelpers/arrayindexingtypes.jl index 0e956b5216c94..d8a0662f0e0e8 100644 --- a/test/testhelpers/arrayindexingtypes.jl +++ b/test/testhelpers/arrayindexingtypes.jl @@ -66,3 +66,6 @@ Base.axes(A::WrapperArray) = axes(A.parent) Base.getindex(A::WrapperArray, i::Int...) = A.parent[i...] Base.setindex!(A::WrapperArray, v, i::Int...) = A.parent[i...] = v Base.similar(A::WrapperArray, ::Type{T}, dims::Dims) where T = similar(A.parent, T, dims) +Base.unsafe_convert(::Type{Ptr{T}}, A::WrapperArray{T}) where {T} = Base.unsafe_convert(Ptr{T}, A.parent) +Base.strides(A::WrapperArray) = strides(A.parent) +Base.elsize(::Type{WrapperArray{T,N,A}}) where {T,N,A<:AbstractArray{T,N}} = Base.elsize(A) From dbf002695a071c4991f8fb37404e0bd56833cf9d Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 2 Nov 2023 16:41:43 +0800 Subject: [PATCH 5/7] fix for wrappedarray --- test/testhelpers/arrayindexingtypes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testhelpers/arrayindexingtypes.jl b/test/testhelpers/arrayindexingtypes.jl index d8a0662f0e0e8..95c1f18e00903 100644 --- a/test/testhelpers/arrayindexingtypes.jl +++ b/test/testhelpers/arrayindexingtypes.jl @@ -66,6 +66,6 @@ Base.axes(A::WrapperArray) = axes(A.parent) Base.getindex(A::WrapperArray, i::Int...) = A.parent[i...] Base.setindex!(A::WrapperArray, v, i::Int...) = A.parent[i...] = v Base.similar(A::WrapperArray, ::Type{T}, dims::Dims) where T = similar(A.parent, T, dims) -Base.unsafe_convert(::Type{Ptr{T}}, A::WrapperArray{T}) where {T} = Base.unsafe_convert(Ptr{T}, A.parent) +Base.cconvert(::Type{Ptr{T}}, A::WrapperArray{T}) where {T} = Base.cconvert(Ptr{T}, A.parent) Base.strides(A::WrapperArray) = strides(A.parent) Base.elsize(::Type{WrapperArray{T,N,A}}) where {T,N,A<:AbstractArray{T,N}} = Base.elsize(A) From 1078feca8ac58eb1417e7081a031948118dc8d1a Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 2 Nov 2023 22:49:32 +0800 Subject: [PATCH 6/7] exclude unrelated change and fix whitespace --- base/subarray.jl | 1 + test/reinterpretarray.jl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/subarray.jl b/base/subarray.jl index 7c72a13a09b94..61d59bddf547d 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -459,6 +459,7 @@ find_extended_inds() = () pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) + function pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::AbstractCartesianIndex{N}) where {N} index = first_index(V) strds = strides(V) diff --git a/test/reinterpretarray.jl b/test/reinterpretarray.jl index 20534c2d14a4a..05a40895b7934 100644 --- a/test/reinterpretarray.jl +++ b/test/reinterpretarray.jl @@ -587,4 +587,4 @@ end @test reinterpret(Bytes15, (Int8(1), Int16(2), Int32(3), Int64(4))) == Bytes15(Int8(1), Int16(2), Int32(3), Int64(4)) @test_throws ArgumentError reinterpret(Tuple{Int32, Int64}, (Int16(1), Int64(4))) -end \ No newline at end of file +end From a629884473406718469d4bbb1d7305a2f0821585 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 15 Feb 2022 10:35:51 +0800 Subject: [PATCH 7/7] Allow `ReshapedArray` as wrapper and `Memory` as storage Also turn off ptr-indexing for `Array`-based storage once element size keeps identity. --- base/reinterpretarray.jl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index b5da7dee69329..23bfb38a28654 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -360,22 +360,24 @@ cconvert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = cconv end end -check_store(a::StridedReinterpretArray) = check_store(parent(a)) -check_store(a::FastContiguousSubArray) = check_store(parent(a)) -check_store(a::Array) = true -check_store(a::AbstractArray) = false +check_ptr_indexable(a::ReinterpretArray, sz = elsize(a)) = check_ptr_indexable(parent(a), sz) +check_ptr_indexable(a::ReshapedArray, sz) = check_ptr_indexable(parent(a), sz) +check_ptr_indexable(a::FastContiguousSubArray, sz) = check_ptr_indexable(parent(a), sz) +check_ptr_indexable(a::Array, sz) = sizeof(eltype(a)) !== sz +check_ptr_indexable(a::Memory, sz) = true +check_ptr_indexable(a::AbstractArray, sz) = false @propagate_inbounds getindex(a::ReinterpretArray) = a[firstindex(a)] @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S} check_readable(a) - check_store(a) && return _getindex_ptr(a, inds...) + check_ptr_indexable(a) && return _getindex_ptr(a, inds...) _getindex_ra(a, inds[1], tail(inds)) end @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S} check_readable(a) - check_store(a) && return _getindex_ptr(a, i) + check_ptr_indexable(a) && return _getindex_ptr(a, i) if isa(IndexStyle(a), IndexLinear) return _getindex_ra(a, i, ()) end @@ -515,13 +517,13 @@ end @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S} check_writable(a) - check_store(a) && return _setindex_ptr!(a, v, inds...) + check_ptr_indexable(a) && return _setindex_ptr!(a, v, inds...) _setindex_ra!(a, v, inds[1], tail(inds)) end @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S} check_writable(a) - check_store(a) && return _setindex_ptr!(a, v, i) + check_ptr_indexable(a) && return _setindex_ptr!(a, v, i) if isa(IndexStyle(a), IndexLinear) return _setindex_ra!(a, v, i, ()) end