Skip to content

Commit

Permalink
Address reviewer comments
Browse files Browse the repository at this point in the history
* Do not assume io.seekable in unsafe_takestring!(::IOBuffer)
* Add fallback definitions unsafe_takestring!(::GenericIOBuffer) and
  takestring!(::GenericIOBuffer)
* Test takestring! with a nonzero offset in buffer
  • Loading branch information
jakobnissen committed Jul 24, 2024
1 parent b2a10c3 commit be1e354
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 10 deletions.
21 changes: 11 additions & 10 deletions base/iobuffer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -489,23 +489,23 @@ function take!(io::IOBuffer)
return data
end

"Internal method. This method can be faster than takestring!, because it assumes
the buffer is seekable, and because it does not reset the buffer to a usable state.
"Internal method. This method can be faster than takestring!, because it does not
reset the buffer to a usable state, and it does not check for io.reinit.
Using the buffer after calling unsafe_takestring! may cause undefined behaviour.
This function is meant to be used when the buffer is only used as a temporary
string builder, which is discarded after the string is built."
function unsafe_takestring!(io::IOBuffer)
off = io.offset
nbytes = io.size - off
start = io.seekable ? io.offset + 1 : io.ptr
nbytes = io.size - start + 1
iszero(nbytes) && return ""
# The C function can only copy from the start of the memory.
# Fortunately, in most cases, the offset will be zero.
return if iszero(off)
return if isone(start)
ccall(:jl_genericmemory_to_string, Ref{String}, (Any, Int), io.data, nbytes)
else
mem = StringMemory(nbytes)
unsafe_copyto!(mem, 1, io.data, off + 1, nbytes)
unsafe_takestring!(mem)
unsafe_copyto!(mem, 1, io.data, start, nbytes)
unsafe_takestring(mem)
end
end

Expand Down Expand Up @@ -538,9 +538,6 @@ function takestring!(io::IOBuffer)
# we can return an empty string without interacting with the buffer at all.
io.reinit && return ""

# Currently, `IOBuffer`s (as opposed to `GenericIOBuffer`) is always seekable, since all
# IOBuffer constructors return seekable buffers, and GenericIOBuffer is internal.
# This makes `unsafe_takestring!` valid.
s = unsafe_takestring!(io)

# Restore the buffer to a usable state, making it no longer undefined behaviour to
Expand All @@ -558,6 +555,10 @@ function takestring!(io::IOBuffer)
s
end

# Fallback methods
unsafe_takestring!(io::GenericIOBuffer) = takestring!(io)
takestring!(io::GenericIOBuffer) = String(take!(io))

"""
_unsafe_take!(io::IOBuffer)
Expand Down
9 changes: 9 additions & 0 deletions test/iobuffer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ end
write(buf, "xyz")
@test takestring!(buf) == "xyz"
buf = IOBuffer()

# Test with a nonzero offset in the buffer
v = rand(UInt8, 8)
for i in 1:8
pushfirst!(v, rand(UInt8))
end
buf = IOBuffer(v)
s = String(copy(v))
@test takestring!(buf) == s
end

@testset "Read/write readonly IOBuffer" begin
Expand Down

0 comments on commit be1e354

Please sign in to comment.