blob: fd3e1bddba36776ac8bcb5ac4f12847623efc502 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 java.util;
/**
* This is a concrete subclass of EnumSet designed specifically for enum type
* with more than 64 elements.
*
*/
@SuppressWarnings("serial")
final class HugeEnumSet<E extends Enum<E>> extends EnumSet<E> {
final private E[] enums;
private long[] bits;
private int size;
private static final int BIT_IN_LONG = 64;
// BEGIN android-changed
/**
* Constructs an instance.
*
* @param elementType non-null; type of the elements
* @param enums non-null; prepopulated array of constants in ordinal
* order
*/
HugeEnumSet(Class<E> elementType, E[] enums) {
super(elementType);
this.enums = enums;
bits = new long[(enums.length + BIT_IN_LONG - 1) / BIT_IN_LONG];
Arrays.fill(bits, 0);
}
// END android-changed
private class HugeEnumSetIterator implements Iterator<E> {
private long[] unProcessedBits;
private int bitsPosition = 0;
/*
* Mask for current element.
*/
private long currentElementMask = 0;
boolean canProcess = true;
private HugeEnumSetIterator() {
unProcessedBits = new long[bits.length];
System.arraycopy(bits, 0, unProcessedBits, 0, bits.length);
bitsPosition = unProcessedBits.length;
findNextNoneZeroPosition(0);
if (bitsPosition == unProcessedBits.length) {
canProcess = false;
}
}
private void findNextNoneZeroPosition(int start) {
for (int i = start; i < unProcessedBits.length; i++) {
if (0 != bits[i]) {
bitsPosition = i;
break;
}
}
}
public boolean hasNext() {
return canProcess;
}
public E next() {
if (!canProcess) {
throw new NoSuchElementException();
}
currentElementMask = unProcessedBits[bitsPosition]
& (-unProcessedBits[bitsPosition]);
unProcessedBits[bitsPosition] -= currentElementMask;
int index = Long.numberOfTrailingZeros(currentElementMask)
+ bitsPosition * BIT_IN_LONG;
if (0 == unProcessedBits[bitsPosition]) {
int oldBitsPosition = bitsPosition;
findNextNoneZeroPosition(bitsPosition + 1);
if (bitsPosition == oldBitsPosition) {
canProcess = false;
}
}
return enums[index];
}
public void remove() {
if (currentElementMask == 0) {
throw new IllegalStateException();
}
bits[bitsPosition] &= ~currentElementMask;
size--;
currentElementMask = 0;
}
}
@Override
public boolean add(E element) {
if (!isValidType(element.getDeclaringClass())) {
throw new ClassCastException();
}
calculateElementIndex(element);
bits[bitsIndex] |= (1l << elementInBits);
if (oldBits == bits[bitsIndex]) {
return false;
}
size++;
return true;
}
@Override
public boolean addAll(Collection<? extends E> collection) {
if (0 == collection.size() || this == collection) {
return false;
}
if (collection instanceof EnumSet) {
EnumSet set = (EnumSet) collection;
if (!isValidType(set.elementClass)) {
throw new ClassCastException();
}
HugeEnumSet hugeSet = (HugeEnumSet) set;
boolean addSuccessful = false;
for (int i = 0; i < bits.length; i++) {
oldBits = bits[i];
bits[i] |= hugeSet.bits[i];
if (oldBits != bits[i]) {
addSuccessful = true;
size = size - Long.bitCount(oldBits)
+ Long.bitCount(bits[i]);
}
}
return addSuccessful;
}
return super.addAll(collection);
}
@Override
public int size() {
return size;
}
@Override
public void clear() {
Arrays.fill(bits, 0);
size = 0;
}
@Override
protected void complement() {
if (0 != enums.length) {
bitsIndex = enums.length / BIT_IN_LONG;
size = 0;
int bitCount = 0;
for (int i = 0; i <= bitsIndex; i++) {
bits[i] = ~bits[i];
bitCount = Long.bitCount(bits[i]);
size += bitCount;
}
bits[bitsIndex] &= (-1l >>> (BIT_IN_LONG - enums.length
% BIT_IN_LONG));
size -= bitCount;
bitCount = Long.bitCount(bits[bitsIndex]);
size += bitCount;
}
}
@SuppressWarnings("unchecked")
@Override
public boolean contains(Object object) {
if (null == object) {
return false;
}
if (!isValidType(object.getClass())) {
return false;
}
calculateElementIndex((E)object);
return (bits[bitsIndex] & (1l << elementInBits)) != 0;
}
@Override
@SuppressWarnings("unchecked")
public HugeEnumSet<E> clone() {
Object set = super.clone();
if (null != set) {
((HugeEnumSet<E>) set).bits = bits.clone();
return (HugeEnumSet<E>) set;
}
return null;
}
@Override
public boolean containsAll(Collection<?> collection) {
if (collection.size() == 0) {
return true;
}
if (collection instanceof HugeEnumSet) {
HugeEnumSet set = (HugeEnumSet) collection;
if(isValidType(set.elementClass )) {
for(int i = 0; i < bits.length; i++) {
if((bits[i] & set.bits[i]) != set.bits[i]){
return false;
}
}
return true;
}
}
return !(collection instanceof EnumSet) && super.containsAll(collection);
}
@Override
public boolean equals(Object object) {
if (null == object) {
return false;
}
if (!isValidType(object.getClass())) {
return super.equals(object);
}
return Arrays.equals(bits, ((HugeEnumSet) object).bits);
}
@Override
public Iterator<E> iterator() {
return new HugeEnumSetIterator();
}
@Override
public boolean remove(Object object) {
if (!contains(object)) {
return false;
}
bits[bitsIndex] -= (1l << elementInBits);
size--;
return true;
}
@Override
public boolean removeAll(Collection<?> collection) {
if (0 == collection.size()) {
return false;
}
if (collection instanceof EnumSet) {
EnumSet<E> set = (EnumSet<E>) collection;
if (!isValidType(set.elementClass)) {
return false;
}
boolean removeSuccessful = false;
long mask = 0;
for (int i = 0; i < bits.length; i++) {
oldBits = bits[i];
mask = bits[i] & ((HugeEnumSet<E>) set).bits[i];
if (mask != 0) {
bits[i] -= mask;
size = (size - Long.bitCount(oldBits) + Long
.bitCount(bits[i]));
removeSuccessful = true;
}
}
return removeSuccessful;
}
return super.removeAll(collection);
}
@Override
public boolean retainAll(Collection<?> collection) {
if (collection instanceof EnumSet) {
EnumSet<E> set = (EnumSet<E>) collection;
if (!isValidType(set.elementClass)) {
clear();
return true;
}
boolean retainSuccessful = false;
oldBits = 0;
for (int i = 0; i < bits.length; i++) {
oldBits = bits[i];
bits[i] &= ((HugeEnumSet<E>) set).bits[i];
if (oldBits != bits[i]) {
size = size - Long.bitCount(oldBits)
+ Long.bitCount(bits[i]);
retainSuccessful = true;
}
}
return retainSuccessful;
}
return super.retainAll(collection);
}
@Override
void setRange(E start, E end) {
calculateElementIndex(start);
int startBitsIndex = bitsIndex;
int startElementInBits = elementInBits;
calculateElementIndex(end);
int endBitsIndex = bitsIndex;
int endElementInBits = elementInBits;
long range = 0;
if (startBitsIndex == endBitsIndex) {
range = (-1l >>> (BIT_IN_LONG -(endElementInBits - startElementInBits + 1))) << startElementInBits;
size -= Long.bitCount(bits[bitsIndex]);
bits[bitsIndex] |= range;
size += Long.bitCount(bits[bitsIndex]);
} else {
range = (-1l >>> startElementInBits) << startElementInBits;
size -= Long.bitCount(bits[startBitsIndex]);
bits[startBitsIndex] |= range;
size += Long.bitCount(bits[startBitsIndex]);
// endElementInBits + 1 is the number of consecutive ones.
// 63 - endElementInBits is the following zeros of the right most one.
range = -1l >>> (BIT_IN_LONG - (endElementInBits + 1));
size -= Long.bitCount(bits[endBitsIndex]);
bits[endBitsIndex] |= range;
size += Long.bitCount(bits[endBitsIndex]);
for(int i = (startBitsIndex + 1); i <= (endBitsIndex - 1); i++) {
size -= Long.bitCount(bits[i]);
bits[i] = -1l;
size += Long.bitCount(bits[i]);
}
}
}
private void calculateElementIndex(E element) {
int elementOrdinal = element.ordinal();
bitsIndex = elementOrdinal / BIT_IN_LONG;
elementInBits = elementOrdinal % BIT_IN_LONG;
oldBits = bits[bitsIndex];
}
private int bitsIndex;
private int elementInBits;
private long oldBits;
}