-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Add takestring!(x) to create a string from the content of x, emptying it #54372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -682,6 +682,7 @@ export | |
split, | ||
string, | ||
strip, | ||
takestring!, | ||
textwidth, | ||
thisind, | ||
titlecase, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -198,7 +198,7 @@ julia> io = IOBuffer(); | |
julia> write(io, "JuliaLang is a GitHub organization.", " It has many members.") | ||
56 | ||
|
||
julia> String(take!(io)) | ||
julia> takestring!(io) | ||
"JuliaLang is a GitHub organization. It has many members." | ||
|
||
julia> io = IOBuffer(b"JuliaLang is a GitHub organization.") | ||
|
@@ -216,7 +216,7 @@ IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=fa | |
julia> write(io, "JuliaLang is a GitHub organization.") | ||
34 | ||
|
||
julia> String(take!(io)) | ||
julia> takestring!(io) | ||
"JuliaLang is a GitHub organization" | ||
|
||
julia> length(read(IOBuffer(b"data", read=true, truncate=false))) | ||
|
@@ -783,6 +783,80 @@ function take!(io::IOBuffer) | |
return data | ||
end | ||
|
||
"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) | ||
used_span = get_used_span(io) | ||
nbytes = length(used_span) | ||
from = first(used_span) | ||
isempty(used_span) && return "" | ||
# The C function can only copy from the start of the memory. | ||
# Fortunately, in most cases, the offset will be zero. | ||
return if isone(from) | ||
ccall(:jl_genericmemory_to_string, Ref{String}, (Any, Int), io.data, nbytes) | ||
else | ||
mem = StringMemory(nbytes % UInt) | ||
unsafe_copyto!(mem, 1, io.data, from, nbytes) | ||
unsafe_takestring(mem) | ||
end | ||
end | ||
|
||
""" | ||
takestring!(io::IOBuffer) -> String | ||
|
||
Return the content of `io` as a `String`, resetting the buffer to its initial | ||
state. | ||
This is preferred over calling `String(take!(io))` to create a string from | ||
an `IOBuffer`. | ||
|
||
# Examples | ||
```jldoctest | ||
julia> io = IOBuffer(); | ||
|
||
julia> write(io, [0x61, 0x62, 0x63]); | ||
|
||
julia> s = takestring!(io) | ||
"abc" | ||
|
||
julia> isempty(take!(io)) # io is now empty | ||
true | ||
``` | ||
|
||
!!! compat "Julia 1.13" | ||
This function requires at least Julia 1.13. | ||
""" | ||
function takestring!(io::IOBuffer) | ||
jakobnissen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# If the buffer has been used up and needs to be replaced, there are no bytes, and | ||
# we can return an empty string without interacting with the buffer at all. | ||
io.reinit && return "" | ||
|
||
# If the iobuffer is writable, taking will remove the buffer from `io`. | ||
# So, we reset the iobuffer, and directly unsafe takestring. | ||
return if io.writable | ||
s = unsafe_takestring!(io) | ||
io.reinit = true | ||
io.mark = -1 | ||
io.ptr = 1 | ||
io.size = 0 | ||
io.offset_or_compacted = 0 | ||
s | ||
else | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When IOBuffer is not writable, the IOBuffer doesn't take ownership of the memory, and allows it to be shared, e.g. between multiple IOBuffers (and that also makes IOBuffer more safe in general since the user can keep a reference to the memory they wrapped the IOBuffer around). Therefore, we need to copy it out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump - @JeffBezanson is this issue resolvable? If so, and there are no other objections, we can go ahead and merge. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JeffBezanson it wasn't clear whether this is blocking but given triage approval, I went ahead |
||
# If the buffer is not writable, taking will NOT remove the buffer, | ||
# so if we just converted the buffer to a string, garbage collecting | ||
# the string would free the memory underneath the iobuffer | ||
used_span = get_used_span(io) | ||
mem = StringMemory(length(used_span)) | ||
unsafe_copyto!(mem, 1, io.data, first(used_span), length(used_span)) | ||
unsafe_takestring(mem) | ||
end | ||
end | ||
|
||
jakobnissen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# Fallback methods | ||
takestring!(io::GenericIOBuffer) = String(take!(io)) | ||
|
||
""" | ||
_unsafe_take!(io::IOBuffer) | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.