Skip to content

Commit dba767d

Browse files
authored
Merge pull request #126 from ReactiveCocoa/bag-perf
Optimize `Bag`.
2 parents 0fcbdc6 + 439f535 commit dba767d

File tree

1 file changed

+39
-42
lines changed

1 file changed

+39
-42
lines changed

Sources/Bag.swift

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,25 @@
88

99
/// A uniquely identifying token for removing a value that was inserted into a
1010
/// Bag.
11-
public final class RemovalToken {
12-
fileprivate var identifier: UInt?
13-
14-
fileprivate init(identifier: UInt) {
15-
self.identifier = identifier
16-
}
17-
}
11+
public final class RemovalToken {}
1812

1913
/// An unordered, non-unique collection of values of type `Element`.
2014
public struct Bag<Element> {
21-
fileprivate var elements: [BagElement<Element>] = []
22-
private var currentIdentifier: UInt = 0
15+
fileprivate var elements: ContiguousArray<BagElement<Element>> = []
2316

24-
public init() {
25-
}
17+
public init() {}
2618

2719
/// Insert the given value into `self`, and return a token that can
28-
/// later be passed to `removeValueForToken()`.
20+
/// later be passed to `remove(using:)`.
2921
///
3022
/// - parameters:
3123
/// - value: A value that will be inserted.
3224
@discardableResult
3325
public mutating func insert(_ value: Element) -> RemovalToken {
34-
let (nextIdentifier, overflow) = UInt.addWithOverflow(currentIdentifier, 1)
35-
if overflow {
36-
reindex()
37-
}
38-
39-
let token = RemovalToken(identifier: currentIdentifier)
40-
let element = BagElement(value: value, identifier: currentIdentifier, token: token)
26+
let token = RemovalToken()
27+
let element = BagElement(value: value, token: token)
4128

4229
elements.append(element)
43-
currentIdentifier = nextIdentifier
44-
4530
return token
4631
}
4732

@@ -52,29 +37,15 @@ public struct Bag<Element> {
5237
/// - parameters:
5338
/// - token: A token returned from a call to `insert()`.
5439
public mutating func remove(using token: RemovalToken) {
55-
if let identifier = token.identifier {
56-
// Removal is more likely for recent objects than old ones.
57-
for i in elements.indices.reversed() {
58-
if elements[i].identifier == identifier {
59-
elements.remove(at: i)
60-
token.identifier = nil
61-
break
62-
}
40+
let tokenIdentifier = ObjectIdentifier(token)
41+
// Removal is more likely for recent objects than old ones.
42+
for i in elements.indices.reversed() {
43+
if ObjectIdentifier(elements[i].token) == tokenIdentifier {
44+
elements.remove(at: i)
45+
break
6346
}
6447
}
6548
}
66-
67-
/// In the event of an identifier overflow (highly, highly unlikely), reset
68-
/// all current identifiers to reclaim a contiguous set of available
69-
/// identifiers for the future.
70-
private mutating func reindex() {
71-
for i in elements.indices {
72-
currentIdentifier = UInt(i)
73-
74-
elements[i].identifier = currentIdentifier
75-
elements[i].token.identifier = currentIdentifier
76-
}
77-
}
7849
}
7950

8051
extension Bag: Collection {
@@ -95,11 +66,14 @@ extension Bag: Collection {
9566
public func index(after i: Index) -> Index {
9667
return i + 1
9768
}
69+
70+
public func makeIterator() -> BagIterator<Element> {
71+
return BagIterator(elements)
72+
}
9873
}
9974

10075
private struct BagElement<Value> {
10176
let value: Value
102-
var identifier: UInt
10377
let token: RemovalToken
10478
}
10579

@@ -108,3 +82,26 @@ extension BagElement: CustomStringConvertible {
10882
return "BagElement(\(value))"
10983
}
11084
}
85+
86+
public struct BagIterator<Element>: IteratorProtocol {
87+
private let base: ContiguousArray<BagElement<Element>>
88+
private var nextIndex: Int
89+
private let endIndex: Int
90+
91+
fileprivate init(_ base: ContiguousArray<BagElement<Element>>) {
92+
self.base = base
93+
nextIndex = base.startIndex
94+
endIndex = base.endIndex
95+
}
96+
97+
public mutating func next() -> Element? {
98+
let currentIndex = nextIndex
99+
100+
if currentIndex < endIndex {
101+
nextIndex = currentIndex + 1
102+
return base[currentIndex].value
103+
}
104+
105+
return nil
106+
}
107+
}

0 commit comments

Comments
 (0)