diff --git a/platform/util/src/com/intellij/util/containers/ByteArrayList.java b/platform/util/src/com/intellij/util/containers/ByteArrayList.java new file mode 100644 index 000000000000..2f47c5de6fad --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/ByteArrayList.java @@ -0,0 +1,852 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2001, Eric D. Friedman All Rights Reserved. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/////////////////////////////////////////////////////////////////////////////// + +package com.intellij.util.containers; + +import gnu.trove.HashFunctions; +import gnu.trove.TIntProcedure; + +import java.util.Arrays; +import java.util.Random; + +/** + * A resizable, array-backed list of int primitives. + * + * Created: Sat Dec 29 14:21:12 2001 + * + * @author Eric D. Friedman + * @version $Id: TIntArrayList.java,v 1.5 2004/09/24 09:11:15 cdr Exp $ + */ + +public class ByteArrayList implements Cloneable { + + /** the data of the list */ + protected transient byte[] _data; + + /** the index after the last entry in the list */ + protected transient int _pos; + + /** the default capacity for new lists */ + protected static final int DEFAULT_CAPACITY = 4; + + /** + * Creates a new TIntArrayList instance with the + * default capacity. + */ + public ByteArrayList() { + this(DEFAULT_CAPACITY); + } + + /** + * Creates a new TIntArrayList instance with the + * specified capacity. + * + * @param capacity an int value + */ + public ByteArrayList(int capacity) { + _data = new byte[capacity]; + _pos = 0; + } + + /** + * Creates a new TIntArrayList instance whose + * capacity is the greater of the length of values and + * DEFAULT_CAPACITY and whose initial contents are the specified + * values. + * + * @param values an int[] value + */ + public ByteArrayList(byte[] values) { + this(Math.max(values.length, DEFAULT_CAPACITY)); + add(values); + } + + // sizing + + /** + * Grow the internal array as needed to accomodate the specified + * number of elements. The size of the array doubles on each + * resize unless capacity requires more than twice the + * current capacity. + * + * @param capacity an int value + */ + public void ensureCapacity(int capacity) { + byte[] data = _data; + if (capacity > data.length) { + int newCap = Math.max(data.length < 100000 ? data.length << 1 : data.length * 4 / 3, capacity); + byte[] tmp = new byte[newCap]; + System.arraycopy(data, 0, tmp, 0, data.length); + _data = tmp; + } + } + + /** + * Returns the number of values in the list. + * + * @return the number of values in the list. + */ + public int size() { + return _pos; + } + + /** + * Tests whether this list contains any values. + * + * @return true if the list is empty. + */ + public boolean isEmpty() { + return _pos == 0; + } + + /** + * Sheds any excess capacity above and beyond the current size of + * the list. + */ + public void trimToSize() { + if (_data.length > size()) { + byte[] tmp = new byte[size()]; + toNativeArray(tmp, 0, tmp.length); + _data = tmp; + } + } + + // modifying + + /** + * Adds val to the end of the list, growing as needed. + * + * @param val an int value + */ + public void add(byte val) { + ensureCapacity(_pos + 1); + _data[_pos++] = val; + } + + /** + * Adds the values in the array vals to the end of the + * list, in order. + * + * @param vals an int[] value + */ + public void add(byte[] vals) { + add(vals, 0, vals.length); + } + + /** + * Adds a subset of the values in the array vals to the + * end of the list, in order. + * + * @param vals an int[] value + * @param offset the offset at which to start copying + * @param length the number of values to copy. + */ + public void add(byte[] vals, int offset, int length) { + ensureCapacity(_pos + length); + System.arraycopy(vals, offset, _data, _pos, length); + _pos += length; + } + + /** + * Inserts value into the list at offset. All + * values including and to the right of offset are shifted + * to the right. + * + * @param offset an int value + * @param value an int value + */ + public void insert(int offset, byte value) { + if (offset == _pos) { + add(value); + return; + } + ensureCapacity(_pos + 1); + // shift right + System.arraycopy(_data, offset, _data, offset + 1, _pos - offset); + // insert + _data[offset] = value; + _pos++; + } + + /** + * Inserts the array of values into the list at + * offset. All values including and to the right of + * offset are shifted to the right. + * + * @param offset an int value + * @param values an int[] value + */ + public void insert(int offset, byte[] values) { + insert(offset, values, 0, values.length); + } + + /** + * Inserts a slice of the array of values into the list + * at offset. All values including and to the right of + * offset are shifted to the right. + * + * @param offset an int value + * @param values an int[] value + * @param valOffset the offset in the values array at which to + * start copying. + * @param len the number of values to copy from the values array + */ + public void insert(int offset, byte[] values, int valOffset, int len) { + if (offset == _pos) { + add(values, valOffset, len); + return; + } + + ensureCapacity(_pos + len); + // shift right + System.arraycopy(_data, offset, _data, offset + len, _pos - offset); + // insert + System.arraycopy(values, valOffset, _data, offset, len); + _pos += len; + } + + /** + * Returns the value at the specified offset. + * + * @param offset an int value + * @return an int value + */ + public byte get(int offset) { + if (offset >= _pos) { + throw new ArrayIndexOutOfBoundsException(offset); + } + return _data[offset]; + } + + /** + * Returns the value at the specified offset without doing any + * bounds checking. + * + * @param offset an int value + * @return an int value + */ + public byte getQuick(int offset) { + return _data[offset]; + } + + /** + * Sets the value at the specified offset. + * + * @param offset an int value + * @param val an int value + */ + public void set(int offset, byte val) { + if (offset < 0 || offset >= _pos) { + throw new ArrayIndexOutOfBoundsException(offset); + } + _data[offset] = val; + } + + /** + * Sets the value at the specified offset and returns the + * previously stored value. + * + * @param offset an int value + * @param val an int value + * @return the value previously stored at offset. + */ + public byte getSet(int offset, byte val) { + if (offset < 0 || offset >= _pos) { + throw new ArrayIndexOutOfBoundsException(offset); + } + byte old = _data[offset]; + _data[offset] = val; + return old; + } + + /** + * Replace the values in the list starting at offset with + * the contents of the values array. + * + * @param offset the first offset to replace + * @param values the source of the new values + */ + public void set(int offset, byte[] values) { + set(offset, values, 0, values.length); + } + + /** + * Replace the values in the list starting at offset with + * length values from the values array, starting + * at valOffset. + * + * @param offset the first offset to replace + * @param values the source of the new values + * @param valOffset the first value to copy from the values array + * @param length the number of values to copy + */ + public void set(int offset, byte[] values, int valOffset, int length) { + if (offset < 0 || offset + length > _pos) { + throw new ArrayIndexOutOfBoundsException(offset); + } + System.arraycopy(_data, offset, values, valOffset, length); + } + + /** + * Sets the value at the specified offset without doing any bounds + * checking. + * + * @param offset an int value + * @param val an int value + */ + public void setQuick(int offset, byte val) { + _data[offset] = val; + } + + /** + * Flushes the internal state of the list, resetting the capacity + * to the default. + */ + public void clear() { + clear(DEFAULT_CAPACITY); + } + + /** + * Flushes the internal state of the list, setting the capacity of + * the empty list to capacity. + * + * @param capacity an int value + */ + public void clear(int capacity) { + _data = new byte[capacity]; + _pos = 0; + } + + /** + * Sets the size of the list to 0, but does not change its + * capacity. This method can be used as an alternative to the + * {@link #clear clear} method if you want to recyle a list without + * allocating new backing arrays. + * + * @see #clear + */ + public void reset() { + _pos = 0; + fill((byte)0); + } + + /** + * Sets the size of the list to 0, but does not change its + * capacity. This method can be used as an alternative to the + * {@link #clear clear} method if you want to recyle a list + * without allocating new backing arrays. This method differs + * from {@link #reset reset} in that it does not clear the old + * values in the backing array. Thus, it is possible for {@link + * #getQuick getQuick} to return stale data if this method is used + * and the caller is careless about bounds checking. + * + * @see #reset + * @see #clear + * @see #getQuick + */ + public void resetQuick() { + _pos = 0; + } + + /** + * Removes the value at offset from the list. + * + * @param offset an int value + * @return the value previously stored at offset. + */ + public byte remove(int offset) { + byte old = get(offset); + remove(offset, 1); + return old; + } + + /** + * Removes length values from the list, starting at + * offset + * + * @param offset an int value + * @param length an int value + */ + public void remove(int offset, int length) { + if (offset < 0 || offset >= _pos) { + throw new ArrayIndexOutOfBoundsException(offset); + } + + if (offset == 0) { + // data at the front + System.arraycopy(_data, length, _data, 0, _pos - length); + } else if (_pos - length == offset) { + // no copy to make, decrementing pos "deletes" values at + // the end + } else { + // data in the middle + System.arraycopy(_data, offset + length, + _data, offset, _pos - (offset + length)); + } + _pos -= length; + // no need to clear old values beyond _pos, because this is a + // primitive collection and 0 takes as much room as any other + // value + } + + + /** + * Reverse the order of the elements in the list. + */ + public void reverse() { + reverse(0, _pos); + } + + /** + * Reverse the order of the elements in the range of the list. + * + * @param from the inclusive index at which to start reversing + * @param to the exclusive index at which to stop reversing + */ + public void reverse(int from, int to) { + if (from == to) { + return; // nothing to do + } + if (from > to) { + throw new IllegalArgumentException("from cannot be greater than to"); + } + for (int i = from, j = to - 1; i < j; i++, j--) { + swap(i, j); + } + } + + /** + * Shuffle the elements of the list using the specified random + * number generator. + * + * @param rand a Random value + */ + public void shuffle(Random rand) { + for (int i = _pos; i-- > 1;) { + swap(i, rand.nextInt(i)); + } + } + + /** + * Swap the values at offsets i and j. + * + * @param i an offset into the data array + * @param j an offset into the data array + */ + private final void swap(int i, int j) { + byte tmp = _data[i]; + _data[i] = _data[j]; + _data[j] = tmp; + } + + // copying + + /** + * Returns a clone of this list. Since this is a primitive + * collection, this will be a deep clone. + * + * @return a deep clone of the list. + */ + public Object clone() { + ByteArrayList clone = null; + try { + clone = (ByteArrayList)super.clone(); + clone._data = (byte[])_data.clone(); + } catch (CloneNotSupportedException e) { + // it's supported + } // end of try-catch + return clone; + } + + /** + * Copies the contents of the list into a native array. + * + * @return an int[] value + */ + public byte[] toNativeArray() { + return toNativeArray(0, _pos); + } + + /** + * Copies a slice of the list into a native array. + * + * @param offset the offset at which to start copying + * @param len the number of values to copy. + * @return an int[] value + */ + public byte[] toNativeArray(int offset, int len) { + byte[] rv = new byte[len]; + toNativeArray(rv, offset, len); + return rv; + } + + /** + * Copies a slice of the list into a native array. + * + * @param dest the array to copy into. + * @param offset the offset of the first value to copy + * @param len the number of values to copy. + */ + public void toNativeArray(byte[] dest, int offset, int len) { + if (len == 0) { + return; // nothing to copy + } + if (offset < 0 || offset >= _pos) { + throw new ArrayIndexOutOfBoundsException(offset); + } + System.arraycopy(_data, offset, dest, 0, len); + } + + // comparing + + /** + * Compares this list to another list, value by value. + * + * @param other the object to compare against + * @return true if other is a TIntArrayList and has exactly the + * same values. + */ + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof ByteArrayList) { + ByteArrayList that = (ByteArrayList)other; + if (that.size() != this.size()) { + return false; + } else { + for (int i = _pos; i-- > 0;) { + if (this._data[i] != that._data[i]) { + return false; + } + } + return true; + } + } else { + return false; + } + } + + public int hashCode() { + int h = 0; + for (int i = _pos; i-- > 0;) { + h += HashFunctions.hash(_data[i]); + } + return h; + } + + // procedures + + /** + * Applies the procedure to each value in the list in ascending + * (front to back) order. + * + * @param procedure a TIntProcedure value + * @return true if the procedure did not terminate prematurely. + */ + public boolean forEach(TIntProcedure procedure) { + for (int i = 0; i < _pos; i++) { + if (! procedure.execute(_data[i])) { + return false; + } + } + return true; + } + + /** + * Applies the procedure to each value in the list in descending + * (back to front) order. + * + * @param procedure a TIntProcedure value + * @return true if the procedure did not terminate prematurely. + */ + public boolean forEachDescending(TIntProcedure procedure) { + for (int i = _pos; i-- > 0;) { + if (! procedure.execute(_data[i])) { + return false; + } + } + return true; + } + + // sorting + + /** + * Sort the values in the list (ascending) using the Sun quicksort + * implementation. + * + * @see java.util.Arrays#sort + */ + public void sort() { + Arrays.sort(_data, 0, _pos); + } + + /** + * Sort a slice of the list (ascending) using the Sun quicksort + * implementation. + * + * @param fromIndex the index at which to start sorting (inclusive) + * @param toIndex the index at which to stop sorting (exclusive) + * @see java.util.Arrays#sort + */ + public void sort(int fromIndex, int toIndex) { + Arrays.sort(_data, fromIndex, toIndex); + } + + // filling + + /** + * Fills every slot in the list with the specified value. + * + * @param val the value to use when filling + */ + public void fill(byte val) { + Arrays.fill(_data, 0, _pos, val); + } + + /** + * Fills a range in the list with the specified value. + * + * @param fromIndex the offset at which to start filling (inclusive) + * @param toIndex the offset at which to stop filling (exclusive) + * @param val the value to use when filling + */ + public void fill(int fromIndex, int toIndex, byte val) { + if (toIndex > _pos) { + ensureCapacity(toIndex); + _pos = toIndex; + } + Arrays.fill(_data, fromIndex, toIndex, val); + } + + // searching + + /** + * Performs a binary search for value in the entire list. + * Note that you must {@link #sort sort} the list before + * doing a search. + * + * @param value the value to search for + * @return the absolute offset in the list of the value, or its + * negative insertion point into the sorted list. + */ + public int binarySearch(byte value) { + return binarySearch(value, 0, _pos); + } + + /** + * Performs a binary search for value in the specified + * range. Note that you must {@link #sort sort} the list + * or the range before doing a search. + * + * @param value the value to search for + * @param fromIndex the lower boundary of the range (inclusive) + * @param toIndex the upper boundary of the range (exclusive) + * @return the absolute offset in the list of the value, or its + * negative insertion point into the sorted list. + */ + public int binarySearch(byte value, int fromIndex, int toIndex) { + if (fromIndex < 0) { + throw new ArrayIndexOutOfBoundsException(fromIndex); + } + if (toIndex > _pos) { + throw new ArrayIndexOutOfBoundsException(toIndex); + } + + int low = fromIndex; + int high = toIndex - 1; + + while (low <= high) { + int mid = (low + high) >> 1; + byte midVal = _data[mid]; + + if (midVal < value) { + low = mid + 1; + } else if (midVal > value) { + high = mid - 1; + } else { + return mid; // value found + } + } + return -(low + 1); // value not found. + } + + /** + * Searches the list front to back for the index of + * value. + * + * @param value an int value + * @return the first offset of the value, or -1 if it is not in + * the list. + * @see #binarySearch for faster searches on sorted lists + */ + public int indexOf(byte value) { + return indexOf(0, value); + } + + /** + * Searches the list front to back for the index of + * value, starting at offset. + * + * @param offset the offset at which to start the linear search + * (inclusive) + * @param value an int value + * @return the first offset of the value, or -1 if it is not in + * the list. + * @see #binarySearch for faster searches on sorted lists + */ + public int indexOf(int offset, byte value) { + for (int i = offset; i < _pos; i++) { + if (_data[i] == value) { + return i; + } + } + return -1; + } + + /** + * Searches the list back to front for the last index of + * value. + * + * @param value an int value + * @return the last offset of the value, or -1 if it is not in + * the list. + * @see #binarySearch for faster searches on sorted lists + */ + public int lastIndexOf(byte value) { + return lastIndexOf(_pos, value); + } + + /** + * Searches the list back to front for the last index of + * value, starting at offset. + * + * @param offset the offset at which to start the linear search + * (exclusive) + * @param value an int value + * @return the last offset of the value, or -1 if it is not in + * the list. + * @see #binarySearch for faster searches on sorted lists + */ + public int lastIndexOf(int offset, byte value) { + for (int i = offset; i-- > 0;) { + if (_data[i] == value) { + return i; + } + } + return -1; + } + + /** + * Searches the list for value + * + * @param value an int value + * @return true if value is in the list. + */ + public boolean contains(byte value) { + return lastIndexOf(value) >= 0; + } + + /** + * Searches the list for values satisfying condition in + * the manner of the *nix grep utility. + * + * @param condition a condition to apply to each element in the list + * @return a list of values which match the condition. + */ + public ByteArrayList grep(TIntProcedure condition) { + ByteArrayList list = new ByteArrayList(); + for (int i = 0; i < _pos; i++) { + if (condition.execute(_data[i])) { + list.add(_data[i]); + } + } + return list; + } + + /** + * Searches the list for values which do not satisfy + * condition. This is akin to *nix grep -v. + * + * @param condition a condition to apply to each element in the list + * @return a list of values which do not match the condition. + */ + public ByteArrayList inverseGrep(TIntProcedure condition) { + ByteArrayList list = new ByteArrayList(); + for (int i = 0; i < _pos; i++) { + if (! condition.execute(_data[i])) { + list.add(_data[i]); + } + } + return list; + } + + /** + * Finds the maximum value in the list. + * + * @return the largest value in the list. + * @exception IllegalStateException if the list is empty + */ + public int max() { + if (size() == 0) { + throw new IllegalStateException("cannot find maximum of an empty list"); + } + int max = _data[_pos - 1]; + for (int i = _pos - 1; i-- > 0;) { + max = Math.max(max, _data[_pos]); + } + return max; + } + + /** + * Finds the minimum value in the list. + * + * @return the smallest value in the list. + * @exception IllegalStateException if the list is empty + */ + public int min() { + if (size() == 0) { + throw new IllegalStateException("cannot find minimum of an empty list"); + } + int min = _data[_pos - 1]; + for (int i = _pos - 1; i-- > 0;) { + min = Math.min(min, _data[_pos]); + } + return min; + } + + // stringification + + /** + * Returns a String representation of the list, front to back. + * + * @return a String value + */ + public String toString() { + final StringBuffer buf = new StringBuffer("{"); + forEach(new TIntProcedure() { + public boolean execute(int val) { + buf.append(val); + buf.append(", "); + return true; + } + }); + buf.append("}"); + return buf.toString(); + } + +} // TIntArrayList diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentIntObjectMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentIntObjectMap.java new file mode 100644 index 000000000000..5b5a44f80e41 --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/ConcurrentIntObjectMap.java @@ -0,0 +1,43 @@ +/* + * 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 org.jetbrains.annotations.NotNull; + +/** + * methods adapted from java.util.concurrent.ConcurrentMap to integer keys + * @see java.util.concurrent.ConcurrentMap + */ +public interface ConcurrentIntObjectMap { + // concurrency related methods + + /** + * @return written value + */ + @NotNull + V cacheOrGet(int key, @NotNull V value); + boolean remove(int key, @NotNull V value); + boolean replace(int key, @NotNull V oldValue, @NotNull V newValue); + + // regular Map methods + V put(int key, @NotNull V value); + V get(int key); + V remove(int key); + boolean containsKey(int key); + void clear(); + @NotNull + Iterable> entries(); +} diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentRefValueIntObjectHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentRefValueIntObjectHashMap.java new file mode 100644 index 000000000000..2a10009a31c4 --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/ConcurrentRefValueIntObjectHashMap.java @@ -0,0 +1,153 @@ +/* + * 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 com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +import java.lang.ref.ReferenceQueue; +import java.util.Iterator; + +abstract class ConcurrentRefValueIntObjectHashMap implements ConcurrentIntObjectMap { + private final StripedLockIntObjectConcurrentHashMap> myMap = new StripedLockIntObjectConcurrentHashMap>(); + private final ReferenceQueue myQueue = new ReferenceQueue(); + + protected abstract IntReference createReference(int key, @NotNull V value, ReferenceQueue queue); + + protected interface IntReference { + int getKey(); + V get(); + } + + void processQueue() { + while(true){ + IntReference ref = (IntReference)myQueue.poll(); + if (ref == null) { + return; + } + int key = ref.getKey(); + myMap.remove(key); + } + } + + @NotNull + @Override + public V cacheOrGet(int key, @NotNull V value) { + processQueue(); + IntReference ref = myMap.putIfAbsent(key, createReference(key, value, myQueue)); + V v = ref == null ? null : ref.get(); + return v == null ? value : v; + } + + @Override + public boolean remove(int key, @NotNull V value) { + processQueue(); + return myMap.remove(key, createReference(key, value, myQueue)); + } + + @Override + public boolean replace(int key, @NotNull V oldValue, @NotNull V newValue) { + processQueue(); + return myMap.replace(key, createReference(key, oldValue,myQueue), createReference(key, newValue,myQueue)); + } + + @Override + public V put(int key, @NotNull V value) { + processQueue(); + IntReference ref = myMap.put(key, createReference(key, value, myQueue)); + return ref == null ? null : ref.get(); + } + + @Override + public V get(int key) { + IntReference ref = myMap.get(key); + return ref == null ? null : ref.get(); + } + + @Override + public V remove(int key) { + processQueue(); + IntReference ref = myMap.remove(key); + return ref == null ? null : ref.get(); + } + + @Override + public boolean containsKey(int key) { + return myMap.containsKey(key); + } + + @Override + public void clear() { + myMap.clear(); + processQueue(); + } + + @NotNull + @Override + public Iterable> entries() { + final Iterator>> entryIterator = myMap.entries().iterator(); + return new Iterable>() { + @Override + public Iterator> iterator() { + return new Iterator>() { + StripedLockIntObjectConcurrentHashMap.IntEntry next = nextAliveEntry(); + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public StripedLockIntObjectConcurrentHashMap.IntEntry next() { + StripedLockIntObjectConcurrentHashMap.IntEntry result = next; + next = nextAliveEntry(); + return result; + } + + private StripedLockIntObjectConcurrentHashMap.IntEntry nextAliveEntry() { + while (entryIterator.hasNext()) { + StripedLockIntObjectConcurrentHashMap.IntEntry> entry = entryIterator.next(); + final V v = entry.getValue().get(); + if (v == null) { + continue; + } + final int key = entry.getKey(); + return new StripedLockIntObjectConcurrentHashMap.IntEntry() { + @Override + public int getKey() { + return key; + } + + @NotNull + @Override + public V getValue() { + return v; + } + }; + } + return null; + } + + @Override + public void remove() { + throw new IncorrectOperationException("not implemented"); + } + }; + } + }; + } +} diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueIntObjectHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueIntObjectHashMap.java new file mode 100644 index 000000000000..1d2643cec415 --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueIntObjectHashMap.java @@ -0,0 +1,57 @@ +/* + * 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 org.jetbrains.annotations.NotNull; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; + +public class ConcurrentSoftValueIntObjectHashMap extends ConcurrentRefValueIntObjectHashMap { + private static class MyRef extends SoftReference implements IntReference { + private final int hash; + private final int key; + + MyRef(int key, @NotNull V referent, ReferenceQueue queue) { + super(referent, queue); + this.key = key; + hash = referent.hashCode(); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object obj) { + V v = get(); + return obj instanceof MyRef && ((MyRef)obj).hash == hash && v != null && v.equals(((MyRef)obj).get()); + } + + @Override + public int getKey() { + return key; + } + } + + @Override + protected IntReference createReference(int key, @NotNull V value, ReferenceQueue queue) { + return new MyRef(key, value, queue); + } +} diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueIntObjectHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueIntObjectHashMap.java new file mode 100644 index 000000000000..861e47ac781d --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueIntObjectHashMap.java @@ -0,0 +1,57 @@ +/* + * 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 org.jetbrains.annotations.NotNull; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +public class ConcurrentWeakValueIntObjectHashMap extends ConcurrentRefValueIntObjectHashMap { + private static class MyRef extends WeakReference implements IntReference { + private final int hash; + private final int key; + + MyRef(int key, @NotNull V referent, ReferenceQueue queue) { + super(referent, queue); + this.key = key; + hash = referent.hashCode(); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object obj) { + V v = get(); + return obj instanceof MyRef && ((MyRef)obj).hash == hash && v != null && v.equals(((MyRef)obj).get()); + } + + @Override + public int getKey() { + return key; + } + } + + @Override + protected IntReference createReference(int key, @NotNull V value, ReferenceQueue queue) { + return new MyRef(key, value, queue); + } +} diff --git a/platform/util/src/com/intellij/util/containers/IntArrayList.java b/platform/util/src/com/intellij/util/containers/IntArrayList.java index d595a53cc008..a34ba3ebf75c 100644 --- a/platform/util/src/com/intellij/util/containers/IntArrayList.java +++ b/platform/util/src/com/intellij/util/containers/IntArrayList.java @@ -50,6 +50,20 @@ public class IntArrayList implements Cloneable { System.arraycopy(oldData, 0, myData, 0, mySize); } } + public void fill(int fromIndex, int toIndex, int value) { + if (toIndex > mySize) { + ensureCapacity(toIndex); + mySize = toIndex; + } + Arrays.fill(myData, fromIndex, toIndex, value); + } + + public void add(int[] values) { + int length = values.length; + ensureCapacity(mySize + length); + System.arraycopy(values, 0, myData, mySize, length); + mySize += length; + } public int size() { return mySize; @@ -105,11 +119,19 @@ public class IntArrayList implements Cloneable { return a; } + public int[] toArray(int startIndex, int length) { + int[] result = new int[length]; + System.arraycopy(myData, startIndex, result, 0, length); + return result; + } public int get(int index) { checkRange(index); return myData[index]; } + public int getQuick(int index) { + return myData[index]; + } public int set(int index, int element) { checkRange(index); @@ -118,6 +140,9 @@ public class IntArrayList implements Cloneable { myData[index] = element; return oldValue; } + public void setQuick(int index, int element) { + myData[index] = element; + } public void add(int o) { ensureCapacity(mySize + 1); diff --git a/platform/util/src/com/intellij/util/containers/StripedLockIntObjectConcurrentHashMap.java b/platform/util/src/com/intellij/util/containers/StripedLockIntObjectConcurrentHashMap.java index a862b2a8b782..f00542bc8ab1 100644 --- a/platform/util/src/com/intellij/util/containers/StripedLockIntObjectConcurrentHashMap.java +++ b/platform/util/src/com/intellij/util/containers/StripedLockIntObjectConcurrentHashMap.java @@ -16,21 +16,22 @@ package com.intellij.util.containers; -import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.NoSuchElementException; /** similar to java.util.ConcurrentHashMap except: keys are ints conserved as much memory as possible by -- using only one Segment -- eliminating unnecessary fields - -- using one of 256 ReentrantLock for Segment staticly preallocated in StripedReentrantLocks + -- using one of 256 ReentrantLock for Segment statically pre-allocated in {@link StripedReentrantLocks} added hashing strategy argument made not Serializable */ -public class StripedLockIntObjectConcurrentHashMap { +public class StripedLockIntObjectConcurrentHashMap implements ConcurrentIntObjectMap { /* ---------------- Constants -------------- */ /** @@ -145,6 +146,7 @@ public class StripedLockIntObjectConcurrentHashMap { * @throws NullPointerException if the key or value is * null. */ + @Override public V put(int key, @NotNull V value) { return put(key, value, false); } @@ -185,19 +187,22 @@ public class StripedLockIntObjectConcurrentHashMap { * @throws NullPointerException if the key is * null. */ + @Override public V remove(int key) { - return remove(key, null); + return doRemove(key, null); } + @Override + public boolean remove(int key, @NotNull V value) { + return doRemove(key, value) != null; + } - - - - - - - - + @NotNull + @Override + public V cacheOrGet(int key, @NotNull V value) { + V prev = putIfAbsent(key, value); + return prev == null ? value : prev; + } /** * Returns an enumeration of the values in this table. @@ -212,13 +217,11 @@ public class StripedLockIntObjectConcurrentHashMap { /* ---------------- Iterator Support -------------- */ private class HashIterator { - private int nextTableIndex; - private IntHashEntry[] currentTable; + private int nextTableIndex = table.length - 1; private IntHashEntry nextEntry; private IntHashEntry lastReturned; private HashIterator() { - nextTableIndex = -1; advance(); } @@ -232,21 +235,10 @@ public class StripedLockIntObjectConcurrentHashMap { } while (nextTableIndex >= 0) { - if ((nextEntry = (IntHashEntry)currentTable[nextTableIndex--]) != null) { + if ((nextEntry = (IntHashEntry)table[nextTableIndex--]) != null) { return; } } - - StripedLockIntObjectConcurrentHashMap seg = StripedLockIntObjectConcurrentHashMap.this; - if (seg.count != 0) { - currentTable = seg.table; - for (int j = currentTable.length - 1; j >= 0; --j) { - if ((nextEntry = (IntHashEntry)currentTable[j]) != null) { - nextTableIndex = j - 1; - return; - } - } - } } public boolean hasNext() { @@ -289,15 +281,33 @@ public class StripedLockIntObjectConcurrentHashMap { } - public Collection> entries() { - HashIterator iterator = new HashIterator(); - Set> result = new THashSet>(); - while (iterator.hasNext()) { - IntHashEntry ie = iterator.nextEntry; - SimpleEntry entry = new SimpleEntry(ie.key, ie.value); - result.add(entry); - } - return result; + @Override + @NotNull + public Iterable> entries() { + return new Iterable>() { + @Override + public Iterator> iterator() { + final HashIterator hashIterator = new HashIterator(); + return new Iterator>() { + @Override + public boolean hasNext() { + return hashIterator.hasNext(); + } + + @Override + public IntEntry next() { + IntHashEntry ie = hashIterator.nextEntry; + hashIterator.nextEntry(); + return new SimpleEntry(ie.key, ie.value); + } + + @Override + public void remove() { + hashIterator.remove(); + } + }; + } + }; } /** @@ -461,6 +471,7 @@ public class StripedLockIntObjectConcurrentHashMap { /* Specialized implementations of map methods */ + @Override public V get(int key) { if (count != 0) { // read-volatile IntHashEntry e = getFirst(key); @@ -478,6 +489,7 @@ public class StripedLockIntObjectConcurrentHashMap { return null; } + @Override public boolean containsKey(int key) { if (count != 0) { // read-volatile IntHashEntry e = getFirst(key); @@ -491,6 +503,7 @@ public class StripedLockIntObjectConcurrentHashMap { return false; } + @Override public boolean replace(int key, @NotNull V oldValue, @NotNull V newValue) { lock(); try { @@ -614,7 +627,7 @@ public class StripedLockIntObjectConcurrentHashMap { /** * Remove; match on key only if value null, else match both. */ - protected V remove(int key, Object value) { + protected V doRemove(int key, V value) { lock(); try { int c = count - 1; @@ -650,6 +663,7 @@ public class StripedLockIntObjectConcurrentHashMap { } } + @Override public void clear() { if (count != 0) { lock();