Skip to content

Commit

Permalink
stream: fix reading LibuvStream into array
Browse files Browse the repository at this point in the history
Adds a new abstraction `take!(::Array{T,N}, ::Array{T,N})` for doing an
efficient `copyto!` equivalent. Previously it was assumed that `compact`
did this automatically, which wasn't a great assumption.

Fixes #56078
  • Loading branch information
vtjnash committed Oct 18, 2024
1 parent e5aff12 commit 21c7423
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 8 deletions.
11 changes: 11 additions & 0 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,17 @@ copy
return $(Expr(:new, :(typeof(a)), :(memoryref(newmem)), :(a.size)))
end

# a mutating version of copyto! that results in dst aliasing src afterwards
function _take!(dst::Array{T,N}, src::Array{T,N}) where {T,N}
if getfield(dst, :ref) !== getfield(src, :ref)
setfield!(dst, :ref, getfield(src, :ref))
end
if getfield(dst, :size) !== getfield(src, :size)
setfield!(dst, :size, getfield(src, :size))
end
return dst
end

## Constructors ##

similar(a::Array{T,1}) where {T} = Vector{T}(undef, size(a,1))
Expand Down
4 changes: 3 additions & 1 deletion base/stream.jl
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,7 @@ function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int)
if bytesavailable(sbuf) >= nb
nread = readbytes!(sbuf, a, nb)
else
initsize = length(a)
newbuf = PipeBuffer(a, maxsize=nb)
newbuf.size = newbuf.offset # reset the write pointer to the beginning
nread = try
Expand All @@ -951,7 +952,8 @@ function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int)
finally
s.buffer = sbuf
end
compact(newbuf)
_take!(a, _unsafe_take!(newbuf))
length(a) >= initsize || resize!(a, initsize)
end
iolock_end()
return nread
Expand Down
22 changes: 15 additions & 7 deletions test/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,27 @@ for (name, f) in l
n2 = readbytes!(s2, a2)
@test n1 == n2
@test length(a1) == length(a2)
@test a1[1:n1] == a2[1:n2]
let l = min(l, n)
@test a1[1:l] == a2[1:l]
end
@test n <= length(text) || eof(s1)
@test n <= length(text) || eof(s2)

cleanup()
end

# Test growing output array
let x = UInt8[],
io = io()
n = readbytes!(io, x)
@test n == 0
@test isempty(x)
n = readbytes!(io, x, typemax(Int))
@test n == length(x)
@test x == codeunits(text)
cleanup()
end

verbose && println("$name read!...")
l = length(text)
for n = [1, 2, l-2, l-1, l]
Expand Down Expand Up @@ -477,12 +491,6 @@ let s = "qwerty"
@test read(IOBuffer(s)) == codeunits(s)
@test read(IOBuffer(s), 10) == codeunits(s)
@test read(IOBuffer(s), 1) == codeunits(s)[1:1]

# Test growing output array
x = UInt8[]
n = readbytes!(IOBuffer(s), x, 10)
@test x == codeunits(s)
@test n == length(x)
end


Expand Down

0 comments on commit 21c7423

Please sign in to comment.