Skip to content

Commit 3d3f7e4

Browse files
committed
Implement forEachSetBit
1 parent afe456d commit 3d3f7e4

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

core/src/main/java/org/neo4j/gds/core/utils/paged/HugeAtomicPagedBitSet.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import java.util.concurrent.atomic.AtomicLongArray;
2626
import java.util.concurrent.atomic.AtomicReference;
27+
import java.util.function.LongConsumer;
2728

2829
public final class HugeAtomicPagedBitSet {
2930

@@ -149,6 +150,28 @@ public long cardinality() {
149150
return setBitCount;
150151
}
151152

153+
public void forEachSetBit(LongConsumer consumer) {
154+
final Pages pages = this.pages.get();
155+
final long pageCount = pages.length();
156+
final long pageSize = this.pageSize;
157+
158+
long base = 0;
159+
160+
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
161+
var page = pages.getPage(pageIndex);
162+
for (int wordIndex = 0; wordIndex < pageSize; wordIndex++) {
163+
long word = page.get(wordIndex);
164+
165+
while (word != 0) {
166+
long next = Long.numberOfTrailingZeros(word);
167+
consumer.accept(Long.SIZE * (base + wordIndex) + next);
168+
word = word ^ Long.lowestOneBit(word);
169+
}
170+
}
171+
base += pageSize;
172+
}
173+
}
174+
152175
public long capacity() {
153176
return pages.get().length() * (1L << pageShift);
154177
}

core/src/test/java/org/neo4j/gds/core/utils/paged/HugeAtomicPagedBitSetTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@
2626
import org.neo4j.gds.core.concurrency.RunWithConcurrency;
2727
import org.neo4j.gds.core.utils.partition.PartitionUtils;
2828

29+
import java.util.HashSet;
2930
import java.util.List;
3031
import java.util.Optional;
32+
import java.util.concurrent.ThreadLocalRandom;
33+
import java.util.stream.Collectors;
34+
import java.util.stream.IntStream;
3135
import java.util.stream.Stream;
3236

3337
import static org.assertj.core.api.Assertions.assertThat;
@@ -120,6 +124,25 @@ void testCardinality(HugeAtomicPagedBitSet atomicBitSet) {
120124
assertThat(atomicBitSet.cardinality()).isEqualTo(3);
121125
}
122126

127+
@ParameterizedTest
128+
@MethodSource("bitsets")
129+
void testForEachSetBit(HugeAtomicPagedBitSet atomicBitSet) {
130+
var rng = ThreadLocalRandom.current();
131+
long bound = 42 * (1L << PAGE_SHIFT_BITS) + 42;
132+
133+
var expected = IntStream
134+
.range(0, 10_000)
135+
.mapToLong(__ -> rng.nextLong(0, bound))
136+
.boxed()
137+
.peek(atomicBitSet::set)
138+
.collect(Collectors.toSet());
139+
140+
var actual = new HashSet<Long>();
141+
atomicBitSet.forEachSetBit(actual::add);
142+
143+
assertThat(actual).isEqualTo(expected);
144+
}
145+
123146
@Test
124147
void writingAndGrowingShouldBeThreadSafe() {
125148
int concurrency = 8;

0 commit comments

Comments
 (0)