Skip to content

Commit 048091d

Browse files
[stdlib] array identical
Co-Authored-By: Ben Rimmington <me@benrimmington.com>
1 parent df3aa1e commit 048091d

File tree

10 files changed

+320
-0
lines changed

10 files changed

+320
-0
lines changed

benchmark/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ set(SWIFT_BENCH_MODULES
3939
single-source/ArrayOfRef
4040
single-source/ArrayRemoveAll
4141
single-source/ArraySetElement
42+
single-source/ArraySliceTests
4243
single-source/ArraySubscript
44+
single-source/ArrayTests
4345
single-source/AsyncTree
4446
single-source/BinaryFloatingPointConversionFromBinaryInteger
4547
single-source/BinaryFloatingPointProperties
@@ -65,6 +67,7 @@ set(SWIFT_BENCH_MODULES
6567
single-source/ClassArrayGetter
6668
single-source/CodableTest
6769
single-source/Combos
70+
single-source/ContiguousArrayTests
6871
single-source/CountAlgo
6972
single-source/DataBenchmarks
7073
single-source/DeadArray
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- ArraySliceTests.swift --------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
public let benchmarks = [
16+
BenchmarkInfo(name: "ArraySliceEqualUnique", runFunction: run_ArraySliceEqualUnique, tags: [.validation, .api, .Array]),
17+
BenchmarkInfo(name: "ArraySliceEqualShared", runFunction: run_ArraySliceEqualShared, tags: [.validation, .api, .Array]),
18+
BenchmarkInfo(name: "ArraySliceIdentical", runFunction: run_ArraySliceIdentical, tags: [.validation, .api, .Array]),
19+
]
20+
21+
@inline(never)
22+
public func run_ArraySliceEqualUnique(_ n: Int) {
23+
let a1 = ArraySlice(0 ..< n)
24+
let a2 = ArraySlice(0 ..< n)
25+
for _ in 0 ..< 100_000 {
26+
check(a1 == a2)
27+
}
28+
}
29+
30+
@inline(never)
31+
public func run_ArraySliceEqualShared(_ n: Int) {
32+
let a1 = ArraySlice(0 ..< n)
33+
let a2 = a1
34+
for _ in 0 ..< 100_000 {
35+
check(a1 == a2)
36+
}
37+
}
38+
39+
@inline(never)
40+
public func run_ArraySliceIdentical(_ n: Int) {
41+
let a1 = ArraySlice(0 ..< n)
42+
let a2 = a1
43+
for _ in 0 ..< 100_000 {
44+
check(a1.isTriviallyIdentical(to: a2))
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- ArrayTests.swift -------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
public let benchmarks = [
16+
BenchmarkInfo(name: "ArrayEqualUnique", runFunction: run_ArrayEqualUnique, tags: [.validation, .api, .Array]),
17+
BenchmarkInfo(name: "ArrayEqualShared", runFunction: run_ArrayEqualShared, tags: [.validation, .api, .Array]),
18+
BenchmarkInfo(name: "ArrayIdentical", runFunction: run_ArrayIdentical, tags: [.validation, .api, .Array]),
19+
]
20+
21+
@inline(never)
22+
public func run_ArrayEqualUnique(_ n: Int) {
23+
let a1 = Array(0 ..< n)
24+
let a2 = Array(0 ..< n)
25+
for _ in 0 ..< 100_000 {
26+
check(a1 == a2)
27+
}
28+
}
29+
30+
@inline(never)
31+
public func run_ArrayEqualShared(_ n: Int) {
32+
let a1 = Array(0 ..< n)
33+
let a2 = a1
34+
for _ in 0 ..< 100_000 {
35+
check(a1 == a2)
36+
}
37+
}
38+
39+
@inline(never)
40+
public func run_ArrayIdentical(_ n: Int) {
41+
let a1 = Array(0 ..< n)
42+
let a2 = a1
43+
for _ in 0 ..< 100_000 {
44+
check(a1.isTriviallyIdentical(to: a2))
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- ContiguousArrayTests.swift ---------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
public let benchmarks = [
16+
BenchmarkInfo(name: "ContiguousArrayEqualUnique", runFunction: run_ContiguousArrayEqualUnique, tags: [.validation, .api, .Array]),
17+
BenchmarkInfo(name: "ContiguousArrayEqualShared", runFunction: run_ContiguousArrayEqualShared, tags: [.validation, .api, .Array]),
18+
BenchmarkInfo(name: "ContiguousArrayIdentical", runFunction: run_ContiguousArrayIdentical, tags: [.validation, .api, .Array]),
19+
]
20+
21+
@inline(never)
22+
public func run_ContiguousArrayEqualUnique(_ n: Int) {
23+
let a1 = ContiguousArray(0 ..< n)
24+
let a2 = ContiguousArray(0 ..< n)
25+
for _ in 0 ..< 100_000 {
26+
check(a1 == a2)
27+
}
28+
}
29+
30+
@inline(never)
31+
public func run_ContiguousArrayEqualShared(_ n: Int) {
32+
let a1 = ContiguousArray(0 ..< n)
33+
let a2 = a1
34+
for _ in 0 ..< 100_000 {
35+
check(a1 == a2)
36+
}
37+
}
38+
39+
@inline(never)
40+
public func run_ContiguousArrayIdentical(_ n: Int) {
41+
let a1 = ContiguousArray(0 ..< n)
42+
let a2 = a1
43+
for _ in 0 ..< 100_000 {
44+
check(a1.isTriviallyIdentical(to: a2))
45+
}
46+
}

benchmark/utils/main.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ import ArrayOfPOD
2727
import ArrayOfRef
2828
import ArrayRemoveAll
2929
import ArraySetElement
30+
import ArraySliceTests
3031
import ArraySubscript
32+
import ArrayTests
3133
import AsyncTree
3234
import BinaryFloatingPointConversionFromBinaryInteger
3335
import BinaryFloatingPointProperties
@@ -53,6 +55,7 @@ import Chars
5355
import ClassArrayGetter
5456
import CodableTest
5557
import Combos
58+
import ContiguousArrayTests
5659
import CountAlgo
5760
import CreateObjects
5861
// rdar://128520766
@@ -229,7 +232,9 @@ register(ArrayOfPOD.benchmarks)
229232
register(ArrayOfRef.benchmarks)
230233
register(ArrayRemoveAll.benchmarks)
231234
register(ArraySetElement.benchmarks)
235+
register(ArraySliceTests.benchmarks)
232236
register(ArraySubscript.benchmarks)
237+
register(ArrayTests.benchmarks)
233238
register(AsyncTree.benchmarks)
234239
register(BinaryFloatingPointConversionFromBinaryInteger.benchmarks)
235240
register(BinaryFloatingPointProperties.benchmarks)
@@ -254,6 +259,7 @@ register(CharacterRecognizer.benchmarks)
254259
register(Chars.benchmarks)
255260
register(CodableTest.benchmarks)
256261
register(Combos.benchmarks)
262+
register(ContiguousArrayTests.benchmarks)
257263
register(CountAlgo.benchmarks)
258264
register(ClassArrayGetter.benchmarks)
259265
register(CreateObjects.benchmarks)

stdlib/public/core/Array.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,3 +2160,44 @@ internal struct _ArrayAnyHashableBox<Element: Hashable>
21602160
}
21612161

21622162
extension Array: @unchecked Sendable where Element: Sendable { }
2163+
2164+
extension Array {
2165+
/// Returns a boolean value indicating whether this array is identical to
2166+
/// `other`.
2167+
///
2168+
/// Two array values are identical if there is no way to distinguish between
2169+
/// them.
2170+
///
2171+
/// For any values `a`, `b`, and `c`:
2172+
///
2173+
/// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity)
2174+
/// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`.
2175+
/// (Symmetry)
2176+
/// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)`
2177+
/// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`.
2178+
/// (Transitivity)
2179+
/// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies
2180+
/// `a == b`
2181+
/// - `a == b` does not imply `a.isTriviallyIdentical(b)`
2182+
///
2183+
/// Values produced by copying the same value, with no intervening mutations,
2184+
/// will compare identical:
2185+
///
2186+
/// ```swift
2187+
/// let d = c
2188+
/// print(c.isTriviallyIdentical(to: d))
2189+
/// // Prints true
2190+
/// ```
2191+
///
2192+
/// Comparing arrays this way includes comparing (normally) hidden
2193+
/// implementation details such as the memory location of any underlying
2194+
/// array storage object. Therefore, identical arrays are guaranteed to
2195+
/// compare equal with `==`, but not all equal arrays are considered
2196+
/// identical.
2197+
///
2198+
/// - Complexity: O(1)
2199+
@_alwaysEmitIntoClient
2200+
public func isTriviallyIdentical(to other: Self) -> Bool {
2201+
unsafe self._buffer.identity == other._buffer.identity
2202+
}
2203+
}

stdlib/public/core/ArraySlice.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,3 +1607,44 @@ extension ArraySlice {
16071607
}
16081608
}
16091609
#endif
1610+
1611+
extension ArraySlice {
1612+
/// Returns a boolean value indicating whether this array is identical to
1613+
/// `other`.
1614+
///
1615+
/// Two array values are identical if there is no way to distinguish between
1616+
/// them.
1617+
///
1618+
/// For any values `a`, `b`, and `c`:
1619+
///
1620+
/// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity)
1621+
/// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`.
1622+
/// (Symmetry)
1623+
/// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)`
1624+
/// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`.
1625+
/// (Transitivity)
1626+
/// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies
1627+
/// `a == b`
1628+
/// - `a == b` does not imply `a.isTriviallyIdentical(b)`
1629+
///
1630+
/// Values produced by copying the same value, with no intervening mutations,
1631+
/// will compare identical:
1632+
///
1633+
/// ```swift
1634+
/// let d = c
1635+
/// print(c.isTriviallyIdentical(to: d))
1636+
/// // Prints true
1637+
/// ```
1638+
///
1639+
/// Comparing arrays this way includes comparing (normally) hidden
1640+
/// implementation details such as the memory location of any underlying
1641+
/// array storage object. Therefore, identical arrays are guaranteed to
1642+
/// compare equal with `==`, but not all equal arrays are considered
1643+
/// identical.
1644+
///
1645+
/// - Complexity: O(1)
1646+
@_alwaysEmitIntoClient
1647+
public func isTriviallyIdentical(to other: Self) -> Bool {
1648+
self._buffer.isTriviallyIdentical(to: other._buffer)
1649+
}
1650+
}

stdlib/public/core/ContiguousArray.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,3 +1515,44 @@ extension ContiguousArray {
15151515

15161516
extension ContiguousArray: @unchecked Sendable
15171517
where Element: Sendable { }
1518+
1519+
extension ContiguousArray {
1520+
/// Returns a boolean value indicating whether this array is identical to
1521+
/// `other`.
1522+
///
1523+
/// Two array values are identical if there is no way to distinguish between
1524+
/// them.
1525+
///
1526+
/// For any values `a`, `b`, and `c`:
1527+
///
1528+
/// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity)
1529+
/// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`.
1530+
/// (Symmetry)
1531+
/// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)`
1532+
/// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`.
1533+
/// (Transitivity)
1534+
/// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies
1535+
/// `a == b`
1536+
/// - `a == b` does not imply `a.isTriviallyIdentical(b)`
1537+
///
1538+
/// Values produced by copying the same value, with no intervening mutations,
1539+
/// will compare identical:
1540+
///
1541+
/// ```swift
1542+
/// let d = c
1543+
/// print(c.isTriviallyIdentical(to: d))
1544+
/// // Prints true
1545+
/// ```
1546+
///
1547+
/// Comparing arrays this way includes comparing (normally) hidden
1548+
/// implementation details such as the memory location of any underlying
1549+
/// array storage object. Therefore, identical arrays are guaranteed to
1550+
/// compare equal with `==`, but not all equal arrays are considered
1551+
/// identical.
1552+
///
1553+
/// - Complexity: O(1)
1554+
@_alwaysEmitIntoClient
1555+
public func isTriviallyIdentical(to other: Self) -> Bool {
1556+
unsafe self._buffer.identity == other._buffer.identity
1557+
}
1558+
}

stdlib/public/core/SliceBuffer.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,3 +511,29 @@ extension _SliceBuffer {
511511
return ContiguousArray(_buffer: result)
512512
}
513513
}
514+
515+
extension _SliceBuffer {
516+
@_alwaysEmitIntoClient
517+
internal func isTriviallyIdentical(to other: Self) -> Bool {
518+
#if $Embedded
519+
if
520+
self.owner == other.owner,
521+
unsafe (self.subscriptBaseAddress == other.subscriptBaseAddress),
522+
self.startIndex == other.startIndex,
523+
self.endIndexAndFlags == other.endIndexAndFlags
524+
{
525+
return true
526+
}
527+
#else
528+
if
529+
self.owner === other.owner,
530+
unsafe (self.subscriptBaseAddress == other.subscriptBaseAddress),
531+
self.startIndex == other.startIndex,
532+
self.endIndexAndFlags == other.endIndexAndFlags
533+
{
534+
return true
535+
}
536+
#endif
537+
return false
538+
}
539+
}

test/stdlib/Inputs/CommonArrayTests.gyb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,3 +696,27 @@ ${Suite}.test("${ArrayType}/AssociatedTypes") {
696696
indexType: Int.self,
697697
indicesType: CountableRange<Int>.self)
698698
}
699+
700+
//===----------------------------------------------------------------------===//
701+
// Trivially Identical
702+
//===----------------------------------------------------------------------===//
703+
704+
${Suite}.test("${ArrayType}/isTriviallyIdentical") {
705+
let a1: ${ArrayType}<Int> = [ 10, 20, 30, 40 ]
706+
expectTrue(a1.isTriviallyIdentical(to: a1))
707+
708+
let a2: ${ArrayType}<Int> = a1
709+
expectTrue(a1.isTriviallyIdentical(to: a2))
710+
711+
var a3: ${ArrayType}<Int> = a2
712+
a3.reserveCapacity(0)
713+
expectFalse(a1.isTriviallyIdentical(to: a3))
714+
715+
let a4: ${ArrayType}<Int> = [ 10, 20, 30, 40 ]
716+
expectFalse(a1.isTriviallyIdentical(to: a4))
717+
718+
let a5: ArraySlice<Int> = a1[...]
719+
let a6: ArraySlice<Int> = a1[1..<3]
720+
721+
expectFalse(a5.isTriviallyIdentical(to: a6))
722+
}

0 commit comments

Comments
 (0)