15
15
@available ( macOS 10 . 15 , iOS 13 , tvOS 13 , watchOS 6 , * )
16
16
extension CollectionDifference {
17
17
fileprivate func _fastEnumeratedApply(
18
- _ consume: ( Change ) -> Void
19
- ) {
18
+ _ consume: ( Change ) throws -> Void
19
+ ) rethrows {
20
20
let totalRemoves = removals. count
21
21
let totalInserts = insertions. count
22
22
var enumeratedRemoves = 0
@@ -41,7 +41,7 @@ extension CollectionDifference {
41
41
preconditionFailure ( )
42
42
}
43
43
44
- consume ( change)
44
+ try consume ( change)
45
45
46
46
switch change {
47
47
case . remove( _, _, _) :
@@ -53,6 +53,9 @@ extension CollectionDifference {
53
53
}
54
54
}
55
55
56
+ // Error type allows the use of throw to unroll state on application failure
57
+ private enum _ApplicationError : Error { case failed }
58
+
56
59
extension RangeReplaceableCollection {
57
60
/// Applies the given difference to this collection.
58
61
///
@@ -66,45 +69,54 @@ extension RangeReplaceableCollection {
66
69
/// number of changes contained by the parameter.
67
70
@available ( macOS 10 . 15 , iOS 13 , tvOS 13 , watchOS 6 , * )
68
71
public func applying( _ difference: CollectionDifference < Element > ) -> Self ? {
69
- var result = Self ( )
70
- var enumeratedRemoves = 0
71
- var enumeratedInserts = 0
72
- var enumeratedOriginals = 0
73
- var currentIndex = self . startIndex
74
72
75
73
func append(
76
74
into target: inout Self ,
77
75
contentsOf source: Self ,
78
76
from index: inout Self . Index , count: Int
79
- ) {
77
+ ) throws {
80
78
let start = index
81
- source. formIndex ( & index, offsetBy: count)
79
+ if !source. formIndex ( & index, offsetBy: count, limitedBy: source. endIndex) {
80
+ throw _ApplicationError. failed
81
+ }
82
82
target. append ( contentsOf: source [ start..< index] )
83
83
}
84
84
85
- difference. _fastEnumeratedApply { change in
86
- switch change {
87
- case . remove( offset: let offset, element: _, associatedWith: _) :
88
- let origCount = offset - enumeratedOriginals
89
- append ( into: & result, contentsOf: self , from: & currentIndex, count: origCount)
90
- enumeratedOriginals += origCount + 1 // Removal consumes an original element
91
- currentIndex = self . index ( after: currentIndex)
92
- enumeratedRemoves += 1
93
- case . insert( offset: let offset, element: let element, associatedWith: _) :
94
- let origCount = ( offset + enumeratedRemoves - enumeratedInserts) - enumeratedOriginals
95
- append ( into: & result, contentsOf: self , from: & currentIndex, count: origCount)
96
- result. append ( element)
97
- enumeratedOriginals += origCount
98
- enumeratedInserts += 1
85
+ var result = Self ( )
86
+ do {
87
+ var enumeratedRemoves = 0
88
+ var enumeratedInserts = 0
89
+ var enumeratedOriginals = 0
90
+ var currentIndex = self . startIndex
91
+ try difference. _fastEnumeratedApply { change in
92
+ switch change {
93
+ case . remove( offset: let offset, element: _, associatedWith: _) :
94
+ let origCount = offset - enumeratedOriginals
95
+ try append ( into: & result, contentsOf: self , from: & currentIndex, count: origCount)
96
+ if currentIndex == self . endIndex {
97
+ // Removing nonexistent element off the end of the collection
98
+ throw _ApplicationError. failed
99
+ }
100
+ enumeratedOriginals += origCount + 1 // Removal consumes an original element
101
+ currentIndex = self . index ( after: currentIndex)
102
+ enumeratedRemoves += 1
103
+ case . insert( offset: let offset, element: let element, associatedWith: _) :
104
+ let origCount = ( offset + enumeratedRemoves - enumeratedInserts) - enumeratedOriginals
105
+ try append ( into: & result, contentsOf: self , from: & currentIndex, count: origCount)
106
+ result. append ( element)
107
+ enumeratedOriginals += origCount
108
+ enumeratedInserts += 1
109
+ }
110
+ _internalInvariant ( enumeratedOriginals <= self . count)
111
+ }
112
+ if currentIndex < self . endIndex {
113
+ result. append ( contentsOf: self [ currentIndex... ] )
99
114
}
100
- _internalInvariant ( enumeratedOriginals <= self . count)
115
+ _internalInvariant ( result. count == self . count + enumeratedInserts - enumeratedRemoves)
116
+ } catch {
117
+ return nil
101
118
}
102
- let origCount = self . count - enumeratedOriginals
103
- append ( into: & result, contentsOf: self , from: & currentIndex, count: origCount)
104
119
105
- _internalInvariant ( currentIndex == self . endIndex)
106
- _internalInvariant ( enumeratedOriginals + origCount == self . count)
107
- _internalInvariant ( result. count == self . count + enumeratedInserts - enumeratedRemoves)
108
120
return result
109
121
}
110
122
}
0 commit comments