Skip to content

Commit 273d91e

Browse files
Make reshape and view on Memory produce Arrays and delete wrap (#53896)
- Make reshape and view with one based indexing on Memory produce Arrays - delete wrap Implements #53552 (comment) --------- Co-authored-by: Jameson Nash <[email protected]>
1 parent c707a53 commit 273d91e

File tree

8 files changed

+63
-80
lines changed

8 files changed

+63
-80
lines changed

HISTORY.md

-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ New library functions
8686
* `copyuntil(out, io, delim)` and `copyline(out, io)` copy data into an `out::IO` stream ([#48273]).
8787
* `eachrsplit(string, pattern)` iterates split substrings right to left.
8888
* `Sys.username()` can be used to return the current user's username ([#51897]).
89-
* `wrap(Array, m::Union{MemoryRef{T}, Memory{T}}, dims)` is the safe counterpart to `unsafe_wrap` ([#52049]).
9089
* `GC.logging_enabled()` can be used to test whether GC logging has been enabled via `GC.enable_logging` ([#51647]).
9190
* `IdSet` is now exported from Base and considered public ([#53262]).
9291

base/array.jl

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

base/exports.jl

-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,6 @@ export
460460
vcat,
461461
vec,
462462
view,
463-
wrap,
464463
zeros,
465464

466465
# search, find, match and related functions

base/genericmemory.jl

+24
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,27 @@ function indcopy(sz::Dims, I::GenericMemory)
288288
src = eltype(I)[I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n]
289289
dst, src
290290
end
291+
292+
# Wrapping a memory region in an Array
293+
@eval begin # @eval for the Array construction. Block for the docstring.
294+
function reshape(m::GenericMemory{M, T}, dims::Vararg{Int, N}) where {M, T, N}
295+
len = Core.checked_dims(dims...)
296+
length(m) == len || throw(DimensionMismatch("parent has $(length(m)) elements, which is incompatible with size $(dims)"))
297+
ref = MemoryRef(m)
298+
$(Expr(:new, :(Array{T, N}), :ref, :dims))
299+
end
300+
301+
"""
302+
view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo})
303+
304+
Create a vector `v::Vector{T}` backed by the specified indices of `m`. It is only safe to
305+
resize `v` if `m` is subseqently not used.
306+
"""
307+
function view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo}) where {M, T}
308+
isempty(inds) && return T[] # needed to allow view(Memory{T}(undef, 0), 2:1)
309+
@boundscheck checkbounds(m, inds)
310+
ref = MemoryRef(m, first(inds)) # @inbounds would be safe here but does not help performance.
311+
dims = (length(inds),)
312+
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
313+
end
314+
end

base/iobuffer.jl

+4-8
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) = wrap(Array, StringMemory(n))
45+
StringVector(n::Integer) = view(StringMemory(n), 1:n)::Vector{UInt8}
4646

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

@@ -456,7 +456,7 @@ function take!(io::IOBuffer)
456456
if nbytes == 0 || io.reinit
457457
data = StringVector(0)
458458
elseif io.writable
459-
data = wrap(Array, MemoryRef(io.data, io.offset + 1), nbytes)
459+
data = view(io.data, io.offset+1:nbytes+io.offset)
460460
else
461461
data = copyto!(StringVector(io.size), 1, io.data, io.offset + 1, nbytes)
462462
end
@@ -465,7 +465,7 @@ function take!(io::IOBuffer)
465465
if nbytes == 0
466466
data = StringVector(0)
467467
elseif io.writable
468-
data = wrap(Array, MemoryRef(io.data, io.ptr), nbytes)
468+
data = view(io.data, io.ptr:io.ptr+nbytes-1)
469469
else
470470
data = read!(io, data)
471471
end
@@ -491,11 +491,7 @@ state. This should only be used internally for performance-critical
491491
It might save an allocation compared to `take!` (if the compiler elides the
492492
Array allocation), as well as omits some checks.
493493
"""
494-
_unsafe_take!(io::IOBuffer) =
495-
wrap(Array, io.size == io.offset ?
496-
MemoryRef(Memory{UInt8}()) :
497-
MemoryRef(io.data, io.offset + 1),
498-
io.size - io.offset)
494+
_unsafe_take!(io::IOBuffer) = view(io.data, io.offset+1:io.size)
499495

500496
function write(to::IO, from::GenericIOBuffer)
501497
written::Int = bytesavailable(from)

base/strings/string.jl

+4-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ 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-
unsafe_wrap(::Type{Vector{UInt8}}, s::String) = wrap(Array, unsafe_wrap(Memory{UInt8}, s))
120+
function unsafe_wrap(::Type{Vector{UInt8}}, s::String)
121+
mem = unsafe_wrap(Memory{UInt8}, s)
122+
view(mem, eachindex(mem))
123+
end
121124

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

doc/src/base/arrays.md

-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ Base.reshape
139139
Base.dropdims
140140
Base.vec
141141
Base.SubArray
142-
Base.wrap
143142
```
144143

145144
## Concatenation and permutation

test/arrayops.jl

+31-17
Original file line numberDiff line numberDiff line change
@@ -3187,23 +3187,37 @@ end
31873187
end
31883188
end
31893189

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)
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(reshape(mem, 5, 2))::Matrix{Int} == reshape(11:20, 5, 2)
3207+
3208+
empty_mem = Memory{Module}(undef, 0)
3209+
@test_throws BoundsError view(empty_mem, 0:1)
3210+
@test_throws BoundsError view(empty_mem, 1:2)
3211+
@test_throws DimensionMismatch reshape(empty_mem, 1)
3212+
@test_throws DimensionMismatch reshape(empty_mem, 1, 2, 3)
3213+
@test_throws ArgumentError reshape(empty_mem, 2^16, 2^16, 2^16, 2^16)
3214+
3215+
@test @inferred(view(empty_mem, 1:0))::Vector{Module} == []
3216+
@test @inferred(view(empty_mem, 10:3))::Vector{Module} == []
3217+
@test isempty(@inferred(reshape(empty_mem, 0, 7, 1))::Array{Module, 3})
3218+
3219+
offset_inds = OffsetArrays.IdOffsetRange(values=3:6, indices=53:56)
3220+
@test @inferred(view(collect(mem), offset_inds)) == view(mem, offset_inds)
32073221
end
32083222

32093223
@testset "Memory size" begin

0 commit comments

Comments
 (0)