@@ -28,6 +28,8 @@ use roaring::RoaringTreemap;
2828use roaring:: treemap:: Iter ;
2929use smallvec:: SmallVec ;
3030
31+ pub use crate :: bitmap:: reader:: BitmapStats ;
32+
3133mod reader;
3234
3335// https://github.com/ClickHouse/ClickHouse/blob/516a6ed6f8bd8c5f6eed3a10e9037580b2fb6152/src/AggregateFunctions/AggregateFunctionGroupBitmapData.h#L914
@@ -776,6 +778,99 @@ pub fn bitmap_max(buf: &[u8]) -> Result<Option<u64>> {
776778 }
777779}
778780
781+ pub fn bitmap_stats ( buf : & [ u8 ] ) -> Result < BitmapStats > {
782+ if buf. is_empty ( ) {
783+ return Ok ( BitmapStats {
784+ len : 0 ,
785+ min : None ,
786+ max : None ,
787+ } ) ;
788+ }
789+ if is_hybrid_large ( buf) {
790+ Ok ( reader:: bitmap_stats ( & buf[ HYBRID_HEADER_LEN ..] ) ?)
791+ } else {
792+ let bm = deserialize_bitmap ( buf) ?;
793+ Ok ( BitmapStats {
794+ len : bm. len ( ) as u64 ,
795+ min : bm. min ( ) ,
796+ max : bm. max ( ) ,
797+ } )
798+ }
799+ }
800+
801+ pub fn bitmap_has_any ( lhs : & [ u8 ] , rhs : & [ u8 ] ) -> Result < bool > {
802+ if lhs. is_empty ( ) || rhs. is_empty ( ) {
803+ return Ok ( false ) ;
804+ }
805+
806+ // Stats fast path: range rejection
807+ // If lhs.max < rhs.min OR rhs.max < lhs.min, no overlap possible
808+ if is_hybrid_large ( lhs) && is_hybrid_large ( rhs) {
809+ let lhs_stats = reader:: bitmap_stats ( & lhs[ HYBRID_HEADER_LEN ..] ) ?;
810+ let rhs_stats = reader:: bitmap_stats ( & rhs[ HYBRID_HEADER_LEN ..] ) ?;
811+ if let ( Some ( lhs_min) , Some ( rhs_max) ) = ( lhs_stats. min , rhs_stats. max )
812+ && lhs_min > rhs_max
813+ {
814+ return Ok ( false ) ;
815+ }
816+ if let ( Some ( rhs_min) , Some ( lhs_max) ) = ( rhs_stats. min , lhs_stats. max )
817+ && rhs_min > lhs_max
818+ {
819+ return Ok ( false ) ;
820+ }
821+ Ok ( reader:: bitmap_has_any (
822+ & lhs[ HYBRID_HEADER_LEN ..] ,
823+ & rhs[ HYBRID_HEADER_LEN ..] ,
824+ ) ?)
825+ } else if is_hybrid_large ( lhs) || is_hybrid_large ( rhs) {
826+ // Normalize: large is the HybridLarge side, other is the non-HybridLarge side
827+ let ( large, other) = if is_hybrid_large ( lhs) {
828+ ( lhs, rhs)
829+ } else {
830+ ( rhs, lhs)
831+ } ;
832+
833+ // Stats fast path: range rejection
834+ let large_stats = reader:: bitmap_stats ( & large[ HYBRID_HEADER_LEN ..] ) ?;
835+ let other_stats = bitmap_stats ( other) ?;
836+ if let ( Some ( large_min) , Some ( other_max) ) = ( large_stats. min , other_stats. max )
837+ && large_min > other_max
838+ {
839+ return Ok ( false ) ;
840+ }
841+ if let ( Some ( other_min) , Some ( large_max) ) = ( other_stats. min , large_stats. max )
842+ && other_min > large_max
843+ {
844+ return Ok ( false ) ;
845+ }
846+
847+ // other is HybridSmall: iterate its (≤31) values through bitmap_contains on large.
848+ // Each bitmap_contains call scans prefix buckets from the start, causing redundant
849+ // work across iterations. This could be improved with a hint/cursor mechanism that
850+ // tracks the last scanned position, but since HybridSmall holds at most 31 values
851+ // the overhead is bounded (~μs-level). Revisit if this path becomes a bottleneck.
852+ if is_hybrid ( other) && !is_hybrid_large ( other) {
853+ let other_bm = deserialize_bitmap ( other) ?;
854+ for value in other_bm. iter ( ) {
855+ if bitmap_contains ( large, value) ? {
856+ return Ok ( true ) ;
857+ }
858+ }
859+ return Ok ( false ) ;
860+ }
861+
862+ // other is Legacy (potentially large): fallback to full deserialization
863+ let lhs_bm = deserialize_bitmap ( lhs) ?;
864+ let rhs_bm = deserialize_bitmap ( rhs) ?;
865+ Ok ( lhs_bm. intersection_len ( & rhs_bm) != 0 )
866+ } else {
867+ // Both else: deserialize both
868+ let lhs_bm = deserialize_bitmap ( lhs) ?;
869+ let rhs_bm = deserialize_bitmap ( rhs) ?;
870+ Ok ( lhs_bm. intersection_len ( & rhs_bm) != 0 )
871+ }
872+ }
873+
779874fn parse_bitmap_rhs ( buf : & [ u8 ] ) -> Result < BitmapRhsView < ' _ > > {
780875 if buf. is_empty ( ) {
781876 return Ok ( BitmapRhsView :: Empty ) ;
@@ -1607,4 +1702,195 @@ mod tests {
16071702 // Empty buffer
16081703 assert_eq ! ( bitmap_max( & [ ] ) . unwrap( ) , None ) ;
16091704 }
1705+
1706+ fn ground_truth_has_any ( lhs : & [ u8 ] , rhs : & [ u8 ] ) -> bool {
1707+ let lhs_bm = deserialize_bitmap ( lhs) . unwrap ( ) ;
1708+ let rhs_bm = deserialize_bitmap ( rhs) . unwrap ( ) ;
1709+ lhs_bm. intersection_len ( & rhs_bm) != 0
1710+ }
1711+
1712+ #[ test]
1713+ fn test_bitmap_has_any ( ) {
1714+ // Both HybridLarge with overlap: 0..50000 and 40000..90000 -> overlap at 40000-49999
1715+ let lhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1716+ let rhs = HybridBitmap :: from_iter ( 40000u64 ..90000 ) ;
1717+ let mut lhs_buf = Vec :: new ( ) ;
1718+ let mut rhs_buf = Vec :: new ( ) ;
1719+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1720+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1721+ assert_eq ! (
1722+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1723+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1724+ ) ;
1725+ assert ! ( bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1726+
1727+ // Both HybridLarge disjoint: 0..50000 and 100000..150000
1728+ let lhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1729+ let rhs = HybridBitmap :: from_iter ( 100000u64 ..150000 ) ;
1730+ let mut lhs_buf = Vec :: new ( ) ;
1731+ let mut rhs_buf = Vec :: new ( ) ;
1732+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1733+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1734+ assert_eq ! (
1735+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1736+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1737+ ) ;
1738+ assert ! ( !bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1739+
1740+ // Both HybridSmall with overlap: 0..31 and 20..51 -> overlap at 20-30
1741+ let lhs = HybridBitmap :: from_iter ( 0u64 ..31 ) ;
1742+ let rhs = HybridBitmap :: from_iter ( 20u64 ..51 ) ;
1743+ let mut lhs_buf = Vec :: new ( ) ;
1744+ let mut rhs_buf = Vec :: new ( ) ;
1745+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1746+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1747+ assert_eq ! (
1748+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1749+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1750+ ) ;
1751+ assert ! ( bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1752+
1753+ // Both HybridSmall disjoint: 0..15 and 20..35
1754+ let lhs = HybridBitmap :: from_iter ( 0u64 ..15 ) ;
1755+ let rhs = HybridBitmap :: from_iter ( 20u64 ..35 ) ;
1756+ let mut lhs_buf = Vec :: new ( ) ;
1757+ let mut rhs_buf = Vec :: new ( ) ;
1758+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1759+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1760+ assert_eq ! (
1761+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1762+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1763+ ) ;
1764+ assert ! ( !bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1765+
1766+ // Mixed (HybridLarge lhs, HybridSmall rhs) with overlap: 0..50000 and 10..41
1767+ let lhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1768+ let rhs = HybridBitmap :: from_iter ( 10u64 ..41 ) ;
1769+ let mut lhs_buf = Vec :: new ( ) ;
1770+ let mut rhs_buf = Vec :: new ( ) ;
1771+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1772+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1773+ assert_eq ! (
1774+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1775+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1776+ ) ;
1777+ assert ! ( bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1778+
1779+ // Mixed (HybridSmall lhs, HybridLarge rhs) with overlap: 10..41 and 0..50000
1780+ let lhs = HybridBitmap :: from_iter ( 10u64 ..41 ) ;
1781+ let rhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1782+ let mut lhs_buf = Vec :: new ( ) ;
1783+ let mut rhs_buf = Vec :: new ( ) ;
1784+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1785+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1786+ assert_eq ! (
1787+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1788+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1789+ ) ;
1790+ assert ! ( bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1791+
1792+ // Empty lhs
1793+ let rhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1794+ let mut rhs_buf = Vec :: new ( ) ;
1795+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1796+ assert ! ( !bitmap_has_any( & [ ] , & rhs_buf) . unwrap( ) ) ;
1797+
1798+ // Empty rhs
1799+ let lhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1800+ let mut lhs_buf = Vec :: new ( ) ;
1801+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1802+ assert ! ( !bitmap_has_any( & lhs_buf, & [ ] ) . unwrap( ) ) ;
1803+
1804+ // Both empty
1805+ assert ! ( !bitmap_has_any( & [ ] , & [ ] ) . unwrap( ) ) ;
1806+
1807+ // Mixed (Legacy lhs, HybridLarge rhs) with overlap
1808+ let mut lhs_tree = RoaringTreemap :: new ( ) ;
1809+ for v in [ 0u64 , 2500 , 40000 ] {
1810+ lhs_tree. insert ( v) ;
1811+ }
1812+ let rhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1813+ let mut lhs_buf = Vec :: new ( ) ;
1814+ let mut rhs_buf = Vec :: new ( ) ;
1815+ lhs_tree. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1816+ rhs. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1817+ assert_eq ! (
1818+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1819+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1820+ ) ;
1821+ assert ! ( bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1822+
1823+ // Mixed (HybridLarge lhs, Legacy rhs) with overlap
1824+ let lhs = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1825+ let mut rhs_tree = RoaringTreemap :: new ( ) ;
1826+ for v in [ 0u64 , 2500 , 40000 ] {
1827+ rhs_tree. insert ( v) ;
1828+ }
1829+ let mut lhs_buf = Vec :: new ( ) ;
1830+ let mut rhs_buf = Vec :: new ( ) ;
1831+ lhs. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1832+ rhs_tree. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1833+ assert_eq ! (
1834+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1835+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1836+ ) ;
1837+ assert ! ( bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1838+
1839+ // Both Legacy with overlap
1840+ let mut lhs_tree = RoaringTreemap :: new ( ) ;
1841+ for v in 0u64 ..100 {
1842+ lhs_tree. insert ( v) ;
1843+ }
1844+ let mut rhs_tree = RoaringTreemap :: new ( ) ;
1845+ for v in 50u64 ..150 {
1846+ rhs_tree. insert ( v) ;
1847+ }
1848+ let mut lhs_buf = Vec :: new ( ) ;
1849+ let mut rhs_buf = Vec :: new ( ) ;
1850+ lhs_tree. serialize_into ( & mut lhs_buf) . unwrap ( ) ;
1851+ rhs_tree. serialize_into ( & mut rhs_buf) . unwrap ( ) ;
1852+ assert_eq ! (
1853+ bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ,
1854+ ground_truth_has_any( & lhs_buf, & rhs_buf)
1855+ ) ;
1856+ assert ! ( bitmap_has_any( & lhs_buf, & rhs_buf) . unwrap( ) ) ;
1857+ }
1858+
1859+ #[ test]
1860+ fn test_bitmap_stats ( ) {
1861+ // HybridLarge stats
1862+ let large = HybridBitmap :: from_iter ( 0u64 ..50000 ) ;
1863+ let mut buf = Vec :: new ( ) ;
1864+ large. serialize_into ( & mut buf) . unwrap ( ) ;
1865+ let stats = bitmap_stats ( & buf) . unwrap ( ) ;
1866+ assert_eq ! ( stats. len, 50000 ) ;
1867+ assert_eq ! ( stats. min, Some ( 0 ) ) ;
1868+ assert_eq ! ( stats. max, Some ( 49999 ) ) ;
1869+
1870+ // HybridSmall stats
1871+ let small = HybridBitmap :: from_iter ( 100u64 ..131 ) ;
1872+ let mut buf = Vec :: new ( ) ;
1873+ small. serialize_into ( & mut buf) . unwrap ( ) ;
1874+ let stats = bitmap_stats ( & buf) . unwrap ( ) ;
1875+ assert_eq ! ( stats. len, 31 ) ;
1876+ assert_eq ! ( stats. min, Some ( 100 ) ) ;
1877+ assert_eq ! ( stats. max, Some ( 130 ) ) ;
1878+
1879+ // Empty stats
1880+ let stats = bitmap_stats ( & [ ] ) . unwrap ( ) ;
1881+ assert_eq ! ( stats. len, 0 ) ;
1882+ assert_eq ! ( stats. min, None ) ;
1883+ assert_eq ! ( stats. max, None ) ;
1884+
1885+ // Legacy stats
1886+ let mut tree = RoaringTreemap :: new ( ) ;
1887+ tree. insert ( 42 ) ;
1888+ tree. insert ( 100 ) ;
1889+ let mut buf = Vec :: new ( ) ;
1890+ tree. serialize_into ( & mut buf) . unwrap ( ) ;
1891+ let stats = bitmap_stats ( & buf) . unwrap ( ) ;
1892+ assert_eq ! ( stats. len, 2 ) ;
1893+ assert_eq ! ( stats. min, Some ( 42 ) ) ;
1894+ assert_eq ! ( stats. max, Some ( 100 ) ) ;
1895+ }
16101896}
0 commit comments