mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
String.Index has an encodedOffset-based initializer and computed property that exists for serialization purposes. It was documented as UTF-16 in the SE proposal introducing it, which was String's underlying encoding at the time, but the dream of String even then was to abstract away whatever encoding happend to be used. Serialization needs an explicit encoding for serialized indices to make sense: the offsets need to align with the view. With String utilizing UTF-8 encoding for native contents in Swift 5, serialization isn't necessarily the most efficient in UTF-16. Furthermore, the majority of usage of encodedOffset in the wild is buggy and operates under the assumption that a UTF-16 code unit was a Swift Character, which isn't even valid if the String is known to be all-ASCII (because CR-LF). This change introduces a pair of semantics-preserving alternatives to encodedOffset that explicitly call out the UTF-16 assumption. These serve as a gentle off-ramp for current mis-uses of encodedOffset.
185 lines
4.8 KiB
Swift
185 lines
4.8 KiB
Swift
// RUN: %empty-directory(%t)
|
||
// RUN: %target-build-swift %s -o %t/a.out_Debug -Onone
|
||
// RUN: %target-build-swift %s -o %t/a.out_Release -O
|
||
//
|
||
// RUN: %target-codesign %t/a.out_Debug
|
||
// RUN: %target-codesign %t/a.out_Release
|
||
// RUN: %target-run %t/a.out_Debug
|
||
// RUN: %target-run %t/a.out_Release
|
||
// REQUIRES: executable_test
|
||
|
||
import StdlibUnittest
|
||
|
||
let testSuiteSuffix = _isDebugAssertConfiguration() ? "_debug" : "_release"
|
||
|
||
var StringTraps = TestSuite("StringTraps" + testSuiteSuffix)
|
||
|
||
StringTraps.test("startIndex/predecessor")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.startIndex
|
||
i = s.index(after: i)
|
||
i = s.index(before: i)
|
||
expectCrashLater()
|
||
i = s.index(before: i)
|
||
}
|
||
|
||
StringTraps.test("endIndex/successor")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.startIndex
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
expectCrashLater()
|
||
i = s.index(after: i)
|
||
}
|
||
|
||
StringTraps.test("subscript(_:)/endIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.startIndex
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
expectCrashLater()
|
||
_ = s[i]
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewSubscript/endIndexSuccessor")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.utf8.startIndex
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
expectCrashLater()
|
||
i = s.utf8.index(after: i)
|
||
_ = s.utf8[i]
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewSubscript/endIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.utf8.startIndex
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
expectCrashLater()
|
||
_ = s.utf8[i]
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewSubscript/DecrementedStartIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.utf16.startIndex
|
||
expectCrashLater()
|
||
i = s.utf16.index(before: i)
|
||
_ = s.utf16[i]
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewSubscript/endIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
var s = "abc"
|
||
var i = s.utf16.startIndex
|
||
i = s.utf16.index(after: i)
|
||
i = s.utf16.index(after: i)
|
||
i = s.utf16.index(after: i)
|
||
expectCrashLater()
|
||
_ = s.utf16[i]
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewIndex/offsetLimited")
|
||
.code {
|
||
let sa = "foo"
|
||
let u16a = sa.utf16
|
||
let s16 = sa + "🤦🏻♀️"
|
||
let u16 = s16.utf16
|
||
|
||
let iaBegin = u16a.index(sa.startIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaBegin)
|
||
let iaEnd = u16a.index(sa.endIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaEnd)
|
||
let i16Begin = u16.index(u16.startIndex, offsetBy: 99, limitedBy: u16.endIndex)
|
||
expectNil(i16Begin)
|
||
let i16End = u16.index(u16.startIndex, offsetBy: 99, limitedBy: u16.endIndex)
|
||
expectNil(i16End)
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewIndex/offsetCrash")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s16 = "foo🤦🏻♀️"
|
||
let u16 = s16.utf16
|
||
expectCrashLater()
|
||
let i = u16.index(u16.startIndex, offsetBy: 99)
|
||
_ = s16.utf16[i]
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewIndex/offsetLimited")
|
||
.code {
|
||
let sa = "foo"
|
||
let u8a = sa.utf8
|
||
let s8 = sa + "🤦🏻♀️"
|
||
let u8 = s8.utf8
|
||
|
||
let iaBegin = u8a.index(sa.startIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaBegin)
|
||
let iaEnd = u8a.index(sa.endIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaEnd)
|
||
let i8Begin = u8.index(u8.startIndex, offsetBy: 99, limitedBy: u8.endIndex)
|
||
expectNil(i8Begin)
|
||
let i8End = u8.index(u8.startIndex, offsetBy: 99, limitedBy: u8.endIndex)
|
||
expectNil(i8End)
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewIndex/offsetCrash")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s8 = "foo🤦🏻♀️"
|
||
let u8 = s8.utf8
|
||
expectCrashLater()
|
||
let i = u8.index(u8.startIndex, offsetBy: 99)
|
||
_ = s8.utf8[i]
|
||
}
|
||
|
||
StringTraps.test("String.Index.utf16Offset(in:)/subscalarUTF8")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "😇"
|
||
let u8 = s.utf8
|
||
let i = u8.index(after: u8.startIndex)
|
||
expectCrashLater()
|
||
_ = i.utf16Offset(in: s)
|
||
}
|
||
|
||
runAllTests()
|
||
|