/* * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.openapi.vfs.impl; import gnu.trove.PrimeFinder; /** * IdentityHashMap that have the (immutable) keys stored in values (2 times more compact). */ final class AddonlyKeylessHash<K, V> { private int size; private Object[] entries; private final KeyValueMapper<K, V> keyValueMapper; public AddonlyKeylessHash(KeyValueMapper<K, V> _keyValueMapper) { this(4, _keyValueMapper); } public AddonlyKeylessHash(int expectedSize, KeyValueMapper<K, V> _keyValueMapper) { int i = PrimeFinder.nextPrime(5 * expectedSize / 4 + 1); entries = new Object[i]; keyValueMapper = _keyValueMapper; } public int size() { return size; } public void add(V item) { if (size >= (4 * entries.length) / 5) rehash(); V v = doPut(entries, item); if (v == null) size++; } private V doPut(Object[] a, V o) { K key = keyValueMapper.key(o); int index = hashIndex(a, key); V obj = (V)a[index]; a[index] = o; return obj; } private int hashIndex(Object[] a, K key) { int hash = keyValueMapper.hash(key) & 0x7fffffff; int index = hash % a.length; V candidate = (V)a[index]; if (candidate == null || keyValueMapper.valueHasKey(candidate, key)) { return index; } final int probe = 1 + (hash % (a.length - 2)); do { index -= probe; if (index < 0) index += a.length; candidate = (V)a[index]; } while (candidate != null && !keyValueMapper.valueHasKey(candidate, key)); return index; } private void rehash() { Object[] b = new Object[PrimeFinder.nextPrime(entries.length * 2)]; for (int i = entries.length; --i >= 0; ) { V ns = (V)entries[i]; if (ns != null) doPut(b, ns); } entries = b; } public V get(K key) { return (V)entries[hashIndex(entries, key)]; } public abstract static class KeyValueMapper<K, V> { public abstract int hash(K k); public abstract K key(V v); public boolean valueHasKey(V value, K key) { return key == key(value); // identity } protected boolean isIdentity() { return true; } } }