diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift index 98da656640188..3ac1c1b45f03e 100644 --- a/stdlib/public/core/String.swift +++ b/stdlib/public/core/String.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -428,53 +428,49 @@ extension String { return } - /// Creates a new String with the specified capacity in UTF-8 code units then - /// calls the given closure with a buffer covering the String's uninitialized - /// memory. + /// Creates a new string with the specified capacity in UTF-8 code units, and + /// then calls the given closure with a buffer covering the string's + /// uninitialized memory. /// /// The closure should return the number of initialized code units, /// or 0 if it couldn't initialize the buffer (for example if the /// requested capacity was too small). /// /// This method replaces ill-formed UTF-8 sequences with the Unicode - /// replacement character (`"\u{FFFD}"`); This may require resizing + /// replacement character (`"\u{FFFD}"`). This may require resizing /// the buffer beyond its original capacity. /// /// The following examples use this initializer with the contents of two - /// different `UInt8` arrays---the first with well-formed UTF-8 code unit - /// sequences and the second with an ill-formed sequence at the end. + /// different `UInt8` arrays---the first with a well-formed UTF-8 code unit + /// sequence, and the second with an ill-formed sequence at the end. /// - /// let validUTF8: [UInt8] = [67, 97, 102, -61, -87, 0] - /// let s = String(unsafeUninitializedCapacity: validUTF8.count, - /// initializingUTF8With: { ptr in - /// ptr.initializeFrom(validUTF8) + /// 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 - /// }) - /// // Prints "Café" + /// } + /// // cafe1 == "Café" /// - /// let invalidUTF8: [UInt8] = [67, 97, 102, -61, 0] - /// let s = String(unsafeUninitializedCapacity: invalidUTF8.count, - /// initializingUTF8With: { ptr in - /// ptr.initializeFrom(invalidUTF8) + /// let cafe2 = String(unsafeUninitializedCapacity: invalidUTF8.count) { + /// _ = $0.initialize(from: invalidUTF8) /// return invalidUTF8.count - /// }) - /// // Prints "Caf�" + /// } + /// // cafe2 == "Caf�" /// - /// let s = String(unsafeUninitializedCapacity: invalidUTF8.count, - /// initializingUTF8With: { ptr in - /// ptr.initializeFrom(invalidUTF8) + /// let empty = String(unsafeUninitializedCapacity: 16) { _ in + /// // Can't initialize the buffer (e.g. the capacity is too small). /// return 0 - /// }) - /// // Prints "" + /// } + /// // empty == "" /// /// - Parameters: /// - capacity: The number of UTF-8 code units worth of memory to allocate - /// for the String. - /// - initializer: A closure that initializes elements and sets the count of - /// the new String - /// - Parameters: - /// - buffer: A buffer covering uninitialized memory with room for the - /// specified number of UTF-8 code units. + /// for the string (excluding the null terminator). + /// - initializer: A closure that accepts a buffer covering uninitialized + /// memory with room for `capacity` UTF-8 code units, initializes + /// that memory, and returns the number of initialized elements. @inline(__always) @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) public init( @@ -484,14 +480,14 @@ extension String { ) throws -> Int ) rethrows { self = try String( - uninitializedCapacity: capacity, + _uninitializedCapacity: capacity, initializingUTF8With: initializer ) } @inline(__always) internal init( - uninitializedCapacity capacity: Int, + _uninitializedCapacity capacity: Int, initializingUTF8With initializer: ( _ buffer: UnsafeMutableBufferPointer ) throws -> Int @@ -800,7 +796,7 @@ extension String { public func lowercased() -> String { if _fastPath(_guts.isFastASCII) { return _guts.withFastUTF8 { utf8 in - return String(uninitializedCapacity: utf8.count) { buffer in + return String(_uninitializedCapacity: utf8.count) { buffer in for i in 0 ..< utf8.count { buffer[i] = _lowercaseASCII(utf8[i]) } @@ -860,7 +856,7 @@ extension String { public func uppercased() -> String { if _fastPath(_guts.isFastASCII) { return _guts.withFastUTF8 { utf8 in - return String(uninitializedCapacity: utf8.count) { buffer in + return String(_uninitializedCapacity: utf8.count) { buffer in for i in 0 ..< utf8.count { buffer[i] = _uppercaseASCII(utf8[i]) } diff --git a/stdlib/public/core/StringGutsRangeReplaceable.swift b/stdlib/public/core/StringGutsRangeReplaceable.swift index ed296670bf07e..344894871da89 100644 --- a/stdlib/public/core/StringGutsRangeReplaceable.swift +++ b/stdlib/public/core/StringGutsRangeReplaceable.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -97,7 +97,7 @@ extension _StringGuts { @inline(never) // slow-path private mutating func _foreignGrow(_ n: Int) { - let newString = String(uninitializedCapacity: n) { buffer in + let newString = String(_uninitializedCapacity: n) { buffer in guard let count = _foreignCopyUTF8(into: buffer) else { fatalError("String capacity was smaller than required") } @@ -161,7 +161,7 @@ extension _StringGuts { @inline(never) @_effects(readonly) private func _foreignConvertedToSmall() -> _SmallString { - let smol = String(uninitializedCapacity: _SmallString.capacity) { buffer in + let smol = String(_uninitializedCapacity: _SmallString.capacity) { buffer in guard let count = _foreignCopyUTF8(into: buffer) else { fatalError("String capacity was smaller than required") } diff --git a/test/stdlib/StringCreate.swift b/test/stdlib/StringCreate.swift index 25903f6b7b536..be0798b74fa7a 100644 --- a/test/stdlib/StringCreate.swift +++ b/test/stdlib/StringCreate.swift @@ -6,28 +6,20 @@ defer { runAllTests() } var StringCreateTests = TestSuite("StringCreateTests") -enum SimpleString: String { +enum SimpleString: String, CaseIterable { case smallASCII = "abcdefg" case smallUnicode = "abéÏ𓀀" case largeASCII = "012345678901234567890" case largeUnicode = "abéÏ012345678901234567890𓀀" case emoji = "😀😃🤢🤮👩🏿‍🎤🧛🏻‍♂️🧛🏻‍♂️👩‍👩‍👦‍👦" + case empty = "" } -let simpleStrings: [String] = [ - SimpleString.smallASCII.rawValue, - SimpleString.smallUnicode.rawValue, - SimpleString.largeASCII.rawValue, - SimpleString.largeUnicode.rawValue, - SimpleString.emoji.rawValue, - "", -] - extension String { var utf32: [UInt32] { return unicodeScalars.map { $0.value } } } -StringCreateTests.test("String(decoding:as)") { +StringCreateTests.test("String(decoding:as:)") { func validateDecodingAs(_ str: String) { // Non-contiguous (maybe) storage expectEqual(str, String(decoding: str.utf8, as: UTF8.self)) @@ -41,8 +33,8 @@ StringCreateTests.test("String(decoding:as)") { } - for str in simpleStrings { - validateDecodingAs(str) + for simpleString in SimpleString.allCases { + validateDecodingAs(simpleString.rawValue) } // Corner-case: UBP with null pointer (https://bugs.swift.org/browse/SR-9869) @@ -54,3 +46,37 @@ StringCreateTests.test("String(decoding:as)") { "", String(decoding: UnsafeBufferPointer(_empty: ()), as: UTF32.self)) } +if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + StringCreateTests.test("String(unsafeUninitializedCapacity:initializingUTF8With:)") { + for simpleString in SimpleString.allCases { + let expected = simpleString.rawValue + let expectedUTF8 = expected.utf8 + let actual = String(unsafeUninitializedCapacity: expectedUTF8.count) { + _ = $0.initialize(from: expectedUTF8) + return expectedUTF8.count + } + 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 + } + expectEqual("Caf�", cafe2) + + let empty = String(unsafeUninitializedCapacity: 16) { _ in + // Can't initialize the buffer (e.g. the capacity is too small). + return 0 + } + expectTrue(empty.isEmpty) + } +}