/* * Copyright 2000-2015 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 org.jetbrains.java.decompiler.util; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class FastSetFactory<E> { private final VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<>(); private int lastBlock; private int lastMask; public FastSetFactory(Collection<E> set) { int block = -1; int mask = -1; int index = 0; for (E element : set) { block = index / 32; if (index % 32 == 0) { mask = 1; } else { mask <<= 1; } colValuesInternal.putWithKey(new int[]{block, mask}, element); index++; } lastBlock = block; lastMask = mask; } private int[] addElement(E element) { if (lastMask == -1 || lastMask == 0x80000000) { lastMask = 1; lastBlock++; } else { lastMask <<= 1; } int[] pointer = new int[]{lastBlock, lastMask}; colValuesInternal.putWithKey(pointer, element); return pointer; } public FastSet<E> spawnEmptySet() { return new FastSet<>(this); } public int getLastBlock() { return lastBlock; } public int getLastMask() { return lastMask; } private VBStyleCollection<int[], E> getInternalValuesCollection() { return colValuesInternal; } public static class FastSet<E> implements Iterable<E> { private final FastSetFactory<E> factory; private final VBStyleCollection<int[], E> colValuesInternal; private int[] data; private FastSet(FastSetFactory<E> factory) { this.factory = factory; this.colValuesInternal = factory.getInternalValuesCollection(); this.data = new int[factory.getLastBlock() + 1]; } public FastSet<E> getCopy() { FastSet<E> copy = new FastSet<>(factory); int arrlength = data.length; int[] cpdata = new int[arrlength]; System.arraycopy(data, 0, cpdata, 0, arrlength); copy.setData(cpdata); return copy; } private int[] ensureCapacity(int index) { int newlength = data.length; if (newlength == 0) { newlength = 1; } while (newlength <= index) { newlength *= 2; } int[] newdata = new int[newlength]; System.arraycopy(data, 0, newdata, 0, data.length); return data = newdata; } public void add(E element) { int[] index = colValuesInternal.getWithKey(element); if (index == null) { index = factory.addElement(element); } if (index[0] >= data.length) { ensureCapacity(index[0]); } data[index[0]] |= index[1]; } public void setAllElements() { int lastblock = factory.getLastBlock(); int lastmask = factory.getLastMask(); if (lastblock >= data.length) { ensureCapacity(lastblock); } for (int i = lastblock - 1; i >= 0; i--) { data[i] = 0xFFFFFFFF; } data[lastblock] = lastmask | (lastmask - 1); } public void addAll(Set<E> set) { for (E element : set) { add(element); } } public void remove(E element) { int[] index = colValuesInternal.getWithKey(element); if (index == null) { index = factory.addElement(element); } if (index[0] < data.length) { data[index[0]] &= ~index[1]; } } public void removeAll(Set<E> set) { for (E element : set) { remove(element); } } public boolean contains(E element) { int[] index = colValuesInternal.getWithKey(element); if (index == null) { index = factory.addElement(element); } return index[0] < data.length && ((data[index[0]] & index[1]) != 0); } public boolean contains(FastSet<E> set) { int[] extdata = set.getData(); int[] intdata = data; int minlength = Math.min(extdata.length, intdata.length); for (int i = minlength - 1; i >= 0; i--) { if ((extdata[i] & ~intdata[i]) != 0) { return false; } } for (int i = extdata.length - 1; i >= minlength; i--) { if (extdata[i] != 0) { return false; } } return true; } public void union(FastSet<E> set) { int[] extdata = set.getData(); int[] intdata = data; int minlength = Math.min(extdata.length, intdata.length); for (int i = minlength - 1; i >= 0; i--) { intdata[i] |= extdata[i]; } boolean expanded = false; for (int i = extdata.length - 1; i >= minlength; i--) { if (extdata[i] != 0) { if (!expanded) { intdata = ensureCapacity(extdata.length - 1); } intdata[i] = extdata[i]; } } } public void intersection(FastSet<E> set) { int[] extdata = set.getData(); int[] intdata = data; int minlength = Math.min(extdata.length, intdata.length); for (int i = minlength - 1; i >= 0; i--) { intdata[i] &= extdata[i]; } for (int i = intdata.length - 1; i >= minlength; i--) { intdata[i] = 0; } } public void symdiff(FastSet<E> set) { int[] extdata = set.getData(); int[] intdata = data; int minlength = Math.min(extdata.length, intdata.length); for (int i = minlength - 1; i >= 0; i--) { intdata[i] ^= extdata[i]; } boolean expanded = false; for (int i = extdata.length - 1; i >= minlength; i--) { if (extdata[i] != 0) { if (!expanded) { intdata = ensureCapacity(extdata.length - 1); } intdata[i] = extdata[i]; } } } public void complement(FastSet<E> set) { int[] extdata = set.getData(); int[] intdata = data; int minlength = Math.min(extdata.length, intdata.length); for (int i = minlength - 1; i >= 0; i--) { intdata[i] &= ~extdata[i]; } } public boolean equals(Object o) { if (o == this) return true; if (o == null || !(o instanceof FastSet)) return false; int[] longdata = ((FastSet)o).getData(); int[] shortdata = data; if (data.length > longdata.length) { shortdata = longdata; longdata = data; } for (int i = shortdata.length - 1; i >= 0; i--) { if (shortdata[i] != longdata[i]) { return false; } } for (int i = longdata.length - 1; i >= shortdata.length; i--) { if (longdata[i] != 0) { return false; } } return true; } public int getCardinality() { boolean found = false; int[] intdata = data; for (int i = intdata.length - 1; i >= 0; i--) { int block = intdata[i]; if (block != 0) { if (found) { return 2; } else { if ((block & (block - 1)) == 0) { found = true; } else { return 2; } } } } return found ? 1 : 0; } public int size() { int size = 0; int[] intdata = data; for (int i = intdata.length - 1; i >= 0; i--) { size += Integer.bitCount(intdata[i]); } return size; } public boolean isEmpty() { int[] intdata = data; for (int i = intdata.length - 1; i >= 0; i--) { if (intdata[i] != 0) { return false; } } return true; } public Iterator<E> iterator() { return new FastSetIterator<>(this); } public Set<E> toPlainSet() { HashSet<E> set = new HashSet<>(); int[] intdata = data; int size = data.length * 32; if (size > colValuesInternal.size()) { size = colValuesInternal.size(); } for (int i = size - 1; i >= 0; i--) { int[] index = colValuesInternal.get(i); if ((intdata[index[0]] & index[1]) != 0) { set.add(colValuesInternal.getKey(i)); } } return set; } public String toBinary() { StringBuilder buffer = new StringBuilder(); int[] intdata = data; for (int i = 0; i < intdata.length; i++) { buffer.append(" ").append(Integer.toBinaryString(intdata[i])); } return buffer.toString(); } private int[] getData() { return data; } private void setData(int[] data) { this.data = data; } public int[] getLoad() { int[] intdata = data; int notempty = 0; for (int i = 0; i < intdata.length; i++) { if (intdata[i] != 0) { notempty++; } } return new int[]{intdata.length, notempty}; } public FastSetFactory<E> getFactory() { return factory; } } public static class FastSetIterator<E> implements Iterator<E> { private final VBStyleCollection<int[], E> colValuesInternal; private final int[] data; private int size; private int pointer = -1; private int next_pointer = -1; private FastSetIterator(FastSet<E> set) { colValuesInternal = set.getFactory().getInternalValuesCollection(); data = set.getData(); size = colValuesInternal.size(); int datasize = data.length * 32; if (datasize < size) { size = datasize; } } public boolean hasNext() { next_pointer = pointer; while (++next_pointer < size) { int[] index = colValuesInternal.get(next_pointer); if ((data[index[0]] & index[1]) != 0) { return true; } } next_pointer = -1; return false; } public E next() { if (next_pointer >= 0) { pointer = next_pointer; } else { while (++pointer < size) { int[] index = colValuesInternal.get(pointer); if ((data[index[0]] & index[1]) != 0) { break; } } } next_pointer = -1; return pointer < size ? colValuesInternal.getKey(pointer) : null; } public void remove() { int[] index = colValuesInternal.get(pointer); data[index[0]] &= ~index[1]; pointer--; } } }