diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/GridPartitionStateMap.java b/modules/core/src/main/java/org/apache/ignite/internal/util/GridPartitionStateMap.java index f5893c02f77b4..b256da9edcb1e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/GridPartitionStateMap.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridPartitionStateMap.java @@ -42,7 +42,19 @@ public class GridPartitionStateMap extends AbstractMap + * +-----------+-----------+-----------+ + * | p0 - 100 | p1 - 011 | p2 - 001 | + * +---+---+---+---+---+---+---+---+---+ + * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | + * +---+---+---+---+---+---+---+---+---+ + * + * 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; /** */ @@ -52,25 +64,36 @@ public class GridPartitionStateMap extends AbstractMap> entrySet() { return new AbstractSet>() { @Override public Iterator> iterator() { - final int size = states.isEmpty() ? 0 : (states.length() - 1) / BITS + 1; - return new Iterator>() { - private int next; + /** Current {@link GridPartitionStateMap#states} index. */ + private int idx; + + /** Current key value. */ private int cur; @Override public boolean hasNext() { - while(state(next) == null && next < size) - next++; + idx = states.nextSetBit(idx); - return next < size; + return idx != -1; } @Override public Entry next() { if (!hasNext()) throw new NoSuchElementException(); - cur = next; - next++; + cur = idx / BITS; + + int bitN = idx % BITS; + + // Get value from BitSet like in GridPartitionStateMap#state, but don't process known bits. + int st = 1 << bitN; + + 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() { int p = cur; @@ -80,7 +103,7 @@ public class GridPartitionStateMap extends AbstractMap> iter = map.entrySet().iterator(); + + for (int i = 0; i < map.size() + 1; i++) + assertTrue(iter.hasNext()); + + Map.Entry entry1 = iter.next(); + + for (int i = 0; i < map.size() + 1; i++) + assertTrue(iter.hasNext()); + + Map.Entry entry2 = iter.next(); + + iter.remove(); + + assertNotNull(entry1.getValue()); + assertNotNull(entry2.getValue()); + + assertEquals(Integer.valueOf(0), entry1.getKey()); + assertEquals(Integer.valueOf(1), entry2.getKey()); + + assertEquals(GridDhtPartitionState.MOVING, entry1.getValue()); + assertEquals(GridDhtPartitionState.RENTING, entry2.getValue()); + } + + /** + * Tests {@link GridDhtPartitionState} compatibility with {@link TreeMap} on random operations. + */ + public void testOnRandomOperations() { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + Map treeMap = new TreeMap<>(); + Map gridMap = new GridPartitionStateMap(); + + int statesNum = GridDhtPartitionState.values().length; + + for (int i = 0; i < 10_000; i++) { + Integer part = rnd.nextInt(1024); + + GridDhtPartitionState state = GridDhtPartitionState.fromOrdinal(rnd.nextInt(statesNum)); + + int rndOperation = rnd.nextInt(9); + + if (rndOperation <= 5) { + treeMap.put(part, state); + gridMap.put(part, state); + } + else if (rndOperation == 6) { + treeMap.remove(part); + gridMap.remove(part); + } + else if (treeMap.size() > 0) { + int n = rnd.nextInt(0, treeMap.size()); + + Iterator> iter1 = treeMap.entrySet().iterator(); + Iterator> iter2 = gridMap.entrySet().iterator(); + + Map.Entry entry1 = iter1.next(); + Map.Entry entry2 = iter2.next(); + + for (int j = 1; j <= n; j++) { + entry1 = iter1.next(); + entry2 = iter2.next(); + } + + if (rndOperation == 7) { + entry1.setValue(state); + entry2.setValue(state); + } + else { + iter1.remove(); + iter2.remove(); + } + } + + assertEquals(treeMap.size(), gridMap.size()); + } + + assertEquals(treeMap, gridMap); + } + /** */ private GridPartitionStateMap initMap(GridPartitionStateMap map) { map.put(0, GridDhtPartitionState.MOVING); @@ -159,4 +250,4 @@ private GridPartitionStateMap initMap(GridPartitionStateMap map) { return map; } -} \ No newline at end of file +}