use std::sync::Arc;
use flatbuffers::FlatBufferBuilder;
use flatbuffers::WIPOffset;
use itertools::Itertools;
use vortex_array::dtype::DType;
use vortex_array::stats::StatsSet;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_ensure_eq;
use vortex_flatbuffers::FlatBufferRoot;
use vortex_flatbuffers::WriteFlatBuffer;
use vortex_flatbuffers::array::ArrayStats;
use vortex_flatbuffers::footer as fb;
use vortex_session::VortexSession;
#[derive(Clone, Debug)]
pub struct FileStatistics {
stats: Arc<[StatsSet]>,
dtypes: Arc<[DType]>,
}
impl FileStatistics {
pub fn new(stats: Arc<[StatsSet]>, dtypes: Arc<[DType]>) -> Self {
assert_eq!(
stats.len(),
dtypes.len(),
"stats and dtypes must have the same length"
);
Self { stats, dtypes }
}
pub fn new_with_dtype(stats: Arc<[StatsSet]>, file_dtype: &DType) -> Self {
if let DType::Struct(struct_fields, _) = file_dtype {
assert_eq!(
stats.len(),
struct_fields.nfields(),
"stats length must match number of struct fields"
);
let dtypes = struct_fields.fields().collect();
Self { stats, dtypes }
} else {
assert_eq!(
stats.len(),
1,
"non-struct dtype must have exactly 1 statistic"
);
Self {
stats,
dtypes: Arc::new([file_dtype.clone()]),
}
}
}
pub fn from_flatbuffer<'a>(
fb: &fb::FileStatistics<'a>,
file_dtype: &DType,
session: &VortexSession,
) -> VortexResult<Self> {
let field_stats = fb.field_stats().unwrap_or_default();
let mut array_stats: Vec<ArrayStats> = field_stats.iter().collect();
if let DType::Struct(struct_fields, _) = file_dtype {
vortex_ensure_eq!(array_stats.len(), struct_fields.nfields());
let stats_sets: Arc<[StatsSet]> = array_stats
.into_iter()
.zip(struct_fields.fields())
.map(|(array_stat, field_dtype)| {
StatsSet::from_flatbuffer(&array_stat, &field_dtype, session)
})
.try_collect()?;
let dtypes = struct_fields.fields().collect();
Ok(Self {
stats: stats_sets,
dtypes,
})
} else {
vortex_ensure_eq!(array_stats.len(), 1);
let array_stat = array_stats
.pop()
.vortex_expect("we just checked that there was 1 field");
let stats_set = StatsSet::from_flatbuffer(&array_stat, file_dtype, session)?;
Ok(Self {
stats: Arc::new([stats_set]),
dtypes: Arc::new([file_dtype.clone()]),
})
}
}
pub fn stats_sets(&self) -> &Arc<[StatsSet]> {
&self.stats
}
pub fn dtypes(&self) -> &Arc<[DType]> {
&self.dtypes
}
pub fn get(&self, field_idx: usize) -> (&StatsSet, &DType) {
(&self.stats[field_idx], &self.dtypes[field_idx])
}
}
impl<'a> IntoIterator for &'a FileStatistics {
type Item = (&'a StatsSet, &'a DType);
type IntoIter = std::iter::Zip<std::slice::Iter<'a, StatsSet>, std::slice::Iter<'a, DType>>;
fn into_iter(self) -> Self::IntoIter {
self.stats.iter().zip(self.dtypes.iter())
}
}
impl FlatBufferRoot for FileStatistics {}
impl WriteFlatBuffer for FileStatistics {
type Target<'a> = fb::FileStatistics<'a>;
fn write_flatbuffer<'fb>(
&self,
fbb: &mut FlatBufferBuilder<'fb>,
) -> VortexResult<WIPOffset<Self::Target<'fb>>> {
let field_stats = self
.stats_sets()
.iter()
.map(|s| s.write_flatbuffer(fbb))
.collect::<VortexResult<Vec<_>>>()?;
let field_stats = fbb.create_vector(field_stats.as_slice());
Ok(fb::FileStatistics::create(
fbb,
&fb::FileStatisticsArgs {
field_stats: Some(field_stats),
},
))
}
}