Skip to content

Commit e2c5630

Browse files
authored
[5.3] [stdlib] Performance fixes for removeFirst and removeLast (#32647)
This replaces the `count` comparison precondition with a limited index offset, which converts the method from O(n) to O(k).
1 parent d2eca7e commit e2c5630

File tree

3 files changed

+44
-19
lines changed

3 files changed

+44
-19
lines changed

stdlib/public/core/BidirectionalCollection.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,12 @@ extension BidirectionalCollection where SubSequence == Self {
342342
public mutating func removeLast(_ k: Int) {
343343
if k == 0 { return }
344344
_precondition(k >= 0, "Number of elements to remove should be non-negative")
345-
_precondition(count >= k,
346-
"Can't remove more items from a collection than it contains")
347-
self = self[startIndex..<index(endIndex, offsetBy: -k)]
345+
guard let end = index(endIndex, offsetBy: -k, limitedBy: startIndex)
346+
else {
347+
_preconditionFailure(
348+
"Can't remove more items from a collection than it contains")
349+
}
350+
self = self[startIndex..<end]
348351
}
349352
}
350353

stdlib/public/core/Collection.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,8 +1664,10 @@ extension Collection where SubSequence == Self {
16641664
public mutating func removeFirst(_ k: Int) {
16651665
if k == 0 { return }
16661666
_precondition(k >= 0, "Number of elements to remove should be non-negative")
1667-
_precondition(count >= k,
1668-
"Can't remove more items from a collection than it contains")
1669-
self = self[index(startIndex, offsetBy: k)..<endIndex]
1667+
guard let idx = index(startIndex, offsetBy: k, limitedBy: endIndex) else {
1668+
_preconditionFailure(
1669+
"Can't remove more items from a collection than it contains")
1670+
}
1671+
self = self[idx..<endIndex]
16701672
}
16711673
}

stdlib/public/core/RangeReplaceableCollection.swift

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -285,12 +285,17 @@ public protocol RangeReplaceableCollection: Collection
285285
/// Customization point for `removeLast()`. Implement this function if you
286286
/// want to replace the default implementation.
287287
///
288+
/// The collection must not be empty.
289+
///
288290
/// - Returns: A non-nil value if the operation was performed.
289291
mutating func _customRemoveLast() -> Element?
290292

291293
/// Customization point for `removeLast(_:)`. Implement this function if you
292294
/// want to replace the default implementation.
293295
///
296+
/// - Parameter n: The number of elements to remove from the collection.
297+
/// `n` must be greater than or equal to zero and must not exceed the
298+
/// number of elements in the collection.
294299
/// - Returns: `true` if the operation was performed.
295300
mutating func _customRemoveLast(_ n: Int) -> Bool
296301

@@ -591,9 +596,10 @@ extension RangeReplaceableCollection {
591596
public mutating func removeFirst(_ k: Int) {
592597
if k == 0 { return }
593598
_precondition(k >= 0, "Number of elements to remove should be non-negative")
594-
_precondition(count >= k,
595-
"Can't remove more items from a collection than it has")
596-
let end = index(startIndex, offsetBy: k)
599+
guard let end = index(startIndex, offsetBy: k, limitedBy: endIndex) else {
600+
_preconditionFailure(
601+
"Can't remove more items from a collection than it has")
602+
}
597603
removeSubrange(startIndex..<end)
598604
}
599605

@@ -699,9 +705,11 @@ extension RangeReplaceableCollection where SubSequence == Self {
699705
public mutating func removeFirst(_ k: Int) {
700706
if k == 0 { return }
701707
_precondition(k >= 0, "Number of elements to remove should be non-negative")
702-
_precondition(count >= k,
703-
"Can't remove more items from a collection than it contains")
704-
self = self[index(startIndex, offsetBy: k)..<endIndex]
708+
guard let idx = index(startIndex, offsetBy: k, limitedBy: endIndex) else {
709+
_preconditionFailure(
710+
"Can't remove more items from a collection than it contains")
711+
}
712+
self = self[idx..<endIndex]
705713
}
706714
}
707715

@@ -800,7 +808,12 @@ extension RangeReplaceableCollection
800808

801809
@inlinable
802810
public mutating func _customRemoveLast(_ n: Int) -> Bool {
803-
self = self[startIndex..<index(endIndex, offsetBy: numericCast(-n))]
811+
guard let end = index(endIndex, offsetBy: -n, limitedBy: startIndex)
812+
else {
813+
_preconditionFailure(
814+
"Can't remove more items from a collection than it contains")
815+
}
816+
self = self[startIndex..<end]
804817
return true
805818
}
806819
}
@@ -864,13 +877,17 @@ extension RangeReplaceableCollection where Self: BidirectionalCollection {
864877
public mutating func removeLast(_ k: Int) {
865878
if k == 0 { return }
866879
_precondition(k >= 0, "Number of elements to remove should be non-negative")
867-
_precondition(count >= k,
868-
"Can't remove more items from a collection than it contains")
869880
if _customRemoveLast(k) {
870881
return
871882
}
872883
let end = endIndex
873-
removeSubrange(index(end, offsetBy: -k)..<end)
884+
guard let start = index(end, offsetBy: -k, limitedBy: startIndex)
885+
else {
886+
_preconditionFailure(
887+
"Can't remove more items from a collection than it contains")
888+
}
889+
890+
removeSubrange(start..<end)
874891
}
875892
}
876893

@@ -934,13 +951,16 @@ where Self: BidirectionalCollection, SubSequence == Self {
934951
public mutating func removeLast(_ k: Int) {
935952
if k == 0 { return }
936953
_precondition(k >= 0, "Number of elements to remove should be non-negative")
937-
_precondition(count >= k,
938-
"Can't remove more items from a collection than it contains")
939954
if _customRemoveLast(k) {
940955
return
941956
}
942957
let end = endIndex
943-
removeSubrange(index(end, offsetBy: -k)..<end)
958+
guard let start = index(end, offsetBy: -k, limitedBy: startIndex)
959+
else {
960+
_preconditionFailure(
961+
"Can't remove more items from a collection than it contains")
962+
}
963+
removeSubrange(start..<end)
944964
}
945965
}
946966

0 commit comments

Comments
 (0)