Bag.swift 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. //
  2. // Bag.swift
  3. // ReactiveCocoa
  4. //
  5. // Created by Justin Spahr-Summers on 2014-07-10.
  6. // Copyright (c) 2014 GitHub. All rights reserved.
  7. //
  8. /// A uniquely identifying token for removing a value that was inserted into a
  9. /// Bag.
  10. public final class RemovalToken {
  11. private var identifier: UInt?
  12. private init(identifier: UInt) {
  13. self.identifier = identifier
  14. }
  15. }
  16. /// An unordered, non-unique collection of values of type `Element`.
  17. public struct Bag<Element> {
  18. private var elements: [BagElement<Element>] = []
  19. private var currentIdentifier: UInt = 0
  20. public init() {
  21. }
  22. /// Insert the given value into `self`, and return a token that can
  23. /// later be passed to `removeValueForToken()`.
  24. ///
  25. /// - parameters:
  26. /// - value: A value that will be inserted.
  27. public mutating func insert(value: Element) -> RemovalToken {
  28. let (nextIdentifier, overflow) = UInt.addWithOverflow(currentIdentifier, 1)
  29. if overflow {
  30. reindex()
  31. }
  32. let token = RemovalToken(identifier: currentIdentifier)
  33. let element = BagElement(value: value, identifier: currentIdentifier, token: token)
  34. elements.append(element)
  35. currentIdentifier = nextIdentifier
  36. return token
  37. }
  38. /// Remove a value, given the token returned from `insert()`.
  39. ///
  40. /// - note: If the value has already been removed, nothing happens.
  41. ///
  42. /// - parameters:
  43. /// - token: A token returned from a call to `insert()`.
  44. public mutating func removeValueForToken(token: RemovalToken) {
  45. if let identifier = token.identifier {
  46. // Removal is more likely for recent objects than old ones.
  47. for i in elements.indices.reverse() {
  48. if elements[i].identifier == identifier {
  49. elements.removeAtIndex(i)
  50. token.identifier = nil
  51. break
  52. }
  53. }
  54. }
  55. }
  56. /// In the event of an identifier overflow (highly, highly unlikely), reset
  57. /// all current identifiers to reclaim a contiguous set of available
  58. /// identifiers for the future.
  59. private mutating func reindex() {
  60. for i in elements.indices {
  61. currentIdentifier = UInt(i)
  62. elements[i].identifier = currentIdentifier
  63. elements[i].token.identifier = currentIdentifier
  64. }
  65. }
  66. }
  67. extension Bag: CollectionType {
  68. public typealias Index = Array<Element>.Index
  69. public var startIndex: Index {
  70. return elements.startIndex
  71. }
  72. public var endIndex: Index {
  73. return elements.endIndex
  74. }
  75. public subscript(index: Index) -> Element {
  76. return elements[index].value
  77. }
  78. }
  79. private struct BagElement<Value> {
  80. let value: Value
  81. var identifier: UInt
  82. let token: RemovalToken
  83. }
  84. extension BagElement: CustomStringConvertible {
  85. var description: String {
  86. return "BagElement(\(value))"
  87. }
  88. }