Skip to content

Commit 1389dce

Browse files
authored
Merge pull request #39105 from Catfish-Man/uninitialized-invalid-2
Keep the buffer in the invalid UTF8 handling path of String(unsafeUninitializedCapacity:initializingWith:) alive while we're using it
2 parents d844d0b + 3f716e3 commit 1389dce

File tree

2 files changed

+25
-13
lines changed

2 files changed

+25
-13
lines changed

stdlib/public/core/StringCreate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ extension String {
120120
)
121121
return result.asString
122122
case .error(let initialRange):
123+
defer { _fixLifetime(result) }
123124
//This could be optimized to use excess tail capacity
124125
return repairUTF8(result.codeUnits, firstKnownBrokenRange: initialRange)
125126
}

test/stdlib/StringCreate.swift

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,33 @@ if #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
5757
}
5858
expectEqual(expected, actual)
5959
}
60-
60+
6161
let validUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3, 0xA9]
6262
let invalidUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3]
63-
64-
let cafe1 = String(unsafeUninitializedCapacity: validUTF8.count) {
65-
_ = $0.initialize(from: validUTF8)
66-
return validUTF8.count
67-
}
68-
expectEqual("Café", cafe1)
69-
70-
let cafe2 = String(unsafeUninitializedCapacity: invalidUTF8.count) {
71-
_ = $0.initialize(from: invalidUTF8)
72-
return invalidUTF8.count
63+
let longerValidUTF8: [UInt8] = [0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x43, 0x61, 0x66, 0xC3, 0xA9]
64+
let longerInvalidUTF8: [UInt8] = [0x21, 0x21, 0x43, 0x61, 0x66, 0xC3, 0x43, 0x61, 0x66, 0xC3, 0x43, 0x61, 0x66, 0xC3]
65+
66+
func test(bufferSize: Int, input: [UInt8], expected: String) {
67+
let strs = (0..<100).map { _ in
68+
String(unsafeUninitializedCapacity: bufferSize) { buffer in
69+
_ = buffer.initialize(from: input)
70+
return input.count
71+
}
72+
}
73+
for str in strs {
74+
expectEqual(expected, str)
75+
}
7376
}
74-
expectEqual("Caf�", cafe2)
75-
77+
78+
test(bufferSize: validUTF8.count, input: validUTF8, expected: "Café")
79+
test(bufferSize: invalidUTF8.count, input: invalidUTF8, expected: "Caf�")
80+
// Force non-smol strings by using a larger capacity
81+
test(bufferSize: 16, input: validUTF8, expected: "Café")
82+
test(bufferSize: 16, input: invalidUTF8, expected: "Caf�")
83+
test(bufferSize: longerValidUTF8.count, input: longerValidUTF8, expected: "!!!!!!!!!!Café")
84+
test(bufferSize: longerInvalidUTF8.count, input: longerInvalidUTF8, expected: "!!Caf�Caf�Caf�")
85+
test(bufferSize: 16, input: longerValidUTF8, expected: "!!!!!!!!!!Café")
86+
test(bufferSize: 16, input: longerInvalidUTF8, expected: "!!Caf�Caf�Caf�")
7687
let empty = String(unsafeUninitializedCapacity: 16) { _ in
7788
// Can't initialize the buffer (e.g. the capacity is too small).
7889
return 0

0 commit comments

Comments
 (0)