2
2
//
3
3
// This source file is part of the Swift.org open source project
4
4
//
5
- // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5
+ // Copyright (c) 2018 Apple Inc. and the Swift project authors
6
6
// Licensed under Apache License v2.0 with Runtime Library Exception
7
7
//
8
8
// See https://swift.org/LICENSE.txt for license information
@@ -35,24 +35,31 @@ 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 > {
38
+ class CacheLookupTable < T: Identifiable & AnyObject > {
39
39
40
40
private typealias Buffer = Array < WeakReference < T > >
41
41
42
42
/// Storage for the hash table.
43
- private var buckets : Buffer
43
+ private var buckets : UnsafeMutablePointer < WeakReference < T > >
44
+ private var bucketCount : Int
44
45
45
46
/// Estimated count of inserted values. This is greater than or equal to
46
47
/// the number of the acually occupied buckets.
47
48
/// i.e. estimatedCount >= _countOccupiedBuckets()
48
49
private var estimatedCount : Int
49
50
50
51
init ( capacity: Int = 0 ) {
51
- buckets = . init( repeating: nil ,
52
- count: CacheLookupTable< T> . _bucketCount( for: capacity) )
52
+ bucketCount = CacheLookupTable< T> . _bucketCount( for: capacity)
53
+ buckets = . allocate( capacity: bucketCount)
54
+ buckets. initialize ( repeating: nil , count: bucketCount)
53
55
estimatedCount = 0
54
56
}
55
57
58
+ deinit {
59
+ buckets. deinitialize ( count: bucketCount)
60
+ buckets. deallocate ( )
61
+ }
62
+
56
63
/// Constant max load factor for hash table.
57
64
private static var maxLoadFactor : Double {
58
65
@inline ( __always) get {
@@ -88,7 +95,7 @@ struct CacheLookupTable<T: Identifiable & AnyObject> {
88
95
89
96
private var _bucketMask : Int {
90
97
@inline ( __always) get {
91
- return buckets . count &- 1
98
+ return bucketCount &- 1
92
99
}
93
100
}
94
101
@@ -116,43 +123,51 @@ struct CacheLookupTable<T: Identifiable & AnyObject> {
116
123
117
124
/// Reserves enough space to store the specified number of elements. Returns
118
125
/// true if resizing happened.
119
- mutating func reserveCapacity( _ requiredCapacity: Int ) -> Bool {
120
- let bucketCount = CacheLookupTable < T > . _bucketCount ( for : requiredCapacity ,
121
- from: buckets . count )
122
- if ( buckets . count >= bucketCount ) {
126
+ func reserveCapacity( _ requiredCapacity: Int ) -> Bool {
127
+ let requiredBucketCount =
128
+ CacheLookupTable < T > . _bucketCount ( for : requiredCapacity , from: bucketCount )
129
+ if ( bucketCount >= requiredBucketCount ) {
123
130
return false
124
131
}
125
132
126
133
// Slow path. Resizing.
127
- var oldBuckets = buckets
128
- buckets = . init( repeating: nil , count: bucketCount)
134
+ let oldBuckets = buckets
135
+ let oldBucketRange = buckets ..< buckets. advanced ( by: bucketCount)
136
+
137
+ bucketCount = requiredBucketCount
138
+ buckets = . allocate( capacity: requiredBucketCount)
139
+ buckets. initialize ( repeating: nil , count: requiredBucketCount)
129
140
130
141
// Move all nodes from the old buffer.
131
- // TODO: move(), when available.
132
- for i in 0 ..< oldBuckets. count {
133
- if let oldValue = oldBuckets [ i] . value {
134
- let pos = _findHole ( oldValue. id) . pos
135
- Swift . swap ( & buckets[ pos] , & oldBuckets[ i] )
142
+ for oldBucket in oldBucketRange {
143
+ if let id = oldBucket. pointee. value? . id {
144
+ let newBucket = buckets. advanced ( by: _findHole ( id) . pos)
145
+ newBucket. moveAssign ( from: oldBucket, count: 1 )
146
+ } else {
147
+ oldBucket. deinitialize ( count: 1 )
136
148
}
137
149
}
150
+
151
+ oldBuckets. deallocate ( )
152
+
138
153
return true
139
154
}
140
155
141
156
/// Count the actual number of occupied buckets.
142
157
@inline ( __always)
143
158
private func _countOccupiedBuckets( ) -> Int {
144
159
var count = 0
145
- for i in 0 ..< buckets . count where buckets [ i] . value != nil {
160
+ for i in 0 ..< bucketCount where buckets [ i] . value != nil {
146
161
count &+= 1
147
162
}
148
163
return count
149
164
}
150
165
151
166
/// Reserves enough space to store a single new value. Returns true if
152
167
/// resizing happened.
153
- mutating private func _ensurePlusOneCapacity( ) -> Bool {
154
- if buckets . count >= CacheLookupTable < T >
155
- . _minimalBucketCount ( for: estimatedCount &+ 1 ) {
168
+ private func _ensurePlusOneCapacity( ) -> Bool {
169
+ if bucketCount >= CacheLookupTable < T >
170
+ . _minimalBucketCount ( for: estimatedCount &+ 1 ) {
156
171
return false
157
172
}
158
173
@@ -164,7 +179,7 @@ struct CacheLookupTable<T: Identifiable & AnyObject> {
164
179
165
180
/// Inserts the given object into the table.
166
181
@discardableResult
167
- mutating func insert( _ obj: T ) -> Bool {
182
+ func insert( _ obj: T ) -> Bool {
168
183
var ( pos, found) = _findHole ( obj. id)
169
184
if found {
170
185
return false
0 commit comments