diff --git a/croaring/src/bitmap/imp.rs b/croaring/src/bitmap/imp.rs index 1529b96..b946e8e 100644 --- a/croaring/src/bitmap/imp.rs +++ b/croaring/src/bitmap/imp.rs @@ -1,7 +1,7 @@ use crate::Bitset; use ffi::roaring_bitmap_t; use std::convert::TryInto; -use std::ffi::c_void; +use std::ffi::{c_void, CStr}; use std::ops::{Bound, RangeBounds}; use std::{mem, ptr}; @@ -1469,6 +1469,40 @@ impl Bitmap { let success = unsafe { ffi::roaring_bitmap_to_bitset(&self.bitmap, bitset.as_raw_mut()) }; success.then_some(bitset) } + + /// Ensure the bitmap is internally valid + /// + /// This is useful for development, but is not needed for normal use: + /// bitmaps should _always_ be internally valid. + /// + /// # Errors + /// + /// Returns an error if the bitmap is not valid, with a description of the problem. + /// + /// # Examples + /// + /// ``` + /// use croaring::Bitmap; + /// + /// let bitmap = Bitmap::from_range(0..100); + /// bitmap.internal_validate().unwrap(); + /// ``` + #[inline] + #[doc(alias = "roaring_bitmap_internal_validate")] + #[doc(hidden)] + pub fn internal_validate(&self) -> Result<(), String> { + let mut error_str = ptr::null(); + let valid = unsafe { ffi::roaring_bitmap_internal_validate(&self.bitmap, &mut error_str) }; + if valid { + Ok(()) + } else { + if error_str.is_null() { + return Err(String::from("Unknown error")); + } + let reason = unsafe { CStr::from_ptr(error_str) }; + Err(reason.to_string_lossy().into_owned()) + } + } } fn range_to_inclusive>(range: R) -> (u32, u32) { diff --git a/fuzz/fuzz_targets/against_bitvec.rs b/fuzz/fuzz_targets/against_bitvec.rs index 938aa54..895a8f6 100644 --- a/fuzz/fuzz_targets/against_bitvec.rs +++ b/fuzz/fuzz_targets/against_bitvec.rs @@ -151,6 +151,7 @@ impl ReadBitmapOp { b.add_offset(i); } } + b.internal_validate().unwrap(); } } @@ -246,6 +247,7 @@ impl BitmapCompOperation { } } } + rhs.internal_validate().unwrap(); } } diff --git a/fuzz/fuzz_targets/arbitrary_ops/mod.rs b/fuzz/fuzz_targets/arbitrary_ops/mod.rs index a522134..d509d35 100644 --- a/fuzz/fuzz_targets/arbitrary_ops/mod.rs +++ b/fuzz/fuzz_targets/arbitrary_ops/mod.rs @@ -134,7 +134,9 @@ impl MutableBitmapOperation { b.add_range(start..=end) } } + b.internal_validate().unwrap(); b.remove_range(MAX_NUM..); + b.internal_validate().unwrap(); } } @@ -142,7 +144,7 @@ impl BitmapCompOperation { pub fn on_roaring(&self, lhs: &mut Bitmap, rhs: &Bitmap) { match *self { BitmapCompOperation::Eq => { - drop(lhs == rhs); + _ = lhs == rhs; assert_eq!(lhs, lhs); } BitmapCompOperation::IsSubset => { @@ -165,6 +167,7 @@ impl BitmapCompOperation { assert_eq!(lhs.and(lhs), *lhs); let res = lhs.and(rhs); + res.internal_validate().unwrap(); assert_eq!(res.cardinality(), lhs.and_cardinality(rhs)); lhs.and_inplace(rhs); assert_eq!(*lhs, res); @@ -173,6 +176,7 @@ impl BitmapCompOperation { assert_eq!(lhs.or(lhs), *lhs); let res = lhs.or(rhs); + res.internal_validate().unwrap(); assert_eq!(res.cardinality(), lhs.or_cardinality(rhs)); assert_eq!(res, Bitmap::fast_or(&[lhs, rhs])); assert_eq!(res, Bitmap::fast_or_heap(&[lhs, rhs])); @@ -184,6 +188,7 @@ impl BitmapCompOperation { assert!(lhs.xor(lhs).is_empty()); let res = lhs.xor(rhs); + res.internal_validate().unwrap(); assert_eq!(res.cardinality(), lhs.xor_cardinality(rhs)); assert_eq!(res, Bitmap::fast_xor(&[lhs, rhs])); @@ -194,11 +199,14 @@ impl BitmapCompOperation { assert!(lhs.andnot(lhs).is_empty()); let res = lhs.andnot(rhs); + res.internal_validate().unwrap(); assert_eq!(res.cardinality(), lhs.andnot_cardinality(rhs)); lhs.andnot_inplace(rhs); assert_eq!(*lhs, res); } } + lhs.internal_validate().unwrap(); + rhs.internal_validate().unwrap(); } }