Skip to content

Commit

Permalink
[stdlib] String index interchange, part III (UTF8)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Abrahams committed May 21, 2017
1 parent bb2424f commit 5bcb52b
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 478 deletions.
38 changes: 36 additions & 2 deletions stdlib/public/core/StringIndex.swift
Expand Up @@ -16,7 +16,7 @@ extension String {

internal enum _Cache {
case utf16
case utf8(encodedScalar: Unicode.UTF8.EncodedScalar)
case utf8(encodedScalar: Unicode.UTF8.EncodedScalar, stride: UInt8)
case character(stride: UInt16)
case unicodeScalar(value: Unicode.Scalar)
}
Expand Down Expand Up @@ -49,13 +49,47 @@ extension String.Index {
}

internal static var _strideBits : Int { return 16 }

internal static var _mask : UInt64 { return (1 &<< _Self._strideBits) &- 1 }

internal mutating func _setEncodedOffset(_ x: Int) {
_compoundOffset = UInt64(x << _Self._strideBits)
}

public var encodedOffset : Int {
return Int(_compoundOffset >> numericCast(_Self._strideBits))
}

/// The offset of this index within whatever encoding this is being viewed as
internal var _transcodedOffset : Int {
get {
return Int(_compoundOffset & _Self._mask)
}
set {
let extended = UInt64(newValue)
_sanityCheck(extended <= _Self._mask)
_compoundOffset &= ~_Self._mask
_compoundOffset |= extended
}
}
}

// backward compatibility for index interchange.
extension Optional where Wrapped == String.Index {
@available(
swift, obsoleted: 4.0,
message: "Any String view index conversion can fail in Swift 4; please unwrap the optional indices")
public static func ..<(
lhs: String.Index?, rhs: String.Index?
) -> Range<String.Index> {
return lhs! ..< rhs!
}

@available(
swift, obsoleted: 4.0,
message: "Any String view index conversion can fail in Swift 4; please unwrap the optional indices")
public static func ...(
lhs: String.Index?, rhs: String.Index?
) -> ClosedRange<String.Index> {
return lhs! ... rhs!
}
}
69 changes: 9 additions & 60 deletions stdlib/public/core/StringIndexConversions.swift
Expand Up @@ -40,44 +40,18 @@ extension String.Index {
/// // Prints "nil"
///
/// - Parameters:
/// - unicodeScalarIndex: A position in the `unicodeScalars` view of the
/// `other` parameter.
/// - other: The string referenced by both `unicodeScalarIndex` and the
/// - sourcePosition: A position in (a view of) the `other` parameter.
/// - target: The string referenced by both `unicodeScalarIndex` and the
/// resulting index.
public init?(
_ unicodeScalarIndex: String.UnicodeScalarIndex,
within other: String
_ sourcePosition: String.Index,
within target: String
) {
if !other.unicodeScalars._isOnGraphemeClusterBoundary(unicodeScalarIndex) {
return nil
}
self = unicodeScalarIndex
}
guard target.unicodeScalars._isOnGraphemeClusterBoundary(sourcePosition)
else { return nil }

/// Creates an index in the given string that corresponds exactly to the
/// specified `UTF8View` position.
///
/// If the position passed in `utf8Index` doesn't have an exact corresponding
/// position in `other`, the result of the initializer is `nil`. For
/// example, an attempt to convert the position of a UTF-8 continuation byte
/// returns `nil`.
///
/// - Parameters:
/// - utf8Index: A position in the `utf8` view of the `other` parameter.
/// - other: The string referenced by both `utf8Index` and the resulting
/// index.
public init?(
_ utf8Index: String.UTF8Index,
within other: String
) {
if let me = utf8Index.samePosition(
in: other.unicodeScalars
)?.samePosition(in: other) {
self = me
}
else {
return nil
}
self = target.characters._index(
atEncodedOffset: sourcePosition.encodedOffset)
}

/// Returns the position in the given UTF-8 view that corresponds exactly to
Expand Down Expand Up @@ -122,33 +96,8 @@ extension String.Index {
/// - Returns: The position in `utf16` that corresponds exactly to this index.
public func samePosition(
in utf16: String.UTF16View
) -> String.UTF16View.Index {
) -> String.UTF16View.Index? {
return String.UTF16View.Index(self, within: utf16)
}

/// Returns the position in the given view of Unicode scalars that
/// corresponds exactly to this index.
///
/// The index must be a valid index of `String(unicodeScalars)`.
///
/// This example first finds the position of the character `"é"` and then uses
/// this method find the same position in the string's `unicodeScalars`
/// view.
///
/// let cafe = "Café"
/// if let i = cafe.index(of: "é") {
/// let j = i.samePosition(in: cafe.unicodeScalars)
/// print(cafe.unicodeScalars[j])
/// }
/// // Prints "é"
///
/// - Parameter unicodeScalars: The view to use for the index conversion.
/// - Returns: The position in `unicodeScalars` that corresponds exactly to
/// this index.
public func samePosition(
in unicodeScalars: String.UnicodeScalarView
) -> String.UnicodeScalarView.Index {
return String.UnicodeScalarView.Index(self, within: unicodeScalars)!
}
}

81 changes: 35 additions & 46 deletions stdlib/public/core/StringUTF16.swift
Expand Up @@ -335,47 +335,6 @@ extension String.UTF16View : _SwiftStringView {

// Index conversions
extension String.UTF16View.Index {
/// Creates an index in the given UTF-16 view that corresponds exactly to the
/// specified `UTF8View` position.
///
/// The following example finds the position of a space in a string's `utf8`
/// view and then converts that position to an index in the string's
/// `utf16` view.
///
/// let cafe = "Café 🍵"
///
/// let utf8Index = cafe.utf8.index(of: 32)!
/// let utf16Index = String.UTF16View.Index(utf8Index, within: cafe.utf16)!
///
/// print(cafe.utf16[..<utf16Index])
/// // Prints "Café"
///
/// If the position passed as `utf8Index` doesn't have an exact corresponding
/// position in `utf16`, the result of the initializer is `nil`. For
/// example, because UTF-8 and UTF-16 represent high Unicode code points
/// differently, an attempt to convert the position of a UTF-8 continuation
/// byte fails.
///
/// - Parameters:
/// - utf8Index: A position in a `UTF8View` instance. `utf8Index` must be
/// an element in `String(utf16).utf8.indices`.
/// - utf16: The `UTF16View` in which to find the new position.
public init?(
_ utf8Index: String.UTF8Index, within utf16: String.UTF16View
) {
let core = utf16._core

_precondition(
utf8Index._coreIndex >= 0 && utf8Index._coreIndex <= core.endIndex,
"Invalid String.UTF8Index for this UTF-16 view")

// Detect positions that have no corresponding index.
if !utf8Index._isOnUnicodeScalarBoundary(in: core) {
return nil
}
self = String.Index(encodedOffset: utf8Index._coreIndex)
}

/// Creates an index in the given UTF-16 view that corresponds exactly to the
/// specified string position.
///
Expand All @@ -391,11 +350,13 @@ extension String.UTF16View.Index {
/// // Prints "Café"
///
/// - Parameters:
/// - index: A position in a string. `index` must be an element in
/// `String(utf16).indices`.
/// - utf16: The `UTF16View` in which to find the new position.
public init(_ index: String.Index, within utf16: String.UTF16View) {
self = index
/// - sourcePosition: A position in a string or one of its views
/// - target: The `UTF16View` in which to find the new position.
public init?(
_ sourcePosition: String.Index, within target: String.UTF16View
) {
guard sourcePosition._transcodedOffset == 0 else { return nil }
self.init(encodedOffset: sourcePosition.encodedOffset)
}

/// Returns the position in the given view of Unicode scalars that
Expand Down Expand Up @@ -520,3 +481,31 @@ extension String.UTF16View.Indices : BidirectionalCollection {
}
}

// backward compatibility for index interchange.
extension String.UTF16View {
@available(
swift, obsoleted: 4.0,
message: "Any String view index conversion can fail in Swift 4; please unwrap the optional index")
public func index(after i: Index?) -> Index {
return index(after: i)
}
@available(
swift, obsoleted: 4.0,
message: "Any String view index conversion can fail in Swift 4; please unwrap the optional index")
public func index(
_ i: Index?, offsetBy n: IndexDistance) -> Index {
return index(i!, offsetBy: n)
}
@available(
swift, obsoleted: 4.0,
message: "Any String view index conversion can fail in Swift 4; please unwrap the optional indices")
public func distance(from i: Index?, to j: Index?) -> IndexDistance {
return distance(from: i!, to: j!)
}
@available(
swift, obsoleted: 4.0,
message: "Any String view index conversion can fail in Swift 4; please unwrap the optional index")
public subscript(i: Index?) -> Unicode.UTF16.CodeUnit {
return self[i!]
}
}

0 comments on commit 5bcb52b

Please sign in to comment.