use std::sync::Arc;
use std::sync::LazyLock;
use vortex_alp::ALP;
use vortex_alp::ALPRD;
use vortex_array::ArrayId;
use vortex_array::VTable;
use vortex_array::arrays::Bool;
use vortex_array::arrays::Chunked;
use vortex_array::arrays::Constant;
use vortex_array::arrays::Decimal;
use vortex_array::arrays::Dict;
use vortex_array::arrays::Extension;
use vortex_array::arrays::FixedSizeList;
use vortex_array::arrays::List;
use vortex_array::arrays::ListView;
use vortex_array::arrays::Masked;
use vortex_array::arrays::Null;
use vortex_array::arrays::Patched;
use vortex_array::arrays::Primitive;
use vortex_array::arrays::Struct;
use vortex_array::arrays::VarBin;
use vortex_array::arrays::VarBinView;
use vortex_array::arrays::patched::use_experimental_patches;
use vortex_array::dtype::FieldPath;
use vortex_btrblocks::BtrBlocksCompressorBuilder;
use vortex_btrblocks::SchemeExt;
use vortex_btrblocks::schemes::integer::IntDictScheme;
use vortex_bytebool::ByteBool;
use vortex_datetime_parts::DateTimeParts;
use vortex_decimal_byte_parts::DecimalByteParts;
use vortex_fastlanes::BitPacked;
use vortex_fastlanes::Delta;
use vortex_fastlanes::FoR;
use vortex_fastlanes::RLE;
use vortex_fsst::FSST;
use vortex_layout::LayoutStrategy;
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::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_pco::Pco;
use vortex_runend::RunEnd;
use vortex_sequence::Sequence;
use vortex_sparse::Sparse;
use vortex_utils::aliases::hash_map::HashMap;
use vortex_utils::aliases::hash_set::HashSet;
use vortex_zigzag::ZigZag;
#[cfg(feature = "zstd")]
use vortex_zstd::Zstd;
#[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
use vortex_zstd::ZstdBuffers;
const ONE_MEG: u64 = 1 << 20;
pub static ALLOWED_ENCODINGS: LazyLock<HashSet<ArrayId>> = LazyLock::new(|| {
let mut allowed = HashSet::new();
allowed.insert(Null.id());
allowed.insert(Bool.id());
allowed.insert(Primitive.id());
allowed.insert(Decimal.id());
allowed.insert(VarBin.id());
allowed.insert(VarBinView.id());
allowed.insert(List.id());
allowed.insert(ListView.id());
allowed.insert(FixedSizeList.id());
allowed.insert(Struct.id());
allowed.insert(Extension.id());
allowed.insert(Chunked.id());
allowed.insert(Constant.id());
allowed.insert(Masked.id());
allowed.insert(Dict.id());
allowed.insert(ALP.id());
allowed.insert(ALPRD.id());
allowed.insert(BitPacked.id());
allowed.insert(ByteBool.id());
allowed.insert(DateTimeParts.id());
allowed.insert(DecimalByteParts.id());
allowed.insert(Delta.id());
allowed.insert(FoR.id());
allowed.insert(FSST.id());
allowed.insert(Pco.id());
allowed.insert(RLE.id());
allowed.insert(RunEnd.id());
allowed.insert(Sequence.id());
allowed.insert(Sparse.id());
allowed.insert(ZigZag.id());
if use_experimental_patches() {
allowed.insert(Patched.id());
}
#[cfg(feature = "zstd")]
allowed.insert(Zstd.id());
#[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
allowed.insert(ZstdBuffers.id());
allowed
});
enum CompressorConfig {
BtrBlocks(BtrBlocksCompressorBuilder),
Opaque(Arc<dyn CompressorPlugin>),
}
pub struct WriteStrategyBuilder {
compressor: CompressorConfig,
row_block_size: usize,
field_writers: HashMap<FieldPath, Arc<dyn LayoutStrategy>>,
allow_encodings: Option<HashSet<ArrayId>>,
flat_strategy: Option<Arc<dyn LayoutStrategy>>,
}
impl Default for WriteStrategyBuilder {
fn default() -> Self {
Self {
compressor: CompressorConfig::BtrBlocks(BtrBlocksCompressorBuilder::default()),
row_block_size: 8192,
field_writers: HashMap::new(),
allow_encodings: Some(ALLOWED_ENCODINGS.clone()),
flat_strategy: None,
}
}
}
impl WriteStrategyBuilder {
pub fn with_row_block_size(mut self, row_block_size: usize) -> Self {
self.row_block_size = row_block_size;
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_allow_encodings(mut self, allow_encodings: HashSet<ArrayId>) -> Self {
self.allow_encodings = Some(allow_encodings);
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 build(self) -> Arc<dyn LayoutStrategy> {
let flat: Arc<dyn LayoutStrategy> = if let Some(flat) = self.flat_strategy {
flat
} else if let Some(allow_encodings) = self.allow_encodings {
Arc::new(FlatLayoutStrategy::default().with_allow_encodings(allow_encodings))
} else {
Arc::new(FlatLayoutStrategy::default())
};
let chunked = ChunkedLayoutStrategy::new(Arc::clone(&flat));
let buffered = BufferedStrategy::new(chunked, 2 * ONE_MEG);
let data_compressor: Arc<dyn CompressorPlugin> = match &self.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: ONE_MEG,
block_len_multiple: self.row_block_size,
block_size_target: Some(ONE_MEG),
canonicalize: true,
},
);
let stats_compressor: Arc<dyn CompressorPlugin> = match self.compressor {
CompressorConfig::BtrBlocks(builder) => Arc::new(builder.build()),
CompressorConfig::Opaque(compressor) => compressor,
};
let compress_then_flat = CompressingStrategy::new(flat, stats_compressor);
let dict = DictStrategy::new(
coalescing.clone(),
compress_then_flat.clone(),
coalescing,
Default::default(),
);
let stats = ZonedStrategy::new(
dict,
compress_then_flat.clone(),
ZonedLayoutOptions {
block_size: self.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);
let table_strategy = TableStrategy::new(Arc::new(validity_strategy), Arc::new(repartition))
.with_field_writers(self.field_writers);
Arc::new(table_strategy)
}
}