[stdlib] Failable String inits from UTF8/16 views

String.UTF8View and String.UTF16View may be slices that don't both start
and end on Unicode scalar boundaries, in which case conversion to a
String must fail.

Swift SVN r24669
This commit is contained in:
Dave Abrahams
2015-01-23 02:46:42 +00:00
parent 6d72340259
commit 02e70d15d1
4 changed files with 58 additions and 30 deletions

View File

@@ -165,9 +165,18 @@ extension String {
}
/// Construct the `String` corresponding to the given sequence of
/// UTF16 code units.
public init(_ utf16: UTF16View) {
self.init(utf16._core[utf16._offset..<utf16._offset+utf16._length])
/// UTF-16 code units. If `utf16` contains unpaired surrogates, the
/// result is `nil`.
public init?(_ utf16: UTF16View) {
let wholeString = String(utf16._core)
if let start = utf16.startIndex.samePositionIn(wholeString) {
if let end = utf16.endIndex.samePositionIn(wholeString) {
self = wholeString[start..<end]
return
}
}
return nil
}
/// The index type for subscripting a `String`\ 's `utf16` view.
@@ -242,7 +251,7 @@ extension String.UTF16View.Index {
/// `utf8Index`. If no such position exists, the result is `nil`.
///
/// Requires: `utf8Index` is an element of
/// `indices(String(utf16).utf8)`.
/// `indices(String(utf16)!.utf8)`.
public init?(
_ utf8Index: String.UTF8Index, within utf16: String.UTF16View
) {
@@ -263,7 +272,7 @@ extension String.UTF16View.Index {
/// `unicodeScalarIndex`.
///
/// Requires: `unicodeScalarIndex` is an element of
/// `indices(String(utf16).unicodeScalars)`.
/// `indices(String(utf16)!.unicodeScalars)`.
public init(
_ unicodeScalarIndex: String.UnicodeScalarIndex,
within utf16: String.UTF16View) {
@@ -274,7 +283,7 @@ extension String.UTF16View.Index {
/// `characterIndex`.
///
/// Requires: `characterIndex` is an element of
/// `indices(String(utf16))`.
/// `indices(String(utf16)!)`.
public init(_ characterIndex: String.Index, within utf16: String.UTF16View) {
_offset = characterIndex._utf16Index
}
@@ -283,7 +292,7 @@ extension String.UTF16View.Index {
/// to `self`, or if no such position exists, `nil`.
///
/// Requires: `self` is an element of
/// `indices(String(utf8).utf16)`.
/// `indices(String(utf8)!.utf16)`.
public func samePositionIn(
utf8: String.UTF8View
) -> String.UTF8View.Index? {

View File

@@ -273,9 +273,18 @@ extension String {
}
/// Construct the `String` corresponding to the given sequence of
/// UTF8 code units.
public init(_ utf8: UTF8View) {
self.init(utf8._core)
/// UTF-8 code units. If `utf8` contains unpaired surrogates, the
/// result is `nil`.
public init?(_ utf8: UTF8View) {
let wholeString = String(utf8._core)
if let start = utf8.startIndex.samePositionIn(wholeString) {
if let end = utf8.endIndex.samePositionIn(wholeString) {
self = wholeString[start..<end]
return
}
}
return nil
}
/// The index type for subscripting a `String`\ 's `.utf8` view.
@@ -327,7 +336,7 @@ extension String.UTF8View.Index {
/// `utf16Index`. If no such position exists, the result is `nil`.
///
/// Requires: `utf8Index` is an element of
/// `indices(String(utf16).utf8)`.
/// `indices(String(utf16)!.utf8)`.
public init?(_ utf16Index: String.UTF16Index, within utf8: String.UTF8View) {
let utf16 = String.UTF16View(utf8._core)
@@ -354,7 +363,7 @@ extension String.UTF8View.Index {
/// `unicodeScalarIndex`.
///
/// Requires: `unicodeScalarIndex` is an element of
/// `indices(String(utf8).unicodeScalars)`.
/// `indices(String(utf8)!.unicodeScalars)`.
public init(
_ unicodeScalarIndex: String.UnicodeScalarIndex,
within utf8: String.UTF8View
@@ -366,7 +375,7 @@ extension String.UTF8View.Index {
/// `characterIndex`.
///
/// Requires: `characterIndex` is an element of
/// `indices(String(utf8))`.
/// `indices(String(utf8)!)`.
public init(_ characterIndex: String.Index, within utf8: String.UTF8View) {
self.init(utf8._core, _utf16Offset: characterIndex._base._position)
}
@@ -374,7 +383,7 @@ extension String.UTF8View.Index {
/// Return the position in `utf16` that corresponds exactly
/// to `self`, or if no such position exists, `nil`.
///
/// Requires: `self` is an element of `indices(String(utf16).utf8)`.
/// Requires: `self` is an element of `indices(String(utf16)!.utf8)`.
public func samePositionIn(
utf16: String.UTF16View
) -> String.UTF16View.Index? {

View File

@@ -415,7 +415,7 @@ extension String.UnicodeScalarIndex {
/// Return the position in `utf8` that corresponds exactly
/// to `self`.
///
/// Requires: `self` is an element of `indices(String(utf8))`.
/// Requires: `self` is an element of `indices(String(utf8)!)`.
public func samePositionIn(utf8: String.UTF8View) -> String.UTF8View.Index {
return String.UTF8View.Index(self, within: utf8)
}
@@ -423,7 +423,7 @@ extension String.UnicodeScalarIndex {
/// Return the position in `utf16` that corresponds exactly
/// to `self`.
///
/// Requires: `self` is an element of `indices(String(utf16))`.
/// Requires: `self` is an element of `indices(String(utf16)!)`.
public func samePositionIn(
utf16: String.UTF16View
) -> String.UTF16View.Index {

View File

@@ -697,25 +697,35 @@ tests.test("UTF8 indexes") {
tests.test("UTF16->String") {
let s = summer + winter + winter + summer
let v = s.utf16
for i in indices(s) {
for j in i..<s.endIndex {
expectEqual(
s[i..<j],
String(v[i.samePositionIn(v)..<j.samePositionIn(v)])
)
for i in indices(v) {
for j in i..<v.endIndex {
if let si = i.samePositionIn(s) {
if let sj = j.samePositionIn(s) {
expectEqual(i, i1)
expectEqual(j, j1)
expectEqual(s[si..<sj], String(v[i..<j]))
continue
}
}
expectEmpty(String(v[i..<j]))
}
}
}
tests.test("UTF8->String") {
let s = summer + winter + winter + summer
let v = s.utf16
for i in indices(s) {
for j in i..<s.endIndex {
expectEqual(
s[i..<j],
String(v[i.samePositionIn(v)..<j.samePositionIn(v)])
)
let v = s.utf8
for i in indices(v) {
for j in i..<v.endIndex {
if let si = i.samePositionIn(s) {
if let sj = j.samePositionIn(s) {
expectEqual(i, i1)
expectEqual(j, j1)
expectEqual(s[si..<sj], String(v[i..<j]))
continue
}
}
expectEmpty(String(v[i..<j]))
}
}
}