//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2023 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.Index { /// Creates an index in the given string that corresponds exactly to the /// specified position. /// /// If the index passed as `sourcePosition` represents the start of an /// extended grapheme cluster---the element type of a string---then the /// initializer succeeds. /// /// The following example converts the position of the Unicode scalar `"e"` /// into its corresponding position in the string. The character at that /// position is the composed `"é"` character. /// /// let cafe = "Cafe\u{0301}" /// print(cafe) /// // Prints "Café" /// /// let scalarsIndex = cafe.unicodeScalars.firstIndex(of: "e")! /// let stringIndex = String.Index(scalarsIndex, within: cafe)! /// /// print(cafe[...stringIndex]) /// // Prints "Café" /// /// If the index passed as `sourcePosition` doesn't have an exact /// corresponding position in `target`, the result of the initializer is /// `nil`. For example, an attempt to convert the position of the combining /// acute accent (`"\u{0301}"`) fails. Combining Unicode scalars do not have /// their own position in a string. /// /// let nextScalarsIndex = cafe.unicodeScalars.index(after: scalarsIndex) /// let nextStringIndex = String.Index(nextScalarsIndex, within: cafe) /// /// print(nextStringIndex) /// // Prints "nil" /// /// - Parameters: /// - sourcePosition: A position in a view of the `target` parameter. /// `sourcePosition` must be a valid index of at least one of the views /// of `target`. /// - target: The string referenced by the resulting index. public init?(_ sourcePosition: String.Index, within target: String) { // As a special exception, we allow `sourcePosition` to be an UTF-16 index // when `self` is a UTF-8 string (or vice versa), to preserve compatibility // with (broken) code that keeps using indices from a bridged string after // converting the string to a native representation. Such indices are // invalid, but returning nil here can break code that appeared to work fine // for ASCII strings in Swift releases prior to 5.7. let i = target._guts.ensureMatchingEncoding(sourcePosition) guard target._isValidIndex(i) else { return nil } self = i._characterAligned } /// Creates an index in the given string that corresponds exactly to the /// specified position. /// /// If the index passed as `sourcePosition` represents the start of an /// extended grapheme cluster---the element type of a string---then the /// initializer succeeds. /// /// The following example converts the position of the Unicode scalar `"e"` /// into its corresponding position in the string. The character at that /// position is the composed `"é"` character. /// /// let cafe = "Cafe\u{0301}" /// print(cafe) /// // Prints "Café" /// /// let scalarsIndex = cafe.unicodeScalars.firstIndex(of: "e")! /// let stringIndex = String.Index(scalarsIndex, within: cafe)! /// /// print(cafe[...stringIndex]) /// // Prints "Café" /// /// If the index passed as `sourcePosition` doesn't have an exact /// corresponding position in `target`, the result of the initializer is /// `nil`. For example, an attempt to convert the position of the combining /// acute accent (`"\u{0301}"`) fails. Combining Unicode scalars do not have /// their own position in a string. /// /// let nextScalarsIndex = cafe.unicodeScalars.index(after: scalarsIndex) /// let nextStringIndex = String.Index(nextScalarsIndex, within: cafe) /// /// print(nextStringIndex) /// // Prints "nil" /// /// - Parameters: /// - sourcePosition: A position in a view of the `target` parameter. /// `sourcePosition` must be a valid index of at least one of the views /// of `target`. /// - target: The string referenced by the resulting index. @available(SwiftStdlib 5.1, *) public init?( _ sourcePosition: String.Index, within target: S ) { if let str = target as? String { self.init(sourcePosition, within: str) return } if let str = target as? Substring { // As a special exception, we allow `sourcePosition` to be an UTF-16 index // when `self` is a UTF-8 string (or vice versa), to preserve // compatibility with (broken) code that keeps using indices from a // bridged string after converting the string to a native representation. // Such indices are invalid, but returning nil here can break code that // appeared to work fine for ASCII strings in Swift releases prior to 5.7. let i = str._wholeGuts.ensureMatchingEncoding(sourcePosition) guard str._isValidIndex(i) else { return nil } self = i return } self.init(sourcePosition, within: String(target)) } /// Returns the position in the given UTF-8 view that corresponds exactly to /// this index. /// /// This example first finds the position of the character `"é"`, and then /// uses this method find the same position in the string's `utf8` view. /// /// let cafe = "Café" /// if let i = cafe.firstIndex(of: "é") { /// let j = i.samePosition(in: cafe.utf8)! /// print(Array(cafe.utf8[j...])) /// } /// // Prints "[195, 169]" /// /// - Parameter utf8: The view to use for the index conversion. This index /// must be a valid index of at least one view of the string shared by /// `utf8`. /// - Returns: The position in `utf8` that corresponds exactly to this index. /// If this index does not have an exact corresponding position in `utf8`, /// this method returns `nil`. For example, an attempt to convert the /// position of a UTF-16 trailing surrogate returns `nil`. public func samePosition( in utf8: String.UTF8View ) -> String.UTF8View.Index? { return String.UTF8View.Index(self, within: utf8) } /// Returns the position in the given UTF-16 view that corresponds exactly to /// this index. /// /// The index must be a valid index of `String(utf16)`. /// /// This example first finds the position of the character `"é"` and then /// uses this method find the same position in the string's `utf16` view. /// /// let cafe = "Café" /// if let i = cafe.firstIndex(of: "é") { /// let j = i.samePosition(in: cafe.utf16)! /// print(cafe.utf16[j]) /// } /// // Prints "233" /// /// - Parameter utf16: The view to use for the index conversion. This index /// must be a valid index of at least one view of the string shared by /// `utf16`. /// - Returns: The position in `utf16` that corresponds exactly to this /// index. If this index does not have an exact corresponding position in /// `utf16`, this method returns `nil`. For example, an attempt to convert /// the position of a UTF-8 continuation byte returns `nil`. public func samePosition( in utf16: String.UTF16View ) -> String.UTF16View.Index? { return String.UTF16View.Index(self, within: utf16) } } extension String { /// Returns the largest valid index in `self` that does not exceed the given /// position. /// /// let cafe = "Cafe\u{301}" // "Café" /// let accent = cafe.unicodeScalars.firstIndex(of: "\u{301")! /// let char = cafe._index(roundingDown: accent) /// print(cafe[char]) // "é" /// /// `String` methods such as `index(after:)` and `distance(from:to:)` /// implicitly round their input indices down to the nearest valid index: /// /// let i = cafe.index(before: char) /// let j = cafe.index(before: accent) /// print(cafe[i], cafe[j]) // "f f" /// print(i == j) // true /// /// This operation lets you perform this rounding yourself. For example, this /// can be used to safely check if `index(before:)` would consider some /// arbitrary index equivalent to the start index before calling it. /// /// - Parameter i: An index that is valid in at least one view of this string. /// - Returns: The largest valid index within this string that doesn't exceed /// `i`. @available(SwiftStdlib 5.8, *) public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { _guts.validateInclusiveCharacterIndex(i) } } extension Substring { /// Returns the largest valid index in `self` that does not exceed the given /// position. /// /// `Substring` methods such as `index(after:)` and `distance(from:to:)` /// implicitly round their input indices down to the nearest valid index. /// This operation lets you perform this rounding yourself. For example, this /// can be used to safely check if `index(before:)` would consider some /// arbitrary index equivalent to the start index before calling it. /// /// - Parameter i: An index that is valid in at least one view of this /// substring. /// - Returns: The largest valid index within this substring that doesn't /// exceed `i`. @available(SwiftStdlib 5.8, *) public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { _wholeGuts.validateInclusiveCharacterIndex(i, in: _bounds) } } extension String.UnicodeScalarView { /// Returns the largest valid index in `self` that does not exceed the given /// position. /// /// Methods such as `index(after:)` and `distance(from:to:)` implicitly round /// their input indices down to the nearest valid index. This operation lets /// you perform this rounding yourself. For example, this can be used to /// safely check if `index(before:)` would consider some arbitrary index /// equivalent to the start index before calling it. /// /// - Parameter i: An index that is valid in at least one view of the string /// shared by this view. /// - Returns: The largest valid index within this view that doesn't exceed /// `i`. @_alwaysEmitIntoClient public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { _guts.validateInclusiveScalarIndex(i) } } extension Substring.UnicodeScalarView { /// Returns the largest valid index in `self` that does not exceed the given /// position. /// /// Methods such as `index(after:)` and `distance(from:to:)` implicitly round /// their input indices down to the nearest valid index. This operation lets /// you perform this rounding yourself. For example, this can be used to /// safely check if `index(before:)` would consider some arbitrary index /// equivalent to the start index before calling it. /// /// - Parameter i: An index that is valid in at least one view of the /// substring shared by this view. /// - Returns: The largest valid index within this view that doesn't exceed /// `i`. @_alwaysEmitIntoClient public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { _wholeGuts.validateInclusiveScalarIndex(i, in: _bounds) } } extension String.UTF8View { /// Returns the largest valid index in `self` that does not exceed the given /// position. /// /// Methods such as `index(after:)` and `distance(from:to:)` implicitly round /// their input indices down to the nearest valid index. This operation lets /// you perform this rounding yourself. For example, this can be used to /// safely check if `index(before:)` would consider some arbitrary index /// equivalent to the start index before calling it. /// /// - Parameter i: An index that is valid in at least one view of the /// substring shared by this view. /// - Returns: The largest valid index within this view that doesn't exceed /// `i`. @_alwaysEmitIntoClient public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { let i = _guts.validateInclusiveSubscalarIndex(i) guard _guts.isForeign else { return i.strippingTranscoding._knownUTF8 } return _utf8AlignForeignIndex(i) } } extension Substring.UTF8View { /// Returns the largest valid index in `self` that does not exceed the given /// position. /// /// Methods such as `index(after:)` and `distance(from:to:)` implicitly round /// their input indices down to the nearest valid index. This operation lets /// you perform this rounding yourself. For example, this can be used to /// safely check if `index(before:)` would consider some arbitrary index /// equivalent to the start index before calling it. /// /// - Parameter i: An index that is valid in at least one view of the /// substring shared by this view. /// - Returns: The largest valid index within this view that doesn't exceed /// `i`. @_alwaysEmitIntoClient public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { let i = _wholeGuts.validateInclusiveSubscalarIndex(i, in: _bounds) guard _wholeGuts.isForeign else { return i.strippingTranscoding._knownUTF8 } return _slice._base._utf8AlignForeignIndex(i) } } extension String.UTF16View { /// Returns the valid index in `self` that this view considers equivalent to /// the given index. /// /// Indices in the UTF-8 view that address positions between Unicode scalars /// are rounded down to the nearest scalar boundary; other indices are left as /// is. /// /// - Parameter i: An index that is valid in at least one view of the /// substring shared by this view. /// - Returns: The valid index in `self` that this view considers equivalent /// to `i`. @_alwaysEmitIntoClient public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { let i = _guts.validateInclusiveSubscalarIndex(i) if _guts.isForeign { return i.strippingTranscoding._knownUTF16 } return _utf16AlignNativeIndex(i) } } extension Substring.UTF16View { /// Returns the valid index in `self` that this view considers equivalent to /// the given index. /// /// Indices in the UTF-8 view that address positions between Unicode scalars /// are rounded down to the nearest scalar boundary; other indices are left as /// is. /// /// - Parameter i: An index that is valid in at least one view of the /// substring shared by this view. /// - Returns: The valid index in `self` that this view considers equivalent /// to `i`. @_alwaysEmitIntoClient public // SPI(Foundation) FIXME: This should be API func _index(roundingDown i: Index) -> Index { let i = _wholeGuts.validateInclusiveSubscalarIndex(i, in: _bounds) if _wholeGuts.isForeign { return i.strippingTranscoding._knownUTF16 } return _slice._base._utf16AlignNativeIndex(i) } }