Skip to content

Commit 7463599

Browse files
Andrej Mihajlovzx2c4
authored andcommitted
UI: iOS: backport DiffableDataSource
Myers implementation by Apple: https://github.com/apple/swift/blob/main/benchmark/single-source/DiffingMyers.swift Signed-off-by: Andrej Mihajlov <[email protected]>
1 parent 6c4f410 commit 7463599

File tree

5 files changed

+1195
-17
lines changed

5 files changed

+1195
-17
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
3+
4+
import Foundation
5+
6+
// Backport of `CollectionChange` from iOS 13+
7+
enum CollectionChangeBackport<Element>: CustomDebugStringConvertible, Hashable where Element: Hashable {
8+
case insert(offset: Int, element: Element, associatedWith: Int?)
9+
case remove(offset: Int, element: Element, associatedWith: Int?)
10+
11+
var debugDescription: String {
12+
switch self {
13+
case .insert(let index, let element, let associatedWith):
14+
return "insert(index: \(index), element: \(element), associatedWith: \(String(describing: associatedWith)))"
15+
case .remove(let index, let element, let associatedWith):
16+
return "remove(index: \(index), element: \(element), associatedWith: \(String(describing: associatedWith)))"
17+
}
18+
}
19+
}
20+
21+
// Backport of `CollectionDifference` from iOS 13+
22+
class CollectionDifferenceBackport<ChangeType> where ChangeType: Hashable {
23+
typealias Change = CollectionChangeBackport<ChangeType>
24+
25+
private(set) var insertions: [Change] = []
26+
private(set) var removals: [Change] = []
27+
28+
init() {}
29+
30+
init<T>(_ changes: T) where T: Collection, T.Element == Change {
31+
for change in changes {
32+
switch change {
33+
case .insert:
34+
insertions.append(change)
35+
case .remove:
36+
removals.append(change)
37+
}
38+
}
39+
}
40+
}
41+
42+
extension CollectionDifferenceBackport: Collection {
43+
public typealias Element = Change
44+
45+
struct Index: Equatable, Comparable, Hashable {
46+
fileprivate let offset: Int
47+
48+
fileprivate init(_ offset: Int) {
49+
self.offset = offset
50+
}
51+
52+
static func == (lhs: Index, rhs: Index) -> Bool {
53+
return lhs.offset == rhs.offset
54+
}
55+
56+
static func < (lhs: Index, rhs: Index) -> Bool {
57+
return lhs.offset < rhs.offset
58+
}
59+
60+
public func hash(into hasher: inout Hasher) {
61+
hasher.combine(offset)
62+
}
63+
}
64+
65+
var startIndex: Index {
66+
return Index(0)
67+
}
68+
69+
var endIndex: Index {
70+
return Index(removals.count + insertions.count)
71+
}
72+
73+
func index(after i: Index) -> Index {
74+
return Index(i.offset + 1)
75+
}
76+
77+
func index(before i: Index) -> Index {
78+
return Index(i.offset - 1)
79+
}
80+
81+
subscript(position: Index) -> Element {
82+
if position.offset < removals.count {
83+
return removals[removals.count - (position.offset + 1)]
84+
}
85+
return insertions[position.offset - removals.count]
86+
}
87+
88+
public func formIndex(_ index: inout Index, offsetBy distance: Int) {
89+
index = Index(index.offset + distance)
90+
}
91+
92+
public func distance(from start: Index, to end: Index) -> Int {
93+
return end.offset - start.offset
94+
}
95+
}
96+
97+
extension Array where Element: Hashable {
98+
func backport_difference(from otherCollection: [Element], using cmp: ((Element, Element) -> Bool)? = nil) -> CollectionDifferenceBackport<Element> {
99+
let cmp = cmp ?? { $0 == $1 }
100+
return myers(from: otherCollection, to: self, using: cmp)
101+
}
102+
}

0 commit comments

Comments
 (0)