//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// import SwiftShims /// A type that can represent a string as a collection of characters. /// /// Do not declare new conformances to `StringProtocol`. Only the `String` and /// `Substring` types in the standard library are valid conforming types. public protocol StringProtocol : BidirectionalCollection, TextOutputStream, TextOutputStreamable, LosslessStringConvertible, ExpressibleByStringLiteral, Hashable, Comparable where Iterator.Element == Character, SubSequence : StringProtocol { associatedtype UTF8View : /*Bidirectional*/Collection where UTF8View.Element == UInt8 // Unicode.UTF8.CodeUnit associatedtype UTF16View : BidirectionalCollection where UTF16View.Element == UInt16 // Unicode.UTF16.CodeUnit associatedtype UnicodeScalarView : BidirectionalCollection where UnicodeScalarView.Element == Unicode.Scalar var utf8: UTF8View { get } var utf16: UTF16View { get } var unicodeScalars: UnicodeScalarView { get } #if _runtime(_ObjC) func hasPrefix(_ prefix: String) -> Bool func hasSuffix(_ prefix: String) -> Bool #endif func lowercased() -> String func uppercased() -> String /// Creates a string from the given Unicode code units in the specified /// encoding. /// /// - Parameters: /// - codeUnits: A collection of code units encoded in the encoding /// specified in `sourceEncoding`. /// - sourceEncoding: The encoding in which `codeUnits` should be /// interpreted. init( decoding codeUnits: C, as sourceEncoding: Encoding.Type ) where C.Iterator.Element == Encoding.CodeUnit /// Creates a string from the null-terminated, UTF-8 encoded sequence of /// bytes at the given pointer. /// /// - Parameter nullTerminatedUTF8: A pointer to a sequence of contiguous, /// UTF-8 encoded bytes ending just before the first zero byte. init(cString nullTerminatedUTF8: UnsafePointer) /// Creates a string from the null-terminated sequence of bytes at the given /// pointer. /// /// - Parameters: /// - nullTerminatedCodeUnits: A pointer to a sequence of contiguous code /// units in the encoding specified in `sourceEncoding`, ending just /// before the first zero code unit. /// - sourceEncoding: The encoding in which the code units should be /// interpreted. init( decodingCString nullTerminatedCodeUnits: UnsafePointer, as sourceEncoding: Encoding.Type) /// Calls the given closure with a pointer to the contents of the string, /// represented as a null-terminated sequence of UTF-8 code units. /// /// The pointer passed as an argument to `body` is valid only during the /// execution of `withCString(_:)`. Do not store or return the pointer for /// later use. /// /// - Parameter body: A closure with a pointer parameter that points to a /// null-terminated sequence of UTF-8 code units. If `body` has a return /// value, that value is also used as the return value for the /// `withCString(_:)` method. The pointer argument is valid only for the /// duration of the method's execution. /// - Returns: The return value, if any, of the `body` closure parameter. func withCString( _ body: (UnsafePointer) throws -> Result) rethrows -> Result /// Calls the given closure with a pointer to the contents of the string, /// represented as a null-terminated sequence of code units. /// /// The pointer passed as an argument to `body` is valid only during the /// execution of `withCString(encodedAs:_:)`. Do not store or return the /// pointer for later use. /// /// - Parameters: /// - body: A closure with a pointer parameter that points to a /// null-terminated sequence of code units. If `body` has a return /// value, that value is also used as the return value for the /// `withCString(encodedAs:_:)` method. The pointer argument is valid /// only for the duration of the method's execution. /// - targetEncoding: The encoding in which the code units should be /// interpreted. /// - Returns: The return value, if any, of the `body` closure parameter. func withCString( encodedAs targetEncoding: Encoding.Type, _ body: (UnsafePointer) throws -> Result ) rethrows -> Result /// The entire String onto whose slice this view is a projection. var _wholeString : String { get } /// The range of storage offsets of this view in `_wholeString`. var _encodedOffsetRange : Range { get } } extension StringProtocol { public var _wholeString: String { return String(self) } public var _encodedOffsetRange: Range { return 0 ..< numericCast(self.utf16.count) } } extension StringProtocol { //@available(swift, deprecated: 3.2, obsoleted: 4.0, message: "Please use the StringProtocol itself") //public var characters: Self { return self } @available(swift, deprecated: 3.2, obsoleted: 4.0, renamed: "UTF8View.Index") public typealias UTF8Index = UTF8View.Index @available(swift, deprecated: 3.2, obsoleted: 4.0, renamed: "UTF16View.Index") public typealias UTF16Index = UTF16View.Index @available(swift, deprecated: 3.2, obsoleted: 4.0, renamed: "UnicodeScalarView.Index") public typealias UnicodeScalarIndex = UnicodeScalarView.Index } /// A protocol that provides fast access to a known representation of String. /// /// Can be used to specialize generic functions that would otherwise end up /// doing grapheme breaking to vend individual characters. @_versioned // FIXME(sil-serialize-all) internal protocol _SwiftStringView { /// A `String`, having the same contents as `self`, that may be unsuitable for /// long-term storage. var _ephemeralContent : String { get } /// A `String`, having the same contents as `self`, that is suitable for /// long-term storage. // // FIXME: Remove once _StringGuts has append(contentsOf:). var _persistentContent : String { get } /// The entire String onto whose slice this view is a projection. var _wholeString : String { get } /// The range of storage offsets of this view in `_wholeString`. var _encodedOffsetRange : Range { get } } extension _SwiftStringView { @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal var _ephemeralContent : String { return _persistentContent } } extension StringProtocol { @_inlineable // FIXME(sil-serialize-all) public // Used in the Foundation overlay var _ephemeralString : String { if _fastPath(self is _SwiftStringView) { return (self as! _SwiftStringView)._ephemeralContent } return String(self) } } extension String : _SwiftStringView { @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal var _persistentContent : String { return self } @_inlineable // FIXME(sil-serialize-all) public var _wholeString : String { return self } @_inlineable // FIXME(sil-serialize-all) public var _encodedOffsetRange : Range { return 0..<_guts.count } } /// Call body with a pointer to zero-terminated sequence of /// `TargetEncoding.CodeUnit` representing the same string as `source`, when /// `source` is interpreted as being encoded with `SourceEncoding`. @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _withCString< Source : Collection, SourceEncoding : Unicode.Encoding, TargetEncoding : Unicode.Encoding, Result >( encodedAs targetEncoding: TargetEncoding.Type, from source: Source, encodedAs sourceEncoding: SourceEncoding.Type, execute body : (UnsafePointer) throws -> Result ) rethrows -> Result where Source.Iterator.Element == SourceEncoding.CodeUnit { return try _withCStringAndLength( encodedAs: targetEncoding, from: source, encodedAs: sourceEncoding) { p, _ in try body(p) } } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.partial.never") internal func _withCStringAndLength< Source : Collection, SourceEncoding : Unicode.Encoding, TargetEncoding : Unicode.Encoding, Result >( encodedAs targetEncoding: TargetEncoding.Type, from source: Source, encodedAs sourceEncoding: SourceEncoding.Type, execute body : (UnsafePointer, Int) throws -> Result ) rethrows -> Result where Source.Iterator.Element == SourceEncoding.CodeUnit { var targetLength = 0 // nul terminator var i = source.makeIterator() SourceEncoding.ForwardParser._parse(&i) { targetLength += numericCast( targetEncoding._transcode($0, from: SourceEncoding.self).count) } var a: [TargetEncoding.CodeUnit] = [] a.reserveCapacity(targetLength + 1) i = source.makeIterator() SourceEncoding.ForwardParser._parse(&i) { a.append( contentsOf: targetEncoding._transcode($0, from: SourceEncoding.self)) } a.append(0) return try body(a, targetLength) } extension _StringGuts { /// Invokes `body` on a null-terminated sequence of code units in the given /// encoding corresponding to the substring in `bounds`. @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _withCSubstring( in bounds: Range, encoding targetEncoding: TargetEncoding.Type, _ body: (UnsafePointer) throws -> Result ) rethrows -> Result { return try _withCSubstringAndLength(in: bounds, encoding: targetEncoding) { p,_ in try body(p) } } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.partial.never") internal func _withCSubstringAndLength< Result, TargetEncoding: Unicode.Encoding >( in bounds: Range, encoding targetEncoding: TargetEncoding.Type, _ body: (UnsafePointer, Int) throws -> Result ) rethrows -> Result { if _slowPath(_isOpaque) { let opaque = _asOpaque()[bounds] return try Swift._withCStringAndLength( encodedAs: targetEncoding, from: opaque, encodedAs: Unicode.UTF16.self, execute: body ) } if isASCII { let ascii = _unmanagedASCIIView[bounds] return try Swift._withCStringAndLength( encodedAs: targetEncoding, from: ascii.buffer, encodedAs: Unicode.ASCII.self, execute: body ) } let utf16 = _unmanagedUTF16View[bounds] return try Swift._withCStringAndLength( encodedAs: targetEncoding, from: utf16.buffer, encodedAs: Unicode.UTF16.self, execute: body ) } } extension String { @_inlineable @_versioned internal static func _fromCodeUnits< Input: Collection, Encoding: Unicode.Encoding >( _ input: Input, encoding: Encoding.Type, repairIllFormedSequences: Bool, minimumCapacity: Int = 0 ) -> (String?, hadError: Bool) where Input.Element == Encoding.CodeUnit { // Determine how many UTF-16 code units we'll need let inputStream = input.makeIterator() guard let (utf16Count, isASCII) = UTF16.transcodedLength( of: inputStream, decodedAs: encoding, repairingIllFormedSequences: repairIllFormedSequences) else { return (nil, true) } let capacity = Swift.max(utf16Count, minimumCapacity) if isASCII { let storage = _SwiftStringStorage.create( capacity: capacity, count: utf16Count) var p = storage.start let sink: (UTF32.CodeUnit) -> Void = { p.pointee = UTF8.CodeUnit($0) p += 1 } let hadError = transcode( input.makeIterator(), from: encoding, to: UTF32.self, stoppingOnError: true, into: sink) _sanityCheck(!hadError, "string cannot be ASCII if there were decoding errors") return (String(_storage: storage), hadError) } else { let storage = _SwiftStringStorage.create( capacity: capacity, count: utf16Count) var p = storage.start let sink: (UTF16.CodeUnit) -> Void = { p.pointee = $0 p += 1 } let hadError = transcode( input.makeIterator(), from: encoding, to: UTF16.self, stoppingOnError: !repairIllFormedSequences, into: sink) return (String(_storage: storage), hadError) } } /// Creates a string from the given Unicode code units in the specified /// encoding. /// /// - Parameters: /// - codeUnits: A collection of code units encoded in the encoding /// specified in `sourceEncoding`. /// - sourceEncoding: The encoding in which `codeUnits` should be /// interpreted. @_inlineable // FIXME(sil-serialize-all) public init( decoding codeUnits: C, as sourceEncoding: Encoding.Type ) where C.Iterator.Element == Encoding.CodeUnit { let (result, _) = String._fromCodeUnits( codeUnits, encoding: sourceEncoding, repairIllFormedSequences: true) self = result! } /// Creates a string from the null-terminated sequence of bytes at the given /// pointer. /// /// - Parameters: /// - nullTerminatedCodeUnits: A pointer to a sequence of contiguous code /// units in the encoding specified in `sourceEncoding`, ending just /// before the first zero code unit. /// - sourceEncoding: The encoding in which the code units should be /// interpreted. @_inlineable // FIXME(sil-serialize-all) public init( decodingCString nullTerminatedCodeUnits: UnsafePointer, as sourceEncoding: Encoding.Type) { let codeUnits = _SentinelCollection( UnsafeBufferPointer(_unboundedStartingAt: nullTerminatedCodeUnits), until: _IsZero() ) self.init(decoding: codeUnits, as: sourceEncoding) } /// Calls the given closure with a pointer to the contents of the string, /// represented as a null-terminated sequence of code units. /// /// The pointer passed as an argument to `body` is valid only during the /// execution of `withCString(encodedAs:_:)`. Do not store or return the /// pointer for later use. /// /// - Parameters: /// - body: A closure with a pointer parameter that points to a /// null-terminated sequence of code units. If `body` has a return /// value, that value is also used as the return value for the /// `withCString(encodedAs:_:)` method. The pointer argument is valid /// only for the duration of the method's execution. /// - targetEncoding: The encoding in which the code units should be /// interpreted. /// - Returns: The return value, if any, of the `body` closure parameter. @_inlineable // FIXME(sil-serialize-all) public func withCString( encodedAs targetEncoding: TargetEncoding.Type, _ body: (UnsafePointer) throws -> Result ) rethrows -> Result { return try _guts._withCSubstring( in: 0..<_guts.count, encoding: TargetEncoding.self, body) } } // FIXME: complexity documentation for most of methods on String ought to be // qualified with "amortized" at least, as Characters are variable-length. /// A Unicode string value that is a collection of characters. /// /// A string is a series of characters, such as `"Swift"`, that forms a /// collection. Strings in Swift are Unicode correct and locale insensitive, /// and are designed to be efficient. The `String` type bridges with the /// Objective-C class `NSString` and offers interoperability with C functions /// that works with strings. /// /// You can create new strings using string literals or string interpolations. /// A *string literal* is a series of characters enclosed in quotes. /// /// let greeting = "Welcome!" /// /// *String interpolations* are string literals that evaluate any included /// expressions and convert the results to string form. String interpolations /// give you an easy way to build a string from multiple pieces. Wrap each /// expression in a string interpolation in parentheses, prefixed by a /// backslash. /// /// let name = "Rosa" /// let personalizedGreeting = "Welcome, \(name)!" /// // personalizedGreeting == "Welcome, Rosa!" /// /// let price = 2 /// let number = 3 /// let cookiePrice = "\(number) cookies: $\(price * number)." /// // cookiePrice == "3 cookies: $6." /// /// Combine strings using the concatenation operator (`+`). /// /// let longerGreeting = greeting + " We're glad you're here!" /// // longerGreeting == "Welcome! We're glad you're here!" /// /// Multiline string literals are enclosed in three double quotation marks /// (`"""`), with each delimiter on its own line. Indentation is stripped from /// each line of a multiline string literal to match the indentation of the /// closing delimiter. /// /// let banner = """ /// __, /// ( o /) _/_ /// `. , , , , // / /// (___)(_(_/_(_ //_ (__ /// /) /// (/ /// """ /// /// Modifying and Comparing Strings /// =============================== /// /// Strings always have value semantics. Modifying a copy of a string leaves /// the original unaffected. /// /// var otherGreeting = greeting /// otherGreeting += " Have a nice time!" /// // otherGreeting == "Welcome! Have a nice time!" /// /// print(greeting) /// // Prints "Welcome!" /// /// Comparing strings for equality using the equal-to operator (`==`) or a /// relational operator (like `<` or `>=`) is always performed using Unicode /// canonical representation. As a result, different representations of a /// string compare as being equal. /// /// let cafe1 = "Cafe\u{301}" /// let cafe2 = "Café" /// print(cafe1 == cafe2) /// // Prints "true" /// /// The Unicode scalar value `"\u{301}"` modifies the preceding character to /// include an accent, so `"e\u{301}"` has the same canonical representation /// as the single Unicode scalar value `"é"`. /// /// Basic string operations are not sensitive to locale settings, ensuring that /// string comparisons and other operations always have a single, stable /// result, allowing strings to be used as keys in `Dictionary` instances and /// for other purposes. /// /// Accessing String Elements /// ========================= /// /// A string is a collection of *extended grapheme clusters*, which approximate /// human-readable characters. Many individual characters, such as "é", "김", /// and "🇮🇳", can be made up of multiple Unicode scalar values. These scalar /// values are combined by Unicode's boundary algorithms into extended /// grapheme clusters, represented by the Swift `Character` type. Each element /// of a string is represented by a `Character` instance. /// /// For example, to retrieve the first word of a longer string, you can search /// for a space and then create a substring from a prefix of the string up to /// that point: /// /// let name = "Marie Curie" /// let firstSpace = name.index(of: " ") ?? name.endIndex /// let firstName = name[..( _ encoding: Encoding.Type, input: Input ) -> String where Input.Element == Encoding.CodeUnit { return String._fromCodeUnitSequence(encoding, input: input)! } @_inlineable // FIXME(sil-serialize-all) public // @testable static func _fromCodeUnitSequence< Encoding : Unicode.Encoding, Input : Collection >( _ encoding: Encoding.Type, input: Input ) -> String? where Input.Element == Encoding.CodeUnit { let (result, _) = String._fromCodeUnits( input, encoding: encoding, repairIllFormedSequences: false) return result } @_inlineable // FIXME(sil-serialize-all) public // @testable static func _fromCodeUnitSequenceWithRepair< Encoding : Unicode.Encoding, Input : Collection >( _ encoding: Encoding.Type, input: Input ) -> (String, hadError: Bool) where Input.Element == Encoding.CodeUnit { let (string, hadError) = String._fromCodeUnits( input, encoding: encoding, repairIllFormedSequences: true) return (string!, hadError) } } extension String : _ExpressibleByBuiltinUnicodeScalarLiteral { @_inlineable // FIXME(sil-serialize-all) @effects(readonly) public // @testable init(_builtinUnicodeScalarLiteral value: Builtin.Int32) { self = String._fromWellFormedCodeUnitSequence( UTF32.self, input: CollectionOfOne(UInt32(value))) } } extension String : _ExpressibleByBuiltinExtendedGraphemeClusterLiteral { @_inlineable @effects(readonly) @_semantics("string.makeUTF8") public init( _builtinExtendedGraphemeClusterLiteral start: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) { self = String._fromWellFormedCodeUnitSequence( UTF8.self, input: UnsafeBufferPointer( start: UnsafeMutablePointer(start), count: Int(utf8CodeUnitCount))) } } extension String : _ExpressibleByBuiltinUTF16StringLiteral { @_inlineable @effects(readonly) @_semantics("string.makeUTF16") public init( _builtinUTF16StringLiteral start: Builtin.RawPointer, utf16CodeUnitCount: Builtin.Word ) { self = String(_StringGuts(_UnmanagedString( start: UnsafePointer(start), count: Int(utf16CodeUnitCount)))) } } extension String : _ExpressibleByBuiltinStringLiteral { @_inlineable @effects(readonly) @_semantics("string.makeUTF8") public init( _builtinStringLiteral start: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1 ) { if Int(utf8CodeUnitCount) == 0 { self.init() return } if _fastPath(Bool(isASCII)) { self = String(_StringGuts(_UnmanagedString( start: UnsafePointer(start), count: Int(utf8CodeUnitCount)))) return } self = String._fromWellFormedCodeUnitSequence( UTF8.self, input: UnsafeBufferPointer( start: UnsafeMutablePointer(start), count: Int(utf8CodeUnitCount))) } } extension String : ExpressibleByStringLiteral { /// Creates an instance initialized to the given string value. /// /// Do not call this initializer directly. It is used by the compiler when you /// initialize a string using a string literal. For example: /// /// let nextStop = "Clark & Lake" /// /// This assignment to the `nextStop` constant calls this string literal /// initializer behind the scenes. @_inlineable // FIXME(sil-serialize-all) public init(stringLiteral value: String) { self = value } } extension String : CustomDebugStringConvertible { /// A representation of the string that is suitable for debugging. @_inlineable // FIXME(sil-serialize-all) public var debugDescription: String { var result = "\"" for us in self.unicodeScalars { result += us.escaped(asASCII: false) } result += "\"" return result } } extension String { /// Returns the number of code units occupied by this string /// in the given encoding. @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _encodedLength< Encoding: Unicode.Encoding >(_ encoding: Encoding.Type) -> Int { var codeUnitCount = 0 self._encode(encoding, into: { _ in codeUnitCount += 1 }) return codeUnitCount } // FIXME: this function may not handle the case when a wrapped NSString // contains unpaired surrogates. Fix this before exposing this function as a // public API. But it is unclear if it is valid to have such an NSString in // the first place. If it is not, we should not be crashing in an obscure // way -- add a test for that. // Related: Please document how NSString interacts // with unpaired surrogates @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _encode( _ encoding: Encoding.Type, into processCodeUnit: (Encoding.CodeUnit) -> Void ) { if _slowPath(_guts._isOpaque) { let opaque = _guts._asOpaque() var i = opaque.makeIterator() Unicode.UTF16.ForwardParser._parse(&i) { Encoding._transcode($0, from: UTF16.self).forEach(processCodeUnit) } return } if _guts.isASCII { let ascii = _guts._unmanagedASCIIView if encoding == Unicode.ASCII.self || encoding == Unicode.UTF8.self || encoding == Unicode.UTF16.self || encoding == Unicode.UTF32.self { ascii.forEach { processCodeUnit(Encoding.CodeUnit(truncatingIfNeeded: $0)) } } else { // TODO: be sure tests exercise this code path. for b in ascii { Encoding._encode( Unicode.Scalar(_unchecked: UInt32(b))).forEach(processCodeUnit) } } return } let utf16 = _guts._unmanagedUTF16View var i = utf16.makeIterator() Unicode.UTF16.ForwardParser._parse(&i) { Encoding._transcode($0, from: UTF16.self).forEach(processCodeUnit) } } } // Support for copy-on-write extension String { /// Appends the given string to this string. /// /// The following example builds a customized greeting by using the /// `append(_:)` method: /// /// var greeting = "Hello, " /// if let name = getUserName() { /// greeting.append(name) /// } else { /// greeting.append("friend") /// } /// print(greeting) /// // Prints "Hello, friend" /// /// - Parameter other: Another string. @_inlineable // FIXME(sil-serialize-all) public mutating func append(_ other: String) { self._guts.append(other._guts) } /// Appends the given Unicode scalar to the string. /// /// - Parameter x: A Unicode scalar value. /// /// - Complexity: Appending a Unicode scalar to a string averages to O(1) /// over many additions. @available(*, unavailable, message: "Replaced by append(_: String)") public mutating func append(_ x: Unicode.Scalar) { Builtin.unreachable() } @_inlineable // FIXME(sil-serialize-all) public init(_storage: _SwiftStringStorage) where CodeUnit : FixedWidthInteger & UnsignedInteger { _guts = _StringGuts(_storage) } } extension String { @_inlineable // FIXME(sil-serialize-all) @effects(readonly) @_semantics("string.concat") public static func + (lhs: String, rhs: String) -> String { var lhs = lhs lhs.append(rhs) return lhs } // String append @_inlineable // FIXME(sil-serialize-all) public static func += (lhs: inout String, rhs: String) { lhs.append(rhs) } } extension String { /// Constructs a `String` in `resultStorage` containing the given UTF-8. /// /// Low-level construction interface used by introspection /// implementation in the runtime library. @_inlineable @_silgen_name("swift_stringFromUTF8InRawMemory") public // COMPILER_INTRINSIC static func _fromUTF8InRawMemory( _ resultStorage: UnsafeMutablePointer, start: UnsafeMutablePointer, utf8CodeUnitCount: Int ) { resultStorage.initialize(to: String._fromWellFormedCodeUnitSequence( UTF8.self, input: UnsafeBufferPointer(start: start, count: utf8CodeUnitCount))) } } extension Sequence where Element: StringProtocol { /// Returns a new string by concatenating the elements of the sequence, /// adding the given separator between each element. /// /// The following example shows how an array of strings can be joined to a /// single, comma-separated string: /// /// let cast = ["Vivien", "Marlon", "Kim", "Karl"] /// let list = cast.joined(separator: ", ") /// print(list) /// // Prints "Vivien, Marlon, Kim, Karl" /// /// - Parameter separator: A string to insert between each of the elements /// in this sequence. The default separator is an empty string. /// - Returns: A single, concatenated string. @_inlineable // FIXME(sil-serialize-all) public func joined(separator: String = "") -> String { return _joined(separator: separator) } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _joined(separator: String = "") -> String { let separatorSize = separator._guts.count var width = separator._guts.byteWidth let reservation = self._preprocessingPass { () -> Int in var r = 0 for chunk in self { r += separatorSize + chunk._encodedOffsetRange.count width = Swift.max(width, chunk._wholeString._guts.byteWidth) } return r > 0 ? r - separatorSize : 0 } if width == 1 { return _joined( capacity: reservation ?? 0, of: UInt8.self, separator: separatorSize == 0 ? nil : separator) } else { return _joined( capacity: reservation ?? 0, of: UTF16.CodeUnit.self, separator: separatorSize == 0 ? nil : separator) } } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _joined( capacity: Int, of codeUnit: CodeUnit.Type, separator: String? ) -> String where CodeUnit : FixedWidthInteger & UnsignedInteger { let result = _SwiftStringStorage.create(capacity: capacity) guard let separator = separator else { for x in self { result._appendInPlace(x) } return String(_storage: result) } var iter = makeIterator() if let first = iter.next() { result._appendInPlace(first) while let next = iter.next() { result._appendInPlace(separator) result._appendInPlace(next) } } return String(_storage: result) } } // This overload is necessary because String now conforms to // BidirectionalCollection, and there are other `joined` overloads that are // considered more specific. See Flatten.swift.gyb. extension BidirectionalCollection where Iterator.Element == String { /// Returns a new string by concatenating the elements of the sequence, /// adding the given separator between each element. /// /// The following example shows how an array of strings can be joined to a /// single, comma-separated string: /// /// let cast = ["Vivien", "Marlon", "Kim", "Karl"] /// let list = cast.joined(separator: ", ") /// print(list) /// // Prints "Vivien, Marlon, Kim, Karl" /// /// - Parameter separator: A string to insert between each of the elements /// in this sequence. The default separator is an empty string. /// - Returns: A single, concatenated string. @_inlineable // FIXME(sil-serialize-all) public func joined(separator: String = "") -> String { return _joined(separator: separator) } } #if _runtime(_ObjC) @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) @_silgen_name("swift_stdlib_NSStringLowercaseString") internal func _stdlib_NSStringLowercaseString(_ str: AnyObject) -> _CocoaString @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) @_silgen_name("swift_stdlib_NSStringUppercaseString") internal func _stdlib_NSStringUppercaseString(_ str: AnyObject) -> _CocoaString #else @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _nativeUnicodeLowercaseString(_ str: String) -> String { let guts = str._guts._extractContiguousUTF16() defer { _fixLifetime(guts) } let utf16 = guts._unmanagedUTF16View var storage = _SwiftStringStorage.create( capacity: utf16.count, count: utf16.count) // Try to write it out to the same length. let z = _swift_stdlib_unicode_strToLower( storage.start, Int32(storage.capacity), // FIXME: handle overflow case utf16.start, Int32(utf16.count)) let correctSize = Int(z) // If more space is needed, do it again with the correct buffer size. if correctSize > storage.capacity { storage = _SwiftStringStorage.create( capacity: correctSize, count: correctSize) _swift_stdlib_unicode_strToLower( storage.start, Int32(storage.capacity), // FIXME: handle overflow case utf16.start, Int32(utf16.count)) } storage.count = correctSize return String(_storage: storage) } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func _nativeUnicodeUppercaseString(_ str: String) -> String { let guts = str._guts._extractContiguousUTF16() defer { _fixLifetime(guts) } let utf16 = guts._unmanagedUTF16View var storage = _SwiftStringStorage.create( capacity: utf16.count, count: utf16.count) // Try to write it out to the same length. let z = _swift_stdlib_unicode_strToUpper( storage.start, Int32(storage.capacity), // FIXME: handle overflow case utf16.start, Int32(utf16.count)) let correctSize = Int(z) // If more space is needed, do it again with the correct buffer size. if correctSize > storage.capacity { storage = _SwiftStringStorage.create( capacity: correctSize, count: correctSize) _swift_stdlib_unicode_strToUpper( storage.start, Int32(storage.capacity), // FIXME: handle overflow case utf16.start, Int32(utf16.count)) } storage.count = correctSize return String(_storage: storage) } #endif // Unicode algorithms extension String { // FIXME: implement case folding without relying on Foundation. // [unicode] Implement case folding /// A "table" for which ASCII characters need to be upper cased. /// To determine which bit corresponds to which ASCII character, subtract 1 /// from the ASCII value of that character and divide by 2. The bit is set iff /// that character is a lower case character. @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal var _asciiLowerCaseTable: UInt64 { @inline(__always) get { return 0b0001_1111_1111_1111_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000 } } /// The same table for upper case characters. @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal var _asciiUpperCaseTable: UInt64 { @inline(__always) get { return 0b0000_0000_0000_0000_0001_1111_1111_1111_0000_0000_0000_0000_0000_0000_0000_0000 } } /// Returns a lowercase version of the string. /// /// Here's an example of transforming a string to all lowercase letters. /// /// let cafe = "BBQ Café 🍵" /// print(cafe.lowercased()) /// // Prints "bbq café 🍵" /// /// - Returns: A lowercase copy of the string. /// /// - Complexity: O(*n*) @_inlineable // FIXME(sil-serialize-all) public func lowercased() -> String { if _guts.isASCII { var guts = _guts guts.withMutableASCIIStorage(unusedCapacity: 0) { storage in for i in 0..= 0x41 && x <= 0x5a): // dest[i] = x &+ 0x20 // case let x: // dest[i] = x // } let value = storage._value.start[i] let isUpper = _asciiUpperCaseTable &>> UInt64(((value &- 1) & 0b0111_1111) &>> 1) let add = (isUpper & 0x1) &<< 5 // Since we are left with either 0x0 or 0x20, we can safely truncate // to a UInt8 and add to our ASCII value (this will not overflow // numbers in the ASCII range). storage._value.start[i] = value &+ UInt8(truncatingIfNeeded: add) } } return String(guts) } #if _runtime(_ObjC) return String(_cocoaString: _stdlib_NSStringLowercaseString(self._bridgeToObjectiveCImpl())) #else return _nativeUnicodeLowercaseString(self) #endif } /// Returns an uppercase version of the string. /// /// The following example transforms a string to uppercase letters: /// /// let cafe = "Café 🍵" /// print(cafe.uppercased()) /// // Prints "CAFÉ 🍵" /// /// - Returns: An uppercase copy of the string. /// /// - Complexity: O(*n*) @_inlineable // FIXME(sil-serialize-all) public func uppercased() -> String { if _guts.isASCII { var guts = _guts guts.withMutableASCIIStorage(unusedCapacity: 0) { storage in for i in 0..> UInt64(((value &- 1) & 0b0111_1111) &>> 1) let add = (isLower & 0x1) &<< 5 storage._value.start[i] = value &- UInt8(truncatingIfNeeded: add) } } return String(guts) } #if _runtime(_ObjC) return String(_cocoaString: _stdlib_NSStringUppercaseString(self._bridgeToObjectiveCImpl())) #else return _nativeUnicodeUppercaseString(self) #endif } /// Creates an instance from the description of a given /// `LosslessStringConvertible` instance. @_inlineable // FIXME(sil-serialize-all) public init(_ value: T) { self = value.description } } extension String : CustomStringConvertible { @_inlineable // FIXME(sil-serialize-all) public var description: String { return self } }