Skip to content

Commit 6b6cf24

Browse files
authored
Merge pull request #100 from Dr-Emann/fuzz_correctness
Add a fuzzing target to check for correctness against a bitvec
2 parents 6a186d2 + df69c73 commit 6b6cf24

File tree

11 files changed

+518
-238
lines changed

11 files changed

+518
-238
lines changed

croaring-sys/CRoaring/bindgen_bundled_version.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
/* automatically generated by rust-bindgen 0.64.0 */
1+
/* automatically generated by rust-bindgen 0.63.0 */
22

3-
pub const ROARING_VERSION: &[u8; 6usize] = b"0.9.6\0";
3+
pub const ROARING_VERSION: &[u8; 6usize] = b"0.9.8\0";
44
pub const ROARING_VERSION_MAJOR: _bindgen_ty_1 = 0;
55
pub const ROARING_VERSION_MINOR: _bindgen_ty_1 = 9;
6-
pub const ROARING_VERSION_REVISION: _bindgen_ty_1 = 6;
6+
pub const ROARING_VERSION_REVISION: _bindgen_ty_1 = 8;
77
pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
88
#[doc = " Roaring arrays are array-based key-value pairs having containers as values\n and 16-bit integer keys. A roaring bitmap might be implemented as such."]
99
#[repr(C)]

croaring-sys/CRoaring/roaring.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// !!! DO NOT EDIT - THIS IS AN AUTO-GENERATED FILE !!!
2-
// Created by amalgamation.sh on 2023-02-11T22:19:22Z
2+
// Created by amalgamation.sh on 2023-02-16T17:16:21Z
33

44
/*
55
* The CRoaring project is under a dual license (Apache/MIT).
@@ -18041,8 +18041,7 @@ bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, uint64_t range_sta
1804118041
}
1804218042
int32_t is = ra_get_index(&r->high_low_container, hb_rs);
1804318043
int32_t ie = ra_get_index(&r->high_low_container, hb_re);
18044-
ie = (ie < 0 ? -ie - 1 : ie);
18045-
if ((is < 0) || ((ie - is) != span) || ie >= hlc_sz) {
18044+
if ((ie < 0) || (is < 0) || ((ie - is) != span) || ie >= hlc_sz) {
1804618045
return false;
1804718046
}
1804818047
const uint32_t lb_rs = range_start & 0xFFFF;

croaring-sys/CRoaring/roaring.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// !!! DO NOT EDIT - THIS IS AN AUTO-GENERATED FILE !!!
2-
// Created by amalgamation.sh on 2023-02-11T22:19:22Z
2+
// Created by amalgamation.sh on 2023-02-16T17:16:21Z
33

44
/*
55
* The CRoaring project is under a dual license (Apache/MIT).
@@ -58,11 +58,11 @@
5858
// /include/roaring/roaring_version.h automatically generated by release.py, do not change by hand
5959
#ifndef ROARING_INCLUDE_ROARING_VERSION
6060
#define ROARING_INCLUDE_ROARING_VERSION
61-
#define ROARING_VERSION "0.9.6"
61+
#define ROARING_VERSION "0.9.8"
6262
enum {
6363
ROARING_VERSION_MAJOR = 0,
6464
ROARING_VERSION_MINOR = 9,
65-
ROARING_VERSION_REVISION = 6
65+
ROARING_VERSION_REVISION = 8
6666
};
6767
#endif // ROARING_INCLUDE_ROARING_VERSION
6868
/* end file include/roaring/roaring_version.h */

croaring-sys/CRoaring/roaring.hh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// !!! DO NOT EDIT - THIS IS AN AUTO-GENERATED FILE !!!
2-
// Created by amalgamation.sh on 2023-02-11T22:19:22Z
2+
// Created by amalgamation.sh on 2023-02-16T17:16:21Z
33

44
/*
55
* The CRoaring project is under a dual license (Apache/MIT).
@@ -929,8 +929,8 @@ inline RoaringSetBitForwardIterator &Roaring::end() const {
929929
#ifndef INCLUDE_ROARING_64_MAP_HH_
930930
#define INCLUDE_ROARING_64_MAP_HH_
931931

932-
#include <inttypes.h>
933932
#include <algorithm>
933+
#include <cinttypes> // PRIu64 macro
934934
#include <cstdarg> // for va_list handling in bitmapOf()
935935
#include <cstdio> // for std::printf() in the printf() method
936936
#include <cstring> // for std::memcpy()

croaring/src/bitmap/imp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl Bitmap {
2222
const _: () = assert!(
2323
ffi::ROARING_VERSION_MAJOR == 0
2424
&& ffi::ROARING_VERSION_MINOR == 9
25-
&& ffi::ROARING_VERSION_REVISION == 6
25+
&& ffi::ROARING_VERSION_REVISION == 8
2626
);
2727
ffi::roaring_free(p as *mut _);
2828
result

croaring/src/bitmap/ops.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl Drop for Bitmap {
8484
const _: () = assert!(
8585
ffi::ROARING_VERSION_MAJOR == 0
8686
&& ffi::ROARING_VERSION_MINOR == 9
87-
&& ffi::ROARING_VERSION_REVISION == 6
87+
&& ffi::ROARING_VERSION_REVISION == 8
8888
);
8989

9090
// Per https://github.com/RoaringBitmap/CRoaring/blob/4f8dbdb0cc884626b20ef0cc9e891f701fe157cf/cpp/roaring.hh#L182

croaring/src/bitmap/view.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl<'a> BitmapView<'a> {
2929
const _: () = assert!(
3030
ffi::ROARING_VERSION_MAJOR == 0
3131
&& ffi::ROARING_VERSION_MINOR == 9
32-
&& ffi::ROARING_VERSION_REVISION == 6
32+
&& ffi::ROARING_VERSION_REVISION == 8
3333
);
3434

3535
assert!(!p.is_null());

fuzz/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ cargo-fuzz = true
99

1010
[dependencies]
1111
libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] }
12+
bitvec = "1.0.1"
1213

1314
croaring = { path = "../croaring" }
1415

@@ -25,3 +26,9 @@ name = "fuzz_ops"
2526
path = "fuzz_targets/fuzz_ops.rs"
2627
test = false
2728
doc = false
29+
30+
[[bin]]
31+
name = "against_bitvec"
32+
path = "fuzz_targets/against_bitvec.rs"
33+
test = false
34+
doc = false

fuzz/fuzz_targets/against_bitvec.rs

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#![no_main]
2+
3+
use crate::arbitrary_ops::*;
4+
use bitvec::prelude::*;
5+
use croaring::Bitmap;
6+
use libfuzzer_sys::arbitrary;
7+
use libfuzzer_sys::arbitrary::Arbitrary;
8+
use libfuzzer_sys::fuzz_target;
9+
10+
mod arbitrary_ops;
11+
12+
fuzz_target!(|input: FuzzInput| {
13+
let mut lhs = Bitmap::create();
14+
let mut rhs = Bitmap::create();
15+
16+
let mut lhs_check = bitvec![0; 4 * 0x1_0000];
17+
let mut rhs_check = bitvec![0; 4 * 0x1_0000];
18+
19+
for op in input.lhs_ops {
20+
op.on_roaring(&mut lhs);
21+
op.on_bitvec(&mut lhs_check);
22+
check_equal(&lhs, &lhs_check);
23+
}
24+
for op in input.rhs_ops {
25+
op.on_roaring(&mut rhs);
26+
op.on_bitvec(&mut rhs_check);
27+
check_equal(&rhs, &rhs_check);
28+
}
29+
30+
for op in &input.comp_ops {
31+
op.on_roaring(&mut lhs, &rhs);
32+
op.on_bitvec(&mut lhs_check, &rhs);
33+
check_equal(&lhs, &lhs_check);
34+
}
35+
36+
for op in &input.view_ops {
37+
op.on_both(&rhs, &rhs_check);
38+
op.on_both(&lhs, &lhs_check);
39+
}
40+
});
41+
42+
#[derive(Arbitrary, Debug)]
43+
struct FuzzInput {
44+
lhs_ops: Vec<MutableBitmapOperation>,
45+
rhs_ops: Vec<MutableBitmapOperation>,
46+
comp_ops: Vec<BitmapCompOperation>,
47+
view_ops: Vec<ReadBitmapOp>,
48+
}
49+
50+
impl ReadBitmapOp {
51+
fn on_both(&self, b: &Bitmap, v: &BitSlice) {
52+
match *self {
53+
ReadBitmapOp::ContainsRange(ref r) => {
54+
assert_eq!(
55+
v[r.start().0 as usize..=r.end().0 as usize].all(),
56+
b.contains_range(r.start().0..=r.end().0)
57+
);
58+
}
59+
ReadBitmapOp::Contains(i) => {
60+
assert_eq!(v[i.0 as usize], b.contains(i.0));
61+
}
62+
ReadBitmapOp::RangeCardinality(ref r) => {
63+
assert_eq!(
64+
v[r.start().0 as usize..=r.end().0 as usize].count_ones() as u64,
65+
b.range_cardinality(r.start().0..=r.end().0)
66+
);
67+
}
68+
ReadBitmapOp::Cardinality => {
69+
assert_eq!(v.count_ones() as u64, b.cardinality());
70+
}
71+
ReadBitmapOp::Flip(ref r) => {
72+
b.flip(r.start().0..=r.end().0);
73+
}
74+
ReadBitmapOp::ToVec => {
75+
let vec_iter = b.to_vec();
76+
assert!(vec_iter.into_iter().eq(v.iter_ones().map(|i| i as u32)));
77+
}
78+
ReadBitmapOp::GetSerializedSizeInBytes => {
79+
b.get_serialized_size_in_bytes();
80+
}
81+
ReadBitmapOp::GetFrozenSerializedSizeInBytes => {
82+
b.get_frozen_serialized_size_in_bytes();
83+
}
84+
ReadBitmapOp::IsEmpty => {
85+
assert_eq!(v.not_any(), b.is_empty());
86+
}
87+
ReadBitmapOp::IntersectWithRange(ref r) => {
88+
assert_eq!(
89+
v[r.start().0 as usize..=r.end().0 as usize].any(),
90+
b.intersect_with_range(r.start().0..=r.end().0)
91+
);
92+
}
93+
ReadBitmapOp::Minimum => {
94+
assert_eq!(v.first_one().map(|i| i as u32), b.minimum());
95+
}
96+
ReadBitmapOp::Maximum => {
97+
assert_eq!(v.last_one().map(|i| i as u32), b.maximum());
98+
}
99+
ReadBitmapOp::Rank(i) => {
100+
assert_eq!(
101+
v.iter_ones().take_while(|&n| n <= i.0 as usize).count() as u64,
102+
b.rank(i.0),
103+
);
104+
}
105+
ReadBitmapOp::Select(i) => {
106+
assert_eq!(
107+
v.iter_ones().nth(i.0 as usize).map(|n| n as u32),
108+
b.select(i.0),
109+
);
110+
}
111+
ReadBitmapOp::Statistics => {
112+
b.statistics();
113+
}
114+
ReadBitmapOp::Clone => {
115+
drop(b.clone());
116+
}
117+
ReadBitmapOp::Debug => {
118+
use std::io::Write;
119+
write!(std::io::sink(), "{:?}", b).unwrap();
120+
}
121+
ReadBitmapOp::WithIter(ref iter_ops) => {
122+
let mut iter = b.iter();
123+
for op in iter_ops {
124+
match *op {
125+
IterOperation::ResetAtOrAfter(i) => {
126+
iter.reset_at_or_after(i);
127+
}
128+
IterOperation::ReadNext => {
129+
iter.next();
130+
}
131+
IterOperation::NextMany(n) => {
132+
let mut v = vec![0; n as usize];
133+
iter.next_many(&mut v);
134+
}
135+
}
136+
}
137+
}
138+
ReadBitmapOp::AddOffset(i) => {
139+
b.add_offset(i);
140+
}
141+
}
142+
}
143+
}
144+
145+
impl MutableBitmapOperation {
146+
fn on_bitvec(&self, b: &mut BitSlice) {
147+
match *self {
148+
MutableBitmapOperation::Add(i) | MutableBitmapOperation::AddChecked(i) => {
149+
b.set(i.0 as usize, true);
150+
}
151+
MutableBitmapOperation::AddMany(ref items) => {
152+
for i in items {
153+
b.set(i.0 as usize, true);
154+
}
155+
}
156+
MutableBitmapOperation::AddRange(ref r) => {
157+
b[r.start().0 as usize..=r.end().0 as usize].fill(true);
158+
}
159+
MutableBitmapOperation::RemoveRange(ref r) => {
160+
b[r.start().0 as usize..=r.end().0 as usize].fill(false);
161+
}
162+
MutableBitmapOperation::Clear => {
163+
b.fill(false);
164+
}
165+
MutableBitmapOperation::Remove(i) | MutableBitmapOperation::RemoveChecked(i) => {
166+
b.set(i.0 as usize, false);
167+
}
168+
MutableBitmapOperation::FlipInplace(ref r) => {
169+
let _ = !&mut b[r.start().0 as usize..=r.end().0 as usize];
170+
}
171+
MutableBitmapOperation::ShrinkToFit
172+
| MutableBitmapOperation::RunOptimize
173+
| MutableBitmapOperation::RemoveRunCompression => {}
174+
MutableBitmapOperation::MakeBitmap { key } => {
175+
if key < (MAX_NUM / 0x1_0000) as u16 {
176+
let key = usize::from(key);
177+
let start = key * 0x1_0000;
178+
let end = start + 9 * 1024;
179+
for i in (start..end).step_by(2) {
180+
b.set(i, true);
181+
}
182+
}
183+
}
184+
MutableBitmapOperation::MakeRange { key } => {
185+
if key < (MAX_NUM / 0x1_0000) as u16 {
186+
let key = usize::from(key);
187+
let start = key * 0x1_0000;
188+
let end = start + 0x0_FFFF;
189+
b[start..=end].fill(true);
190+
}
191+
}
192+
}
193+
}
194+
}
195+
196+
impl BitmapCompOperation {
197+
fn on_bitvec(&self, lhs: &mut BitSlice, rhs: &Bitmap) {
198+
match *self {
199+
BitmapCompOperation::Eq
200+
| BitmapCompOperation::IsSubset
201+
| BitmapCompOperation::IsStrictSubset
202+
| BitmapCompOperation::Intersect
203+
| BitmapCompOperation::JacardIndex => {}
204+
BitmapCompOperation::And => {
205+
let tmp = to_bitvec(rhs, lhs.len());
206+
*lhs &= &tmp;
207+
}
208+
BitmapCompOperation::Or => {
209+
for i in rhs.iter() {
210+
let i = i as usize;
211+
if i >= lhs.len() {
212+
break;
213+
}
214+
lhs.set(i, true);
215+
}
216+
}
217+
BitmapCompOperation::Xor => {
218+
for i in rhs.iter() {
219+
let i = i as usize;
220+
if i >= lhs.len() {
221+
break;
222+
}
223+
let old_val = *lhs.get(i).unwrap();
224+
lhs.set(i, !old_val);
225+
}
226+
}
227+
BitmapCompOperation::AndNot => {
228+
for i in rhs.iter() {
229+
let i = i as usize;
230+
if i >= lhs.len() {
231+
break;
232+
}
233+
lhs.set(i, false);
234+
}
235+
}
236+
}
237+
}
238+
}
239+
240+
fn to_bitvec(b: &Bitmap, max: usize) -> BitVec {
241+
let mut res = bitvec![0; max];
242+
for i in b.iter() {
243+
let i = i as usize;
244+
if i >= max {
245+
break;
246+
}
247+
res.set(i, true);
248+
}
249+
res
250+
}
251+
252+
fn check_equal(b: &Bitmap, v: &BitSlice) {
253+
let lhs = b.iter().take_while(|&i| i < v.len() as u32);
254+
let rhs = v.iter_ones().map(|i| i as u32);
255+
256+
assert!(lhs.eq(rhs), "{b:?}")
257+
}

0 commit comments

Comments
 (0)