intern recent vfs user data with more than one key-value mapping

This commit is contained in:
peter
2015-03-24 12:45:29 +01:00
parent b2092a09f6
commit fd893067f8
3 changed files with 123 additions and 13 deletions
@@ -15,30 +15,116 @@
*/
package com.intellij.openapi.vfs.newvfs.impl;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.reference.SoftReference;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.keyFMap.ArrayBackedFMap;
import com.intellij.util.keyFMap.KeyFMap;
import com.intellij.util.keyFMap.OneElementFMap;
import com.intellij.util.keyFMap.PairElementsFMap;
import org.jetbrains.annotations.NotNull;
import java.nio.charset.Charset;
import java.util.concurrent.ConcurrentMap;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Map;
/**
* @author peter
*/
class UserDataInterner {
private static final ConcurrentMap<OneElementFMap, OneElementFMap> ourCache = ContainerUtil.createConcurrentWeakMap();
private static final LinkedHashMap<MapReference, MapReference> ourCache = new LinkedHashMap<MapReference, MapReference>(20, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<MapReference, MapReference> eldest) {
return size() > 15;
}
};
static KeyFMap internUserData(@NotNull KeyFMap map) {
if (map instanceof OneElementFMap && shouldIntern((OneElementFMap)map)) {
return ConcurrencyUtil.cacheOrGet(ourCache, (OneElementFMap)map, (OneElementFMap)map);
if (shouldIntern(map)) {
MapReference key = new MapReference(map);
synchronized (ourCache) {
KeyFMap cached = SoftReference.dereference(ourCache.get(key));
if (cached != null) return cached;
ourCache.put(key, key);
}
return map;
}
return map;
}
private static boolean shouldIntern(OneElementFMap map) {
Object value = map.getValue();
return value instanceof Enum || value instanceof Boolean || value instanceof Charset;
private static boolean shouldIntern(@NotNull KeyFMap map) {
return map instanceof OneElementFMap ||
map instanceof PairElementsFMap ||
map instanceof ArrayBackedFMap && ((ArrayBackedFMap)map).getKeys().length <= 5;
}
}
class MapReference extends WeakReference<KeyFMap> {
final int myHash;
MapReference(KeyFMap referent) {
super(referent);
myHash = computeHashCode(referent);
}
private static int computeHashCode(KeyFMap object) {
if (object instanceof OneElementFMap) {
return ((OneElementFMap)object).getKey().hashCode() * 31 + System.identityHashCode(((OneElementFMap)object).getValue());
}
if (object instanceof PairElementsFMap) {
PairElementsFMap map = (PairElementsFMap)object;
return (map.getKey1().hashCode() * 31 + map.getKey2().hashCode()) * 31 +
System.identityHashCode(map.getValue1()) + System.identityHashCode(map.getValue2());
}
if (object instanceof ArrayBackedFMap) {
int hc = Arrays.hashCode(((ArrayBackedFMap)object).getKeys());
for (Object o : ((ArrayBackedFMap)object).getValues()) {
hc = hc * 31 + System.identityHashCode(o);
}
return hc;
}
return 0;
}
@Override
public int hashCode() {
return myHash;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof MapReference) || myHash != ((MapReference)obj).myHash) return false;
KeyFMap o1 = get();
KeyFMap o2 = ((MapReference)obj).get();
if (o1 == null || o2 == null) return false;
if (o1 instanceof OneElementFMap && o2 instanceof OneElementFMap) {
OneElementFMap m1 = (OneElementFMap)o1;
OneElementFMap m2 = (OneElementFMap)o2;
return m1.getKey() == m2.getKey() && m1.getValue() == m2.getValue();
}
if (o1 instanceof PairElementsFMap && o2 instanceof PairElementsFMap) {
PairElementsFMap m1 = (PairElementsFMap)o1;
PairElementsFMap m2 = (PairElementsFMap)o2;
return m1.getKey1() == m2.getKey1() && m1.getKey2() == m2.getKey2() &&
m1.getValue1() == m2.getValue1() && m1.getValue2() == m2.getValue2();
}
if (o1 instanceof ArrayBackedFMap && o2 instanceof ArrayBackedFMap) {
ArrayBackedFMap m1 = (ArrayBackedFMap)o1;
ArrayBackedFMap m2 = (ArrayBackedFMap)o2;
return Arrays.equals(m1.getKeys(), m2.getKeys()) && containSameElements(m1.getValues(), m2.getValues());
}
return false;
}
private static boolean containSameElements(Object[] v1, Object[] v2) {
if (v1.length != v2.length) return false;
for (int i = 0; i < v1.length; i++) {
if (v1[i] != v2[i]) return false;
}
return true;
}
}
@@ -18,7 +18,7 @@ package com.intellij.util.keyFMap;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
class ArrayBackedFMap implements KeyFMap {
public class ArrayBackedFMap implements KeyFMap {
static final int ARRAY_THRESHOLD = 8;
private final int[] keys;
private final Object[] values;
@@ -134,4 +134,12 @@ class ArrayBackedFMap implements KeyFMap {
public boolean isEmpty() {
return false;
}
public int[] getKeys() {
return keys;
}
public Object[] getValues() {
return values;
}
}
@@ -18,7 +18,7 @@ package com.intellij.util.keyFMap;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
class PairElementsFMap implements KeyFMap {
public class PairElementsFMap implements KeyFMap {
private final Key key1;
private final Key key2;
private final Object value1;
@@ -63,4 +63,20 @@ class PairElementsFMap implements KeyFMap {
public boolean isEmpty() {
return false;
}
public Key getKey1() {
return key1;
}
public Key getKey2() {
return key2;
}
public Object getValue1() {
return value1;
}
public Object getValue2() {
return value2;
}
}