blob: 3e77e25aad7b1393e9c9f52f8535a7df6c1ed7bd [file] [log] [blame]
/*
* Copyright 2000-2014 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.indexing.containers;
import com.intellij.util.indexing.ValueContainer;
import gnu.trove.TIntProcedure;
/**
* Created by Maxim.Mossienko on 5/27/2014.
*/
public class SortedIdSet implements Cloneable, RandomAccessIntContainer {
private int[] mySet;
private int mySetLength;
private int mySize;
public SortedIdSet(final int initialCapacity) {
assert initialCapacity < Short.MAX_VALUE;
mySet = new int[initialCapacity]; // todo slightly increase size
}
public SortedIdSet(final int[] array, int size) {
mySet = array;
mySetLength = mySize = size;
}
public boolean isEmpty() {
return mySize == 0;
}
public int size() {
return mySize;
}
public boolean add(int value) {
assert value > 0;
int pos;
if (mySetLength == 0 || (mySetLength > 0 && Math.abs(mySet[mySetLength -1]) < value)) {
pos = -mySetLength-1; // most of the time during bulk indexing we add near the end
}
else {
pos = binarySearch(mySet, 0, mySetLength, value);
}
if (pos >= 0) {
if (mySet[pos] > 0) return false;
pos = -pos - 1; // found removed
}
if (mySetLength == mySet.length) {
int nextArraySize = mySet.length < 1024 ? mySet.length << 1 : mySet.length + mySet.length / 5;
int[] newSet = new int[nextArraySize];
System.arraycopy(mySet, 0, newSet, 0, mySet.length);
mySet = newSet;
}
pos = -pos - 1;
boolean lengthIsIncreased = pos == mySetLength; // insert at end
if (!lengthIsIncreased && Math.abs(mySet[pos]) != value) { // todo we can shift until first removed
System.arraycopy(mySet, pos, mySet, pos + 1, mySetLength - pos);
lengthIsIncreased = true;
}
mySet[pos] = value;
++mySize;
if (lengthIsIncreased) ++mySetLength;
return true;
}
public boolean remove(int value) {
assert value > 0;
int pos = binarySearch(mySet, 0, mySetLength, value);
if (pos < 0 || mySet[pos] < 0) return false;
mySet[pos] = -value;
//if (pos != mySetLength - 1) System.arraycopy(mySet, pos + 1, mySet, pos, mySetLength - pos - 1);
--mySize;
//--mySetLength;
return true;
}
@Override
public ValueContainer.IntIterator intIterator() {
return new Iterator();
}
@Override
public ValueContainer.IntPredicate intPredicate() {
return new ValueContainer.IntPredicate() {
@Override
public boolean contains(int id) {
return SortedIdSet.this.contains(id);
}
};
}
private class Iterator implements ValueContainer.IntIterator {
private int myCursor;
Iterator() {
myCursor = findNext(0);
}
@Override
public boolean hasNext() {
return myCursor != -1;
}
@Override
public int next() {
int result = get(myCursor);
myCursor = findNext(myCursor + 1);
return result;
}
@Override
public int size() {
return SortedIdSet.this.size();
}
@Override
public boolean hasAscendingOrder() {
return true;
}
@Override
public ValueContainer.IntIterator createCopyInInitialState() {
return new Iterator();
}
}
private static int binarySearch(int[] set, int off, int length, int key) {
int low = off;
int high = length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = Math.abs(set[mid]);
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
public void forEach(TIntProcedure procedure) {
for(int i = 0; i < mySetLength; ++i) {
int value = mySet[i];
if (value > 0 && !procedure.execute(value)) break;
}
}
public boolean contains(int value) {
assert value > 0;
int pos = binarySearch(mySet, 0, mySetLength, value);
return pos >= 0 && mySet[pos] > 0;
}
@Override
public Object clone() {
try {
SortedIdSet set = (SortedIdSet)super.clone();
set.mySet = mySet.clone();
return set;
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public void compact() {
if(2 * mySize < mySetLength && mySetLength > 5) {
int positivePosition = -1;
for(int i = 0; i < mySetLength; ++i) {
if (mySet[i] < 0) {
while(i < mySetLength && mySet[i] < 0) ++i;
if (i == mySetLength) {
break;
} else {
mySet[++positivePosition] = mySet[i];
}
} else {
++positivePosition;
if (i != positivePosition) mySet[positivePosition] = mySet[i];
}
}
// todo slightly decrease size
mySetLength = (short)(positivePosition + 1);
}
}
public RandomAccessIntContainer ensureContainerCapacity(int count) {
int newSize = mySetLength + count;
if (newSize < mySet.length) return this;
if (newSize > ChangeBufferingList.MAX_FILES) {
return new IdBitSet(this, count);
}
newSize = ChangeBufferingList.calcNextArraySize(mySet.length, newSize);
assert newSize < Short.MAX_VALUE;
int[] newSet = new int[newSize]; // todo slightly increase size and compact
System.arraycopy(mySet, 0, newSet, 0, mySetLength);
mySet = newSet;
return this;
}
public int findNext(int i) {
while(i < mySetLength) {
if (mySet[i] > 0) return i;
++i;
}
return -1;
}
public int get(int cursor) {
assert cursor < mySetLength;
int value = mySet[cursor];
assert value > 0;
return value;
}
}