/** {@inheritDoc} */ @Override public boolean contains(final KType key) { if (Intrinsics.<KType>isEmpty(key)) { return this.allocatedDefaultKey; } final int mask = this.keys.length - 1; final KType[] keys = Intrinsics.<KType[]>cast(this.keys); int slot = REHASH(key) & mask; KType existing; /*! #if ($RH) !*/ final int[] cached = this.hash_cache; int dist = 0; /*! #end !*/ while (!Intrinsics.<KType>isEmpty(existing = keys[slot]) /*! #if ($RH) !*/ && dist <= probe_distance(slot, cached) /*! #end !*/) { if (KEYEQUALS(key, existing)) { return true; } slot = (slot + 1) & mask; /*! #if ($RH) !*/ dist++; /*! #end !*/ } // end while true return false; }
/** {@inheritDoc} */ @Override public int removeAll(final KTypePredicate<? super KType> predicate) { final int before = this.size(); if (this.allocatedDefaultKey) { if (predicate.apply(Intrinsics.<KType>empty())) { this.allocatedDefaultKey = false; } } final KType[] keys = Intrinsics.<KType[]>cast(this.keys); for (int i = 0; i < keys.length; ) { KType existing; if (!Intrinsics.<KType>isEmpty(existing = keys[i]) && predicate.apply(existing)) { shiftConflictingKeys(i); // Shift, do not increment slot. } else { i++; } } return before - this.size(); }
/** {@inheritDoc} */ @Override public <T extends KTypePredicate<? super KType>> T forEach(final T predicate) { if (this.allocatedDefaultKey) { if (!predicate.apply(Intrinsics.<KType>empty())) { return predicate; } } final KType[] keys = Intrinsics.<KType[]>cast(this.keys); // Iterate in reverse for side-stepping the longest conflict chain // in another hash, in case apply() is actually used to fill another hash container. for (int i = keys.length - 1; i >= 0; i--) { KType existing; if (!Intrinsics.<KType>isEmpty(existing = keys[i])) { if (!predicate.apply(existing)) { break; } } } return predicate; }
/** {@inheritDoc} */ @Override public int hashCode() { int h = 0; // allocated default key has hash = 0 final KType[] keys = Intrinsics.<KType[]>cast(this.keys); for (int i = keys.length; --i >= 0; ) { KType existing; if (!Intrinsics.<KType>isEmpty(existing = keys[i])) { h += BitMixer.mix(existing); } } return h; }
/** {@inheritDoc} */ @Override public KType[] toArray(final KType[] target) { int count = 0; if (this.allocatedDefaultKey) { target[count++] = Intrinsics.<KType>empty(); } final KType[] keys = Intrinsics.<KType[]>cast(this.keys); for (int i = 0; i < keys.length; i++) { KType existing; if (!Intrinsics.<KType>isEmpty(existing = keys[i])) { target[count++] = existing; } } assert count == this.size(); return target; }
private boolean is_allocated(final int slot, final KType[] keys) { return !Intrinsics.<KType>isEmpty(keys[slot]); }
/** Shift all the slot-conflicting keys allocated to (and including) <code>slot</code>. */ private void shiftConflictingKeys(int gapSlot) { final int mask = this.keys.length - 1; final KType[] keys = Intrinsics.<KType[]>cast(this.keys); /*! #if ($RH) !*/ final int[] cached = this.hash_cache; /*! #else final int perturb = this.perturbation; #end !*/ // Perform shifts of conflicting keys to fill in the gap. int distance = 0; while (true) { final int slot = (gapSlot + (++distance)) & mask; final KType existing = keys[slot]; if (Intrinsics.<KType>isEmpty(existing)) { break; } /*! #if ($RH) !*/ // use the cached value, no need to recompute final int idealSlotModMask = cached[slot]; /*! #if($DEBUG) !*/ // Check invariants assert idealSlotModMask == (REHASH(existing) & mask); /*! #end !*/ /*! #else final int idealSlotModMask = REHASH2(existing, perturb) & mask; #end !*/ // original HPPC code: shift = (slot - idealSlot) & mask; // equivalent to shift = (slot & mask - idealSlot & mask) & mask; // since slot and idealSlotModMask are already folded, we have : final int shift = (slot - idealSlotModMask) & mask; if (shift >= distance) { // Entry at this position was originally at or before the gap slot. // Move the conflict-shifted entry to the gap's position and repeat the procedure // for any entries to the right of the current position, treating it // as the new gap. keys[gapSlot] = existing; /*! #if ($RH) !*/ cached[gapSlot] = idealSlotModMask; /*! #if($DEBUG) !*/ assert cached[gapSlot] == (REHASH(existing) & mask); /*! #end !*/ /*! #end !*/ gapSlot = slot; distance = 0; } } // end while // Mark the last found gap slot without a conflict as empty. keys[gapSlot] = Intrinsics.<KType>empty(); this.assigned--; }
/** * Expand the internal storage buffers (capacity) or rehash current keys and values if there are a * lot of deleted slots. */ private void expandAndAdd(final KType pendingKey, final int freeSlot) { assert this.assigned == this.resizeAt; // default sentinel value is never in the keys[] array, so never trigger reallocs assert (!Intrinsics.<KType>isEmpty(pendingKey)); // Try to allocate new buffers first. If we OOM, it'll be now without // leaving the data structure in an inconsistent state. final KType[] oldKeys = Intrinsics.<KType[]>cast(this.keys); allocateBuffers( HashContainers.nextBufferSize(this.keys.length, this.assigned, this.loadFactor)); // We have succeeded at allocating new data so insert the pending key/value at // the free slot in the old arrays before rehashing. this.assigned++; oldKeys[freeSlot] = pendingKey; // Variables for adding final int mask = this.keys.length - 1; KType key = Intrinsics.<KType>empty(); // adding phase int slot = -1; final KType[] keys = Intrinsics.<KType[]>cast(this.keys); /*! #if ($RH) !*/ final int[] cached = this.hash_cache; /*! #end !*/ /*! #if ($RH) !*/ KType tmpKey = Intrinsics.<KType>empty(); int tmpAllocated = -1; int initial_slot = -1; int dist = -1; int existing_distance = -1; /*! #end !*/ // iterate all the old arrays to add in the newly allocated buffers // It is important to iterate backwards to minimize the conflict chain length ! final int perturb = this.perturbation; for (int i = oldKeys.length; --i >= 0; ) { // only consider non-empty slots, of course if (!Intrinsics.<KType>isEmpty(key = oldKeys[i])) { slot = REHASH2(key, perturb) & mask; /*! #if ($RH) !*/ initial_slot = slot; dist = 0; /*! #end !*/ // similar to add(), except all inserted keys are known to be unique. while (is_allocated(slot, keys)) { /*! #if ($RH) !*/ // re-shuffle keys to minimize variance existing_distance = probe_distance(slot, cached); if (dist > existing_distance) { // swap current (key, value, initial_slot) with slot places tmpKey = keys[slot]; keys[slot] = key; key = tmpKey; tmpAllocated = cached[slot]; cached[slot] = initial_slot; initial_slot = tmpAllocated; /*! #if($DEBUG) !*/ // Check invariants assert cached[slot] == (REHASH(keys[slot]) & mask); assert initial_slot == (REHASH(key) & mask); /*! #end !*/ dist = existing_distance; } // endif /*! #end !*/ slot = (slot + 1) & mask; /*! #if ($RH) !*/ dist++; /*! #end !*/ } // end while // place it at that position /*! #if ($RH) !*/ cached[slot] = initial_slot; /*! #end !*/ keys[slot] = key; /*! #if ($RH) !*/ /*! #if($DEBUG) !*/ // Check invariants assert cached[slot] == (REHASH(keys[slot]) & mask); /*! #end !*/ /*! #end !*/ } } }
/** {@inheritDoc} */ @Override public boolean add(KType key) { if (Intrinsics.<KType>isEmpty(key)) { if (this.allocatedDefaultKey) { return false; } this.allocatedDefaultKey = true; return true; } final int mask = this.keys.length - 1; final KType[] keys = Intrinsics.<KType[]>cast(this.keys); int slot = REHASH(key) & mask; KType existing; /*! #if ($RH) !*/ final int[] cached = this.hash_cache; KType tmpKey; int tmpAllocated; int initial_slot = slot; int dist = 0; int existing_distance = 0; /*! #if($DEBUG) !*/ final KType originalKey = key; /*! #end !*/ /*! #end !*/ while (!Intrinsics.<KType>isEmpty(existing = keys[slot])) { /*! #if ($RH) !*/ existing_distance = probe_distance(slot, cached); // When first entering the while loop, then key == original key to search. // So either: // 1) key is immediately found and the routine bail out, // or // 2) If the Robin-hood criteria of distance is not met, we search the next slot, (usual // linear probing) // or // 3) else the criteria of distance is met, then (key) is swapped with the ones in // slot position which becomes the new (key) to consider. This is OK because keys are swapped // only if dist > existing_distance, // i.e only if the key to add is NOT in the set, see contains(). So we steal the rich (a // previously entered key, favored because having being inserted // in a less crowed array) to give to the poor, the now inserted key. Then, we start searching // again in the next slot. /*! #if($DEBUG) !*/ // if the original key been swapped by the Robin-hood process, we actually never enter the // following if, so we are fine. if (!KEYEQUALS(key, originalKey)) { assert !KEYEQUALS(key, existing); } /*! #end !*/ /*! #end !*/ /*! #if($RH) !*/ // Robin-hood shortcut: if key exists, it can only be found in dist <= existing_distance // range. // indeed we should expect to never see an existing element with a shorter probe count // (existing_distance) // than our current count (dist): if that had happened, there would’ve been a swap during // insertion, see below. // also see contains() and remove() for the same trick. /*! #end !*/ if ( /*! #if ($RH) !*/ dist <= existing_distance && /*! #end !*/ KEYEQUALS(key, existing)) { return false; } /*! #if ($RH) !*/ // re-shuffle keys to minimize variance if (dist > existing_distance) { // we actually enter here only if the key to add is NOT in the set. // swap current (key, value, initial_slot) with slot places tmpKey = keys[slot]; keys[slot] = key; key = tmpKey; tmpAllocated = cached[slot]; cached[slot] = initial_slot; initial_slot = tmpAllocated; /*! #if($DEBUG) !*/ // Check invariants assert cached[slot] == (REHASH(keys[slot]) & mask); assert initial_slot == (REHASH(key) & mask); /*! #end !*/ dist = existing_distance; } /*! #end !*/ slot = (slot + 1) & mask; /*! #if ($RH) !*/ dist++; /*! #end !*/ } // Check if we need to grow. If so, reallocate new data, // fill in the last element and rehash. if (this.assigned == this.resizeAt) { expandAndAdd(key, slot); } else { this.assigned++; /*! #if ($RH) !*/ cached[slot] = initial_slot; /*! #end !*/ keys[slot] = key; /*! #if ($RH) !*/ /*! #if($DEBUG) !*/ // Check invariants assert cached[slot] == (REHASH(keys[slot]) & mask); /*! #end !*/ /*! #end !*/ } return true; }