diff --git a/src/bitmap/immutable.rs b/src/bitmap/immutable.rs index 0b1ab34ea42..ca978b6ea6c 100644 --- a/src/bitmap/immutable.rs +++ b/src/bitmap/immutable.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::{buffer::bytes::Bytes, buffer::MutableBuffer, trusted_len::TrustedLen}; use super::{ - utils::{get_bit, get_bit_unchecked, null_count, BitChunk, BitChunks, BitmapIter}, + utils::{fmt, get_bit, get_bit_unchecked, null_count, BitChunk, BitChunks, BitmapIter}, MutableBitmap, }; @@ -14,7 +14,7 @@ use super::{ /// * memory on this container is sharable across thread boundaries /// * Cloning [`Bitmap`] is `O(1)` /// * Slicing [`Bitmap`] is `O(1)` -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Bitmap { bytes: Arc>, // both are measured in bits. They are used to bound the bitmap to a region of Bytes. @@ -24,6 +24,12 @@ pub struct Bitmap { null_count: usize, } +impl std::fmt::Debug for Bitmap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt(&self.bytes, self.offset, self.length, f) + } +} + impl Default for Bitmap { fn default() -> Self { MutableBitmap::new().into() @@ -272,4 +278,12 @@ mod tests { assert_eq!(6, b.offset()); } + + #[test] + fn test_debug() { + let b = Bitmap::from([true, true, false, true, true, true, true, true, true]); + let b = b.slice(2, 7); + + assert_eq!(format!("{:?}", b), "[0b111110__, 0b_______1]"); + } } diff --git a/src/bitmap/mutable.rs b/src/bitmap/mutable.rs index 6255f3a0cb3..f096354caff 100644 --- a/src/bitmap/mutable.rs +++ b/src/bitmap/mutable.rs @@ -2,7 +2,7 @@ use std::iter::FromIterator; use crate::{buffer::MutableBuffer, trusted_len::TrustedLen}; -use super::utils::{get_bit, null_count, set, set_bit, BitmapIter}; +use super::utils::{fmt, get_bit, null_count, set, set_bit, BitmapIter}; use super::Bitmap; /// A container to store booleans. [`MutableBitmap`] is semantically equivalent @@ -12,12 +12,17 @@ use super::Bitmap; /// The main difference against [`Vec`] is that a bitmap cannot be represented as `&[bool]`. /// # Implementation /// This container is backed by [`MutableBuffer`]. -#[derive(Debug)] pub struct MutableBitmap { buffer: MutableBuffer, length: usize, } +impl std::fmt::Debug for MutableBitmap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt(&self.buffer, 0, self.len(), f) + } +} + impl PartialEq for MutableBitmap { fn eq(&self, other: &Self) -> bool { self.iter().eq(other.iter()) @@ -603,4 +608,22 @@ mod tests { assert_eq!(bitmap.len(), 6); assert_eq!(bitmap.buffer[0], 0b00101101); } + + #[test] + fn test_debug() { + let mut b = MutableBitmap::new(); + assert_eq!(format!("{:?}", b), "[]"); + b.push(true); + b.push(false); + assert_eq!(format!("{:?}", b), "[0b______01]"); + b.push(false); + b.push(false); + b.push(false); + b.push(false); + b.push(true); + b.push(true); + assert_eq!(format!("{:?}", b), "[0b11000001]"); + b.push(true); + assert_eq!(format!("{:?}", b), "[0b11000001, 0b_______1]"); + } } diff --git a/src/bitmap/utils/fmt.rs b/src/bitmap/utils/fmt.rs new file mode 100644 index 00000000000..ca6b417e60f --- /dev/null +++ b/src/bitmap/utils/fmt.rs @@ -0,0 +1,115 @@ +use std::fmt::Write; + +use super::is_set; + +/// Formats `bytes` taking into account an offset and length of the form +pub fn fmt( + bytes: &[u8], + offset: usize, + length: usize, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + assert!(offset < 8); + + f.write_char('[')?; + let mut remaining = length; + if remaining == 0 { + f.write_char(']')?; + return Ok(()); + } + + let first = bytes[0]; + let bytes = &bytes[1..]; + let empty_before = 8usize.saturating_sub(remaining + offset); + f.write_str("0b")?; + for _ in 0..empty_before { + f.write_char('_')?; + } + let until = std::cmp::min(8, offset + remaining); + for i in offset..until { + if is_set(first, offset + until - 1 - i) { + f.write_char('1')?; + } else { + f.write_char('0')?; + } + } + for _ in 0..offset { + f.write_char('_')?; + } + remaining -= until - offset; + + if remaining == 0 { + f.write_char(']')?; + return Ok(()); + } + + let number_of_bytes = remaining / 8; + for byte in &bytes[..number_of_bytes] { + f.write_str(", ")?; + f.write_fmt(format_args!("{:#010b}", byte))?; + } + remaining -= number_of_bytes * 8; + if remaining == 0 { + f.write_char(']')?; + return Ok(()); + } + + let last = bytes[std::cmp::min((length + offset + 7) / 8, bytes.len() - 1)]; + let remaining = (length + offset) % 8; + f.write_str(", ")?; + f.write_str("0b")?; + for _ in 0..(8 - remaining) { + f.write_char('_')?; + } + for i in 0..remaining { + if is_set(last, remaining - 1 - i) { + f.write_char('1')?; + } else { + f.write_char('0')?; + } + } + f.write_char(']') +} + +#[cfg(test)] +mod tests { + use super::*; + + struct A<'a>(&'a [u8], usize, usize); + impl<'a> std::fmt::Debug for A<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt(self.0, self.1, self.2, f) + } + } + + #[test] + fn test_debug() -> std::fmt::Result { + assert_eq!(format!("{:?}", A(&[1], 0, 0)), "[]"); + assert_eq!(format!("{:?}", A(&[0b11000001], 0, 8)), "[0b11000001]"); + assert_eq!( + format!("{:?}", A(&[0b11000001, 1], 0, 9)), + "[0b11000001, 0b_______1]" + ); + assert_eq!(format!("{:?}", A(&[1], 0, 2)), "[0b______01]"); + assert_eq!(format!("{:?}", A(&[1], 1, 2)), "[0b_____00_]"); + assert_eq!(format!("{:?}", A(&[1], 2, 2)), "[0b____00__]"); + assert_eq!(format!("{:?}", A(&[1], 3, 2)), "[0b___00___]"); + assert_eq!(format!("{:?}", A(&[1], 4, 2)), "[0b__00____]"); + assert_eq!(format!("{:?}", A(&[1], 5, 2)), "[0b_00_____]"); + assert_eq!(format!("{:?}", A(&[1], 6, 2)), "[0b00______]"); + assert_eq!( + format!("{:?}", A(&[0b11000001, 1], 1, 9)), + "[0b1100000_, 0b______01]" + ); + // extra bytes are ignored + assert_eq!( + format!("{:?}", A(&[0b11000001, 1, 1, 1], 1, 9)), + "[0b1100000_, 0b______01]" + ); + assert_eq!( + format!("{:?}", A(&[0b11000001, 1, 1], 2, 16)), + "[0b110000__, 0b00000001, 0b______01]" + ); + Ok(()) + } +} diff --git a/src/bitmap/utils/mod.rs b/src/bitmap/utils/mod.rs index 538c193b0b2..9adfaa937ff 100644 --- a/src/bitmap/utils/mod.rs +++ b/src/bitmap/utils/mod.rs @@ -1,9 +1,11 @@ mod chunk_iterator; +mod fmt; mod iterator; mod slice_iterator; mod zip_validity; pub use chunk_iterator::{BitChunk, BitChunkIterExact, BitChunks, BitChunksExact}; +pub use fmt::fmt; pub use iterator::BitmapIter; pub use slice_iterator::SlicesIterator; pub use zip_validity::{zip_validity, ZipValidity}; diff --git a/src/buffer/immutable.rs b/src/buffer/immutable.rs index 065bfa47ba1..476f8ffdf28 100644 --- a/src/buffer/immutable.rs +++ b/src/buffer/immutable.rs @@ -1,6 +1,4 @@ -use std::sync::Arc; -use std::{convert::AsRef, usize}; -use std::{fmt::Debug, iter::FromIterator}; +use std::{convert::AsRef, iter::FromIterator, sync::Arc, usize}; use crate::{trusted_len::TrustedLen, types::NativeType}; @@ -9,7 +7,7 @@ use super::mutable::MutableBuffer; /// Buffer represents a contiguous memory region that can be shared with other buffers and across /// thread boundaries. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq)] pub struct Buffer { /// the internal byte buffer. data: Arc>, @@ -22,6 +20,12 @@ pub struct Buffer { length: usize, } +impl std::fmt::Debug for Buffer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&**self, f) + } +} + impl Default for Buffer { #[inline] fn default() -> Self { @@ -226,4 +230,12 @@ mod tests { let ptr = buffer.as_ptr(); assert_eq!(unsafe { *ptr }, 1); } + + #[test] + fn test_debug() { + let buffer = Buffer::::from(&[0, 1, 2, 3]); + let buffer = buffer.slice(1, 2); + let a = format!("{:?}", buffer); + assert_eq!(a, "[1, 2]") + } } diff --git a/src/buffer/mutable.rs b/src/buffer/mutable.rs index 7cf5cdae394..a021c7a3438 100644 --- a/src/buffer/mutable.rs +++ b/src/buffer/mutable.rs @@ -1,7 +1,7 @@ use std::iter::FromIterator; +use std::mem::size_of; use std::ptr::NonNull; use std::usize; -use std::{fmt::Debug, mem::size_of}; use crate::types::NativeType; use crate::{alloc, trusted_len::TrustedLen}; @@ -32,7 +32,6 @@ fn capacity_multiple_of_64(capacity: usize) -> usize { /// let buffer: Buffer = buffer.into(); /// assert_eq!(buffer.as_slice(), &[256, 1]) /// ``` -#[derive(Debug)] pub struct MutableBuffer { // dangling iff capacity = 0 ptr: NonNull, @@ -41,6 +40,12 @@ pub struct MutableBuffer { capacity: usize, } +impl std::fmt::Debug for MutableBuffer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&**self, f) + } +} + impl PartialEq for MutableBuffer { fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() @@ -770,4 +775,11 @@ mod tests { let b: Bytes = b.into(); assert_eq!(b.as_ref(), &[0, 1, 2]); } + + #[test] + fn test_debug() { + let buffer = MutableBuffer::::from(&[0, 1, 2, 3]); + let a = format!("{:?}", buffer); + assert_eq!(a, "[0, 1, 2, 3]") + } }