diff --git a/Sources/Bag.swift b/Sources/Bag.swift index 657f74639..3434f5152 100644 --- a/Sources/Bag.swift +++ b/Sources/Bag.swift @@ -8,40 +8,25 @@ /// A uniquely identifying token for removing a value that was inserted into a /// Bag. -public final class RemovalToken { - fileprivate var identifier: UInt? - - fileprivate init(identifier: UInt) { - self.identifier = identifier - } -} +public final class RemovalToken {} /// An unordered, non-unique collection of values of type `Element`. public struct Bag { - fileprivate var elements: [BagElement] = [] - private var currentIdentifier: UInt = 0 + fileprivate var elements: ContiguousArray> = [] - public init() { - } + public init() {} /// Insert the given value into `self`, and return a token that can - /// later be passed to `removeValueForToken()`. + /// later be passed to `remove(using:)`. /// /// - parameters: /// - value: A value that will be inserted. @discardableResult public mutating func insert(_ value: Element) -> RemovalToken { - let (nextIdentifier, overflow) = UInt.addWithOverflow(currentIdentifier, 1) - if overflow { - reindex() - } - - let token = RemovalToken(identifier: currentIdentifier) - let element = BagElement(value: value, identifier: currentIdentifier, token: token) + let token = RemovalToken() + let element = BagElement(value: value, token: token) elements.append(element) - currentIdentifier = nextIdentifier - return token } @@ -52,29 +37,15 @@ public struct Bag { /// - parameters: /// - token: A token returned from a call to `insert()`. public mutating func remove(using token: RemovalToken) { - if let identifier = token.identifier { - // Removal is more likely for recent objects than old ones. - for i in elements.indices.reversed() { - if elements[i].identifier == identifier { - elements.remove(at: i) - token.identifier = nil - break - } + let tokenIdentifier = ObjectIdentifier(token) + // Removal is more likely for recent objects than old ones. + for i in elements.indices.reversed() { + if ObjectIdentifier(elements[i].token) == tokenIdentifier { + elements.remove(at: i) + break } } } - - /// In the event of an identifier overflow (highly, highly unlikely), reset - /// all current identifiers to reclaim a contiguous set of available - /// identifiers for the future. - private mutating func reindex() { - for i in elements.indices { - currentIdentifier = UInt(i) - - elements[i].identifier = currentIdentifier - elements[i].token.identifier = currentIdentifier - } - } } extension Bag: Collection { @@ -95,11 +66,14 @@ extension Bag: Collection { public func index(after i: Index) -> Index { return i + 1 } + + public func makeIterator() -> BagIterator { + return BagIterator(elements) + } } private struct BagElement { let value: Value - var identifier: UInt let token: RemovalToken } @@ -108,3 +82,26 @@ extension BagElement: CustomStringConvertible { return "BagElement(\(value))" } } + +public struct BagIterator: IteratorProtocol { + private let base: ContiguousArray> + private var nextIndex: Int + private let endIndex: Int + + fileprivate init(_ base: ContiguousArray>) { + self.base = base + nextIndex = base.startIndex + endIndex = base.endIndex + } + + public mutating func next() -> Element? { + let currentIndex = nextIndex + + if currentIndex < endIndex { + nextIndex = currentIndex + 1 + return base[currentIndex].value + } + + return nil + } +}