//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// extension String : StringProtocol, RangeReplaceableCollection { /// A type that represents the number of steps between two `String.Index` /// values, where one value is reachable from the other. /// /// In Swift, *reachability* refers to the ability to produce one value from /// the other through zero or more applications of `index(after:)`. public typealias IndexDistance = Int public typealias SubSequence = Substring /// Creates a string representing the given character repeated the specified /// number of times. /// /// For example, use this initializer to create a string with ten `"0"` /// characters in a row. /// /// let zeroes = String(repeating: "0" as Character, count: 10) /// print(zeroes) /// // Prints "0000000000" /// /// - Parameters: /// - repeatedValue: The character to repeat. /// - count: The number of times to repeat `repeatedValue` in the /// resulting string. @inlinable // FIXME(sil-serialize-all) public init(repeating repeatedValue: Character, count: Int) { self.init(repeating: String(repeatedValue), count: count) } // This initializer disambiguates between the following intitializers, now // that String conforms to Collection: // - init(_ value: T) where T : LosslessStringConvertible // - init(_ characters: S) where S : Sequence, S.Element == Character /// Creates a new string containing the characters in the given sequence. /// /// You can use this initializer to create a new string from the result of /// one or more collection operations on a string's characters. For example: /// /// let str = "The rain in Spain stays mainly in the plain." /// /// let vowels: Set = ["a", "e", "i", "o", "u"] /// let disemvoweled = String(str.lazy.filter { !vowels.contains($0) }) /// /// print(disemvoweled) /// // Prints "Th rn n Spn stys mnly n th pln." /// /// - Parameter other: A string instance or another sequence of /// characters. @inlinable // FIXME(sil-serialize-all) public init(_ other: S) where S.Element == Character { self = other.description } // The defaulted argument prevents this initializer from satisfies the // LosslessStringConvertible conformance. You can satisfy a protocol // requirement with something that's not yet available, but not with // something that has become unavailable. Without this, the code won't // compile as Swift 4. @inlinable // FIXME(sil-serialize-all) @available(swift, obsoleted: 4, message: "String.init(_:String) is no longer failable") public init?(_ other: String, obsoletedInSwift4: () = ()) { self.init(other._guts) } /// The position of the first character in a nonempty string. /// /// In an empty string, `startIndex` is equal to `endIndex`. @inlinable // FIXME(sil-serialize-all) public var startIndex: Index { return Index(encodedOffset: 0) } /// A string's "past the end" position---that is, the position one greater /// than the last valid subscript argument. /// /// In an empty string, `endIndex` is equal to `startIndex`. @inlinable // FIXME(sil-serialize-all) public var endIndex: Index { return Index(encodedOffset: _guts.count) } @inlinable @inline(__always) internal func _boundsCheck(_ index: Index) { _precondition(index.encodedOffset >= 0 && index.encodedOffset < _guts.count, "String index is out of bounds") } @inlinable @inline(__always) internal func _boundsCheck(_ range: Range) { _precondition( range.lowerBound.encodedOffset >= 0 && range.upperBound.encodedOffset <= _guts.count, "String index range is out of bounds") } @inlinable @inline(__always) internal func _boundsCheck(_ range: ClosedRange) { _precondition( range.lowerBound.encodedOffset >= 0 && range.upperBound.encodedOffset < _guts.count, "String index range is out of bounds") } @inlinable // FIXME(sil-serialize-all) internal func _index(atEncodedOffset offset: Int) -> Index { return _visitGuts(_guts, args: offset, ascii: { ascii, offset in return ascii.characterIndex(atOffset: offset) }, utf16: { utf16, offset in return utf16.characterIndex(atOffset: offset) }, opaque: { opaque, offset in return opaque.characterIndex(atOffset: offset) }) } /// Returns the position immediately after the given index. /// /// - Parameter i: A valid index of the collection. `i` must be less than /// `endIndex`. /// - Returns: The index value immediately after `i`. @inlinable // FIXME(sil-serialize-all) public func index(after i: Index) -> Index { return _visitGuts(_guts, args: i, ascii: { ascii, i in ascii.characterIndex(after: i) }, utf16: { utf16, i in utf16.characterIndex(after: i) }, opaque: { opaque, i in opaque.characterIndex(after: i) }) } /// Returns the position immediately before the given index. /// /// - Parameter i: A valid index of the collection. `i` must be greater than /// `startIndex`. /// - Returns: The index value immediately before `i`. @inlinable // FIXME(sil-serialize-all) public func index(before i: Index) -> Index { return _visitGuts(_guts, args: i, ascii: { ascii, i in ascii.characterIndex(before: i) }, utf16: { utf16, i in utf16.characterIndex(before: i) }, opaque: { opaque, i in opaque.characterIndex(before: i) }) } /// Returns an index that is the specified distance from the given index. /// /// The following example obtains an index advanced four positions from a /// string's starting index and then prints the character at that position. /// /// let s = "Swift" /// let i = s.index(s.startIndex, offsetBy: 4) /// print(s[i]) /// // Prints "t" /// /// The value passed as `n` must not offset `i` beyond the bounds of the /// collection. /// /// - Parameters: /// - i: A valid index of the collection. /// - n: The distance to offset `i`. /// - Returns: An index offset by `n` from the index `i`. If `n` is positive, /// this is the same value as the result of `n` calls to `index(after:)`. /// If `n` is negative, this is the same value as the result of `-n` calls /// to `index(before:)`. /// /// - Complexity: O(*n*), where *n* is the absolute value of `n`. @inlinable // FIXME(sil-serialize-all) public func index(_ i: Index, offsetBy n: IndexDistance) -> Index { return _visitGuts(_guts, args: (i, n), ascii: { ascii, args in let (i, n) = args return ascii.characterIndex(i, offsetBy: n) }, utf16: { utf16, args in let (i, n) = args return utf16.characterIndex(i, offsetBy: n) }, opaque: { opaque, args in let (i, n) = args return opaque.characterIndex(i, offsetBy: n) }) } /// Returns an index that is the specified distance from the given index, /// unless that distance is beyond a given limiting index. /// /// The following example obtains an index advanced four positions from a /// string's starting index and then prints the character at that position. /// The operation doesn't require going beyond the limiting `s.endIndex` /// value, so it succeeds. /// /// let s = "Swift" /// if let i = s.index(s.startIndex, offsetBy: 4, limitedBy: s.endIndex) { /// print(s[i]) /// } /// // Prints "t" /// /// The next example attempts to retrieve an index six positions from /// `s.startIndex` but fails, because that distance is beyond the index /// passed as `limit`. /// /// let j = s.index(s.startIndex, offsetBy: 6, limitedBy: s.endIndex) /// print(j) /// // Prints "nil" /// /// The value passed as `n` must not offset `i` beyond the bounds of the /// collection, unless the index passed as `limit` prevents offsetting /// beyond those bounds. /// /// - Parameters: /// - i: A valid index of the collection. /// - n: The distance to offset `i`. /// - limit: A valid index of the collection to use as a limit. If `n > 0`, /// a limit that is less than `i` has no effect. Likewise, if `n < 0`, a /// limit that is greater than `i` has no effect. /// - Returns: An index offset by `n` from the index `i`, unless that index /// would be beyond `limit` in the direction of movement. In that case, /// the method returns `nil`. /// /// - Complexity: O(*n*), where *n* is the absolute value of `n`. @inlinable // FIXME(sil-serialize-all) public func index( _ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index ) -> Index? { return _visitGuts(_guts, args: (i, n, limit), ascii: { ascii, args in let (i, n, limit) = args return ascii.characterIndex(i, offsetBy: n, limitedBy: limit) }, utf16: { utf16, args in let (i, n, limit) = args return utf16.characterIndex(i, offsetBy: n, limitedBy: limit) }, opaque: { opaque, args in let (i, n, limit) = args return opaque.characterIndex(i, offsetBy: n, limitedBy: limit) }) } /// Returns the distance between two indices. /// /// - Parameters: /// - start: A valid index of the collection. /// - end: Another valid index of the collection. If `end` is equal to /// `start`, the result is zero. /// - Returns: The distance between `start` and `end`. /// /// - Complexity: O(*n*), where *n* is the resulting distance. @inlinable // FIXME(sil-serialize-all) public func distance(from start: Index, to end: Index) -> IndexDistance { return _visitGuts(_guts, args: (start, end), ascii: { ascii, args in let (start, end) = args return ascii.characterDistance(from: start, to: end) }, utf16: { utf16, args in let (start, end) = args return utf16.characterDistance(from: start, to: end) }, opaque: { opaque, args in let (start, end) = args return opaque.characterDistance(from: start, to: end) }) } /// Accesses the character at the given position. /// /// You can use the same indices for subscripting a string and its substring. /// For example, this code finds the first letter after the first space: /// /// let str = "Greetings, friend! How are you?" /// let firstSpace = str.firstIndex(of: " ") ?? str.endIndex /// let substr = str[firstSpace...] /// if let nextCapital = substr.firstIndex(where: { $0 >= "A" && $0 <= "Z" }) { /// print("Capital after a space: \(str[nextCapital])") /// } /// // Prints "Capital after a space: H" /// /// - Parameter i: A valid index of the string. `i` must be less than the /// string's end index. @inlinable // FIXME(sil-serialize-all) public subscript(i: Index) -> Character { return _visitGuts(_guts, args: i, ascii: { ascii, i in return ascii.character(at: i) }, utf16: { utf16, i in return utf16.character(at: i) }, opaque: { opaque, i in return opaque.character(at: i) }) } } extension String { /// Creates a new string containing the characters in the given sequence. /// /// You can use this initializer to create a new string from the result of /// one or more collection operations on a string's characters. For example: /// /// let str = "The rain in Spain stays mainly in the plain." /// /// let vowels: Set = ["a", "e", "i", "o", "u"] /// let disemvoweled = String(str.lazy.filter { !vowels.contains($0) }) /// /// print(disemvoweled) /// // Prints "Th rn n Spn stys mnly n th pln." /// /// - Parameter characters: A string instance or another sequence of /// characters. @inlinable // FIXME(sil-serialize-all) public init(_ characters: S) where S.Iterator.Element == Character { self = "" self.append(contentsOf: characters) } /// Reserves enough space in the string's underlying storage to store the /// specified number of ASCII characters. /// /// Because each character in a string can require more than a single ASCII /// character's worth of storage, additional allocation may be necessary /// when adding characters to a string after a call to /// `reserveCapacity(_:)`. /// /// - Parameter n: The minimum number of ASCII character's worth of storage /// to allocate. /// /// - Complexity: O(*n*) @inlinable // FIXME(sil-serialize-all) public mutating func reserveCapacity(_ n: Int) { _guts.reserveCapacity(n) } /// Appends the given character to the string. /// /// The following example adds an emoji globe to the end of a string. /// /// var globe = "Globe " /// globe.append("🌍") /// print(globe) /// // Prints "Globe 🌍" /// /// - Parameter c: The character to append to the string. @inlinable // FIXME(sil-serialize-all) public mutating func append(_ c: Character) { if let small = c._smallUTF16 { _guts.append(contentsOf: small) } else { _guts.append(c._largeUTF16!.unmanagedView) _fixLifetime(c) } } @inlinable // FIXME(sil-serialize-all) public mutating func append(contentsOf newElements: String) { append(newElements) } @inlinable // FIXME(sil-serialize-all) public mutating func append(contentsOf newElements: Substring) { _guts.append( newElements._wholeString._guts, range: newElements._encodedOffsetRange) } /// Appends the characters in the given sequence to the string. /// /// - Parameter newElements: A sequence of characters. @inlinable // FIXME(sil-serialize-all) public mutating func append(contentsOf newElements: S) where S.Iterator.Element == Character { if _fastPath(newElements is _SwiftStringView) { let v = newElements as! _SwiftStringView _guts.append(v._wholeString._guts, range: v._encodedOffsetRange) return } _guts.reserveUnusedCapacity( newElements.underestimatedCount, ascii: _guts.isASCII) for c in newElements { self.append(c) } } /// Replaces the text within the specified bounds with the given characters. /// /// Calling this method invalidates any existing indices for use with this /// string. /// /// - Parameters: /// - bounds: The range of text to replace. The bounds of the range must be /// valid indices of the string. /// - newElements: The new characters to add to the string. /// /// - Complexity: O(*m*), where *m* is the combined length of the string and /// `newElements`. If the call to `replaceSubrange(_:with:)` simply /// removes text at the end of the string, the complexity is O(*n*), where /// *n* is equal to `bounds.count`. @inlinable // FIXME(sil-serialize-all) public mutating func replaceSubrange( _ bounds: Range, with newElements: C ) where C : Collection, C.Iterator.Element == Character { let offsetRange: Range = bounds.lowerBound.encodedOffset ..< bounds.upperBound.encodedOffset let lazyUTF16 = newElements.lazy.flatMap { $0.utf16 } _guts.replaceSubrange(offsetRange, with: lazyUTF16) } /// Inserts a new character at the specified position. /// /// Calling this method invalidates any existing indices for use with this /// string. /// /// - Parameters: /// - newElement: The new character to insert into the string. /// - i: A valid index of the string. If `i` is equal to the string's end /// index, this methods appends `newElement` to the string. /// /// - Complexity: O(*n*), where *n* is the length of the string. @inlinable // FIXME(sil-serialize-all) public mutating func insert(_ newElement: Character, at i: Index) { let offset = i.encodedOffset _guts.replaceSubrange(offset..( contentsOf newElements: S, at i: Index ) where S.Iterator.Element == Character { let offset = i.encodedOffset let utf16 = newElements.lazy.flatMap { $0.utf16 } _guts.replaceSubrange(offset.. Character { let offset = i.encodedOffset let stride = _stride(of: i) let range: Range = offset ..< offset + stride let old = Character(_unverified: _guts, range: range) _guts.replaceSubrange(range, with: EmptyCollection()) return old } @inlinable // FIXME(sil-serialize-all) internal func _stride(of i: Index) -> Int { if case .character(let stride) = i._cache { // TODO: should _fastPath the case somehow _sanityCheck(stride > 0) return Int(stride) } let offset = i.encodedOffset return _visitGuts(_guts, args: offset, ascii: { ascii, offset in return ascii.characterStride(atOffset: offset) }, utf16: { utf16, offset in return utf16.characterStride(atOffset: offset) }, opaque: { opaque, offset in return opaque.characterStride(atOffset: offset) }) } /// Removes the characters in the given range. /// /// Calling this method invalidates any existing indices for use with this /// string. /// /// - Parameter bounds: The range of the elements to remove. The upper and /// lower bounds of `bounds` must be valid indices of the string and not /// equal to the string's end index. /// - Parameter bounds: The range of the elements to remove. The upper and /// lower bounds of `bounds` must be valid indices of the string. @inlinable // FIXME(sil-serialize-all) public mutating func removeSubrange(_ bounds: Range) { let start = bounds.lowerBound.encodedOffset let end = bounds.upperBound.encodedOffset _guts.replaceSubrange(start..(_ x: T, _ y: T) -> T { return Swift.max(x,y) } // This is needed because of the issue described in SR-4660 which causes // source compatibility issues when String becomes a collection @inlinable // FIXME(sil-serialize-all) @_transparent public func min(_ x: T, _ y: T) -> T { return Swift.min(x,y) } } //===----------------------------------------------------------------------===// // The following overloads of flatMap are carefully crafted to allow the code // like the following: // ["hello"].flatMap { $0 } // return an array of strings without any type context in Swift 3 mode, at the // same time allowing the following code snippet to compile: // [0, 1].flatMap { x in // if String(x) == "foo" { return "bar" } else { return nil } // } // Note that the second overload is declared on a more specific protocol. // See: test/stdlib/StringFlatMap.swift for tests. extension Sequence { @inlinable // FIXME(sil-serialize-all) @available(swift, obsoleted: 4) public func flatMap( _ transform: (Element) throws -> String ) rethrows -> [String] { return try map(transform) } } extension Collection { @available(swift, deprecated: 4.1, renamed: "compactMap(_:)", message: "Please use compactMap(_:) for the case where closure returns an optional value") @inline(__always) public func flatMap( _ transform: (Element) throws -> String? ) rethrows -> [String] { return try _compactMap(transform) } } //===----------------------------------------------------------------------===// extension Sequence where Element == String { @available(*, unavailable, message: "Operator '+' cannot be used to append a String to a sequence of strings") public static func + (lhs: Self, rhs: String) -> Never { fatalError() } @available(*, unavailable, message: "Operator '+' cannot be used to append a String to a sequence of strings") public static func + (lhs: String, rhs: Self) -> Never { fatalError() } }