Skip to content

Commit b880962

Browse files
committed
FastIndex addition, leveraging same approach as FastFilter to help derive the index
1 parent bd53fae commit b880962

File tree

1 file changed

+66
-6
lines changed

1 file changed

+66
-6
lines changed

FastIndex/FastIndex/FastFilter.cs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,22 @@ public class FastFilter<T, IFinger, NUM> : IContains<T>
9494
private readonly int _xorHashSegmentSize;
9595

9696
private readonly ArraySegment<NUM>[] _fingerprintXorHashSegment;
97+
private readonly Byte[] _iQIndexXor;
98+
private readonly ArraySegment<Byte>[] iQIndexXorSegment;
9799

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)
100102
{
101103
}
102104

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)
104106
{
105107
(typeof(NUM) == typeof(Byte) || typeof(NUM) == typeof(UInt16) || typeof(NUM) == typeof(UInt32)).MustBe(true);
106108
fingerprintXorHash.MustNotBeNull();
107109
hasher.MustNotBeNull();
108110

111+
_iQIndexXor = iQIndexXor;
112+
109113
_hasher = hasher;
110114

111115
Seed = seed;
@@ -121,6 +125,15 @@ public FastFilter(IList<NUM> fingerprintXorHash, ulong seed, int numHashes, Func
121125
_fingerprintXorHashSegment[i] = new ArraySegment<NUM>(Data, _xorHashSegmentSize * i, _xorHashSegmentSize);
122126
}
123127

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+
}
124137
}
125138

126139
public int FingerprintBitSize { get; } = ICall.Bits();
@@ -146,6 +159,47 @@ public bool Contains(ulong key)
146159
return ICall.Equals(fingerprint, fingerprintXor);
147160
}
148161

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+
}
149203
}
150204

151205
/// <summary>
@@ -262,9 +316,10 @@ public FastFilter<T, IFinger, NUM> Construct(HashSet<ulong> keys, ulong seed)
262316
success = GeneratePerfectHash(keys, seed, xorHashSegmentSize);
263317
} while (!success);
264318

265-
var fingerprints = GenerateKeyFingerprints(fingerprintArraySize, xorHashSegmentSize);
319+
Byte[] iQIndexXor;
320+
var fingerprints = GenerateKeyFingerprints(fingerprintArraySize, xorHashSegmentSize, out iQIndexXor);
266321

267-
return new FastFilter<T, IFinger, NUM>(fingerprints, seed, _numHashes);
322+
return new FastFilter<T, IFinger, NUM>(fingerprints, seed, _numHashes, iQIndexXor);
268323
}
269324

270325
private static HashKeyCounter EmptyKeyCount = new HashKeyCounter();
@@ -363,14 +418,17 @@ private bool GeneratePerfectHash(HashSet<ulong> keys, ulong seed, int xorHashSeg
363418
}
364419

365420
/// Algorithm 4 - Generate Fingerprints
366-
private NUM[] GenerateKeyFingerprints(int fingerprintArraySize, int xorHashSegmentSize)
421+
private NUM[] GenerateKeyFingerprints(int fingerprintArraySize, int xorHashSegmentSize, out Byte[]iQIndexXor)
367422
{
368423
var fingerprintXorHash = new NUM[fingerprintArraySize];
424+
iQIndexXor = new Byte[fingerprintArraySize];
369425

370426
var fingerprintXorHashSegment = new ArraySegment<NUM>[_numHashes];
427+
var iQIndexXorSegment = new ArraySegment<Byte>[_numHashes];
371428
for (var i = 0; i < _numHashes; i++)
372429
{
373430
fingerprintXorHashSegment[i] = new ArraySegment<NUM>(fingerprintXorHash, xorHashSegmentSize * i, xorHashSegmentSize);
431+
iQIndexXorSegment[i] = new ArraySegment<Byte>(iQIndexXor, xorHashSegmentSize * i, xorHashSegmentSize);
374432
}
375433

376434
foreach ((var keyHash, var absoluteIndex) in _discoveryStack)
@@ -394,9 +452,11 @@ private NUM[] GenerateKeyFingerprints(int fingerprintArraySize, int xorHashSegme
394452
var iQMap = QSegmentMap[iQ, i];
395453
var hashIndex = XorHashIndexers[iQMap](keyHash, xorHashSegmentSize);
396454
fingerprintXor = ICall.XOR(fingerprintXor, fingerprintXorHashSegment[iQMap][hashIndex]);
455+
iQXor ^= iQIndexXorSegment[iQMap][hashIndex];
397456
}
398457

399458
fingerprintXorHash[absoluteIndex] = fingerprintXor;
459+
iQIndexXor[absoluteIndex] = (Byte)iQXor;
400460
}
401461

402462
return fingerprintXorHash;

0 commit comments

Comments
 (0)