mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
intern recent vfs user data with more than one key-value mapping
This commit is contained in:
+97
-11
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user