Skip to content

Commit 88de90a

Browse files
committed
stdlib: fix incorrect distance measurement between UTF-16 indices
String.UTF16View.Index.distance(to:) was returning a negated result. Fixes SR-1988.
1 parent 3dc793c commit 88de90a

File tree

2 files changed

+120
-1
lines changed

2 files changed

+120
-1
lines changed

stdlib/public/SDK/Foundation/ExtraStringAPIs.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extension String.UTF16View.Index : Strideable {
2121
}
2222

2323
public func distance(to other: String.UTF16View.Index) -> Int {
24-
return other._offset.distance(to: _offset)
24+
return _offset.distance(to: other._offset)
2525
}
2626

2727
public func advanced(by n: Int) -> String.UTF16View.Index {

validation-test/stdlib/StringViews.swift

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import Swift
1616
import StdlibUnittest
17+
import StdlibCollectionUnittest
1718

1819
#if _runtime(_ObjC)
1920
// FIXME: Foundation leaks through StdlibUnittest. It adds some conformances
@@ -740,5 +741,123 @@ tests.test("UnicodeScalars->String") {
740741
}
741742
}
742743

744+
struct StringViewTest {
745+
var string: String
746+
var utf8: [UInt8]
747+
var utf16: [UInt16]
748+
var unicodeScalars: [UnicodeScalar]
749+
750+
init(string: String, utf8: [UInt8], utf16: [UInt16], utf32: [UInt32]) {
751+
self.string = string
752+
self.utf8 = utf8
753+
self.utf16 = utf16
754+
self.unicodeScalars = utf32.map { UnicodeScalar($0)! }
755+
}
756+
}
757+
758+
var stringViewTests: [StringViewTest] = [
759+
StringViewTest(
760+
string: "",
761+
utf8: [],
762+
utf16: [],
763+
utf32: []),
764+
StringViewTest(
765+
string: "\u{0000}",
766+
utf8: [0x00],
767+
utf16: [0x00],
768+
utf32: [0x00]),
769+
StringViewTest(
770+
string: "a",
771+
utf8: [0x61],
772+
utf16: [0x61],
773+
utf32: [0x61]),
774+
StringViewTest(
775+
string: "aa",
776+
utf8: [0x61, 0x61],
777+
utf16: [0x61, 0x61],
778+
utf32: [0x61, 0x61]),
779+
StringViewTest(
780+
string: "ab",
781+
utf8: [0x61, 0x62],
782+
utf16: [0x61, 0x62],
783+
utf32: [0x61, 0x62]),
784+
StringViewTest(
785+
string: "abc",
786+
utf8: [0x61, 0x62, 0x63],
787+
utf16: [0x61, 0x62, 0x63],
788+
utf32: [0x61, 0x62, 0x63]),
789+
StringViewTest(
790+
string: "\u{007f}",
791+
utf8: [0x7f],
792+
utf16: [0x7f],
793+
utf32: [0x7f]),
794+
StringViewTest(
795+
string: "\u{0430}",
796+
utf8: [0xd0, 0xb0],
797+
utf16: [0x0430],
798+
utf32: [0x0430]),
799+
StringViewTest(
800+
string: "\u{0430}\u{0431}\u{0432}",
801+
utf8: [0xd0, 0xb0, 0xd0, 0xb1, 0xd0, 0xb2],
802+
utf16: [0x0430, 0x0431, 0x0432],
803+
utf32: [0x0430, 0x0431, 0x0432]),
804+
StringViewTest(
805+
string: "\u{1f425}",
806+
utf8: [0xf0, 0x9f, 0x90, 0xa5],
807+
utf16: [0xd83d, 0xdc25],
808+
utf32: [0x1f425]),
809+
]
810+
811+
#if _runtime(_ObjC)
812+
tests.test("String.UTF16View.Index/Strideable")
813+
.forEach(in: stringViewTests) {
814+
test in
815+
816+
func allIndices<C : Collection>(of c: C) -> [C.Index]
817+
where C.Indices.Iterator.Element == C.Index
818+
{
819+
var result = Array(c.indices)
820+
result.append(c.endIndex)
821+
return result
822+
}
823+
824+
checkStrideable(
825+
instances: allIndices(of: test.string.utf16),
826+
distances: Array(0..<test.string.utf16.count),
827+
distanceOracle: { $1 - $0 })
828+
}
829+
#endif
830+
831+
tests.test("String.UTF8View/Collection")
832+
.forEach(in: stringViewTests) {
833+
test in
834+
835+
// FIXME(ABI): should be `checkBidirectionalCollection`.
836+
checkForwardCollection(test.utf8, test.string.utf8) { $0 == $1 }
837+
}
838+
839+
#if _runtime(_Native)
840+
tests.test("String.UTF16View/BidirectionalCollection")
841+
.forEach(in: stringViewTests) {
842+
test in
843+
844+
checkBidirectionalCollection(test.utf16, test.string.utf16) { $0 == $1 }
845+
}
846+
#else
847+
tests.test("String.UTF16View/RandomAccessCollection")
848+
.forEach(in: stringViewTests) {
849+
test in
850+
851+
checkRandomAccessCollection(test.utf16, test.string.utf16) { $0 == $1 }
852+
}
853+
#endif
854+
855+
tests.test("String.UTF32View/BidirectionalCollection")
856+
.forEach(in: stringViewTests) {
857+
test in
858+
859+
checkBidirectionalCollection(
860+
test.unicodeScalars, test.string.unicodeScalars) { $0 == $1 }
861+
}
743862

744863
runAllTests()

0 commit comments

Comments
 (0)