@@ -35,24 +35,29 @@ private struct WeakReference<T: AnyObject>: ExpressibleByNilLiteral {
35
35
/// References are stored in a hash table with simple open adressing. Because
36
36
/// of weak reference, unlike normal open addressing, erased bucket are simply
37
37
/// turned into 'nil'.
38
- struct CacheLookupTable < T: Identifiable & AnyObject > {
39
-
40
- private typealias Buffer = Array < WeakReference < T > >
38
+ class CacheLookupTable < T: Identifiable & AnyObject > {
41
39
42
40
/// Storage for the hash table.
43
- private var buckets : Buffer
41
+ private var buckets : UnsafeMutablePointer < WeakReference < T > >
42
+ private var bucketCount : Int
44
43
45
44
/// Estimated count of inserted values. This is greater than or equal to
46
45
/// the number of actually occupied buckets.
47
46
/// i.e. estimatedCount >= _countOccupiedBuckets()
48
47
private var estimatedCount : Int
49
48
50
49
init ( capacity: Int = 0 ) {
51
- buckets = . init( repeating: nil ,
52
- count: CacheLookupTable< T> . _bucketCount( for: capacity) )
50
+ bucketCount = CacheLookupTable< T> . _bucketCount( for: capacity)
51
+ buckets = . allocate( capacity: bucketCount)
52
+ buckets. initialize ( repeating: nil , count: bucketCount)
53
53
estimatedCount = 0
54
54
}
55
55
56
+ deinit {
57
+ buckets. deinitialize ( count: bucketCount)
58
+ buckets. deallocate ( )
59
+ }
60
+
56
61
/// Constant max load factor for hash table.
57
62
private static var maxLoadFactor : Double {
58
63
@inline ( __always) get {
@@ -88,7 +93,7 @@ struct CacheLookupTable<T: Identifiable & AnyObject> {
88
93
89
94
private var _bucketMask : Int {
90
95
@inline ( __always) get {
91
- return buckets . count &- 1
96
+ return bucketCount &- 1
92
97
}
93
98
}
94
99
@@ -117,43 +122,51 @@ struct CacheLookupTable<T: Identifiable & AnyObject> {
117
122
118
123
/// Reserves enough space to store the specified number of elements. Returns
119
124
/// true if resizing happened.
120
- mutating func reserveCapacity( _ requiredCapacity: Int ) -> Bool {
121
- let bucketCount = CacheLookupTable < T > . _bucketCount ( for : requiredCapacity ,
122
- from: buckets . count )
123
- if ( buckets . count >= bucketCount ) {
125
+ func reserveCapacity( _ requiredCapacity: Int ) -> Bool {
126
+ let requiredBucketCount =
127
+ CacheLookupTable < T > . _bucketCount ( for : requiredCapacity , from: bucketCount )
128
+ if ( bucketCount >= requiredBucketCount ) {
124
129
return false
125
130
}
126
131
127
132
// Slow path. Resizing.
128
- var oldBuckets = buckets
129
- buckets = . init( repeating: nil , count: bucketCount)
133
+ let oldBuckets = buckets
134
+ let oldBucketRange = buckets ..< buckets. advanced ( by: bucketCount)
135
+
136
+ bucketCount = requiredBucketCount
137
+ buckets = . allocate( capacity: requiredBucketCount)
138
+ buckets. initialize ( repeating: nil , count: requiredBucketCount)
130
139
131
140
// Move all nodes from the old buffer.
132
- // TODO: move(), when available.
133
- for i in 0 ..< oldBuckets. count {
134
- if let oldValue = oldBuckets [ i] . value {
135
- let pos = _findHole ( oldValue. id) . pos
136
- Swift . swap ( & buckets[ pos] , & oldBuckets[ i] )
141
+ for oldBucket in oldBucketRange {
142
+ if let id = oldBucket. pointee. value? . id {
143
+ let newBucket = buckets. advanced ( by: _findHole ( id) . pos)
144
+ newBucket. moveAssign ( from: oldBucket, count: 1 )
145
+ } else {
146
+ oldBucket. deinitialize ( count: 1 )
137
147
}
138
148
}
149
+
150
+ oldBuckets. deallocate ( )
151
+
139
152
return true
140
153
}
141
154
142
155
/// Count the actual number of occupied buckets.
143
156
@inline ( __always)
144
157
private func _countOccupiedBuckets( ) -> Int {
145
158
var count = 0
146
- for i in 0 ..< buckets . count where buckets [ i] . value != nil {
159
+ for i in 0 ..< bucketCount where buckets [ i] . value != nil {
147
160
count &+= 1
148
161
}
149
162
return count
150
163
}
151
164
152
165
/// Reserves enough space to store a single new object. Returns true if
153
166
/// resizing happened.
154
- mutating private func _ensurePlusOneCapacity( ) -> Bool {
155
- if buckets . count >= CacheLookupTable < T >
156
- . _minimalBucketCount ( for: estimatedCount &+ 1 ) {
167
+ private func _ensurePlusOneCapacity( ) -> Bool {
168
+ if bucketCount >= CacheLookupTable < T >
169
+ . _minimalBucketCount ( for: estimatedCount &+ 1 ) {
157
170
return false
158
171
}
159
172
@@ -164,7 +177,7 @@ struct CacheLookupTable<T: Identifiable & AnyObject> {
164
177
165
178
/// Inserts the given object into the table.
166
179
@discardableResult
167
- mutating func insert( _ obj: T ) -> Bool {
180
+ func insert( _ obj: T ) -> Bool {
168
181
var ( pos, found) = _findHole ( obj. id)
169
182
if found {
170
183
return false
0 commit comments