//===----------------------------------------------------------------------===// // // 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 { // FIXME(strings): at least temporarily remove it to see where it was applied /// Creates a new string from the given substring. /// /// - Parameter substring: A substring to convert to a standalone `String` /// instance. /// /// - Complexity: O(*n*), where *n* is the length of `substring`. public init(_ substring: Substring) { self = String(substring._slice) } } /// A slice of a string. /// /// When you create a slice of a string, a `Substring` instance is the result. /// Operating on substrings is fast and efficient because a substring shares /// its storage with the original string. The `Substring` type presents the /// same interface as `String`, so you can avoid or defer any copying of the /// string's contents. /// /// The following example creates a `greeting` string, and then finds the /// substring of the first sentence: /// /// let greeting = "Hi there! It's nice to meet you! 👋" /// let endOfSentence = greeting.index(of: "!")! /// let firstSentence = greeting[...endOfSentence] /// // firstSentence == "Hi there!" /// /// You can perform many string operations on a substring. Here, we find the /// length of the first sentence and create an uppercase version. /// /// print("'\(firstSentence)' is \(firstSentence.count) characters long.") /// // Prints "'Hi there!' is 9 characters long." /// /// let shoutingSentence = firstSentence.uppercased() /// // shoutingSentence == "HI THERE!" /// /// Converting a Substring to a String /// ================================== /// /// This example defines a `rawData` string with some unstructured data, and /// then uses the string's `prefix(while:)` method to create a substring of /// the numeric prefix: /// /// let rawInput = "126 a.b 22219 zzzzzz" /// let numericPrefix = rawInput.prefix(while: { "0"..."9" ~= $0 }) /// // numericPrefix is the substring "126" /// /// When you need to store a substring or pass it to a function that requires a /// `String` instance, convert it using the `String.init(_:)` initializer. /// /// _ = Int(numericPrefix, radix: 10) /// // error: cannot convert value... /// let prefix = Int(String(numericPrefix), radix: 10) /// // prefix == 126 /// /// Calling this initializer copies the contents of the substring to a new /// string. /// /// - Important: Don't store substrings longer than you need to perform a /// specific operation. A substring holds a reference to the entire storage /// of the string it came from, not just to the portion it presents, even /// when there is no other reference to the original string. Storing /// substrings may therefore prolong the lifetime of string data that is no /// longer otherwise accessible, which can appear to be memory leakage. public struct Substring : StringProtocol { public typealias Index = String.Index public typealias IndexDistance = String.IndexDistance public typealias SubSequence = Substring internal var _slice: RangeReplaceableBidirectionalSlice /// Creates an empty substring. public init() { _slice = RangeReplaceableBidirectionalSlice() } internal init(_slice: RangeReplaceableBidirectionalSlice) { self._slice = _slice } /// Creates a substring with the specified bounds within the given string. /// /// - Parameters: /// - base: The string to create a substring of. /// - bounds: The range of `base` to use for the substring. The lower and /// upper bounds of `bounds` must be valid indices of `base`. public init(_base base: String, _ bounds: Range) { _slice = RangeReplaceableBidirectionalSlice(base: base, bounds: bounds) } internal init( _base base: String, _ bounds: R ) where R.Bound == Index { self.init(_base: base, bounds.relative(to: base)) } public var startIndex: Index { return _slice.startIndex } public var endIndex: Index { return _slice.endIndex } public func index(after i: Index) -> Index { _precondition(i < endIndex, "Cannot increment beyond endIndex") _precondition(i >= startIndex, "Cannot increment an invalid index") // FIXME(strings): slice types currently lack necessary bound checks return _slice.index(after: i) } public func index(before i: Index) -> Index { _precondition(i <= endIndex, "Cannot decrement an invalid index") _precondition(i > startIndex, "Cannot decrement beyond startIndex") // FIXME(strings): slice types currently lack necessary bound checks return _slice.index(before: i) } public func index(_ i: Index, offsetBy n: IndexDistance) -> Index { let result = _slice.index(i, offsetBy: n) // FIXME(strings): slice types currently lack necessary bound checks _precondition( (_slice._startIndex ... _slice.endIndex).contains(result), "Operation results in an invalid index") return result } public func index( _ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index ) -> Index? { let result = _slice.index(i, offsetBy: n, limitedBy: limit) // FIXME(strings): slice types currently lack necessary bound checks _precondition(result.map { (_slice._startIndex ... _slice.endIndex).contains($0) } ?? true, "Operation results in an invalid index") return result } public func distance(from start: Index, to end: Index) -> IndexDistance { return _slice.distance(from: start, to: end) } public subscript(i: Index) -> Character { return _slice[i] } public mutating func replaceSubrange( _ bounds: Range, with newElements: C ) where C : Collection, C.Iterator.Element == Iterator.Element { // FIXME(strings): slice types currently lack necessary bound checks _slice.replaceSubrange(bounds, with: newElements) } % for Range in ['Range', 'ClosedRange']: public mutating func replaceSubrange( _ bounds: ${Range}, with newElements: Substring ) { replaceSubrange(bounds, with: newElements._slice) } % end /// Creates a string from the given Unicode code units in the specified /// encoding. /// /// - Parameters: /// - codeUnits: A collection of code units encoded in the ecoding /// specified in `sourceEncoding`. /// - sourceEncoding: The encoding in which `codeUnits` should be /// interpreted. public init( decoding codeUnits: C, as sourceEncoding: Encoding.Type ) where C.Iterator.Element == Encoding.CodeUnit { self.init(String(decoding: codeUnits, as: sourceEncoding)) } /// 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. public init(cString nullTerminatedUTF8: UnsafePointer) { self.init(String(cString: nullTerminatedUTF8)) } /// 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. public init( decodingCString nullTerminatedCodeUnits: UnsafePointer, as sourceEncoding: Encoding.Type ) { self.init( String(decodingCString: nullTerminatedCodeUnits, as: sourceEncoding)) } /// 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, it is 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 of the `body` closure parameter, if any. public func withCString( _ body: (UnsafePointer) throws -> Result) rethrows -> Result { return try _slice._base._core._withCSubstringAndLength( in: startIndex._base._position..( encodedAs targetEncoding: TargetEncoding.Type, _ body: (UnsafePointer) throws -> Result ) rethrows -> Result { return try _slice._base._core._withCSubstring( in: startIndex._base._position.. Bool { return String(lhs) == String(rhs) } // These are not Equatable requirements, but sufficiently similar to be in // this extension. // FIXME(strings): should be gone if/when an implicit conversion from/to // String is available. // FIXME(ABI): public static func ==(lhs: String, rhs: Substring) -> Bool { return lhs == String(rhs) } public static func ==(lhs: Substring, rhs: String) -> Bool { return String(lhs) == rhs } public static func !=(lhs: String, rhs: Substring) -> Bool { return lhs != String(rhs) } public static func !=(lhs: Substring, rhs: String) -> Bool { return String(lhs) != rhs } } extension Substring : Comparable { public static func <(lhs: Substring, rhs: Substring) -> Bool { return String(lhs) < String(rhs) } } extension Substring : Hashable { public var hashValue : Int { return String(self).hashValue } } extension Substring { % for (property, ViewPrefix) in [ % ('utf8', 'UTF8'), % ('utf16', 'UTF16'), % ('unicodeScalars', 'UnicodeScalar'), % ('characters', 'Character')]: public typealias ${ViewPrefix}Index = String.${ViewPrefix}View.Index public var ${property}: String.${ViewPrefix}View { get { return String(self).${property} } set { let base = String(describing: newValue) self = Substring(_base: base, base.startIndex ..< base.endIndex) } } % end } #if _runtime(_ObjC) extension Substring { public func hasPrefix(_ prefix: String) -> Bool { return String(self).hasPrefix(prefix) } public func hasSuffix(_ suffix: String) -> Bool { return String(self).hasSuffix(suffix) } } #endif extension Substring : RangeReplaceableCollection { public init(_ elements: S) where S.Element == Character { let e0 = elements as? _SwiftStringView if _fastPath(e0 != nil), let e = e0 { self = e._ephemeralContent[...] } else { self = String(elements)[...] } } public mutating func append(contentsOf elements: S) where S.Element == Character { var c = self._ephemeralContent._core self = Substring() let e0 = elements as? _SwiftStringView if _fastPath(e0 != nil), let e1 = e0 { c.append(contentsOf: e1._ephemeralContent.utf16) self = String(c)[...] } else { var s = String(c) s.append(contentsOf: elements) self = s[...] } } } extension Substring { public func lowercased() -> String { return String(self).lowercased() } public func uppercased() -> String { return String(self).uppercased() } } extension Substring : TextOutputStream { public mutating func write(_ other: String) { append(contentsOf: other) } } extension Substring : TextOutputStreamable { public func write(to target: inout Target) { target.write(String(self)) } } extension Substring : ExpressibleByUnicodeScalarLiteral { public init(unicodeScalarLiteral value: String) { self.init(_base: value, value.startIndex ..< value.endIndex) } } extension Substring : ExpressibleByExtendedGraphemeClusterLiteral { public init(extendedGraphemeClusterLiteral value: String) { self.init(_base: value, value.startIndex ..< value.endIndex) } } extension Substring : ExpressibleByStringLiteral { public init(stringLiteral value: String) { self.init(_base: value, value.startIndex ..< value.endIndex) } } //===--- String/Substring Slicing Support ---------------------------------===// /// In Swift 3.2, in the absence of type context, /// /// someString[someString.startIndex..) -> Substring { return Substring( _slice: RangeReplaceableBidirectionalSlice(base: self, bounds: r)) } @available(swift, obsoleted: 4) public subscript(bounds: Range) -> String { return String(characters[bounds]) } @available(swift, obsoleted: 4) public subscript(bounds: ClosedRange) -> String { return String(characters[bounds]) } } extension Substring { @available(swift, introduced: 4) public subscript(r: Range) -> Substring { return Substring(_slice: _slice[r]) } @available(swift, obsoleted: 4) public subscript(bounds: Range) -> String { return String(characters[bounds]) } @available(swift, obsoleted: 4) public subscript(bounds: ClosedRange) -> String { return String(characters[bounds]) } } //===----------------------------------------------------------------------===//