final void splitSegmentAndPut(SmoothieMap<K, V> map, long hash, K key, V value) { int mc = map.modCount; cancelAllocBeyondCapacity(map); int segments = map.segments(); int tierDiff = map.segmentsTier - this.tier; // Double segments array, if needed if (tierDiff == 0) { map.doubleSegments(); segments *= 2; tierDiff = 1; } // Create a new Segment and replace half indexes pointing to the old segment with // refs to the new int segmentOccursEach = segments >>> tierDiff; int lowestSegmentIndex = (int) (hash & (segmentOccursEach - 1)); int newTier = this.tier += 1; Segment<K, V> higherSegment = map.makeSegment(newTier); for (int segmentIndex = lowestSegmentIndex + segmentOccursEach; segmentIndex < segments; segmentIndex += segmentOccursEach * 2) { map.segments[segmentIndex] = higherSegment; } // Rebalance entries between two segments int balance = 0; for (long slotIndex = 0, slot; slotIndex < HASH_TABLE_SIZE; slotIndex++) { if ((slot = readSlot(slotIndex)) != 0) { long allocIndex; K k; long kHash = map.keyHashCode(k = readKey(allocIndex = allocIndex(slot))); if ((kHash & segmentOccursEach) != 0) { V v = readValue(allocIndex); eraseAlloc(allocIndex); if (shiftRemove(slotIndex) != slotIndex) slotIndex--; // don't skip shifted slot higherSegment.putOnSplit(map, kHash, k, v); balance++; } } } // Check rebalanced something, otherwise we are going to split segments infinitely // ending up with OutOfMemoryError if (balance == 0) checkHashCodesAreNotAllSame(map); if (balance >= map.allocCapacity) higherSegment.checkHashCodesAreNotAllSame(map); // Finally, put the entry Segment<K, V> segmentForPut; segmentForPut = (hash & segmentOccursEach) != 0 ? higherSegment : this; segmentForPut.put(map, hash, key, value, false); mc++; if (mc != map.modCount) throw new ConcurrentModificationException(); }
final void iterationRemove(SmoothieMap<K, V> map, K key, long allocIndex) { long slotIndex, slot, hash = map.keyHashCode(key); long storedHash = storedHash(hash); for (slotIndex = slotIndex(hash); (slot = readSlot(slotIndex)) != 0; slotIndex = nextSlotIndex(slotIndex)) { if (hash(slot) == storedHash && allocIndex(slot) == allocIndex) { remove(map, slotIndex, allocIndex); return; } } throw new ConcurrentModificationException("Unable to find entry in segment's hash table"); }
private void checkHashCodesAreNotAllSame(SmoothieMap<K, V> map) { long firstHash = -1; for (long slotIndex = 0, slot; slotIndex < HASH_TABLE_SIZE; slotIndex++) { if ((slot = readSlot(slotIndex)) != 0) { long kHash = map.keyHashCode(readKey(allocIndex(slot))); kHash &= GUARANTEED_JAVA_ARRAY_POWER_OF_TWO_CAPACITY - 1; if (firstHash < 0) firstHash = kHash; if (kHash != firstHash) return; } } // we checked all keys and all hashes collide throw new IllegalStateException( map.allocCapacity + " inserted keys has " + Integer.numberOfTrailingZeros(GUARANTEED_JAVA_ARRAY_POWER_OF_TWO_CAPACITY) + " lowest bits of hash code\n" + "colliding, try to override SmoothieMap.keyHashCode() and implement a hash\n" + "function with better distribution"); }