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 V merge( SmoothieMap<K, V> map, long hash, K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { long slotIndex, slot, allocIndex, storedHash = storedHash(hash); K k; V oldValue; for (slotIndex = slotIndex(hash); (slot = readSlot(slotIndex)) != 0; slotIndex = nextSlotIndex(slotIndex)) { if (hash(slot) == storedHash && ((k = readKey((allocIndex = allocIndex(slot)))) == key || (key != null && map.keysEqual(key, k)))) { if ((oldValue = readValue(allocIndex)) == null || (value = remappingFunction.apply(oldValue, value)) != null) { writeValue(allocIndex, value); return value; } else { remove(map, slotIndex, allocIndex); return null; } } } insert(map, hash, key, value, slotIndex, storedHash); return value; }
final void clear(SmoothieMap<K, V> map) { U.setMemory(this, HASH_TABLE_OFFSET, HASH_TABLE_SIZE * HASH_TABLE_SLOT_SIZE, (byte) 0); map.size -= size(); for (long allocIndex = 1; allocIndex <= map.allocCapacity; allocIndex++) { writeKey(allocIndex, null); writeValue(allocIndex, null); } bitSet = CLEAR_BIT_SET; }
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"); }
/** * @param matchValue {@code true} if should compare the mapped value to the given {@code value} * before remove */ final long remove( SmoothieMap<K, V> map, long hash, Object key, Object value, boolean matchValue) { long slotIndex, slot, allocIndex, storedHash = storedHash(hash); K k; V v; for (slotIndex = slotIndex(hash); (slot = readSlot(slotIndex)) != 0; slotIndex = nextSlotIndex(slotIndex)) { if (hash(slot) == storedHash && ((k = readKey((allocIndex = allocIndex(slot)))) == key || (key != null && map.keysEqual(key, k)))) { if (!matchValue || (v = readValue(allocIndex)) == value || (value != null && map.valuesEqual(value, v))) { removeButAlloc(map, slotIndex); return allocIndex; } else { return 0; } } } return 0; }
/** @return allocation index, or 0 if not found */ final long find(SmoothieMap<K, V> map, long hash, Object key) { long slotIndex, slot, allocIndex, storedHash = storedHash(hash); K k; for (slotIndex = slotIndex(hash); (slot = readSlot(slotIndex)) != 0; slotIndex = nextSlotIndex(slotIndex)) { if (hash(slot) == storedHash && ((k = readKey((allocIndex = allocIndex(slot)))) == key || (key != null && map.keysEqual(key, k)))) { return allocIndex; } } return 0; }
final boolean containsValue(SmoothieMap<K, V> map, V value) { V v; for (long a, tail, allocations = (a = ~bitSet) << (tail = Long.numberOfLeadingZeros(a)), allocIndex = 64 - tail; allocations != 0; allocations <<= 1, allocIndex--) { if (allocations < 0) { if (((v = readValue(allocIndex)) == value || (value != null && map.valuesEqual(value, v)))) { return true; } } } return false; }
final V put(SmoothieMap<K, V> map, long hash, K key, V value, boolean onlyIfAbsent) { long slotIndex, slot, allocIndex, storedHash = storedHash(hash); K k; V oldValue; for (slotIndex = slotIndex(hash); (slot = readSlot(slotIndex)) != 0; slotIndex = nextSlotIndex(slotIndex)) { if (hash(slot) == storedHash && ((k = readKey((allocIndex = allocIndex(slot)))) == key || (key != null && map.keysEqual(key, k)))) { oldValue = readValue(allocIndex); if (!onlyIfAbsent || oldValue == null) writeValue(allocIndex, value); return oldValue; } } insert(map, hash, key, value, slotIndex, storedHash); return null; }
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"); }
final V computeIfAbsent( SmoothieMap<K, V> map, long hash, K key, Function<? super K, ? extends V> mappingFunction) { long slotIndex, slot, allocIndex, storedHash = storedHash(hash); K k; V value; for (slotIndex = slotIndex(hash); (slot = readSlot(slotIndex)) != 0; slotIndex = nextSlotIndex(slotIndex)) { if (hash(slot) == storedHash && ((k = readKey((allocIndex = allocIndex(slot)))) == key || (key != null && map.keysEqual(key, k)))) { if ((value = readValue(allocIndex)) != null) return value; if ((value = mappingFunction.apply(key)) != null) writeValue(allocIndex, value); return value; } } if ((value = mappingFunction.apply(key)) != null) insert(map, hash, key, value, slotIndex, storedHash); return value; }
final void putOnSplit(SmoothieMap<K, V> map, long hash, K key, V value) { long slotIndex, slot, allocIndex, storedHash = storedHash(hash); K k; for (slotIndex = slotIndex(hash); (slot = readSlot(slotIndex)) != 0; slotIndex = nextSlotIndex(slotIndex)) { if (hash(slot) == storedHash && ((k = readKey((allocIndex(slot)))) == key || (key != null && map.keysEqual(key, k)))) { throw new IllegalStateException( "When inserting entries into newly split segment it could not find\n" + "a duplicate key. It means either SmoothieMap is updated\n" + "concurrently OR keys are mutable and keyHashCode() and\n" + "keysEqual() calls return different results in time, applied to\n" + "the same arguments."); } } if ((allocIndex = alloc()) <= map.allocCapacity) { writeEntry(key, value, slotIndex, storedHash, allocIndex); } else { throw new ConcurrentModificationException( "When inserting entries into newly split segment it could be filled up " + "only concurrently"); } }