@@ -94,18 +94,22 @@ public class FastFilter<T, IFinger, NUM> : IContains<T>
94
94
private readonly int _xorHashSegmentSize ;
95
95
96
96
private readonly ArraySegment < NUM > [ ] _fingerprintXorHashSegment ;
97
+ private readonly Byte [ ] _iQIndexXor ;
98
+ private readonly ArraySegment < Byte > [ ] iQIndexXorSegment ;
97
99
98
- public FastFilter ( IList < NUM > fingerprints , ulong seed , int numHashes )
99
- : this ( fingerprints , seed , numHashes , FNVHash )
100
+ public FastFilter ( IList < NUM > fingerprints , ulong seed , int numHashes , Byte [ ] iQIndexXor = null )
101
+ : this ( fingerprints , seed , numHashes , iQIndexXor , FNVHash )
100
102
{
101
103
}
102
104
103
- public FastFilter ( IList < NUM > fingerprintXorHash , ulong seed , int numHashes , Func < ulong , ulong > hasher )
105
+ public FastFilter ( IList < NUM > fingerprintXorHash , ulong seed , int numHashes , Byte [ ] iQIndexXor , Func < ulong , ulong > hasher )
104
106
{
105
107
( typeof ( NUM ) == typeof ( Byte ) || typeof ( NUM ) == typeof ( UInt16 ) || typeof ( NUM ) == typeof ( UInt32 ) ) . MustBe ( true ) ;
106
108
fingerprintXorHash . MustNotBeNull ( ) ;
107
109
hasher . MustNotBeNull ( ) ;
108
110
111
+ _iQIndexXor = iQIndexXor ;
112
+
109
113
_hasher = hasher ;
110
114
111
115
Seed = seed ;
@@ -121,6 +125,15 @@ public FastFilter(IList<NUM> fingerprintXorHash, ulong seed, int numHashes, Func
121
125
_fingerprintXorHashSegment [ i ] = new ArraySegment < NUM > ( Data , _xorHashSegmentSize * i , _xorHashSegmentSize ) ;
122
126
}
123
127
128
+ iQIndexXorSegment = null ;
129
+ if ( _iQIndexXor != null )
130
+ {
131
+ iQIndexXorSegment = new ArraySegment < byte > [ numHashes ] ;
132
+ for ( var i = 0 ; i < numHashes ; i ++ )
133
+ {
134
+ iQIndexXorSegment [ i ] = new ArraySegment < Byte > ( _iQIndexXor , _xorHashSegmentSize * i , _xorHashSegmentSize ) ;
135
+ }
136
+ }
124
137
}
125
138
126
139
public int FingerprintBitSize { get ; } = ICall . Bits ( ) ;
@@ -146,6 +159,47 @@ public bool Contains(ulong key)
146
159
return ICall . Equals ( fingerprint , fingerprintXor ) ;
147
160
}
148
161
162
+ /// <summary>
163
+ /// Combine Contains and ability to construct an index to return the unique index within CalcFingerprintArraySize(totalKeys) for the given key.
164
+ /// Along the lines of Perfect Hash, this then is a Perfect Index.
165
+ ///
166
+ /// All kinds of uses. Effectively solves the sparse array problem, packing data and mapping it into a dense array that can be indexed into.
167
+ ///
168
+ /// Provides O(1) lookup.
169
+ ///
170
+ /// Saves electricity. Saves the planet.
171
+ ///
172
+ /// </summary>
173
+ /// <param name="key">Key to lookup index for</param>
174
+ /// <returns>-2 if not configured to support indexes, -1 if not Contains, or index in range 0..CalcFingerprintArraySize(totalKeys)</returns>
175
+ public unsafe int Index ( ulong key )
176
+ {
177
+ if ( iQIndexXorSegment == null )
178
+ {
179
+ return - 2 ;
180
+ }
181
+
182
+ var hash = _hasher ( key + Seed ) ;
183
+
184
+ var fingerprint = ICall . ToFingerPrintType ( hash ^ ( hash >> XorHashShift ) ) ;
185
+
186
+ var index = stackalloc int [ _fingerprintXorHashSegment . Length ] ;
187
+
188
+ // NOTE - only perf test this on a Release build!
189
+ for ( var i = 0 ; i < _fingerprintXorHashSegment . Length ; i ++ )
190
+ {
191
+ index [ i ] = XorHashIndexers [ i ] ( hash , _xorHashSegmentSize ) ;
192
+ }
193
+ var fingerprintXor = _fingerprintXorHashSegment [ 0 ] [ index [ 0 ] ] ;
194
+ var iQ = iQIndexXorSegment [ 0 ] [ index [ 0 ] ] ;
195
+ for ( var i = 1 ; i < _fingerprintXorHashSegment . Length ; i ++ )
196
+ {
197
+ fingerprintXor = ICall . XOR ( fingerprintXor , _fingerprintXorHashSegment [ i ] [ index [ i ] ] ) ;
198
+ iQ ^= iQIndexXorSegment [ i ] [ index [ i ] ] ;
199
+ }
200
+
201
+ return ICall . Equals ( fingerprint , fingerprintXor ) ? index [ iQ ] + iQ * _xorHashSegmentSize : - 1 ;
202
+ }
149
203
}
150
204
151
205
/// <summary>
@@ -262,9 +316,10 @@ public FastFilter<T, IFinger, NUM> Construct(HashSet<ulong> keys, ulong seed)
262
316
success = GeneratePerfectHash ( keys , seed , xorHashSegmentSize ) ;
263
317
} while ( ! success ) ;
264
318
265
- var fingerprints = GenerateKeyFingerprints ( fingerprintArraySize , xorHashSegmentSize ) ;
319
+ Byte [ ] iQIndexXor ;
320
+ var fingerprints = GenerateKeyFingerprints ( fingerprintArraySize , xorHashSegmentSize , out iQIndexXor ) ;
266
321
267
- return new FastFilter < T , IFinger , NUM > ( fingerprints , seed , _numHashes ) ;
322
+ return new FastFilter < T , IFinger , NUM > ( fingerprints , seed , _numHashes , iQIndexXor ) ;
268
323
}
269
324
270
325
private static HashKeyCounter EmptyKeyCount = new HashKeyCounter ( ) ;
@@ -363,14 +418,17 @@ private bool GeneratePerfectHash(HashSet<ulong> keys, ulong seed, int xorHashSeg
363
418
}
364
419
365
420
/// Algorithm 4 - Generate Fingerprints
366
- private NUM [ ] GenerateKeyFingerprints ( int fingerprintArraySize , int xorHashSegmentSize )
421
+ private NUM [ ] GenerateKeyFingerprints ( int fingerprintArraySize , int xorHashSegmentSize , out Byte [ ] iQIndexXor )
367
422
{
368
423
var fingerprintXorHash = new NUM [ fingerprintArraySize ] ;
424
+ iQIndexXor = new Byte [ fingerprintArraySize ] ;
369
425
370
426
var fingerprintXorHashSegment = new ArraySegment < NUM > [ _numHashes ] ;
427
+ var iQIndexXorSegment = new ArraySegment < Byte > [ _numHashes ] ;
371
428
for ( var i = 0 ; i < _numHashes ; i ++ )
372
429
{
373
430
fingerprintXorHashSegment [ i ] = new ArraySegment < NUM > ( fingerprintXorHash , xorHashSegmentSize * i , xorHashSegmentSize ) ;
431
+ iQIndexXorSegment [ i ] = new ArraySegment < Byte > ( iQIndexXor , xorHashSegmentSize * i , xorHashSegmentSize ) ;
374
432
}
375
433
376
434
foreach ( ( var keyHash , var absoluteIndex ) in _discoveryStack )
@@ -394,9 +452,11 @@ private NUM[] GenerateKeyFingerprints(int fingerprintArraySize, int xorHashSegme
394
452
var iQMap = QSegmentMap [ iQ , i ] ;
395
453
var hashIndex = XorHashIndexers [ iQMap ] ( keyHash , xorHashSegmentSize ) ;
396
454
fingerprintXor = ICall . XOR ( fingerprintXor , fingerprintXorHashSegment [ iQMap ] [ hashIndex ] ) ;
455
+ iQXor ^= iQIndexXorSegment [ iQMap ] [ hashIndex ] ;
397
456
}
398
457
399
458
fingerprintXorHash [ absoluteIndex ] = fingerprintXor ;
459
+ iQIndexXor [ absoluteIndex ] = ( Byte ) iQXor ;
400
460
}
401
461
402
462
return fingerprintXorHash ;
0 commit comments