use std::io::{Read, Write};
use arrow::array::PrimitiveArray;
use arrow::bitmap::Bitmap;
use arrow::error::Result;
use byteorder::{LittleEndian, ReadBytesExt};
use crate::{
compression::{integer::RLE, is_valid, Compression, SAMPLE_COUNT, SAMPLE_SIZE},
write::WriteOptions,
};
use super::{compress_sample_ratio, DoubleCompression, DoubleStats, DoubleType};
impl<T: DoubleType> DoubleCompression<T> for RLE {
fn compress(
&self,
array: &PrimitiveArray<T>,
_stats: &DoubleStats<T>,
_write_options: &WriteOptions,
output: &mut Vec<u8>,
) -> Result<usize> {
let size = output.len();
self.compress_double(output, array.values().clone(), array.validity())?;
Ok(output.len() - size)
}
fn decompress(&self, input: &[u8], length: usize, output: &mut Vec<T>) -> Result<()> {
let _ = self.decompress_double(input, length, output)?;
Ok(())
}
fn to_compression(&self) -> Compression {
Compression::Rle
}
fn compress_ratio(&self, stats: &DoubleStats<T>) -> f64 {
#[cfg(debug_assertions)]
{
if option_env!("STRAWBOAT_RLE_COMPRESSION") == Some("1") {
return f64::MAX;
}
}
compress_sample_ratio(self, stats, SAMPLE_COUNT, SAMPLE_SIZE)
}
}
impl RLE {
pub fn compress_double<T: DoubleType, W: Write>(
&self,
w: &mut W,
values: impl IntoIterator<Item = T>,
validity: Option<&Bitmap>,
) -> Result<()> {
let mut seen_count: u32 = 0;
let mut last_value = T::default().as_order();
let mut all_null = true;
for (i, item) in values.into_iter().enumerate() {
let item = item.as_order();
if is_valid(&validity, i) {
if all_null {
all_null = false;
last_value = item;
seen_count += 1;
} else if last_value != item {
w.write_all(&seen_count.to_le_bytes())?;
w.write_all(T::from_order(last_value).to_le_bytes().as_ref())?;
last_value = item;
seen_count = 1;
} else {
seen_count += 1;
}
} else {
seen_count += 1;
}
}
if seen_count != 0 {
w.write_all(&seen_count.to_le_bytes())?;
w.write_all(T::from_order(last_value).to_le_bytes().as_ref())?;
}
Ok(())
}
pub fn decompress_double<'a, T: DoubleType>(
&self,
mut input: &'a [u8],
length: usize,
array: &mut Vec<T>,
) -> Result<&'a [u8]> {
let mut bs = vec![0u8; std::mem::size_of::<T>()];
let mut num_values = 0;
array.reserve(length);
loop {
let len = input.read_u32::<LittleEndian>()?;
input.read_exact(&mut bs)?;
let a: T::Bytes = match bs.as_slice().try_into() {
Ok(a) => a,
Err(_) => unreachable!(),
};
let t = T::from_le_bytes(a);
for _ in 0..len {
array.push(t);
}
num_values += len as usize;
if num_values >= length {
break;
}
}
Ok(input)
}
}