| // Licensed to the Apache Software Foundation (ASF) under one or more | |
| // contributor license agreements. See the NOTICE file distributed with | |
| // this work for additional information regarding copyright ownership. | |
| // The ASF licenses this file to You under the Apache License, Version 2.0 | |
| // (the "License"); you may not use this file except in compliance with | |
| // the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| use std::io::{Read, Write}; | |
| use byteorder::{BE, ReadBytesExt, WriteBytesExt}; | |
| use datafusion::common::Result; | |
| use crate::assume; | |
| // native implementation of org.apache.spark.util.sketch.BitArray | |
| #[derive(Default, Clone)] | |
| pub struct SparkBitArray { | |
| data: Vec<u64>, | |
| } | |
| impl SparkBitArray { | |
| pub fn new(data: Vec<u64>) -> Self { | |
| Self { data } | |
| } | |
| pub fn new_with_num_bits(num_bits: usize) -> Self { | |
| assert!( | |
| num_bits > 0, | |
| "num_bits must be positive, but got {num_bits}" | |
| ); | |
| assert!( | |
| num_bits <= i32::MAX as usize, | |
| "cannot allocate enough space for {num_bits} bits" | |
| ); | |
| let data_len = num_bits.div_ceil(64); | |
| Self::new(vec![0; data_len]) | |
| } | |
| pub fn read_from(r: &mut impl Read) -> Result<Self> { | |
| let data_len = r.read_i32::<BE>()? as usize; | |
| let mut data = vec![0; data_len]; | |
| for datum in &mut data { | |
| *datum = r.read_i64::<BE>()? as u64; | |
| } | |
| Ok(Self::new(data)) | |
| } | |
| pub fn write_to(&self, w: &mut impl Write) -> Result<()> { | |
| w.write_i32::<BE>(self.data.len() as i32)?; | |
| for &datum in &self.data { | |
| w.write_i64::<BE>(datum as i64)?; | |
| } | |
| Ok(()) | |
| } | |
| pub fn set(&mut self, index: usize) { | |
| let data_offset = index >> 6; | |
| let bit_offset = index & 0b00111111; | |
| assume!(data_offset < self.data.len()); | |
| self.data[data_offset] |= 1 << bit_offset; | |
| } | |
| pub fn get(&self, index: usize) -> bool { | |
| let data_offset = index >> 6; | |
| let bit_offset = index & 0b00111111; | |
| assume!(data_offset < self.data.len()); | |
| let datum = &self.data[data_offset]; | |
| (datum >> bit_offset) & 1 == 1 | |
| } | |
| /// Combines the two BitArrays using bitwise OR | |
| pub fn put_all(&mut self, bit_array: &SparkBitArray) { | |
| assert_eq!(self.data.len(), bit_array.data.len()); | |
| for (datum, bit_array_datum) in self.data.iter_mut().zip(&bit_array.data) { | |
| *datum |= *bit_array_datum; | |
| } | |
| } | |
| pub fn and(&mut self, bit_array: &SparkBitArray) { | |
| assert_eq!(self.data.len(), bit_array.data.len()); | |
| for (datum, bit_array_datum) in self.data.iter_mut().zip(&bit_array.data) { | |
| *datum &= *bit_array_datum; | |
| } | |
| } | |
| pub fn mem_size(&self) -> usize { | |
| self.data.len() * 8 | |
| } | |
| pub fn bit_size(&self) -> usize { | |
| self.data.len() * 64 | |
| } | |
| pub fn true_count(&self) -> usize { | |
| self.data | |
| .iter() | |
| .map(|&datum| datum.count_ones() as usize) | |
| .sum() | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| // these test cases come from org.apache.spark.util.sketch.BitArraySuite | |
| use itertools::Itertools; | |
| use rand::{Rng, SeedableRng}; | |
| use super::*; | |
| #[test] | |
| #[should_panic] | |
| fn test_bit_array_error1() { | |
| SparkBitArray::new_with_num_bits(0); | |
| } | |
| #[test] | |
| #[should_panic] | |
| fn test_bit_array_error2() { | |
| SparkBitArray::new_with_num_bits(64 * i32::MAX as usize + 1); | |
| } | |
| #[test] | |
| fn test_bit_size() { | |
| assert_eq!(SparkBitArray::new_with_num_bits(64).bit_size(), 64); | |
| assert_eq!(SparkBitArray::new_with_num_bits(65).bit_size(), 128); | |
| assert_eq!(SparkBitArray::new_with_num_bits(127).bit_size(), 128); | |
| assert_eq!(SparkBitArray::new_with_num_bits(128).bit_size(), 128); | |
| } | |
| #[test] | |
| fn test_normal_operation() { | |
| // use a fixed seed to make the test predictable. | |
| let mut r = rand::rngs::StdRng::seed_from_u64(37); | |
| let mut bit_array = SparkBitArray::new_with_num_bits(320); | |
| let indexes: Vec<usize> = (1..=100) | |
| .map(|_| r.random_range(0..320) as usize) | |
| .unique() | |
| .collect(); | |
| indexes.iter().for_each(|&i| { | |
| bit_array.set(i); | |
| }); | |
| indexes.iter().for_each(|&i| assert!(bit_array.get(i))); | |
| } | |
| #[test] | |
| fn test_merge() { | |
| // use a fixed seed to make the test predictable. | |
| let mut r = rand::rngs::StdRng::seed_from_u64(37); | |
| let mut bit_array1 = SparkBitArray::new_with_num_bits(64 * 6); | |
| let mut bit_array2 = SparkBitArray::new_with_num_bits(64 * 6); | |
| let indexes1: Vec<usize> = (1..=100) | |
| .map(|_| r.random_range(0..64 * 6) as usize) | |
| .unique() | |
| .collect(); | |
| let indexes2: Vec<usize> = (1..=100) | |
| .map(|_| r.random_range(0..64 * 6) as usize) | |
| .unique() | |
| .collect(); | |
| indexes1.iter().for_each(|&i| { | |
| bit_array1.set(i); | |
| }); | |
| indexes2.iter().for_each(|&i| { | |
| bit_array2.set(i); | |
| }); | |
| bit_array1.put_all(&bit_array2); | |
| indexes1.iter().for_each(|&i| assert!(bit_array1.get(i))); | |
| indexes2.iter().for_each(|&i| assert!(bit_array1.get(i))); | |
| } | |
| } |