Skip to content

Commit fc09c27

Browse files
author
KristofferC
committed
Revert "Make reshape and view on Memory produce Arrays and delete wrap (#53896)"
This reverts commit 146a7db.
1 parent 6aba3b2 commit fc09c27

File tree

8 files changed

+80
-66
lines changed

8 files changed

+80
-66
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ New library functions
8888
* `Sys.username()` can be used to return the current user's username ([#51897]).
8989
* `Sys.isreadable(), Sys.iswritable()` can be used to check if the current user has access permissions
9090
that permit reading and writing, respectively. ([#53320]).
91+
* `wrap(Array, m::Union{MemoryRef{T}, Memory{T}}, dims)` is the safe counterpart to `unsafe_wrap` ([#52049]).
9192
* `GC.logging_enabled()` can be used to test whether GC logging has been enabled via `GC.enable_logging` ([#51647]).
9293
* `IdSet` is now exported from Base and considered public ([#53262]).
9394
* `@time` now reports a count of any lock conflicts where a `ReentrantLock` had to wait, plus a new macro

base/array.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3060,3 +3060,54 @@ intersect(r::AbstractRange, v::AbstractVector) = intersect(v, r)
30603060
_getindex(v, i)
30613061
end
30623062
end
3063+
3064+
"""
3065+
wrap(Array, m::Union{Memory{T}, MemoryRef{T}}, dims)
3066+
3067+
Create an array of size `dims` using `m` as the underlying memory. This can be thought of as a safe version
3068+
of [`unsafe_wrap`](@ref) utilizing `Memory` or `MemoryRef` instead of raw pointers.
3069+
"""
3070+
function wrap end
3071+
3072+
# validity checking for _wrap calls, separate from allocation of Array so that it can be more likely to inline into the caller
3073+
function _wrap(ref::MemoryRef{T}, dims::NTuple{N, Int}) where {T, N}
3074+
mem = ref.mem
3075+
mem_len = length(mem) + 1 - memoryrefoffset(ref)
3076+
len = Core.checked_dims(dims...)
3077+
@boundscheck mem_len >= len || invalid_wrap_err(mem_len, dims, len)
3078+
if N != 1 && !(ref === GenericMemoryRef(mem) && len === mem_len)
3079+
mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len)
3080+
ref = MemoryRef(mem)
3081+
end
3082+
return ref
3083+
end
3084+
3085+
@noinline invalid_wrap_err(len, dims, proddims) = throw(DimensionMismatch(
3086+
"Attempted to wrap a MemoryRef of length $len with an Array of size dims=$dims, which is invalid because prod(dims) = $proddims > $len, so that the array would have more elements than the underlying memory can store."))
3087+
3088+
@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, dims::NTuple{N, Integer}) where {T, N}
3089+
dims = convert(Dims, dims)
3090+
ref = _wrap(m, dims)
3091+
$(Expr(:new, :(Array{T, N}), :ref, :dims))
3092+
end
3093+
3094+
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N}
3095+
dims = convert(Dims, dims)
3096+
ref = _wrap(MemoryRef(m), dims)
3097+
$(Expr(:new, :(Array{T, N}), :ref, :dims))
3098+
end
3099+
@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T}
3100+
dims = (Int(l),)
3101+
ref = _wrap(m, dims)
3102+
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
3103+
end
3104+
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T}
3105+
dims = (Int(l),)
3106+
ref = _wrap(MemoryRef(m), (l,))
3107+
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
3108+
end
3109+
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}) where {T}
3110+
ref = MemoryRef(m)
3111+
dims = (length(m),)
3112+
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
3113+
end

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ export
460460
vcat,
461461
vec,
462462
view,
463+
wrap,
463464
zeros,
464465

465466
# search, find, match and related functions

base/genericmemory.jl

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -310,28 +310,3 @@ function indcopy(sz::Dims, I::GenericMemory)
310310
src = eltype(I)[I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n]
311311
dst, src
312312
end
313-
314-
# Wrapping a memory region in an Array
315-
@eval begin # @eval for the Array construction. Block for the docstring.
316-
function reshape(m::GenericMemory{M, T}, dims::Vararg{Int, N}) where {M, T, N}
317-
len = Core.checked_dims(dims...)
318-
length(m) == len || throw(DimensionMismatch("parent has $(length(m)) elements, which is incompatible with size $(dims)"))
319-
ref = MemoryRef(m)
320-
$(Expr(:new, :(Array{T, N}), :ref, :dims))
321-
end
322-
323-
"""
324-
view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo})
325-
326-
Create a vector `v::Vector{T}` backed by the specified indices of `m`. It is only safe to
327-
resize `v` if `m` is subseqently not used.
328-
"""
329-
function view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo}) where {M, T}
330-
isempty(inds) && return T[] # needed to allow view(Memory{T}(undef, 0), 2:1)
331-
@boundscheck checkbounds(m, inds)
332-
ref = MemoryRef(m, first(inds)) # @inbounds would be safe here but does not help performance.
333-
dims = (length(inds),)
334-
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
335-
end
336-
end
337-
view(m::GenericMemory, inds::Colon) = view(m, eachindex(m))

base/iobuffer.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ end
4242

4343
# allocate Vector{UInt8}s for IOBuffer storage that can efficiently become Strings
4444
StringMemory(n::Integer) = unsafe_wrap(Memory{UInt8}, _string_n(n))
45-
StringVector(n::Integer) = view(StringMemory(n), 1:n)::Vector{UInt8}
45+
StringVector(n::Integer) = wrap(Array, StringMemory(n))
4646

4747
# IOBuffers behave like Files. They are typically readable and writable. They are seekable. (They can be appendable).
4848

@@ -466,7 +466,7 @@ function take!(io::IOBuffer)
466466
if nbytes == 0 || io.reinit
467467
data = StringVector(0)
468468
elseif io.writable
469-
data = view(io.data, io.offset+1:nbytes+io.offset)
469+
data = wrap(Array, MemoryRef(io.data, io.offset + 1), nbytes)
470470
else
471471
data = copyto!(StringVector(nbytes), 1, io.data, io.offset + 1, nbytes)
472472
end
@@ -475,7 +475,7 @@ function take!(io::IOBuffer)
475475
if nbytes == 0
476476
data = StringVector(0)
477477
elseif io.writable
478-
data = view(io.data, io.ptr:io.ptr+nbytes-1)
478+
data = wrap(Array, MemoryRef(io.data, io.ptr), nbytes)
479479
else
480480
data = read!(io, data)
481481
end
@@ -501,7 +501,11 @@ state. This should only be used internally for performance-critical
501501
It might save an allocation compared to `take!` (if the compiler elides the
502502
Array allocation), as well as omits some checks.
503503
"""
504-
_unsafe_take!(io::IOBuffer) = view(io.data, io.offset+1:io.size)
504+
_unsafe_take!(io::IOBuffer) =
505+
wrap(Array, io.size == io.offset ?
506+
MemoryRef(Memory{UInt8}()) :
507+
MemoryRef(io.data, io.offset + 1),
508+
io.size - io.offset)
505509

506510
function write(to::IO, from::GenericIOBuffer)
507511
written::Int = bytesavailable(from)

base/strings/string.jl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,7 @@ String(s::AbstractString) = print_to_string(s)
117117
@assume_effects :total String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s))
118118

119119
unsafe_wrap(::Type{Memory{UInt8}}, s::String) = ccall(:jl_string_to_genericmemory, Ref{Memory{UInt8}}, (Any,), s)
120-
function unsafe_wrap(::Type{Vector{UInt8}}, s::String)
121-
mem = unsafe_wrap(Memory{UInt8}, s)
122-
view(mem, eachindex(mem))
123-
end
120+
unsafe_wrap(::Type{Vector{UInt8}}, s::String) = wrap(Array, unsafe_wrap(Memory{UInt8}, s))
124121

125122
Vector{UInt8}(s::CodeUnits{UInt8,String}) = copyto!(Vector{UInt8}(undef, length(s)), s)
126123
Vector{UInt8}(s::String) = Vector{UInt8}(codeunits(s))

doc/src/base/arrays.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ Base.reshape
140140
Base.dropdims
141141
Base.vec
142142
Base.SubArray
143+
Base.wrap
143144
```
144145

145146
## Concatenation and permutation

test/arrayops.jl

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,39 +3187,23 @@ end
31873187
end
31883188
end
31893189

3190-
@testset "Wrapping Memory into Arrays with view and reshape" begin
3191-
mem::Memory{Int} = Memory{Int}(undef, 10) .= 11:20
3192-
3193-
@test_throws DimensionMismatch reshape(mem, 10, 10)
3194-
@test_throws DimensionMismatch reshape(mem, 5)
3195-
@test_throws BoundsError view(mem, 1:10, 1:10)
3196-
@test_throws BoundsError view(mem, 1:11)
3197-
@test_throws BoundsError view(mem, 3:11)
3198-
@test_throws BoundsError view(mem, 0:4)
3199-
3200-
@test @inferred(view(mem, 1:5))::Vector{Int} == 11:15
3201-
@test @inferred(view(mem, 1:2))::Vector{Int} == 11:12
3202-
@test @inferred(view(mem, 1:10))::Vector{Int} == 11:20
3203-
@test @inferred(view(mem, 3:8))::Vector{Int} == 13:18
3204-
@test @inferred(view(mem, 20:19))::Vector{Int} == []
3205-
@test @inferred(view(mem, -5:-7))::Vector{Int} == []
3206-
@test @inferred(view(mem, :))::Vector{Int} == mem
3207-
@test @inferred(reshape(mem, 5, 2))::Matrix{Int} == reshape(11:20, 5, 2)
3208-
3209-
empty_mem = Memory{Module}(undef, 0)
3210-
@test_throws BoundsError view(empty_mem, 0:1)
3211-
@test_throws BoundsError view(empty_mem, 1:2)
3212-
@test_throws DimensionMismatch reshape(empty_mem, 1)
3213-
@test_throws DimensionMismatch reshape(empty_mem, 1, 2, 3)
3214-
@test_throws ArgumentError reshape(empty_mem, 2^16, 2^16, 2^16, 2^16)
3215-
3216-
@test @inferred(view(empty_mem, 1:0))::Vector{Module} == []
3217-
@test @inferred(view(empty_mem, 10:3))::Vector{Module} == []
3218-
@test @inferred(view(empty_mem, :))::Vector{Module} == empty_mem
3219-
@test isempty(@inferred(reshape(empty_mem, 0, 7, 1))::Array{Module, 3})
3220-
3221-
offset_inds = OffsetArrays.IdOffsetRange(values=3:6, indices=53:56)
3222-
@test @inferred(view(collect(mem), offset_inds)) == view(mem, offset_inds)
3190+
@testset "Wrapping Memory into Arrays" begin
3191+
mem = Memory{Int}(undef, 10) .= 1
3192+
memref = MemoryRef(mem)
3193+
@test_throws DimensionMismatch wrap(Array, mem, (10, 10))
3194+
@test wrap(Array, mem, (5,)) == ones(Int, 5)
3195+
@test wrap(Array, mem, 2) == ones(Int, 2)
3196+
@test wrap(Array, memref, 10) == ones(Int, 10)
3197+
@test wrap(Array, memref, (2,2,2)) == ones(Int,2,2,2)
3198+
@test wrap(Array, mem, (5, 2)) == ones(Int, 5, 2)
3199+
3200+
memref2 = MemoryRef(mem, 3)
3201+
@test wrap(Array, memref2, (5,)) == ones(Int, 5)
3202+
@test wrap(Array, memref2, 2) == ones(Int, 2)
3203+
@test wrap(Array, memref2, (2,2,2)) == ones(Int,2,2,2)
3204+
@test wrap(Array, memref2, (3, 2)) == ones(Int, 3, 2)
3205+
@test_throws DimensionMismatch wrap(Array, memref2, 9)
3206+
@test_throws DimensionMismatch wrap(Array, memref2, 10)
32233207
end
32243208

32253209
@testset "Memory size" begin

0 commit comments

Comments
 (0)