@@ -24,13 +24,15 @@ internal struct _HashTable {
24
24
internal let buckets : UnsafeMutablePointer < Bucket >
25
25
26
26
@usableFromInline
27
- internal let bucketCount : Int
27
+ internal let bucketMask : Int
28
28
29
29
@inlinable
30
30
@inline ( __always)
31
31
internal init ( buckets: UnsafeMutablePointer < Bucket > , bucketCount: Int ) {
32
32
self . buckets = buckets
33
- self . bucketCount = bucketCount
33
+ // The bucket count is a positive power of two, so subtracting 1 will
34
+ // never overflow and get us a nice mask.
35
+ self . bucketMask = bucketCount &- 1
34
36
}
35
37
36
38
@inlinable
@@ -41,11 +43,9 @@ internal struct _HashTable {
41
43
}
42
44
43
45
@inlinable
44
- internal var bucketMask : Int {
46
+ internal var bucketCount : Int {
45
47
@inline ( __always) get {
46
- // The bucket count is a positive power of two, so subtracting 1 will
47
- // never overflow and get us a nice mask.
48
- return bucketCount &- 1
48
+ return bucketMask &+ 1
49
49
}
50
50
}
51
51
}
@@ -67,7 +67,7 @@ extension _HashTable {
67
67
68
68
/// The inverse of the maximum hash table load factor.
69
69
private static var maxLoadFactorInverse : Double {
70
- @inline ( __always) get { return 100 / 90 }
70
+ @inline ( __always) get { return 100 / 75 }
71
71
}
72
72
73
73
internal static func capacity( forScale scale: Int ) -> Int {
@@ -107,14 +107,6 @@ extension _HashTable {
107
107
@usableFromInline
108
108
@_fixed_layout
109
109
internal struct Entry {
110
- @inlinable
111
- internal static var occupiedFlag : UInt8 {
112
- @inline ( __always) get { return 0x80 }
113
- }
114
- @inlinable
115
- internal static var payloadMask : UInt8 {
116
- @inline ( __always) get { return 0x7F }
117
- }
118
110
@inlinable
119
111
internal static var unoccupied : Entry {
120
112
@inline ( __always) get { return Entry ( _value: 0 ) }
@@ -132,16 +124,15 @@ extension _HashTable {
132
124
@inlinable
133
125
@inline ( __always)
134
126
internal init ( payload: UInt8 ) {
135
- _sanityCheck ( payload & ~ Entry . payloadMask = = 0 )
136
- self . init ( _value: Entry . occupiedFlag | payload)
127
+ _sanityCheck ( payload ! = 0 )
128
+ self . init ( _value: payload)
137
129
}
138
130
139
131
@inlinable
140
132
@inline ( __always)
141
133
internal init ( forHashValue hashValue: Int ) {
142
- // Use the highest seven bits of the hash value.
143
- let payload = UInt ( bitPattern: hashValue) &>> ( Int . bitWidth &- 7 )
144
- self . init ( payload: UInt8 ( truncatingIfNeeded: payload) )
134
+ let payload = UInt ( bitPattern: hashValue) &>> ( Int . bitWidth &- 8 )
135
+ self . init ( payload: Swift . max ( 1 , UInt8 ( truncatingIfNeeded: payload) ) )
145
136
}
146
137
147
138
@inlinable
@@ -152,12 +143,23 @@ extension _HashTable {
152
143
@inlinable
153
144
internal var payload : UInt8 {
154
145
@inline ( __always) get {
155
- return _value & _HashTable . Entry . payloadMask
146
+ return _value
156
147
}
157
148
}
158
149
}
159
150
}
160
151
152
+ extension _HashTable . Entry {
153
+ @inlinable
154
+ var pattern : UInt64 {
155
+ @inline ( __always) get {
156
+ // Fill a 64-bit integer with 8 copies of this entry.
157
+ let p = UInt64 ( truncatingIfNeeded: _value)
158
+ return p &* 0x01010101_01010101
159
+ }
160
+ }
161
+ }
162
+
161
163
extension _HashTable . Entry : Equatable {
162
164
@inlinable
163
165
@inline ( __always)
@@ -270,7 +272,7 @@ extension _HashTable.Bucket {
270
272
@inline ( __always)
271
273
get {
272
274
// Holes are always at the end, so it's enough to check the highest byte.
273
- return _value &>> ( UInt64 . bitWidth &- UInt8 . bitWidth) != 0
275
+ return _value. leadingZeroBitCount < UInt8 . bitWidth
274
276
}
275
277
}
276
278
@@ -295,7 +297,7 @@ extension _HashTable.Bucket {
295
297
@inline ( __always)
296
298
get {
297
299
// Holes are zero bytes at the most significant places.
298
- let shift = UInt64 . bitWidth - ( _value. leadingZeroBitCount & ~ 7 )
300
+ let shift = UInt64 . bitWidth & - ( _value. leadingZeroBitCount & ~ 7 )
299
301
return _HashTable. Slot ( shift: shift)
300
302
}
301
303
}
@@ -363,14 +365,9 @@ extension _HashTable.Bucket {
363
365
364
366
/// Returns a sequence of Slots matching the given entry.
365
367
@inlinable
366
- internal func slots( matching entry: _HashTable . Entry ) -> SlotSet {
367
- // Fill a 64-bit integer with 8 copies of the entry we're looking for.
368
- var p = UInt64 ( entry. _value)
369
- p |= p &<< 8
370
- p |= p &<< 16
371
- p |= p &<< 32
372
- // Xoring `p` to `self._value` turns matching bytes into zeroes.
373
- p ^= self . _value
368
+ @inline ( __always)
369
+ internal func _slots( matching pattern: UInt64 ) -> SlotSet {
370
+ let p = self . _value ^ pattern
374
371
// The problem now reduces to finding zero bytes in `p`. For every 8-bit
375
372
// integer `b`, `x = ((b & 0x7F) + 0x7F) | b` has bit 7 set iff `b !=
376
373
// 0`. Further, `~(x | 0x7F)` leaves bit 7 set to one iff `b == 0` and
@@ -384,6 +381,13 @@ extension _HashTable.Bucket {
384
381
// bits to the start of their corresponding bytes.
385
382
return SlotSet ( _shifts: y &>> 7 )
386
383
}
384
+
385
+ /// Returns a sequence of Slots matching the given entry.
386
+ @inlinable
387
+ @inline ( __always)
388
+ internal func slots( matching entry: _HashTable . Entry ) -> SlotSet {
389
+ return _slots ( matching: entry. pattern)
390
+ }
387
391
}
388
392
389
393
extension _HashTable {
@@ -483,7 +487,6 @@ extension _HashTable: Collection {
483
487
}
484
488
485
489
@inlinable
486
- @inline ( __always)
487
490
internal func formIndex( after index: inout Index ) {
488
491
index. offset += 1
489
492
if index. bucket >= bucketCount { return }
@@ -560,7 +563,7 @@ extension _HashTable {
560
563
@usableFromInline
561
564
let _hashTable : _HashTable
562
565
@usableFromInline
563
- let _entry : Entry
566
+ let _pattern : UInt64
564
567
@usableFromInline
565
568
var _bucket : Int
566
569
@usableFromInline
@@ -569,14 +572,15 @@ extension _HashTable {
569
572
@inlinable
570
573
internal init ( hashTable: _HashTable , hashValue: Int ) {
571
574
self . _hashTable = hashTable
572
- self . _entry = Entry ( forHashValue: hashValue)
573
575
self . _bucket = hashTable. _idealBucket ( forHashValue: hashValue)
574
- self . _matches = hashTable. buckets [ _bucket] . slots ( matching: _entry)
576
+ let b = hashTable. buckets [ _bucket]
577
+ self . _pattern = Entry ( forHashValue: hashValue) . pattern
578
+ self . _matches = b. _slots ( matching: _pattern)
575
579
}
576
580
577
581
@inlinable
578
582
internal mutating func next( ) -> ( index: Index , found: Bool ) {
579
- repeat {
583
+ while true {
580
584
if let slot = _matches. next ( ) {
581
585
return ( Index ( bucket: _bucket, slot: slot) , true )
582
586
}
@@ -586,8 +590,8 @@ extension _HashTable {
586
590
return ( Index ( bucket: _bucket, slot: hole) , false )
587
591
}
588
592
_bucket = _hashTable. _succ ( _bucket)
589
- _matches = _hashTable. buckets [ _bucket] . slots ( matching: _entry )
590
- } while true
593
+ _matches = _hashTable. buckets [ _bucket] . _slots ( matching: _pattern )
594
+ }
591
595
}
592
596
}
593
597
@@ -598,7 +602,37 @@ extension _HashTable {
598
602
}
599
603
600
604
extension _HashTable {
601
- @usableFromInline
605
+ @inlinable
606
+ @inline ( __always)
607
+ func contains< Element: Equatable > (
608
+ hashValue: Int ,
609
+ element: Element ,
610
+ elements: UnsafePointer < Element >
611
+ ) -> Bool {
612
+ var bucket = _idealBucket ( forHashValue: hashValue)
613
+ var b = self . buckets [ bucket]
614
+ let pattern = Entry ( forHashValue: hashValue) . pattern
615
+ var matches = b. _slots ( matching: pattern) . _shifts
616
+ while true {
617
+ if _fastPath ( matches != 0 ) {
618
+ let shift = matches. trailingZeroBitCount
619
+ let index = Index ( bucket: bucket, slotOffset: shift &>> 3 )
620
+ if elements [ index. offset] == element { return true }
621
+ matches &= matches &- 1
622
+ } else if b. isFull {
623
+ bucket = _succ ( bucket)
624
+ b = self . buckets [ bucket]
625
+ matches = b. _slots ( matching: pattern) . _shifts
626
+ } else {
627
+ return false
628
+ }
629
+ }
630
+ }
631
+ }
632
+
633
+ extension _HashTable {
634
+ @inlinable
635
+ @inline ( __always)
602
636
@_effects ( releasenone)
603
637
internal func copyContents( of other: _HashTable ) {
604
638
_sanityCheck ( bucketCount == other. bucketCount)
@@ -607,7 +641,8 @@ extension _HashTable {
607
641
608
642
/// Insert a new entry with the specified hash value into the table.
609
643
/// The entry must not already exist in the table -- duplicates are ignored.
610
- @usableFromInline
644
+ @inlinable
645
+ @inline ( __always)
611
646
@_effects ( releasenone)
612
647
internal func insertNew( hashValue: Int ) -> Index {
613
648
var bucket = _idealBucket ( forHashValue: hashValue)
@@ -621,14 +656,16 @@ extension _HashTable {
621
656
/// Insert a new entry for an element with the specified hash value at
622
657
/// `bucket`. The bucket must have been returned by `lookupFirst` or
623
658
/// `lookupNext` for the same hash value, with `found == false`.
624
- @usableFromInline
659
+ @inlinable
625
660
@inline ( __always)
626
661
@_effects ( releasenone)
627
662
internal func insert( hashValue: Int , at index: Index ) {
628
663
_sanityCheck ( !isOccupied( index) )
629
664
self [ index] = Entry ( forHashValue: hashValue)
630
665
}
631
666
667
+ @inlinable
668
+ @inline ( __always)
632
669
internal func removeAll( ) {
633
670
buckets. assign ( repeating: Bucket ( 0 ) , count: bucketCount)
634
671
}
0 commit comments