It doesn't make a whole lot of sense for `BitArray#clear` to grow the underlying storage array just to clear the bit. We *already* treat indices outside of the storage array as unset. This turns such operations into a noop.
This commit is contained in:
parent
856d9bfbc1
commit
e23c3f915f
|
@ -32,6 +32,10 @@ public final class BitArray implements Releasable {
|
||||||
private final BigArrays bigArrays;
|
private final BigArrays bigArrays;
|
||||||
private LongArray bits;
|
private LongArray bits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the {@linkplain BitArray}.
|
||||||
|
* @param initialSize the initial size of underlying storage.
|
||||||
|
*/
|
||||||
public BitArray(int initialSize, BigArrays bigArrays) {
|
public BitArray(int initialSize, BigArrays bigArrays) {
|
||||||
this.bigArrays = bigArrays;
|
this.bigArrays = bigArrays;
|
||||||
this.bits = bigArrays.newLongArray(initialSize, true);
|
this.bits = bigArrays.newLongArray(initialSize, true);
|
||||||
|
@ -41,21 +45,31 @@ public final class BitArray implements Releasable {
|
||||||
* Set the {@code index}th bit.
|
* Set the {@code index}th bit.
|
||||||
*/
|
*/
|
||||||
public void set(int index) {
|
public void set(int index) {
|
||||||
fill(index, true);
|
int wordNum = wordNum(index);
|
||||||
|
bits = bigArrays.grow(bits, wordNum + 1);
|
||||||
|
bits.set(wordNum, bits.get(wordNum) | bitmask(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the {@code index}th bit.
|
* Clear the {@code index}th bit.
|
||||||
*/
|
*/
|
||||||
public void clear(int index) {
|
public void clear(int index) {
|
||||||
fill(index, false);
|
int wordNum = wordNum(index);
|
||||||
|
if (wordNum >= bits.size()) {
|
||||||
|
/*
|
||||||
|
* No need to resize the array just to clear the bit because we'll
|
||||||
|
* initialize them to false when we grow the array anyway.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bits.set(wordNum, bits.get(wordNum) & ~bitmask(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the {@code index}th bit set?
|
* Is the {@code index}th bit set?
|
||||||
*/
|
*/
|
||||||
public boolean get(int index) {
|
public boolean get(int index) {
|
||||||
int wordNum = index >> 6;
|
int wordNum = wordNum(index);
|
||||||
if (wordNum >= bits.size()) {
|
if (wordNum >= bits.size()) {
|
||||||
/*
|
/*
|
||||||
* If the word is bigger than the array then it could *never* have
|
* If the word is bigger than the array then it could *never* have
|
||||||
|
@ -67,12 +81,12 @@ public final class BitArray implements Releasable {
|
||||||
return (bits.get(wordNum) & bitmask) != 0;
|
return (bits.get(wordNum) & bitmask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fill(int index, boolean bit) {
|
private static int wordNum(int index) {
|
||||||
int wordNum = index >> 6;
|
return index >> 6;
|
||||||
bits = bigArrays.grow(bits,wordNum+1);
|
}
|
||||||
long bitmask = 1L << index;
|
|
||||||
long value = bit ? bits.get(wordNum) | bitmask : bits.get(wordNum) & ~bitmask;
|
private static long bitmask(int index) {
|
||||||
bits.set(wordNum, value);
|
return 1L << index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,12 +19,21 @@
|
||||||
|
|
||||||
package org.elasticsearch.common.util;
|
package org.elasticsearch.common.util;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.breaker.CircuitBreaker;
|
||||||
|
import org.elasticsearch.common.breaker.CircuitBreakingException;
|
||||||
|
import org.elasticsearch.common.breaker.NoopCircuitBreaker;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
|
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class BitArrayTests extends ESTestCase {
|
public class BitArrayTests extends ESTestCase {
|
||||||
public void testRandom() {
|
public void testRandom() {
|
||||||
try (BitArray bitArray = new BitArray(1, BigArrays.NON_RECYCLING_INSTANCE)) {
|
try (BitArray bitArray = new BitArray(1, BigArrays.NON_RECYCLING_INSTANCE)) {
|
||||||
|
@ -63,4 +72,31 @@ public class BitArrayTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testClearingDoesntAllocate() {
|
||||||
|
CircuitBreakerService breaker = mock(CircuitBreakerService.class);
|
||||||
|
ByteSizeValue max = new ByteSizeValue(1, ByteSizeUnit.KB);
|
||||||
|
when(breaker.getBreaker(CircuitBreaker.REQUEST)).thenReturn(new NoopCircuitBreaker(CircuitBreaker.REQUEST) {
|
||||||
|
private long total = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double addEstimateBytesAndMaybeBreak(long bytes, String label) throws CircuitBreakingException {
|
||||||
|
total += bytes;
|
||||||
|
if (total > max.getBytes()) {
|
||||||
|
throw new CircuitBreakingException("test error", bytes, max.getBytes(), Durability.TRANSIENT);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long addWithoutBreaking(long bytes) {
|
||||||
|
total += bytes;
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
BigArrays bigArrays = new BigArrays(null, breaker, CircuitBreaker.REQUEST, true);
|
||||||
|
try (BitArray bitArray = new BitArray(1, bigArrays)) {
|
||||||
|
bitArray.clear(100000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue