blob: 5ae5c5cad083949d481e84d27c0905c83d80dbb3 [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 org.apache.ignite.internal.util;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.BitSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState;
/**
* Grid partition state map. States are encoded using bits.
* <p>
* Null values are prohibited.
*/
public class GridPartitionStateMap extends AbstractMap<Integer, GridDhtPartitionState> implements Serializable {
/** Empty map. */
public static final GridPartitionStateMap EMPTY = new GridPartitionStateMap(0);
/** */
private static final long serialVersionUID = 0L;
/** Required bits to hold all state. Additional zero state is required as well. */
private static final int BITS = Integer.SIZE -
Integer.numberOfLeadingZeros(GridDhtPartitionState.values().length + 1);
/**
* Contains partition map.
* For a map containing 3 partitions state with a size of 3 bits storage will be done this way:
* <pre>
* +-----------+-----------+-----------+
* | p0 - 100 | p1 - 011 | p2 - 001 |
* +---+---+---+---+---+---+---+---+---+
* | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
* +---+---+---+---+---+---+---+---+---+
* </pre>
* The first element takes the first {@link GridPartitionStateMap#BITS} bits in reverse order,
* the second element next {@link GridPartitionStateMap#BITS} bits in reverse order, etc.
*/
private final BitSet states;
/** */
private int size;
/** {@inheritDoc} */
@Override public Set<Entry<Integer, GridDhtPartitionState>> entrySet() {
return new AbstractSet<Entry<Integer, GridDhtPartitionState>>() {
@Override public Iterator<Entry<Integer, GridDhtPartitionState>> iterator() {
return new Iterator<Entry<Integer, GridDhtPartitionState>>() {
/** Current {@link GridPartitionStateMap#states} index. */
private int idx;
/** Current key value. */
private int cur;
@Override public boolean hasNext() {
idx = states.nextSetBit(idx);
return idx != -1;
}
@Override public Entry<Integer, GridDhtPartitionState> next() {
if (!hasNext())
throw new NoSuchElementException();
cur = idx / BITS;
int bitN = idx % BITS;
// Get state value from BitSet like in GridPartitionStateMap#state, but don't process known zero bits.
int st = 1 << bitN;
// Accumulating values of remaining bits
for (int i = 1; i < BITS - bitN; i++)
st |= (states.get(idx + i) ? 1 : 0) << i + bitN;
final int ordinal = st - 1;
idx += (BITS - bitN);
return new Entry<Integer, GridDhtPartitionState>() {
int p = cur;
@Override public Integer getKey() {
return p;
}
@Override public GridDhtPartitionState getValue() {
return GridDhtPartitionState.fromOrdinal(ordinal);
}
@Override public GridDhtPartitionState setValue(GridDhtPartitionState val) {
return setState(p, val);
}
};
}
@Override public void remove() {
setState(cur, null);
}
};
}
@Override public int size() {
return GridPartitionStateMap.this.size();
}
};
}
/**
* Default constructor.
*/
public GridPartitionStateMap() {
states = new BitSet();
}
/**
* @param parts Partitions to hold.
*/
public GridPartitionStateMap(int parts) {
states = new BitSet(parts * BITS);
}
/**
* Creates map copy.
* @param from Source map.
* @param onlyActive Retains only active partitions.
*/
public GridPartitionStateMap(GridPartitionStateMap from, boolean onlyActive) {
size = from.size();
states = (BitSet)from.states.clone();
if (onlyActive) {
int part = 0;
int maxPart = states.size() / BITS;
while (part < maxPart) {
GridDhtPartitionState state = from.state(part);
if (state != null && !state.active())
remove(part);
part++;
}
}
}
/** {@inheritDoc} */
@Override public GridDhtPartitionState put(Integer key, GridDhtPartitionState val) {
assert val != null;
return setState(key, val);
}
/** {@inheritDoc} */
@Override public GridDhtPartitionState get(Object key) {
return state((Integer)key);
}
/** {@inheritDoc} */
@Override public GridDhtPartitionState remove(Object key) {
return setState((Integer)key, null);
}
/** {@inheritDoc} */
@Override public boolean containsKey(Object key) {
return state((Integer)key) != null;
}
/** {@inheritDoc} */
@Override public int size() {
return size;
}
/** */
private GridDhtPartitionState setState(int part, GridDhtPartitionState st) {
GridDhtPartitionState old = state(part);
if (old == st)
return old;
int off = part * BITS;
int ist = st == null ? 0 : st.ordinal() + 1; // Reserve all zero bits for empty value
for (int i = 0; i < BITS; i++) {
states.set(off + i, (ist & 1) == 1);
ist >>>= 1;
}
size += (st == null ? -1 : old == null ? 1 : 0);
return old;
}
/** */
private GridDhtPartitionState state(int part) {
int off = part * BITS;
int st = 0;
for (int i = 0; i < BITS; i++)
st |= ((states.get(off + i) ? 1 : 0) << i);
return st == 0 ? null : GridDhtPartitionState.fromOrdinal(st - 1);
}
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
GridPartitionStateMap map = (GridPartitionStateMap)o;
return size == map.size && states.equals(map.states);
}
/** {@inheritDoc} */
@Override public int hashCode() {
return 31 * states.hashCode() + size;
}
}