1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.bloomfilter;
18
19 import java.util.Arrays;
20 import java.util.Objects;
21 import java.util.function.IntPredicate;
22 import java.util.function.LongPredicate;
23
24
25
26
27
28
29 public final class SimpleBloomFilter implements BloomFilter<SimpleBloomFilter> {
30
31
32
33
34 private final long[] bitMap;
35
36
37
38
39 private final Shape shape;
40
41
42
43
44 private int cardinality;
45
46
47
48
49
50
51 public SimpleBloomFilter(final Shape shape) {
52 Objects.requireNonNull(shape, "shape");
53 this.shape = shape;
54 this.bitMap = BitMaps.newBitMap(shape);
55 this.cardinality = 0;
56 }
57
58
59
60
61
62
63 private SimpleBloomFilter(final SimpleBloomFilter source) {
64 this.shape = source.shape;
65 this.bitMap = source.bitMap.clone();
66 this.cardinality = source.cardinality;
67 }
68
69 @Override
70 public long[] asBitMapArray() {
71 return Arrays.copyOf(bitMap, bitMap.length);
72 }
73
74 @Override
75 public int cardinality() {
76
77 int c = cardinality;
78 if (c < 0) {
79 cardinality = c = SetOperations.cardinality(this);
80 }
81 return c;
82 }
83
84 @Override
85 public int characteristics() {
86 return 0;
87 }
88
89 @Override
90 public void clear() {
91 Arrays.fill(bitMap, 0L);
92 cardinality = 0;
93 }
94
95 @Override
96 public boolean contains(final IndexExtractor indexExtractor) {
97 return indexExtractor.processIndices(idx -> BitMaps.contains(bitMap, idx));
98 }
99
100
101
102
103
104
105 @Override
106 public SimpleBloomFilter copy() {
107 return new SimpleBloomFilter(this);
108 }
109
110 @Override
111 public Shape getShape() {
112 return shape;
113 }
114
115 @Override
116 public boolean isEmpty() {
117 return cardinality == 0 || processBitMaps(y -> y == 0);
118 }
119
120 @Override
121 public boolean merge(final BitMapExtractor bitMapExtractor) {
122 Objects.requireNonNull(bitMapExtractor, "bitMapExtractor");
123 try {
124 final int[] idx = new int[1];
125 bitMapExtractor.processBitMaps(value -> {
126 bitMap[idx[0]++] |= value;
127 return true;
128 });
129
130 idx[0]--;
131 final int idxLimit = BitMaps.getLongIndex(shape.getNumberOfBits());
132 if (idxLimit == idx[0]) {
133 final long excess = bitMap[idxLimit] >> shape.getNumberOfBits();
134 if (excess != 0) {
135 throw new IllegalArgumentException(
136 String.format("BitMapExtractor set a bit higher than the limit for the shape: %s", shape.getNumberOfBits()));
137 }
138 }
139 cardinality = -1;
140 } catch (final IndexOutOfBoundsException e) {
141 throw new IllegalArgumentException(String.format("BitMapExtractor should send at most %s maps", bitMap.length), e);
142 }
143 return true;
144 }
145
146 @Override
147 public boolean merge(final BloomFilter<?> other) {
148 Objects.requireNonNull(other, "other");
149 if ((other.characteristics() & SPARSE) != 0) {
150 merge((IndexExtractor) other);
151 } else {
152 merge((BitMapExtractor) other);
153 }
154 return true;
155 }
156
157 @Override
158 public boolean merge(final Hasher hasher) {
159 Objects.requireNonNull(hasher, "hasher");
160 return merge(hasher.indices(shape));
161 }
162
163 @Override
164 public boolean merge(final IndexExtractor indexExtractor) {
165 Objects.requireNonNull(indexExtractor, "indexExtractor");
166 indexExtractor.processIndices(idx -> {
167 if (idx < 0 || idx >= shape.getNumberOfBits()) {
168 throw new IllegalArgumentException(String.format("IndexExtractor should only send values in the range[0,%s)", shape.getNumberOfBits()));
169 }
170 BitMaps.set(bitMap, idx);
171 return true;
172 });
173 cardinality = -1;
174 return true;
175 }
176
177 @Override
178 public boolean processBitMapPairs(final BitMapExtractor other, final LongBiPredicate func) {
179 final CountingLongPredicate p = new CountingLongPredicate(bitMap, func);
180 return other.processBitMaps(p) && p.processRemaining();
181 }
182
183 @Override
184 public boolean processBitMaps(final LongPredicate consumer) {
185 Objects.requireNonNull(consumer, "consumer");
186 for (final long l : bitMap) {
187 if (!consumer.test(l)) {
188 return false;
189 }
190 }
191 return true;
192 }
193
194 @Override
195 public boolean processIndices(final IntPredicate consumer) {
196 Objects.requireNonNull(consumer, "consumer");
197 return IndexExtractor.fromBitMapExtractor(this).processIndices(consumer);
198 }
199 }