Skip to content

Commit baaaf03

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 baaaf03

File tree

2 files changed

+109
-1
lines changed

2 files changed

+109
-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: 108 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,112 @@ 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+
tests.test("String.UTF16View.Index/Strideable")
812+
.forEach(in: stringViewTests) {
813+
test in
814+
815+
func allIndices<C : Collection>(of c: C) -> [C.Index]
816+
where C.Indices.Iterator.Element == C.Index
817+
{
818+
var result = Array(c.indices)
819+
result.append(c.endIndex)
820+
return result
821+
}
822+
823+
checkStrideable(
824+
instances: allIndices(of: test.string.utf16),
825+
distances: Array(0..<test.string.utf16.count),
826+
distanceOracle: { $1 - $0 })
827+
}
828+
829+
tests.test("String.UTF8View/Collection")
830+
.forEach(in: stringViewTests) {
831+
test in
832+
833+
// FIXME(ABI): should be `checkBidirectionalCollection`.
834+
checkForwardCollection(test.utf8, test.string.utf8) { $0 == $1 }
835+
}
836+
837+
tests.test("String.UTF16View/RandomAccessCollection")
838+
.forEach(in: stringViewTests) {
839+
test in
840+
841+
checkRandomAccessCollection(test.utf16, test.string.utf16) { $0 == $1 }
842+
}
843+
844+
tests.test("String.UTF32View/BidirectionalCollection")
845+
.forEach(in: stringViewTests) {
846+
test in
847+
848+
checkBidirectionalCollection(
849+
test.unicodeScalars, test.string.unicodeScalars) { $0 == $1 }
850+
}
743851

744852
runAllTests()

0 commit comments

Comments
 (0)