performance/memory improvements

This commit is contained in:
Alexey Kudravtsev
2012-07-11 13:21:28 +04:00
parent 8fa0499ffa
commit c5588decda
12 changed files with 945 additions and 28 deletions
@@ -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 {