From 6787b733348c07b4fc5d9c3454c6ffff61a0f2a2 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 31 Aug 2021 08:51:33 -0700 Subject: [PATCH 1/2] Keep the __StringStorage alive while we're using its buffer (cherry picked from commit 89144300fb8eebe4e9a4b863167ab07580fafb74) --- stdlib/public/core/StringCreate.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/core/StringCreate.swift b/stdlib/public/core/StringCreate.swift index d30fd0d52b67d..4ea7b11eb0573 100644 --- a/stdlib/public/core/StringCreate.swift +++ b/stdlib/public/core/StringCreate.swift @@ -120,6 +120,7 @@ extension String { ) return result.asString case .error(let initialRange): + defer { _fixLifetime(result) } //This could be optimized to use excess tail capacity return repairUTF8(result.codeUnits, firstKnownBrokenRange: initialRange) } From 09df30a0a9b2d38511de47eae108ace35328e7f8 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 31 Aug 2021 09:14:49 -0700 Subject: [PATCH 2/2] Tests for invalid UTF8 handling (cherry picked from commit 3f716e3e0f68cfb84ed39ec9df8efe4f85116666) --- test/stdlib/StringCreate.swift | 37 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/test/stdlib/StringCreate.swift b/test/stdlib/StringCreate.swift index e31a6788e67d4..183f99c8b052e 100644 --- a/test/stdlib/StringCreate.swift +++ b/test/stdlib/StringCreate.swift @@ -57,22 +57,33 @@ if #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { } expectEqual(expected, actual) } - + let validUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3, 0xA9] let invalidUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3] - - let cafe1 = String(unsafeUninitializedCapacity: validUTF8.count) { - _ = $0.initialize(from: validUTF8) - return validUTF8.count - } - expectEqual("Café", cafe1) - - let cafe2 = String(unsafeUninitializedCapacity: invalidUTF8.count) { - _ = $0.initialize(from: invalidUTF8) - return invalidUTF8.count + let longerValidUTF8: [UInt8] = [0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x43, 0x61, 0x66, 0xC3, 0xA9] + let longerInvalidUTF8: [UInt8] = [0x21, 0x21, 0x43, 0x61, 0x66, 0xC3, 0x43, 0x61, 0x66, 0xC3, 0x43, 0x61, 0x66, 0xC3] + + func test(bufferSize: Int, input: [UInt8], expected: String) { + let strs = (0..<100).map { _ in + String(unsafeUninitializedCapacity: bufferSize) { buffer in + _ = buffer.initialize(from: input) + return input.count + } + } + for str in strs { + expectEqual(expected, str) + } } - expectEqual("Caf�", cafe2) - + + test(bufferSize: validUTF8.count, input: validUTF8, expected: "Café") + test(bufferSize: invalidUTF8.count, input: invalidUTF8, expected: "Caf�") + // Force non-smol strings by using a larger capacity + test(bufferSize: 16, input: validUTF8, expected: "Café") + test(bufferSize: 16, input: invalidUTF8, expected: "Caf�") + test(bufferSize: longerValidUTF8.count, input: longerValidUTF8, expected: "!!!!!!!!!!Café") + test(bufferSize: longerInvalidUTF8.count, input: longerInvalidUTF8, expected: "!!Caf�Caf�Caf�") + test(bufferSize: 16, input: longerValidUTF8, expected: "!!!!!!!!!!Café") + test(bufferSize: 16, input: longerInvalidUTF8, expected: "!!Caf�Caf�Caf�") let empty = String(unsafeUninitializedCapacity: 16) { _ in // Can't initialize the buffer (e.g. the capacity is too small). return 0