Skip to content

Commit 2b3a0f0

Browse files
authored
Make String(::Memory) copy (#54457)
A more targeted fix of #54369 than #54372 Preserves the performance improvements added in #53962 by creating a new internal `_unsafe_takestring!(v::Memory{UInt8})` function that does what `String(::Memory{UInt8})` used to do.
1 parent db3d816 commit 2b3a0f0

File tree

7 files changed

+29
-17
lines changed

7 files changed

+29
-17
lines changed

base/gmp.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor,
1111
bin, oct, dec, hex, isequal, invmod, _prevpow2, _nextpow2, ndigits0zpb,
1212
widen, signed, unsafe_trunc, trunc, iszero, isone, big, flipsign, signbit,
1313
sign, hastypemax, isodd, iseven, digits!, hash, hash_integer, top_set_bit,
14-
clamp
14+
clamp, unsafe_takestring
1515

1616
if Clong == Int32
1717
const ClongMax = Union{Int8, Int16, Int32}
@@ -761,7 +761,7 @@ function string(n::BigInt; base::Integer = 10, pad::Integer = 1)
761761
sv[i] = '0' % UInt8
762762
end
763763
isneg(n) && (sv[1] = '-' % UInt8)
764-
String(sv)
764+
unsafe_takestring(sv)
765765
end
766766

767767
function digits!(a::AbstractVector{T}, n::BigInt; base::Integer = 10) where {T<:Integer}

base/intfuncs.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ function bin(x::Unsigned, pad::Int, neg::Bool)
792792
i -= 1
793793
end
794794
neg && (@inbounds a[1] = 0x2d) # UInt8('-')
795-
String(a)
795+
unsafe_takestring(a)
796796
end
797797

798798
function oct(x::Unsigned, pad::Int, neg::Bool)
@@ -806,7 +806,7 @@ function oct(x::Unsigned, pad::Int, neg::Bool)
806806
i -= 1
807807
end
808808
neg && (@inbounds a[1] = 0x2d) # UInt8('-')
809-
String(a)
809+
unsafe_takestring(a)
810810
end
811811

812812
# 2-digit decimal characters ("00":"99")
@@ -876,7 +876,7 @@ function dec(x::Unsigned, pad::Int, neg::Bool)
876876
a = StringMemory(n)
877877
append_c_digits_fast(n, x, a, 1)
878878
neg && (@inbounds a[1] = 0x2d) # UInt8('-')
879-
String(a)
879+
unsafe_takestring(a)
880880
end
881881

882882
function hex(x::Unsigned, pad::Int, neg::Bool)
@@ -897,7 +897,7 @@ function hex(x::Unsigned, pad::Int, neg::Bool)
897897
@inbounds a[i] = d + ifelse(d > 0x9, 0x57, 0x30)
898898
end
899899
neg && (@inbounds a[1] = 0x2d) # UInt8('-')
900-
String(a)
900+
unsafe_takestring(a)
901901
end
902902

903903
const base36digits = UInt8['0':'9';'a':'z']
@@ -922,7 +922,7 @@ function _base(base::Integer, x::Integer, pad::Int, neg::Bool)
922922
i -= 1
923923
end
924924
neg && (@inbounds a[1] = 0x2d) # UInt8('-')
925-
String(a)
925+
unsafe_takestring(a)
926926
end
927927

928928
split_sign(n::Integer) = unsigned(abs(n)), n < 0
@@ -998,7 +998,7 @@ function bitstring(x::T) where {T}
998998
x = lshr_int(x, 4)
999999
i -= 4
10001000
end
1001-
return String(str)
1001+
return unsafe_takestring(str)
10021002
end
10031003

10041004
"""

base/strings/string.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,7 @@ by [`take!`](@ref) on a writable [`IOBuffer`](@ref) and by calls to
6161
In other cases, `Vector{UInt8}` data may be copied, but `v` is truncated anyway
6262
to guarantee consistent behavior.
6363
"""
64-
String(v::AbstractVector{UInt8}) = String(copyto!(StringMemory(length(v)), v))
65-
function String(v::Memory{UInt8})
66-
len = length(v)
67-
len == 0 && return ""
68-
return ccall(:jl_genericmemory_to_string, Ref{String}, (Any, Int), v, len)
69-
end
64+
String(v::AbstractVector{UInt8}) = unsafe_takestring(copyto!(StringMemory(length(v)), v))
7065
function String(v::Vector{UInt8})
7166
#return ccall(:jl_array_to_string, Ref{String}, (Any,), v)
7267
len = length(v)
@@ -83,6 +78,12 @@ function String(v::Vector{UInt8})
8378
return str
8479
end
8580

81+
"Create a string re-using the memory, if possible.
82+
Mutating or reading the memory after calling this function is undefined behaviour."
83+
function unsafe_takestring(m::Memory{UInt8})
84+
isempty(m) ? "" : ccall(:jl_genericmemory_to_string, Ref{String}, (Any, Int), m, length(m))
85+
end
86+
8687
"""
8788
unsafe_string(p::Ptr{UInt8}, [length::Integer])
8889

base/strings/util.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ function bytes2hex(itr)
12171217
b[2i - 1] = hex_chars[1 + x >> 4]
12181218
b[2i ] = hex_chars[1 + x & 0xf]
12191219
end
1220-
return String(b)
1220+
return unsafe_takestring(b)
12211221
end
12221222

12231223
function bytes2hex(io::IO, itr)

base/uuid.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ let groupings = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1]
9898
u >>= 4
9999
end
100100
@inbounds a[24] = a[19] = a[14] = a[9] = '-'
101-
return String(a)
101+
return unsafe_takestring(a)
102102
end
103103
end
104104

stdlib/FileWatching/src/pidfile.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ function open_exclusive(path::String;
304304
end
305305

306306
function _rand_filename(len::Int=4) # modified from Base.Libc
307-
slug = Base.StringMemory(len)
307+
slug = Base.StringVector(len)
308308
chars = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
309309
for i = 1:len
310310
slug[i] = chars[(Libc.rand() % length(chars)) + 1]

test/strings/basic.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,17 @@ let v = [0x40,0x41,0x42]
10931093
@test String(view(v, 2:3)) == "AB"
10941094
end
10951095

1096+
# issue #54369
1097+
let v = Base.StringMemory(3)
1098+
v .= [0x41,0x42,0x43]
1099+
s = String(v)
1100+
@test s == "ABC"
1101+
@test v == [0x41,0x42,0x43]
1102+
v[1] = 0x43
1103+
@test s == "ABC"
1104+
@test v == [0x43,0x42,0x43]
1105+
end
1106+
10961107
# make sure length for identical String and AbstractString return the same value, PR #25533
10971108
let rng = MersenneTwister(1), strs = ["∀εa∀aε"*String(rand(rng, UInt8, 100))*"∀εa∀aε",
10981109
String(rand(rng, UInt8, 200))]

0 commit comments

Comments
 (0)