Skip to content

Commit ed7cabb

Browse files
authored
Merge pull request #1 from lorentey/numist/diffing
Preparations for landing numist/swift
2 parents 5ed93b5 + 23afad1 commit ed7cabb

File tree

2 files changed

+229
-184
lines changed

2 files changed

+229
-184
lines changed

stdlib/public/core/CollectionDifference.swift

Lines changed: 94 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
/// A type that represents the difference between two ordered collection states.
14-
@available(swift, introduced: 5.1)
14+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
1515
public struct CollectionDifference<ChangeElement> {
1616
/// A type that represents a single change to a collection.
1717
///
@@ -25,7 +25,7 @@ public struct CollectionDifference<ChangeElement> {
2525
case remove(offset: Int, element: ChangeElement, associatedWith: Int?)
2626

2727
// Internal common field accessors
28-
var offset: Int {
28+
internal var _offset: Int {
2929
get {
3030
switch self {
3131
case .insert(offset: let o, element: _, associatedWith: _):
@@ -35,7 +35,7 @@ public struct CollectionDifference<ChangeElement> {
3535
}
3636
}
3737
}
38-
var element: ChangeElement {
38+
internal var _element: ChangeElement {
3939
get {
4040
switch self {
4141
case .insert(offset: _, element: let e, associatedWith: _):
@@ -45,7 +45,7 @@ public struct CollectionDifference<ChangeElement> {
4545
}
4646
}
4747
}
48-
var associatedOffset: Int? {
48+
internal var _associatedOffset: Int? {
4949
get {
5050
switch self {
5151
case .insert(offset: _, element: _, associatedWith: let o):
@@ -57,6 +57,12 @@ public struct CollectionDifference<ChangeElement> {
5757
}
5858
}
5959

60+
/// The `.insert` changes contained by this difference, from lowest offset to highest
61+
public let insertions: [Change]
62+
63+
/// The `.remove` changes contained by this difference, from lowest offset to highest
64+
public let removals: [Change]
65+
6066
/// The public initializer calls this function to ensure that its parameter
6167
/// meets the conditions set in its documentation.
6268
///
@@ -71,7 +77,9 @@ public struct CollectionDifference<ChangeElement> {
7177
/// 3. All associations between insertions and removals are symmetric
7278
///
7379
/// Complexity: O(`changes.count`)
74-
private static func validateChanges<C>(_ changes : C) -> Bool where C:Collection, C.Element == Change {
80+
private static func _validateChanges<Changes: Collection>(
81+
_ changes : Changes
82+
) -> Bool where Changes.Element == Change {
7583
if changes.count == 0 { return true }
7684

7785
var insertAssocToOffset = Dictionary<Int,Int>()
@@ -80,7 +88,7 @@ public struct CollectionDifference<ChangeElement> {
8088
var removeOffset = Set<Int>()
8189

8290
for c in changes {
83-
let offset = c.offset
91+
let offset = c._offset
8492
if offset < 0 { return false }
8593

8694
switch c {
@@ -92,7 +100,7 @@ public struct CollectionDifference<ChangeElement> {
92100
insertOffset.insert(offset)
93101
}
94102

95-
if let assoc = c.associatedOffset {
103+
if let assoc = c._associatedOffset {
96104
if assoc < 0 { return false }
97105
switch c {
98106
case .remove(_, _, _):
@@ -126,12 +134,14 @@ public struct CollectionDifference<ChangeElement> {
126134
///
127135
/// - Complexity: O(*n* * log(*n*)), where *n* is the length of the
128136
/// parameter.
129-
public init?<C>(_ c: C) where C:Collection, C.Element == Change {
130-
if !CollectionDifference<ChangeElement>.validateChanges(c) {
137+
public init?<Changes: Collection>(
138+
_ changes: Changes
139+
) where Changes.Element == Change {
140+
if !CollectionDifference<ChangeElement>._validateChanges(changes) {
131141
return nil
132142
}
133143

134-
self.init(validatedChanges: c)
144+
self.init(_validatedChanges: changes)
135145
}
136146

137147
/// Internal initializer for use by algorithms that cannot produce invalid
@@ -146,27 +156,29 @@ public struct CollectionDifference<ChangeElement> {
146156
///
147157
/// - Complexity: O(*n* * log(*n*)), where *n* is the length of the
148158
/// parameter.
149-
init<C>(validatedChanges c: C) where C:Collection, C.Element == Change {
150-
let changes = c.sorted { (a, b) -> Bool in
159+
internal init<Changes: Collection>(
160+
_validatedChanges changes: Changes
161+
) where Changes.Element == Change {
162+
let sortedChanges = changes.sorted { (a, b) -> Bool in
151163
switch (a, b) {
152164
case (.remove(_, _, _), .insert(_, _, _)):
153165
return true
154166
case (.insert(_, _, _), .remove(_, _, _)):
155167
return false
156168
default:
157-
return a.offset < b.offset
169+
return a._offset < b._offset
158170
}
159171
}
160172

161173
// Find first insertion via binary search
162174
let firstInsertIndex: Int
163-
if changes.count == 0 {
175+
if sortedChanges.count == 0 {
164176
firstInsertIndex = 0
165177
} else {
166-
var range = 0...changes.count
178+
var range = 0...sortedChanges.count
167179
while range.lowerBound != range.upperBound {
168180
let i = (range.lowerBound + range.upperBound) / 2
169-
switch changes[i] {
181+
switch sortedChanges[i] {
170182
case .insert(_, _, _):
171183
range = range.lowerBound...i
172184
case .remove(_, _, _):
@@ -176,15 +188,9 @@ public struct CollectionDifference<ChangeElement> {
176188
firstInsertIndex = range.lowerBound
177189
}
178190

179-
removals = Array(changes[0..<firstInsertIndex])
180-
insertions = Array(changes[firstInsertIndex..<changes.count])
191+
removals = Array(sortedChanges[0..<firstInsertIndex])
192+
insertions = Array(sortedChanges[firstInsertIndex..<sortedChanges.count])
181193
}
182-
183-
/// The `.insert` changes contained by this difference, from lowest offset to highest
184-
public let insertions: [Change]
185-
186-
/// The `.remove` changes contained by this difference, from lowest offset to highest
187-
public let removals: [Change]
188194
}
189195

190196
/// A CollectionDifference is itself a Collection.
@@ -207,61 +213,75 @@ public struct CollectionDifference<ChangeElement> {
207213
/// }
208214
/// }
209215
/// ```
210-
extension CollectionDifference : Collection {
211-
public typealias Element = CollectionDifference<ChangeElement>.Change
212-
213-
// Opaque index type is isomorphic to Int
214-
public struct Index: Comparable, Hashable {
215-
public static func < (
216-
lhs: CollectionDifference<ChangeElement>.Index,
217-
rhs: CollectionDifference<ChangeElement>.Index
218-
) -> Bool {
219-
return lhs.i < rhs.i
220-
}
221-
222-
let i: Int
223-
init(_ index: Int) {
224-
i = index
216+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
217+
extension CollectionDifference: Collection {
218+
public typealias Element = Change
219+
220+
public struct Index: Equatable, Hashable, Comparable {
221+
// Opaque index type is isomorphic to Int
222+
internal let _offset: Int
223+
224+
internal init(_offset offset: Int) {
225+
_offset = offset
225226
}
226227
}
227228

228-
public var startIndex: CollectionDifference<ChangeElement>.Index {
229-
return Index(0)
229+
public var startIndex: Index {
230+
return Index(_offset: 0)
230231
}
231-
232-
public var endIndex: CollectionDifference<ChangeElement>.Index {
233-
return Index(removals.count + insertions.count)
232+
233+
public var endIndex: Index {
234+
return Index(_offset: removals.count + insertions.count)
234235
}
235-
236-
public func index(after index: CollectionDifference<ChangeElement>.Index) -> CollectionDifference<ChangeElement>.Index {
237-
return Index(index.i + 1)
236+
237+
public func index(after index: Index) -> Index {
238+
return Index(_offset: index._offset + 1)
238239
}
239-
240-
public subscript(position: CollectionDifference<ChangeElement>.Index) -> Element {
241-
return position.i < removals.count ? removals[removals.count - (position.i + 1)] : insertions[position.i - removals.count]
240+
241+
public subscript(position: Index) -> Element {
242+
if position._offset < removals.count {
243+
return removals[removals.count - (position._offset + 1)]
244+
}
245+
return insertions[position._offset - removals.count]
242246
}
243-
244-
public func index(before index: CollectionDifference<ChangeElement>.Index) -> CollectionDifference<ChangeElement>.Index {
245-
return Index(index.i - 1)
247+
248+
public func index(before index: Index) -> Index {
249+
return Index(_offset: index._offset - 1)
246250
}
247-
248-
public func formIndex(_ index: inout CollectionDifference<ChangeElement>.Index, offsetBy distance: Int) {
249-
index = Index(index.i + distance)
251+
252+
public func formIndex(_ index: inout Index, offsetBy distance: Int) {
253+
index = Index(_offset: index._offset + distance)
250254
}
251-
252-
public func distance(from start: CollectionDifference<ChangeElement>.Index, to end: CollectionDifference<ChangeElement>.Index) -> Int {
253-
return end.i - start.i
255+
256+
public func distance(from start: Index, to end: Index) -> Int {
257+
return end._offset - start._offset
258+
}
259+
}
260+
261+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
262+
extension CollectionDifference.Index { // Comparable
263+
public static func < (
264+
lhs: CollectionDifference.Index,
265+
rhs: CollectionDifference.Index
266+
) -> Bool {
267+
return lhs._offset < rhs._offset
254268
}
255269
}
256270

271+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
257272
extension CollectionDifference.Change: Equatable where ChangeElement: Equatable {}
258273

274+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
259275
extension CollectionDifference: Equatable where ChangeElement: Equatable {}
260276

277+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
261278
extension CollectionDifference.Change: Hashable where ChangeElement: Hashable {}
262279

263-
extension CollectionDifference: Hashable where ChangeElement: Hashable {
280+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
281+
extension CollectionDifference: Hashable where ChangeElement: Hashable {}
264282

283+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
284+
extension CollectionDifference where ChangeElement: Hashable {
265285
/// Infers which `ChangeElement`s have been both inserted and removed only
266286
/// once and returns a new difference with those associations.
267287
///
@@ -272,11 +292,11 @@ extension CollectionDifference: Hashable where ChangeElement: Hashable {
272292
let removeDict: [ChangeElement:Int?] = {
273293
var res = [ChangeElement:Int?](minimumCapacity: Swift.min(removals.count, insertions.count))
274294
for r in removals {
275-
let element = r.element
295+
let element = r._element
276296
if res[element] != .none {
277297
res[element] = .some(.none)
278298
} else {
279-
res[element] = .some(r.offset)
299+
res[element] = .some(r._offset)
280300
}
281301
}
282302
return res.filter { (_, v) -> Bool in v != .none }
@@ -285,17 +305,17 @@ extension CollectionDifference: Hashable where ChangeElement: Hashable {
285305
let insertDict: [ChangeElement:Int?] = {
286306
var res = [ChangeElement:Int?](minimumCapacity: Swift.min(removals.count, insertions.count))
287307
for i in insertions {
288-
let element = i.element
308+
let element = i._element
289309
if res[element] != .none {
290310
res[element] = .some(.none)
291311
} else {
292-
res[element] = .some(i.offset)
312+
res[element] = .some(i._offset)
293313
}
294314
}
295315
return res.filter { (_, v) -> Bool in v != .none }
296316
}()
297317

298-
return CollectionDifference.init(validatedChanges:map({ (c: CollectionDifference<ChangeElement>.Change) -> CollectionDifference<ChangeElement>.Change in
318+
return CollectionDifference(_validatedChanges: map({ (c: Change) -> Change in
299319
switch c {
300320
case .remove(offset: let o, element: let e, associatedWith: _):
301321
if removeDict[e] == nil {
@@ -317,16 +337,17 @@ extension CollectionDifference: Hashable where ChangeElement: Hashable {
317337
}
318338
}
319339

340+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
320341
extension CollectionDifference.Change: Codable where ChangeElement: Codable {
321-
private enum CodingKeys: String, CodingKey {
342+
private enum _CodingKeys: String, CodingKey {
322343
case offset
323344
case element
324345
case associatedOffset
325346
case isRemove
326347
}
327348

328349
public init(from decoder: Decoder) throws {
329-
let values = try decoder.container(keyedBy: CodingKeys.self)
350+
let values = try decoder.container(keyedBy: _CodingKeys.self)
330351
let offset = try values.decode(Int.self, forKey: .offset)
331352
let element = try values.decode(ChangeElement.self, forKey: .element)
332353
let associatedOffset = try values.decode(Int?.self, forKey: .associatedOffset)
@@ -337,20 +358,21 @@ extension CollectionDifference.Change: Codable where ChangeElement: Codable {
337358
self = .insert(offset: offset, element: element, associatedWith: associatedOffset)
338359
}
339360
}
340-
361+
341362
public func encode(to encoder: Encoder) throws {
342-
var container = encoder.container(keyedBy: CodingKeys.self)
363+
var container = encoder.container(keyedBy: _CodingKeys.self)
343364
switch self {
344365
case .remove(_, _, _):
345366
try container.encode(true, forKey: .isRemove)
346367
case .insert(_, _, _):
347368
try container.encode(false, forKey: .isRemove)
348369
}
349370

350-
try container.encode(offset, forKey: .offset)
351-
try container.encode(element, forKey: .element)
352-
try container.encode(associatedOffset, forKey: .associatedOffset)
371+
try container.encode(_offset, forKey: .offset)
372+
try container.encode(_element, forKey: .element)
373+
try container.encode(_associatedOffset, forKey: .associatedOffset)
353374
}
354375
}
355376

377+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1)
356378
extension CollectionDifference: Codable where ChangeElement: Codable {}

0 commit comments

Comments
 (0)