/*
* Copyright 2016 the original author or authors.
*
* 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 net.jodah.failsafe.internal.util;
import java.util.BitSet;
/**
* A circular BitSet implementation that tracks the cardinality and ratios of positive and negative bits.
*
* @author Jonathan Halterman
*/
public class CircularBitSet {
final BitSet bitSet;
private final int size;
/** Index to write next entry to */
volatile int nextIndex;
private volatile int occupiedBits;
private volatile int positives;
private volatile int negatives;
public CircularBitSet(int size, CircularBitSet oldBitSet) {
this.bitSet = new BitSet(size);
this.size = size;
if (oldBitSet != null) {
synchronized (oldBitSet) {
copyBits(oldBitSet, this);
}
}
}
/**
* Copies the most recent bits from the {@code left} BitSet to the {@code right} BitSet in order from oldest to
* newest.
*/
static void copyBits(CircularBitSet left, CircularBitSet right) {
int bitsToCopy = Math.min(left.occupiedBits, right.size);
int index = left.nextIndex - bitsToCopy;
if (index < 0)
index += left.occupiedBits;
for (int i = 0; i < bitsToCopy; i++, index = left.indexAfter(index))
right.setNext(left.bitSet.get(index));
}
/**
* Returns the ratio of positive bits to the number of occupied bits.
*/
public double negativeRatio() {
return (double) negatives / (double) occupiedBits;
}
/**
* Returns the number of occupied bits in the set.
*/
public int occupiedBits() {
return occupiedBits;
}
/**
* Returns the ratio of positive bits to the number of occupied bits.
*/
public double positiveRatio() {
return (double) positives / (double) occupiedBits;
}
/**
* Sets the value of the next bit in the bitset, returning the previous value, else -1 if no previous value was set
* for the bit.
*/
public synchronized int setNext(boolean value) {
int previousValue = -1;
if (occupiedBits < size)
occupiedBits++;
else
previousValue = bitSet.get(nextIndex) ? 1 : 0;
bitSet.set(nextIndex, value);
nextIndex = indexAfter(nextIndex);
if (value) {
if (previousValue != 1)
positives++;
if (previousValue == 0)
negatives--;
} else {
if (previousValue != 0)
negatives++;
if (previousValue == 1)
positives--;
}
return previousValue;
}
/**
* Returns an array representation of the BitSet entries.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder().append('[');
for (int i = 0; i < occupiedBits; i++) {
if (i > 0)
sb.append(", ");
sb.append(bitSet.get(i));
}
return sb.append(']').toString();
}
/**
* Returns the index after the {@code index}.
*/
private int indexAfter(int index) {
return index == size - 1 ? 0 : index + 1;
}
}