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
+ fileprivate enum _ApplicationError : Error { case failed }
58
+
56
59
extension RangeReplaceableCollection {
57
60
/// Applies the given difference to this collection.
58
61
///
@@ -76,34 +79,44 @@ extension RangeReplaceableCollection {
76
79
into target: inout Self ,
77
80
contentsOf source: Self ,
78
81
from index: inout Self . Index , count: Int
79
- ) {
82
+ ) throws {
80
83
let start = index
81
- source. formIndex ( & index, offsetBy: count)
84
+ if !source. formIndex ( & index, offsetBy: count, limitedBy: source. endIndex) {
85
+ throw _ApplicationError. failed
86
+ }
82
87
target. append ( contentsOf: source [ start..< index] )
83
88
}
84
89
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
90
+ do {
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)
99
111
}
100
- _internalInvariant ( enumeratedOriginals <= self . count)
112
+ let origCount = self . count - enumeratedOriginals
113
+ try append ( into: & result, contentsOf: self , from: & currentIndex, count: origCount)
114
+ _internalInvariant ( enumeratedOriginals + origCount == self . count)
115
+ } catch {
116
+ return nil
101
117
}
102
- let origCount = self . count - enumeratedOriginals
103
- append ( into: & result, contentsOf: self , from: & currentIndex, count: origCount)
104
118
105
119
_internalInvariant ( currentIndex == self . endIndex)
106
- _internalInvariant ( enumeratedOriginals + origCount == self . count)
107
120
_internalInvariant ( result. count == self . count + enumeratedInserts - enumeratedRemoves)
108
121
return result
109
122
}
0 commit comments