Skip to content

Commit f5a7631

Browse files
committed
Revert "Make reshape and view on Memory produce Arrays and delete wrap (#53896)"
This reverts commit 146a7db.
1 parent 4b92406 commit f5a7631

File tree

8 files changed

+80
-66
lines changed

8 files changed

+80
-66
lines changed

NEWS.md

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ New library functions
6161
* The new `isfull(c::Channel)` function can be used to check if `put!(c, some_value)` will block. ([#53159])
6262
* `waitany(tasks; throw=false)` and `waitall(tasks; failfast=false, throw=false)` which wait multiple tasks at once ([#53341]).
6363
* `uuid7()` creates an RFC 9652 compliant UUID with version 7 ([#54834]).
64+
* `wrap(Array, m::Union{MemoryRef{T}, Memory{T}}, dims)` is the safe counterpart to `unsafe_wrap` ([#52049]).
6465

6566
New library features
6667
--------------------

base/array.jl

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

base/exports.jl

+1
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

-25
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

+8-4
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

+1-4
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,7 @@ String(s::AbstractString) = print_to_string(s)
115115
@assume_effects :total String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s))
116116

117117
unsafe_wrap(::Type{Memory{UInt8}}, s::String) = ccall(:jl_string_to_genericmemory, Ref{Memory{UInt8}}, (Any,), s)
118-
function unsafe_wrap(::Type{Vector{UInt8}}, s::String)
119-
mem = unsafe_wrap(Memory{UInt8}, s)
120-
view(mem, eachindex(mem))
121-
end
118+
unsafe_wrap(::Type{Vector{UInt8}}, s::String) = wrap(Array, unsafe_wrap(Memory{UInt8}, s))
122119

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

doc/src/base/arrays.md

+1
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

+17-33
Original file line numberDiff line numberDiff line change
@@ -3215,39 +3215,23 @@ end
32153215
end
32163216
end
32173217

3218-
@testset "Wrapping Memory into Arrays with view and reshape" begin
3219-
mem::Memory{Int} = Memory{Int}(undef, 10) .= 11:20
3220-
3221-
@test_throws DimensionMismatch reshape(mem, 10, 10)
3222-
@test_throws DimensionMismatch reshape(mem, 5)
3223-
@test_throws BoundsError view(mem, 1:10, 1:10)
3224-
@test_throws BoundsError view(mem, 1:11)
3225-
@test_throws BoundsError view(mem, 3:11)
3226-
@test_throws BoundsError view(mem, 0:4)
3227-
3228-
@test @inferred(view(mem, 1:5))::Vector{Int} == 11:15
3229-
@test @inferred(view(mem, 1:2))::Vector{Int} == 11:12
3230-
@test @inferred(view(mem, 1:10))::Vector{Int} == 11:20
3231-
@test @inferred(view(mem, 3:8))::Vector{Int} == 13:18
3232-
@test @inferred(view(mem, 20:19))::Vector{Int} == []
3233-
@test @inferred(view(mem, -5:-7))::Vector{Int} == []
3234-
@test @inferred(view(mem, :))::Vector{Int} == mem
3235-
@test @inferred(reshape(mem, 5, 2))::Matrix{Int} == reshape(11:20, 5, 2)
3236-
3237-
empty_mem = Memory{Module}(undef, 0)
3238-
@test_throws BoundsError view(empty_mem, 0:1)
3239-
@test_throws BoundsError view(empty_mem, 1:2)
3240-
@test_throws DimensionMismatch reshape(empty_mem, 1)
3241-
@test_throws DimensionMismatch reshape(empty_mem, 1, 2, 3)
3242-
@test_throws ArgumentError reshape(empty_mem, 2^16, 2^16, 2^16, 2^16)
3243-
3244-
@test @inferred(view(empty_mem, 1:0))::Vector{Module} == []
3245-
@test @inferred(view(empty_mem, 10:3))::Vector{Module} == []
3246-
@test @inferred(view(empty_mem, :))::Vector{Module} == empty_mem
3247-
@test isempty(@inferred(reshape(empty_mem, 0, 7, 1))::Array{Module, 3})
3248-
3249-
offset_inds = OffsetArrays.IdOffsetRange(values=3:6, indices=53:56)
3250-
@test @inferred(view(collect(mem), offset_inds)) == view(mem, offset_inds)
3218+
@testset "Wrapping Memory into Arrays" begin
3219+
mem = Memory{Int}(undef, 10) .= 1
3220+
memref = memoryref(mem)
3221+
@test_throws DimensionMismatch wrap(Array, mem, (10, 10))
3222+
@test wrap(Array, mem, (5,)) == ones(Int, 5)
3223+
@test wrap(Array, mem, 2) == ones(Int, 2)
3224+
@test wrap(Array, memref, 10) == ones(Int, 10)
3225+
@test wrap(Array, memref, (2,2,2)) == ones(Int,2,2,2)
3226+
@test wrap(Array, mem, (5, 2)) == ones(Int, 5, 2)
3227+
3228+
memref2 = memoryref(mem, 3)
3229+
@test wrap(Array, memref2, (5,)) == ones(Int, 5)
3230+
@test wrap(Array, memref2, 2) == ones(Int, 2)
3231+
@test wrap(Array, memref2, (2,2,2)) == ones(Int,2,2,2)
3232+
@test wrap(Array, memref2, (3, 2)) == ones(Int, 3, 2)
3233+
@test_throws DimensionMismatch wrap(Array, memref2, 9)
3234+
@test_throws DimensionMismatch wrap(Array, memref2, 10)
32513235
end
32523236

32533237
@testset "Memory size" begin

0 commit comments

Comments
 (0)