use std::num::NonZeroUsize;
use std::sync::Arc;
use vortex_array::dtype::FieldPath;
use vortex_btrblocks::BtrBlocksCompressorBuilder;
use vortex_btrblocks::SchemeExt;
use vortex_btrblocks::schemes::integer::IntDictScheme;
use vortex_edition::ComponentKind;
use vortex_edition::EditionSessionExt;
use vortex_error::VortexExpect;
use vortex_layout::LayoutStrategy;
use vortex_layout::VTable;
use vortex_layout::layouts::buffered::BufferedStrategy;
use vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy;
use vortex_layout::layouts::collect::CollectStrategy;
use vortex_layout::layouts::compressed::CompressingStrategy;
use vortex_layout::layouts::compressed::CompressorPlugin;
use vortex_layout::layouts::dict::writer::DictStrategy;
use vortex_layout::layouts::flat::writer::FlatLayoutStrategy;
use vortex_layout::layouts::list::List;
use vortex_layout::layouts::list::writer::ListLayoutStrategy;
use vortex_layout::layouts::repartition::RepartitionStrategy;
use vortex_layout::layouts::repartition::RepartitionWriterOptions;
use vortex_layout::layouts::table::TableStrategy;
use vortex_layout::layouts::zoned::writer::ZonedLayoutOptions;
use vortex_layout::layouts::zoned::writer::ZonedStrategy;
use vortex_session::VortexSession;
use vortex_utils::aliases::hash_map::HashMap;
const ONE_MEG: u64 = 1 << 20;
enum CompressorConfig {
BtrBlocks(BtrBlocksCompressorBuilder),
Opaque(Arc<dyn CompressorPlugin>),
}
pub struct WriteStrategyBuilder {
compressor: CompressorConfig,
row_block_size: usize,
data_block_target_bytes: Option<u64>,
field_writers: HashMap<FieldPath, Arc<dyn LayoutStrategy>>,
flat_strategy: Option<Arc<dyn LayoutStrategy>>,
probe_compressor: Option<Arc<dyn CompressorPlugin>>,
use_list_layout: bool,
}
impl WriteStrategyBuilder {
pub fn from_session(session: &VortexSession) -> Self {
Self {
compressor: CompressorConfig::BtrBlocks(BtrBlocksCompressorBuilder::from_session(
session,
)),
row_block_size: 8192,
data_block_target_bytes: Some(ONE_MEG),
field_writers: HashMap::new(),
flat_strategy: None,
probe_compressor: None,
use_list_layout: session
.enabled_component_ids(ComponentKind::Layout)
.contains(&List.id()),
}
}
pub fn with_row_block_size(mut self, row_block_size: usize) -> Self {
self.row_block_size = row_block_size;
self
}
pub fn with_data_block_target_bytes(mut self, target_bytes: Option<u64>) -> Self {
self.data_block_target_bytes = target_bytes;
self
}
pub fn with_field_writer(
mut self,
field: impl Into<FieldPath>,
writer: Arc<dyn LayoutStrategy>,
) -> Self {
self.field_writers.insert(field.into(), writer);
self
}
pub fn with_flat_strategy(mut self, flat: Arc<dyn LayoutStrategy>) -> Self {
self.flat_strategy = Some(flat);
self
}
pub fn with_btrblocks_builder(mut self, builder: BtrBlocksCompressorBuilder) -> Self {
self.compressor = CompressorConfig::BtrBlocks(builder);
self
}
pub fn with_compressor<C: CompressorPlugin>(mut self, compressor: C) -> Self {
self.compressor = CompressorConfig::Opaque(Arc::new(compressor));
self
}
pub fn with_probe_compressor<C: CompressorPlugin>(mut self, compressor: C) -> Self {
self.probe_compressor = Some(Arc::new(compressor));
self
}
pub fn build(self) -> Arc<dyn LayoutStrategy> {
let flat: Arc<dyn LayoutStrategy> = if let Some(flat) = self.flat_strategy {
flat
} else {
Arc::new(FlatLayoutStrategy::default())
};
let compressor = self.compressor;
let chunked = ChunkedLayoutStrategy::new(Arc::clone(&flat));
let buffered = BufferedStrategy::new(chunked, 2 * ONE_MEG);
let data_compressor: Arc<dyn CompressorPlugin> = match &compressor {
CompressorConfig::BtrBlocks(builder) => Arc::new(
builder
.clone()
.exclude_schemes([IntDictScheme.id()])
.build(),
),
CompressorConfig::Opaque(compressor) => Arc::clone(compressor),
};
let compressing = CompressingStrategy::new(buffered, data_compressor);
let coalescing = RepartitionStrategy::new(
compressing,
RepartitionWriterOptions {
block_size_minimum: self.data_block_target_bytes.unwrap_or(0),
block_len_multiple: self.row_block_size,
block_size_target: self.data_block_target_bytes,
canonicalize: true,
},
);
let stats_compressor: Arc<dyn CompressorPlugin> = match compressor {
CompressorConfig::BtrBlocks(builder) => Arc::new(builder.build()),
CompressorConfig::Opaque(compressor) => compressor,
};
let compress_then_flat = CompressingStrategy::new(flat, Arc::clone(&stats_compressor));
let probe_compressor = if let Some(probe_compressor) = self.probe_compressor {
probe_compressor
} else {
Arc::clone(&stats_compressor)
};
let dict = DictStrategy::new(
coalescing.clone(),
compress_then_flat.clone(),
coalescing,
Default::default(),
probe_compressor,
);
let row_block_size = NonZeroUsize::new(self.row_block_size).vortex_expect("must be non 0");
let stats = ZonedStrategy::new(
dict,
compress_then_flat.clone(),
ZonedLayoutOptions {
block_size: row_block_size,
..Default::default()
},
);
let repartition = RepartitionStrategy::new(
stats,
RepartitionWriterOptions {
block_size_minimum: 0,
block_len_multiple: self.row_block_size,
block_size_target: None,
canonicalize: false,
},
);
let validity_strategy = CollectStrategy::new(compress_then_flat.clone());
let mut table_strategy =
TableStrategy::new(Arc::new(validity_strategy), Arc::new(repartition))
.with_field_writers(self.field_writers);
if self.use_list_layout {
table_strategy = table_strategy.with_list_layout_factory(
move |list_layout: ListLayoutStrategy| -> Arc<dyn LayoutStrategy> {
let zoned = ZonedStrategy::new(
list_layout,
compress_then_flat.clone(),
ZonedLayoutOptions {
block_size: row_block_size,
..Default::default()
},
);
Arc::new(RepartitionStrategy::new(
zoned,
RepartitionWriterOptions {
block_size_minimum: 0,
block_len_multiple: row_block_size.get(),
block_size_target: None,
canonicalize: false,
},
))
},
);
}
Arc::new(table_strategy)
}
}