Skip to content

Commit c560f14

Browse files
committed
string identical
1 parent 23013ca commit c560f14

File tree

4 files changed

+78
-2
lines changed

4 files changed

+78
-2
lines changed

Benchmarks/Benchmarks/AttributedString/BenchmarkAttributedString.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,10 @@ let benchmarks = {
431431
blackHole(manyAttributesString == manyAttributesString2)
432432
}
433433

434+
Benchmark("isIdentical") { benchmark in
435+
blackHole(manyAttributesString.isIdentical(to: manyAttributesString))
436+
}
437+
434438
Benchmark("equalityDifferingCharacters") { benchmark in
435439
blackHole(manyAttributesString == manyAttributesString3)
436440
}
@@ -442,7 +446,11 @@ let benchmarks = {
442446
Benchmark("substringEquality") { benchmark in
443447
blackHole(manyAttributesSubstring == manyAttributes2Substring)
444448
}
445-
449+
450+
Benchmark("substringIsIdentical") { benchmark in
451+
blackHole(manyAttributesSubstring.isIdentical(to: manyAttributesSubstring))
452+
}
453+
446454
Benchmark("hashAttributedString") { benchmark in
447455
var hasher = Hasher()
448456
manyAttributesString.hash(into: &hasher)

Sources/FoundationEssentials/AttributedString/AttributedString.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,22 @@ extension Range where Bound == BigString.Index {
403403
Range<AttributedString.Index>(uncheckedBounds: (AttributedString.Index(lowerBound, version: version), AttributedString.Index(upperBound, version: version)))
404404
}
405405
}
406+
407+
extension AttributedString {
408+
/// Returns a boolean value indicating whether this string is identical to
409+
/// `other`.
410+
///
411+
/// Two string values are identical if there is no way to distinguish between
412+
/// them.
413+
///
414+
/// Comparing strings this way includes comparing (normally) hidden
415+
/// implementation details such as the memory location of any underlying
416+
/// string storage object. Therefore, identical strings are guaranteed to
417+
/// compare equal with `==`, but not all equal strings are considered
418+
/// identical.
419+
///
420+
/// - Performance: O(1)
421+
public func isIdentical(to other: Self) -> Bool {
422+
self._guts === other._guts
423+
}
424+
}

Sources/FoundationEssentials/AttributedString/AttributedSubstring.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,23 @@ extension AttributedSubstring {
205205
}
206206
}
207207
}
208+
209+
extension AttributedSubstring {
210+
/// Returns a boolean value indicating whether this substring is identical to
211+
/// `other`.
212+
///
213+
/// Two substring values are identical if there is no way to distinguish
214+
/// between them.
215+
///
216+
/// Comparing substrings this way includes comparing (normally) hidden
217+
/// implementation details such as the memory location of any underlying
218+
/// substring storage object. Therefore, identical substrings are guaranteed
219+
/// to compare equal with `==`, but not all equal substrings are considered
220+
/// identical.
221+
///
222+
/// - Performance: O(1)
223+
public func isIdentical(to other: Self) -> Bool {
224+
self._guts === other._guts &&
225+
self._range == other._range
226+
}
227+
}

Tests/FoundationEssentialsTests/AttributedString/AttributedStringTests.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ import AppKit
3333
#endif
3434
#endif
3535

36+
func createManyAttributesString() -> AttributedString {
37+
var str = AttributedString("a")
38+
for i in 0..<10000 {
39+
str += AttributedString("a", attributes: AttributeContainer().testInt(i))
40+
}
41+
return str
42+
}
43+
3644
/// Regression and coverage tests for `AttributedString` and its associated objects
3745
@Suite("AttributedString")
3846
private struct AttributedStringTests {
@@ -360,6 +368,15 @@ private struct AttributedStringTests {
360368
#expect(a2.characters.elementsEqual(a3.characters))
361369
}
362370

371+
@Test func attributedStringIdentical() {
372+
let manyAttributesString = createManyAttributesString()
373+
let manyAttributesString2 = createManyAttributesString()
374+
#expect(manyAttributesString.isIdentical(to: manyAttributesString))
375+
#expect(manyAttributesString2.isIdentical(to: manyAttributesString2))
376+
#expect(!(manyAttributesString.isIdentical(to: manyAttributesString2)))
377+
#expect(!(manyAttributesString2.isIdentical(to: manyAttributesString)))
378+
}
379+
363380
@Test func attributedSubstringEquality() {
364381
let emptyStr = AttributedString("01234567890123456789")
365382

@@ -389,7 +406,19 @@ private struct AttributedStringTests {
389406

390407
#expect(emptyStr[index0 ..< index5] == AttributedString("01234"))
391408
}
392-
409+
410+
@Test func attributedSubstringIdentical() {
411+
let manyAttributesString = createManyAttributesString()
412+
let manyAttributesString2 = createManyAttributesString()
413+
let manyAttributesStringRange = manyAttributesString.characters.index(manyAttributesString.startIndex, offsetBy: manyAttributesString.characters.count / 2)...
414+
let manyAttributesSubstring = manyAttributesString[manyAttributesStringRange]
415+
let manyAttributes2Substring = manyAttributesString2[manyAttributesStringRange]
416+
#expect(manyAttributesSubstring.isIdentical(to: manyAttributesSubstring))
417+
#expect(manyAttributes2Substring.isIdentical(to: manyAttributes2Substring))
418+
#expect(!(manyAttributesSubstring.isIdentical(to: manyAttributes2Substring)))
419+
#expect(!(manyAttributes2Substring.isIdentical(to: manyAttributesSubstring)))
420+
}
421+
393422
@Test func runEquality() {
394423
var attrStr = AttributedString("Hello", attributes: AttributeContainer().testInt(1))
395424
attrStr += AttributedString(" ")

0 commit comments

Comments
 (0)