mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
performance/memory improvements
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.openapi.util;
|
||||
|
||||
import com.intellij.util.containers.ConcurrentWeakValueHashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -33,9 +34,11 @@ public class Key<T> {
|
||||
private static final AtomicInteger ourKeysCounter = new AtomicInteger();
|
||||
private final int myIndex = ourKeysCounter.getAndIncrement();
|
||||
private final String myName; // for debug purposes only
|
||||
private static final ConcurrentWeakValueHashMap<Integer, Key> allKeys = new ConcurrentWeakValueHashMap<Integer, Key>();
|
||||
|
||||
public Key(@NotNull @NonNls String name) {
|
||||
myName = name;
|
||||
allKeys.put(myIndex, this);
|
||||
}
|
||||
|
||||
public final int hashCode() {
|
||||
@@ -62,6 +65,7 @@ public class Key<T> {
|
||||
|
||||
@Nullable
|
||||
public T get(@Nullable Map<Key, ?> holder) {
|
||||
//noinspection unchecked
|
||||
return holder == null ? null : (T)holder.get(this);
|
||||
}
|
||||
|
||||
@@ -93,4 +97,9 @@ public class Key<T> {
|
||||
holder.put(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Key<T> getKeyByIndex(int index) {
|
||||
//noinspection unchecked
|
||||
return (Key<T>)allKeys.get(index);
|
||||
}
|
||||
}
|
||||
@@ -16,39 +16,37 @@
|
||||
package com.intellij.openapi.util;
|
||||
|
||||
|
||||
import com.intellij.util.SmartFMap;
|
||||
import com.intellij.util.concurrency.AtomicFieldUpdater;
|
||||
import com.intellij.util.keyFMap.KeyFMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class UserDataHolderBase implements UserDataHolderEx, Cloneable {
|
||||
private static final Key<SmartFMap<Key, Object>> COPYABLE_USER_MAP_KEY = Key.create("COPYABLE_USER_MAP_KEY");
|
||||
public static final Key<KeyFMap> COPYABLE_USER_MAP_KEY = Key.create("COPYABLE_USER_MAP_KEY");
|
||||
|
||||
/**
|
||||
* Concurrent writes to this field are via CASes only, using the {@link #updater}
|
||||
*/
|
||||
@NotNull private volatile SmartFMap<Key, Object> myUserMap = SmartFMap.emptyMap();
|
||||
@NotNull private volatile KeyFMap myUserMap = KeyFMap.EMPTY_MAP;
|
||||
|
||||
@Override
|
||||
protected Object clone() {
|
||||
try {
|
||||
UserDataHolderBase clone = (UserDataHolderBase)super.clone();
|
||||
clone.myUserMap = SmartFMap.emptyMap();
|
||||
clone.myUserMap = KeyFMap.EMPTY_MAP;
|
||||
copyCopyableDataTo(clone);
|
||||
return clone;
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public String getUserDataString() {
|
||||
final SmartFMap<Key, Object> userMap = myUserMap;
|
||||
final Map copyableMap = getUserData(COPYABLE_USER_MAP_KEY);
|
||||
final KeyFMap userMap = myUserMap;
|
||||
final KeyFMap copyableMap = getUserData(COPYABLE_USER_MAP_KEY);
|
||||
return userMap.toString() + (copyableMap == null ? "" : copyableMap.toString());
|
||||
}
|
||||
|
||||
@@ -56,64 +54,70 @@ public class UserDataHolderBase implements UserDataHolderEx, Cloneable {
|
||||
other.myUserMap = myUserMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getUserData(@NotNull Key<T> key) {
|
||||
//noinspection unchecked
|
||||
return (T)myUserMap.get(key);
|
||||
return myUserMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
while (true) {
|
||||
SmartFMap<Key, Object> map = myUserMap;
|
||||
SmartFMap<Key, Object> newMap = value == null ? map.minus(key) : map.plus(key, value);
|
||||
KeyFMap map = myUserMap;
|
||||
KeyFMap newMap = value == null ? map.minus(key) : map.plus(key, value);
|
||||
if (newMap == map || updater.compareAndSet(this, map, newMap)) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
T data = getUserData(key);
|
||||
assert Comparing.equal(data, value) : key + " -> " + value + "; actual: " + data + "; " + myUserMap + ": " + myUserMap.getClass();
|
||||
}
|
||||
|
||||
public <T> T getCopyableUserData(Key<T> key) {
|
||||
SmartFMap<Key, Object> map = getUserData(COPYABLE_USER_MAP_KEY);
|
||||
KeyFMap map = getUserData(COPYABLE_USER_MAP_KEY);
|
||||
//noinspection unchecked,ConstantConditions
|
||||
return map == null ? null : (T)map.get(key);
|
||||
return map == null ? null : map.get(key);
|
||||
}
|
||||
|
||||
public <T> void putCopyableUserData(Key<T> key, T value) {
|
||||
while (true) {
|
||||
SmartFMap<Key, Object> map = myUserMap;
|
||||
@SuppressWarnings("unchecked") SmartFMap<Key, Object> copyableMap = (SmartFMap<Key, Object>)map.get(COPYABLE_USER_MAP_KEY);
|
||||
KeyFMap map = myUserMap;
|
||||
KeyFMap copyableMap = map.get(COPYABLE_USER_MAP_KEY);
|
||||
if (copyableMap == null) {
|
||||
copyableMap = SmartFMap.emptyMap();
|
||||
copyableMap = KeyFMap.EMPTY_MAP;
|
||||
}
|
||||
SmartFMap<Key, Object> newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value);
|
||||
SmartFMap<Key, Object> newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap);
|
||||
KeyFMap newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value);
|
||||
KeyFMap newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap);
|
||||
if (newMap == map || updater.compareAndSet(this, map, newMap)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean replace(@NotNull Key<T> key, @Nullable T oldValue, @Nullable T newValue) {
|
||||
while (true) {
|
||||
SmartFMap<Key, Object> map = myUserMap;
|
||||
KeyFMap map = myUserMap;
|
||||
if (map.get(key) != oldValue) {
|
||||
return false;
|
||||
}
|
||||
SmartFMap<Key, Object> newMap = newValue == null ? map.minus(key) : map.plus(key, newValue);
|
||||
KeyFMap newMap = newValue == null ? map.minus(key) : map.plus(key, newValue);
|
||||
if (newMap == map || updater.compareAndSet(this, map, newMap)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public <T> T putUserDataIfAbsent(@NotNull final Key<T> key, @NotNull final T value) {
|
||||
while (true) {
|
||||
SmartFMap<Key, Object> map = myUserMap;
|
||||
@SuppressWarnings("unchecked") T oldValue = (T)map.get(key);
|
||||
KeyFMap map = myUserMap;
|
||||
T oldValue = map.get(key);
|
||||
if (oldValue != null) {
|
||||
return oldValue;
|
||||
}
|
||||
SmartFMap<Key, Object> newMap = map.plus(key, value);
|
||||
KeyFMap newMap = map.plus(key, value);
|
||||
if (newMap == map || updater.compareAndSet(this, map, newMap)) {
|
||||
return value;
|
||||
}
|
||||
@@ -125,8 +129,12 @@ public class UserDataHolderBase implements UserDataHolderEx, Cloneable {
|
||||
}
|
||||
|
||||
protected void clearUserData() {
|
||||
myUserMap = SmartFMap.emptyMap();
|
||||
myUserMap = KeyFMap.EMPTY_MAP;
|
||||
}
|
||||
|
||||
private static final AtomicFieldUpdater<UserDataHolderBase, SmartFMap> updater = AtomicFieldUpdater.forFieldOfType(UserDataHolderBase.class, SmartFMap.class);
|
||||
public boolean isUserDataEmpty() {
|
||||
return myUserMap.isEmpty();
|
||||
}
|
||||
|
||||
private static final AtomicFieldUpdater<UserDataHolderBase, KeyFMap> updater = AtomicFieldUpdater.forFieldOfType(UserDataHolderBase.class, KeyFMap.class);
|
||||
}
|
||||
|
||||
@@ -375,6 +375,17 @@ public class ArrayUtil extends ArrayUtilRt {
|
||||
System.arraycopy(src, idx + 1, result, idx, length - idx - 1);
|
||||
return result;
|
||||
}
|
||||
@NotNull
|
||||
public static short[] remove(@NotNull final short[] src, int idx) {
|
||||
int length = src.length;
|
||||
if (idx < 0 || idx >= length) {
|
||||
throw new IllegalArgumentException("invalid index: " + idx);
|
||||
}
|
||||
short[] result = new short[src.length - 1];
|
||||
System.arraycopy(src, 0, result, 0, idx);
|
||||
System.arraycopy(src, idx + 1, result, idx, length - idx - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param src source array.
|
||||
@@ -650,6 +661,13 @@ public class ArrayUtil extends ArrayUtilRt {
|
||||
|
||||
return -1;
|
||||
}
|
||||
public static int indexOf(@NotNull short[] ints, short value) {
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
if (ints[i] == value) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static boolean contains(final Object o, final Object... objects) {
|
||||
return indexOf(objects, o) >= 0;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.containers;
|
||||
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class WeakValueIntObjectHashMap<V> {
|
||||
private final TIntObjectHashMap<MyReference<V>> myMap = new TIntObjectHashMap<MyReference<V>>();
|
||||
private final ReferenceQueue<V> myQueue = new ReferenceQueue<V>();
|
||||
|
||||
private static class MyReference<T> extends WeakReference<T> {
|
||||
private final int key;
|
||||
String name;
|
||||
|
||||
private MyReference(int key, T referent, ReferenceQueue<? super T> q) {
|
||||
super(referent, q);
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
private void processQueue() {
|
||||
while(true){
|
||||
MyReference ref = (MyReference)myQueue.poll();
|
||||
if (ref == null) {
|
||||
return;
|
||||
}
|
||||
int key = ref.key;
|
||||
myMap.remove(key);
|
||||
keyExpired(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected void keyExpired(int key) {
|
||||
|
||||
}
|
||||
|
||||
public final V get(int key) {
|
||||
MyReference<V> ref = myMap.get(key);
|
||||
if (ref == null) return null;
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
public final V put(int key, @NotNull V value) {
|
||||
processQueue();
|
||||
MyReference<V> ref = new MyReference<V>(key, value, myQueue);
|
||||
ref.name = value.toString();
|
||||
MyReference<V> oldRef = myMap.put(key, ref);
|
||||
return oldRef != null ? oldRef.get() : null;
|
||||
}
|
||||
|
||||
public final V remove(int key) {
|
||||
processQueue();
|
||||
MyReference<V> ref = myMap.remove(key);
|
||||
return ref != null ? ref.get() : null;
|
||||
}
|
||||
|
||||
public final void clear() {
|
||||
myMap.clear();
|
||||
processQueue();
|
||||
}
|
||||
|
||||
public final int size() {
|
||||
return myMap.size();
|
||||
}
|
||||
|
||||
public final boolean isEmpty() {
|
||||
return myMap.isEmpty();
|
||||
}
|
||||
|
||||
public final boolean containsKey(int key) {
|
||||
return get(key) != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public final Collection<V> values() {
|
||||
List<V> result = new ArrayList<V>();
|
||||
Object[] refs = myMap.getValues();
|
||||
for (Object o : refs) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final V value = ((MyReference<V>)o).get();
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.keyFMap;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class ArrayBackedFMap implements KeyFMap {
|
||||
static final int ARRAY_THRESHOLD = 8;
|
||||
private final int[] keys;
|
||||
private final Object[] values;
|
||||
|
||||
ArrayBackedFMap(@NotNull int[] keys, @NotNull Object[] values) {
|
||||
this.keys = keys;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value) {
|
||||
int oldSize = size();
|
||||
int keyCode = key.hashCode();
|
||||
int[] newKeys = null;
|
||||
Object[] newValues = null;
|
||||
int i;
|
||||
for (i = 0; i < oldSize; i++) {
|
||||
int oldKey = keys[i];
|
||||
if (keyCode == oldKey) {
|
||||
if (value == values[i]) return this;
|
||||
newKeys = new int[oldSize];
|
||||
newValues = new Object[oldSize];
|
||||
System.arraycopy(keys, 0, newKeys, 0, oldSize);
|
||||
System.arraycopy(values, 0, newValues, 0, oldSize);
|
||||
newValues[i] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == oldSize) {
|
||||
if (oldSize == ARRAY_THRESHOLD) {
|
||||
return new MapBackedFMap(keys, keyCode, values, value);
|
||||
}
|
||||
int newSize = oldSize + 1;
|
||||
newKeys = new int[newSize];
|
||||
newValues = new Object[newSize];
|
||||
System.arraycopy(keys, 0, newKeys, 0, oldSize);
|
||||
System.arraycopy(values, 0, newValues, 0, oldSize);
|
||||
newKeys[oldSize] = keyCode;
|
||||
newValues[oldSize] = value;
|
||||
}
|
||||
return new ArrayBackedFMap(newKeys, newValues);
|
||||
}
|
||||
|
||||
private int size() {
|
||||
return keys.length;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyFMap minus(@NotNull Key<?> key) {
|
||||
int oldSize = size();
|
||||
int keyCode = key.hashCode();
|
||||
for (int i = 0; i< oldSize; i++) {
|
||||
int oldKey = keys[i];
|
||||
if (keyCode == oldKey) {
|
||||
if (oldSize == 3) {
|
||||
int i1 = (2-i)/2;
|
||||
int i2 = 3 - (i+2)/2;
|
||||
return new PairElementsFMap(keys[i1], values[i1], keys[i2], values[i2]);
|
||||
}
|
||||
int newSize = oldSize - 1;
|
||||
int[] newKeys = new int[newSize];
|
||||
Object[] newValues = new Object[newSize];
|
||||
System.arraycopy(keys, 0, newKeys, 0, i);
|
||||
System.arraycopy(values, 0, newValues, 0, i);
|
||||
System.arraycopy(keys, i+1, newKeys, i, oldSize-i-1);
|
||||
System.arraycopy(values, i+1, newValues, i, oldSize-i-1);
|
||||
return new ArrayBackedFMap(newKeys, newValues);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
//if (i == oldSize) {
|
||||
//newKeys = new int[oldSize];
|
||||
//newValues = new Object[oldSize];
|
||||
//System.arraycopy(keys, 0, newKeys, 0, oldSize);
|
||||
//System.arraycopy(values, 0, newValues, 0, oldSize);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(@NotNull Key<V> key) {
|
||||
int oldSize = size();
|
||||
int keyCode = key.hashCode();
|
||||
for (int i = 0; i < oldSize; i++) {
|
||||
int oldKey = keys[i];
|
||||
if (keyCode == oldKey) {
|
||||
//noinspection unchecked
|
||||
return (V)values[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = "";
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
int key = keys[i];
|
||||
Object value = values[i];
|
||||
s += (s.isEmpty() ? "" : ", ") + Key.getKeyByIndex(key) + " -> " + value;
|
||||
}
|
||||
return "(" + s + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.keyFMap;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class EmptyFMap implements KeyFMap {
|
||||
EmptyFMap() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value) {
|
||||
return new OneElementFMap<V>(key.hashCode(), value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyFMap minus(@NotNull Key<?> key) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(@NotNull Key<V> key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<empty>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.keyFMap;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An immutable map optimized for storing few {@link Key} entries with relatively rare updates
|
||||
* To construct a map, start with {@link KeyFMap#EMPTY_MAP} and call {@link #plus} and {@link #minus}
|
||||
*
|
||||
* @author peter
|
||||
*/
|
||||
public interface KeyFMap {
|
||||
KeyFMap EMPTY_MAP = new EmptyFMap();
|
||||
|
||||
@NotNull
|
||||
<V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value);
|
||||
@NotNull
|
||||
KeyFMap minus(@NotNull Key<?> key);
|
||||
|
||||
@Nullable
|
||||
<V> V get(@NotNull Key<V> key);
|
||||
|
||||
String toString();
|
||||
|
||||
boolean isEmpty();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.keyFMap;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class MapBackedFMap extends TIntObjectHashMap<Object> implements KeyFMap {
|
||||
private MapBackedFMap(@NotNull MapBackedFMap oldMap, final int exclude) {
|
||||
super(oldMap.size());
|
||||
oldMap.forEachEntry(new TIntObjectProcedure<Object>() {
|
||||
@Override
|
||||
public boolean execute(int key, Object val) {
|
||||
if (key != exclude) put(key, val);
|
||||
assert key >= 0 : key;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
assert size() > ArrayBackedFMap.ARRAY_THRESHOLD;
|
||||
}
|
||||
|
||||
MapBackedFMap(@NotNull int[] keys, int newKey, @NotNull Object[] values, @NotNull Object newValue) {
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
int key = keys[i];
|
||||
Object value = values[i];
|
||||
put(key, value);
|
||||
assert key >= 0 : key;
|
||||
}
|
||||
put(newKey, newValue);
|
||||
assert newKey >= 0 : newKey;
|
||||
assert size() > ArrayBackedFMap.ARRAY_THRESHOLD;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value) {
|
||||
int keyCode = key.hashCode();
|
||||
assert keyCode >= 0 : key;
|
||||
@SuppressWarnings("unchecked")
|
||||
V oldValue = (V)get(keyCode);
|
||||
if (value == oldValue) return this;
|
||||
MapBackedFMap newMap = new MapBackedFMap(this, -1);
|
||||
newMap.put(keyCode, value);
|
||||
return newMap;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyFMap minus(@NotNull Key<?> key) {
|
||||
int oldSize = size();
|
||||
int keyCode = key.hashCode();
|
||||
if (!containsKey(keyCode)) {
|
||||
return this;
|
||||
}
|
||||
if (oldSize == ArrayBackedFMap.ARRAY_THRESHOLD + 1) {
|
||||
int[] keys = keys();
|
||||
Object[] values = getValues();
|
||||
int i = ArrayUtil.indexOf(keys, keyCode);
|
||||
keys = ArrayUtil.remove(keys, i);
|
||||
values = ArrayUtil.remove(values, i);
|
||||
return new ArrayBackedFMap(keys, values);
|
||||
}
|
||||
return new MapBackedFMap(this, keyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(@NotNull Key<V> key) {
|
||||
//noinspection unchecked
|
||||
return (V)get(key.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder s = new StringBuilder();
|
||||
forEachEntry(new TIntObjectProcedure<Object>() {
|
||||
@Override
|
||||
public boolean execute(int key, Object value) {
|
||||
s.append(s.length() == 0 ? "" : ", ").append(Key.getKeyByIndex(key)).append(" -> ").append(value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return "[" + s.toString() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.keyFMap;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class OneElementFMap<V> implements KeyFMap {
|
||||
private final int myKeyCode;
|
||||
private final V myValue;
|
||||
|
||||
OneElementFMap(int keyCode, @NotNull V value) {
|
||||
myKeyCode = keyCode;
|
||||
myValue = value;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value) {
|
||||
int keyCode = key.hashCode();
|
||||
if (myKeyCode == keyCode) return new OneElementFMap<V>(keyCode, value);
|
||||
return new PairElementsFMap(myKeyCode, myValue, keyCode, value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyFMap minus(@NotNull Key<?> key) {
|
||||
if (key.hashCode() == myKeyCode) {
|
||||
return KeyFMap.EMPTY_MAP;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(@NotNull Key<V> key) {
|
||||
//noinspection unchecked
|
||||
return myKeyCode == key.hashCode() ? (V)myValue : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<"+Key.getKeyByIndex(myKeyCode) + " -> " + myValue+">";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.keyFMap;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class PairElementsFMap implements KeyFMap {
|
||||
private final int key1;
|
||||
private final int key2;
|
||||
private final Object value1;
|
||||
private final Object value2;
|
||||
|
||||
PairElementsFMap(int key1, @NotNull Object value1, int key2, @NotNull Object value2) {
|
||||
this.key1 = key1;
|
||||
this.value1 = value1;
|
||||
this.key2 = key2;
|
||||
this.value2 = value2;
|
||||
assert key1 != key2;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value) {
|
||||
int keyCode = key.hashCode();
|
||||
if (keyCode == key1) return new PairElementsFMap(keyCode, value, key2, value2);
|
||||
if (keyCode == key2) return new PairElementsFMap(keyCode, value, key1, value1);
|
||||
return new ArrayBackedFMap(new int[]{key1, key2, keyCode}, new Object[]{value1, value2, value});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyFMap minus(@NotNull Key<?> key) {
|
||||
int keyCode = key.hashCode();
|
||||
if (keyCode == key1) return new OneElementFMap<Object>(key2, value2);
|
||||
if (keyCode == key2) return new OneElementFMap<Object>(key1, value1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(@NotNull Key<V> key) {
|
||||
int keyCode = key.hashCode();
|
||||
//noinspection unchecked
|
||||
return keyCode == key1 ? (V)value1 : keyCode == key2 ? (V)value2 : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pair: ("+ Key.getKeyByIndex(key1) + " -> " + value1+"; "+Key.getKeyByIndex(key2) + " -> " + value2 + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.util.keyFMap;
|
||||
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
import gnu.trove.TPrimitiveHash;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ShortObjectHashMap<V> extends TPrimitiveHash {
|
||||
private V[] _values;
|
||||
private short[] _set;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new <code>TIntObjectHashMap</code> instance with the default
|
||||
* capacity and load factor.
|
||||
*/
|
||||
public ShortObjectHashMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>TIntObjectHashMap</code> instance with a prime
|
||||
* capacity equal to or greater than <tt>initialCapacity</tt> and
|
||||
* with the default load factor.
|
||||
*
|
||||
* @param initialCapacity an <code>int</code> value
|
||||
*/
|
||||
public ShortObjectHashMap(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes the hashtable to a prime capacity which is at least
|
||||
* <tt>initialCapacity + 1</tt>.
|
||||
*
|
||||
* @param initialCapacity an <code>int</code> value
|
||||
* @return the actual capacity chosen
|
||||
*/
|
||||
@Override
|
||||
protected int setUp(int initialCapacity) {
|
||||
|
||||
int capacity = super.setUp(initialCapacity);
|
||||
//noinspection unchecked
|
||||
_values = (V[])new Object[capacity];
|
||||
_set = new short[capacity];
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a key/value pair into the map.
|
||||
*
|
||||
* @param key an <code>int</code> value
|
||||
* @param value an <code>Object</code> value
|
||||
* @return the previous value associated with <tt>key</tt>,
|
||||
* or null if none was found.
|
||||
*/
|
||||
public V put(short key, @NotNull V value) {
|
||||
V previous = null;
|
||||
int index = insertionIndex(key);
|
||||
boolean isNewMapping = true;
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
previous = _values[index];
|
||||
isNewMapping = false;
|
||||
}
|
||||
byte previousState = _states[index];
|
||||
_set[index] = key;
|
||||
_states[index] = FULL;
|
||||
_values[index] = value;
|
||||
if (isNewMapping) {
|
||||
postInsertHook(previousState == FREE);
|
||||
}
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* rehashes the map to the new capacity.
|
||||
*
|
||||
* @param newCapacity an <code>int</code> value
|
||||
*/
|
||||
@Override
|
||||
protected void rehash(int newCapacity) {
|
||||
int oldCapacity = _set.length;
|
||||
short[] oldKeys = _set;
|
||||
V[] oldVals = _values;
|
||||
byte[] oldStates = _states;
|
||||
|
||||
_set = new short[newCapacity];
|
||||
//noinspection unchecked
|
||||
_values = (V[])new Object[newCapacity];
|
||||
_states = new byte[newCapacity];
|
||||
|
||||
for (int i = oldCapacity; i-- > 0; ) {
|
||||
if (oldStates[i] == FULL) {
|
||||
short o = oldKeys[i];
|
||||
int index = insertionIndex(o);
|
||||
_set[index] = o;
|
||||
_values[index] = oldVals[i];
|
||||
_states[index] = FULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieves the value for <tt>key</tt>
|
||||
*
|
||||
* @param key an <code>int</code> value
|
||||
* @return the value of <tt>key</tt> or null if no such mapping exists.
|
||||
*/
|
||||
public V get(short key) {
|
||||
int index = index(key);
|
||||
return index < 0 ? null : _values[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the map.
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
|
||||
Arrays.fill(_set, (short)0);
|
||||
Arrays.fill(_values, null);
|
||||
Arrays.fill(_states, FREE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a key/value pair from the map.
|
||||
*
|
||||
* @param key an <code>int</code> value
|
||||
* @return an <code>Object</code> value
|
||||
*/
|
||||
public V remove(short key) {
|
||||
V prev = null;
|
||||
int index = index(key);
|
||||
if (index >= 0) {
|
||||
prev = _values[index];
|
||||
removeAt(index); // clear key,state; adjust size
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* removes the mapping at <tt>index</tt> from the map.
|
||||
*
|
||||
* @param index an <code>int</code> value
|
||||
*/
|
||||
@Override
|
||||
protected void removeAt(int index) {
|
||||
_values[index] = null;
|
||||
_set[index] = 0;
|
||||
super.removeAt(index); // clear key, state; adjust size
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the map.
|
||||
*
|
||||
* @return a <code>Collection</code> value
|
||||
*/
|
||||
public Object[] getValues() {
|
||||
Object[] vals = new Object[size()];
|
||||
V[] v = _values;
|
||||
byte[] states = _states;
|
||||
|
||||
for (int i = v.length, j = 0; i-- > 0; ) {
|
||||
if (states[i] == FULL) {
|
||||
vals[j++] = v[i];
|
||||
}
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the keys of the map.
|
||||
*
|
||||
* @return a <code>Set</code> value
|
||||
*/
|
||||
public short[] keys() {
|
||||
short[] keys = new short[size()];
|
||||
short[] k = _set;
|
||||
byte[] states = _states;
|
||||
|
||||
for (int i = k.length, j = 0; i-- > 0; ) {
|
||||
if (states[i] == FULL) {
|
||||
keys[j++] = k[i];
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checks for the present of <tt>key</tt> in the keys of the map.
|
||||
*
|
||||
* @param key an <code>int</code> value
|
||||
* @return a <code>boolean</code> value
|
||||
*/
|
||||
public boolean containsKey(int key) {
|
||||
return index(key) >= 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locates the index of <tt>val</tt>.
|
||||
*
|
||||
* @param val an <code>int</code> value
|
||||
* @return the index of <tt>val</tt> or -1 if it isn't in the set.
|
||||
*/
|
||||
protected int index(int val) {
|
||||
byte[] states = _states;
|
||||
short[] set = _set;
|
||||
int length = states.length;
|
||||
int hash = val & 0x7fffffff;
|
||||
int index = hash % length;
|
||||
|
||||
if (states[index] != FREE &&
|
||||
(states[index] == REMOVED || set[index] != val)) {
|
||||
// see Knuth, p. 529
|
||||
int probe = 1 + hash % (length - 2);
|
||||
|
||||
do {
|
||||
index -= probe;
|
||||
if (index < 0) {
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
while (states[index] != FREE &&
|
||||
(states[index] == REMOVED || set[index] != val));
|
||||
}
|
||||
|
||||
return states[index] == FREE ? -1 : index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the index at which <tt>val</tt> can be inserted. if
|
||||
* there is already a value equal()ing <tt>val</tt> in the set,
|
||||
* returns that value as a negative integer.
|
||||
*
|
||||
* @param val an <code>int</code> value
|
||||
* @return an <code>int</code> value
|
||||
*/
|
||||
protected int insertionIndex(int val) {
|
||||
|
||||
byte[] states = _states;
|
||||
short[] set = _set;
|
||||
int length = states.length;
|
||||
int hash = val & 0x7fffffff;
|
||||
int index = hash % length;
|
||||
|
||||
if (states[index] == FREE) {
|
||||
return index; // empty, all done
|
||||
}
|
||||
else if (states[index] == FULL && set[index] == val) {
|
||||
return -index - 1; // already stored
|
||||
}
|
||||
else { // already FULL or REMOVED, must probe
|
||||
// compute the double hash
|
||||
int probe = 1 + hash % (length - 2);
|
||||
// starting at the natural offset, probe until we find an
|
||||
// offset that isn't full.
|
||||
do {
|
||||
index -= probe;
|
||||
if (index < 0) {
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
while (states[index] == FULL && set[index] != val);
|
||||
|
||||
// if the index we found was removed: continue probing until we
|
||||
// locate a free location or an element which equal()s the
|
||||
// one we have.
|
||||
if (states[index] == REMOVED) {
|
||||
int firstRemoved = index;
|
||||
while (states[index] != FREE &&
|
||||
(states[index] == REMOVED || set[index] != val)) {
|
||||
index -= probe;
|
||||
if (index < 0) {
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
return states[index] == FULL ? -index - 1 : firstRemoved;
|
||||
}
|
||||
// if it's full, the key is already stored
|
||||
return states[index] == FULL ? -index - 1 : index;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean forEachEntry(TIntObjectProcedure<V> procedure) {
|
||||
byte[] states = _states;
|
||||
short[] keys = _set;
|
||||
V[] values = _values;
|
||||
for (int i = keys.length; i-- > 0; ) {
|
||||
if (states[i] == FULL && !procedure.execute(keys[i], values[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // TIntObjectHashMap
|
||||
@@ -30,6 +30,7 @@ import com.intellij.uiDesigner.lw.CompiledClassPropertiesProvider;
|
||||
import com.intellij.uiDesigner.lw.LwRootContainer;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.asm4.ClassWriter;
|
||||
|
||||
@@ -65,6 +66,7 @@ public class AsmCodeGeneratorTest extends TestCase {
|
||||
|
||||
java.util.List<URL> cp = new ArrayList<URL>();
|
||||
appendPath(cp, JBTabbedPane.class);
|
||||
appendPath(cp, TIntObjectHashMap.class);
|
||||
appendPath(cp, UIUtil.class);
|
||||
appendPath(cp, SystemInfoRt.class);
|
||||
appendPath(cp, ApplicationManager.class);
|
||||
@@ -319,7 +321,7 @@ public class AsmCodeGeneratorTest extends TestCase {
|
||||
assertTrue(panel.getBorder() instanceof TitledBorder);
|
||||
TitledBorder border = (TitledBorder) panel.getBorder();
|
||||
assertEquals("BorderTitle", border.getTitle());
|
||||
assertTrue(border.getBorder() instanceof EtchedBorder);
|
||||
assertTrue(border.getBorder().toString(), border.getBorder() instanceof EtchedBorder);
|
||||
}
|
||||
|
||||
public void testMnemonic() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user