in hollow/src/main/java/com/netflix/hollow/core/index/HollowHashIndexBuilder.java [106:256]
public void buildIndex() {
matchIndexHashAndSizeArray = new GrowingSegmentedLongArray(memoryRecycler);
BitSet populatedOrdinals = preindexer.getHollowTypeDataAccess().getTypeState().getPopulatedOrdinals();
/// an initial guess at how big this table might be -- one match per top-level element.
int guessNumberOfMatches = populatedOrdinals.cardinality();
intermediateMatchHashTableSize = HashCodes.hashTableSize(guessNumberOfMatches);
bitsPerIntermediateListIdentifier = bitsRequiredToRepresentValue(intermediateMatchHashTableSize - 1);
bitsPerIntermediateMatchHashEntry = bitsPerMatchHashKey + bitsPerIntermediateListIdentifier;
intermediateMatchHashMask = intermediateMatchHashTableSize - 1;
intermediateMatchHashTableSizeBeforeGrow = intermediateMatchHashTableSize * 7 / 10;
matchCount = 0;
/// a data structure which keeps canonical matches for comparison (the matchHashTable)
intermediateMatchHashTable = new FixedLengthElementArray(memoryRecycler, (long)intermediateMatchHashTableSize * bitsPerIntermediateMatchHashEntry);
/// a data structure which tracks lists of matches under canonical matches.
MultiLinkedElementArray intermediateSelectLists = new MultiLinkedElementArray(memoryRecycler);
HollowIndexerValueTraverser traverser = preindexer.getTraverser();
int ordinal = populatedOrdinals.nextSetBit(0);
while(ordinal != HollowConstants.ORDINAL_NONE) {
traverser.traverse(ordinal);
for(int i=0;i<traverser.getNumMatches();i++) {
int matchHash = getMatchHash(i);
long bucket = matchHash & intermediateMatchHashMask;
long hashBucketBit = bucket * bitsPerIntermediateMatchHashEntry;
boolean bucketIsEmpty = intermediateMatchHashTable.getElementValue(hashBucketBit, bitsPerTraverserField[0]) == 0;
long bucketMatchListIdx = intermediateMatchHashTable.getElementValue(hashBucketBit + bitsPerMatchHashKey, bitsPerIntermediateListIdentifier);
int bucketMatchHashCode = (int)matchIndexHashAndSizeArray.get(bucketMatchListIdx);
while(!bucketIsEmpty && (bucketMatchHashCode != (matchHash & Integer.MAX_VALUE) || !intermediateMatchIsEqual(i, hashBucketBit))) {
bucket = (bucket + 1) & intermediateMatchHashMask;
hashBucketBit = bucket * bitsPerIntermediateMatchHashEntry;
bucketIsEmpty = intermediateMatchHashTable.getElementValue(hashBucketBit, bitsPerTraverserField[0]) == 0;
bucketMatchListIdx = intermediateMatchHashTable.getElementValue(hashBucketBit + bitsPerMatchHashKey, bitsPerIntermediateListIdentifier);
bucketMatchHashCode = (int)matchIndexHashAndSizeArray.get(bucketMatchListIdx);
}
int matchListIdx;
if(bucketIsEmpty) {
matchListIdx = intermediateSelectLists.newList();
for(int j=0;j<preindexer.getNumMatchTraverserFields();j++)
intermediateMatchHashTable.setElementValue(hashBucketBit + offsetPerTraverserField[j], bitsPerTraverserField[j], traverser.getMatchOrdinal(i, j) + 1);
intermediateMatchHashTable.setElementValue(hashBucketBit + bitsPerMatchHashKey, bitsPerIntermediateListIdentifier, matchListIdx);
matchIndexHashAndSizeArray.set(matchListIdx, matchHash & Integer.MAX_VALUE);
matchCount++;
/// GROW IF NECESSARY!
if(matchCount > intermediateMatchHashTableSizeBeforeGrow) {
growIntermediateHashTable();
}
} else {
matchListIdx = (int)intermediateMatchHashTable.getElementValue(hashBucketBit + bitsPerMatchHashKey, bitsPerIntermediateListIdentifier);
}
intermediateSelectLists.add(matchListIdx, traverser.getMatchOrdinal(i, preindexer.getSelectFieldSpec().getBaseIteratorFieldIdx()));
}
ordinal = populatedOrdinals.nextSetBit(ordinal + 1);
}
/// turn those data structures into a compact one optimized for hash lookup
long totalNumberOfSelectBucketsAndBitsRequiredForSelectTableSize = calculateDedupedSizesAndTotalNumberOfSelectBuckets(intermediateSelectLists, matchIndexHashAndSizeArray);
long totalNumberOfSelectBuckets = totalNumberOfSelectBucketsAndBitsRequiredForSelectTableSize & 0xFFFFFFFFFFFFFFL;
long totalNumberOfMatchBuckets = HashCodes.hashTableSize(matchCount);
int bitsPerFinalSelectBucketPointer = bitsRequiredToRepresentValue(totalNumberOfSelectBuckets);
int bitsPerSelectTableSize = (int)(totalNumberOfSelectBucketsAndBitsRequiredForSelectTableSize >>> 56);
int finalBitsPerMatchHashEntry = bitsPerMatchHashKey + bitsPerSelectTableSize + bitsPerFinalSelectBucketPointer;
FixedLengthElementArray finalMatchArray = new FixedLengthElementArray(memoryRecycler, totalNumberOfMatchBuckets * finalBitsPerMatchHashEntry);
FixedLengthElementArray finalSelectArray = new FixedLengthElementArray(memoryRecycler, totalNumberOfSelectBuckets * bitsPerSelectHashEntry);
long finalMatchHashMask = totalNumberOfMatchBuckets - 1;
long currentSelectArrayBucket = 0;
for(int i=0;i<matchCount;i++) {
long matchIndexHashAndSize = matchIndexHashAndSizeArray.get(i);
int matchIndexSize = (int)(matchIndexHashAndSize >> 32);
int matchIndexTableSize = HashCodes.hashTableSize(matchIndexSize);
int matchIndexBucketMask = matchIndexTableSize - 1;
HollowOrdinalIterator selectOrdinalIter = intermediateSelectLists.iterator(i);
int selectOrdinal = selectOrdinalIter.next();
while(selectOrdinal != HollowOrdinalIterator.NO_MORE_ORDINALS) {
int selectBucket = HashCodes.hashInt(selectOrdinal) & matchIndexBucketMask;
int bucketOrdinal = (int)finalSelectArray.getElementValue((currentSelectArrayBucket + selectBucket) * bitsPerSelectHashEntry, bitsPerSelectHashEntry) - 1;
while(bucketOrdinal != HollowConstants.ORDINAL_NONE && bucketOrdinal != selectOrdinal) {
///TODO: If select field type is not REFERENCE, then we should dedup -- unless we are reference counting for delta application
///ordinals here with the same value for the specified field.
selectBucket = (selectBucket + 1) & matchIndexBucketMask;
bucketOrdinal = (int)finalSelectArray.getElementValue((currentSelectArrayBucket + selectBucket) * bitsPerSelectHashEntry, bitsPerSelectHashEntry) - 1;
}
if(bucketOrdinal == HollowConstants.ORDINAL_NONE)
finalSelectArray.setElementValue((currentSelectArrayBucket + selectBucket) * bitsPerSelectHashEntry, bitsPerSelectHashEntry, selectOrdinal + 1);
selectOrdinal = selectOrdinalIter.next();
}
long finalMatchIndexBucket = matchIndexHashAndSize & finalMatchHashMask;
long finalMatchIndexBucketBit = finalMatchIndexBucket * finalBitsPerMatchHashEntry;
while(finalMatchArray.getElementValue(finalMatchIndexBucketBit, bitsPerTraverserField[0]) != 0) {
finalMatchIndexBucket = (finalMatchIndexBucket + 1) & finalMatchHashMask;
finalMatchIndexBucketBit = finalMatchIndexBucket * finalBitsPerMatchHashEntry;
}
long intermediateMatchHashBucket = matchIndexHashAndSize & intermediateMatchHashMask;
long intermediateMatchIndexBucketBit = intermediateMatchHashBucket * bitsPerIntermediateMatchHashEntry;
while(intermediateMatchHashTable.getElementValue(intermediateMatchIndexBucketBit + bitsPerMatchHashKey, bitsPerIntermediateListIdentifier) != i) {
intermediateMatchHashBucket = (intermediateMatchHashBucket + 1) & intermediateMatchHashMask;
intermediateMatchIndexBucketBit = intermediateMatchHashBucket * bitsPerIntermediateMatchHashEntry;
}
if(bitsPerMatchHashKey < 56) {
long matchHashKey = intermediateMatchHashTable.getElementValue(intermediateMatchIndexBucketBit, bitsPerMatchHashKey);
finalMatchArray.setElementValue(finalMatchIndexBucketBit, bitsPerMatchHashKey, matchHashKey);
} else {
finalMatchArray.copyBits(intermediateMatchHashTable, intermediateMatchIndexBucketBit, finalMatchIndexBucketBit, bitsPerMatchHashKey);
}
finalMatchArray.setElementValue(finalMatchIndexBucketBit + bitsPerMatchHashKey, bitsPerSelectTableSize, matchIndexSize);
finalMatchArray.setElementValue(finalMatchIndexBucketBit + bitsPerMatchHashKey + bitsPerSelectTableSize, bitsPerFinalSelectBucketPointer, currentSelectArrayBucket);
currentSelectArrayBucket += matchIndexTableSize;
}
this.finalMatchHashTable = finalMatchArray;
this.finalSelectHashArray = finalSelectArray;
this.finalBitsPerMatchHashEntry = finalBitsPerMatchHashEntry;
this.finalBitsPerSelectTablePointer = bitsPerFinalSelectBucketPointer;
this.finalBitsPerSelectTableSize = bitsPerSelectTableSize;
this.finalMatchHashMask = finalMatchHashMask;
}