/** {@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; }
/** * Iterate backwards w.r.t the buffer, to minimize collision chains when filling another hash * container (ex. with putAll()) */ @Override protected KTypeCursor<KType> fetch() { if (this.cursor.index == KTypeHashSet.this.keys.length + 1) { if (KTypeHashSet.this.allocatedDefaultKey) { this.cursor.index = KTypeHashSet.this.keys.length; this.cursor.value = Intrinsics.<KType>empty(); return this.cursor; } // no value associated with the default key, continue iteration... this.cursor.index = KTypeHashSet.this.keys.length; } int i = this.cursor.index - 1; while (i >= 0 && !is_allocated(i, Intrinsics.<KType[]>cast(KTypeHashSet.this.keys))) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.value = Intrinsics.<KType>cast(KTypeHashSet.this.keys[i]); return this.cursor; }
/** {@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; }
/** 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 !*/ } } }