1use std::num::NonZeroUsize;
7use std::sync::Arc;
8use std::sync::LazyLock;
9
10use vortex_alp::ALP;
11use vortex_alp::ALPRD;
12use vortex_array::ArrayId;
13use vortex_array::VTable;
14use vortex_array::arrays::Bool;
15use vortex_array::arrays::Chunked;
16use vortex_array::arrays::Constant;
17use vortex_array::arrays::Decimal;
18use vortex_array::arrays::Dict;
19use vortex_array::arrays::Extension;
20use vortex_array::arrays::FixedSizeList;
21use vortex_array::arrays::List;
22use vortex_array::arrays::ListView;
23use vortex_array::arrays::Masked;
24use vortex_array::arrays::Null;
25use vortex_array::arrays::Patched;
26use vortex_array::arrays::Primitive;
27use vortex_array::arrays::Struct;
28use vortex_array::arrays::VarBin;
29use vortex_array::arrays::VarBinView;
30use vortex_array::arrays::Variant;
31use vortex_array::arrays::patched::use_experimental_patches;
32use vortex_array::dtype::FieldPath;
33use vortex_btrblocks::BtrBlocksCompressorBuilder;
34use vortex_btrblocks::SchemeExt;
35use vortex_btrblocks::schemes::integer::IntDictScheme;
36use vortex_bytebool::ByteBool;
37use vortex_datetime_parts::DateTimeParts;
38use vortex_decimal_byte_parts::DecimalByteParts;
39use vortex_error::VortexExpect;
40use vortex_fastlanes::BitPacked;
41use vortex_fastlanes::Delta;
42use vortex_fastlanes::FoR;
43use vortex_fastlanes::RLE;
44use vortex_fsst::FSST;
45use vortex_layout::LayoutStrategy;
46use vortex_layout::layouts::buffered::BufferedStrategy;
47use vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy;
48use vortex_layout::layouts::collect::CollectStrategy;
49use vortex_layout::layouts::compressed::CompressingStrategy;
50use vortex_layout::layouts::compressed::CompressorPlugin;
51use vortex_layout::layouts::dict::writer::DictStrategy;
52use vortex_layout::layouts::flat::writer::FlatLayoutStrategy;
53use vortex_layout::layouts::repartition::RepartitionStrategy;
54use vortex_layout::layouts::repartition::RepartitionWriterOptions;
55use vortex_layout::layouts::table::TableStrategy;
56use vortex_layout::layouts::zoned::writer::ZonedLayoutOptions;
57use vortex_layout::layouts::zoned::writer::ZonedStrategy;
58#[cfg(feature = "unstable_encodings")]
59use vortex_onpair::OnPair;
60use vortex_pco::Pco;
61use vortex_runend::RunEnd;
62use vortex_sequence::Sequence;
63use vortex_sparse::Sparse;
64use vortex_utils::aliases::hash_map::HashMap;
65use vortex_utils::aliases::hash_set::HashSet;
66use vortex_zigzag::ZigZag;
67#[cfg(feature = "zstd")]
68use vortex_zstd::Zstd;
69#[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
70use vortex_zstd::ZstdBuffers;
71
72const ONE_MEG: u64 = 1 << 20;
73
74pub static ALLOWED_ENCODINGS: LazyLock<HashSet<ArrayId>> = LazyLock::new(|| {
79 let mut allowed = HashSet::new();
80
81 allowed.insert(Null.id());
83 allowed.insert(Bool.id());
84 allowed.insert(Primitive.id());
85 allowed.insert(Decimal.id());
86 allowed.insert(VarBin.id());
87 allowed.insert(VarBinView.id());
88 allowed.insert(List.id());
89 allowed.insert(ListView.id());
90 allowed.insert(FixedSizeList.id());
91 allowed.insert(Struct.id());
92 allowed.insert(Extension.id());
93 allowed.insert(Chunked.id());
94 allowed.insert(Constant.id());
95 allowed.insert(Masked.id());
96 allowed.insert(Dict.id());
97 allowed.insert(Variant.id());
98
99 allowed.insert(ALP.id());
101 allowed.insert(ALPRD.id());
102 allowed.insert(BitPacked.id());
103 allowed.insert(ByteBool.id());
104 allowed.insert(DateTimeParts.id());
105 allowed.insert(DecimalByteParts.id());
106 allowed.insert(Delta.id());
107 allowed.insert(FoR.id());
108 allowed.insert(FSST.id());
109 #[cfg(feature = "unstable_encodings")]
110 allowed.insert(OnPair.id());
111 allowed.insert(Pco.id());
112 allowed.insert(RLE.id());
113 allowed.insert(RunEnd.id());
114 allowed.insert(Sequence.id());
115 allowed.insert(Sparse.id());
116 allowed.insert(ZigZag.id());
117
118 if use_experimental_patches() {
121 allowed.insert(Patched.id());
122 }
123
124 #[cfg(feature = "zstd")]
125 allowed.insert(Zstd.id());
126 #[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
127 allowed.insert(ZstdBuffers.id());
128
129 allowed
130});
131
132enum CompressorConfig {
134 BtrBlocks(BtrBlocksCompressorBuilder),
138 Opaque(Arc<dyn CompressorPlugin>),
140}
141
142pub struct WriteStrategyBuilder {
154 compressor: CompressorConfig,
155 row_block_size: usize,
156 field_writers: HashMap<FieldPath, Arc<dyn LayoutStrategy>>,
157 allow_encodings: Option<HashSet<ArrayId>>,
158 flat_strategy: Option<Arc<dyn LayoutStrategy>>,
159}
160
161impl Default for WriteStrategyBuilder {
162 fn default() -> Self {
165 Self {
166 compressor: CompressorConfig::BtrBlocks(BtrBlocksCompressorBuilder::default()),
167 row_block_size: 8192,
168 field_writers: HashMap::new(),
169 allow_encodings: Some(ALLOWED_ENCODINGS.clone()),
170 flat_strategy: None,
171 }
172 }
173}
174
175impl WriteStrategyBuilder {
176 pub fn with_row_block_size(mut self, row_block_size: usize) -> Self {
181 self.row_block_size = row_block_size;
182 self
183 }
184
185 pub fn with_field_writer(
190 mut self,
191 field: impl Into<FieldPath>,
192 writer: Arc<dyn LayoutStrategy>,
193 ) -> Self {
194 self.field_writers.insert(field.into(), writer);
195 self
196 }
197
198 pub fn with_allow_encodings(mut self, allow_encodings: HashSet<ArrayId>) -> Self {
203 self.allow_encodings = Some(allow_encodings);
204 self
205 }
206
207 pub fn with_flat_strategy(mut self, flat: Arc<dyn LayoutStrategy>) -> Self {
212 self.flat_strategy = Some(flat);
213 self
214 }
215
216 pub fn with_btrblocks_builder(mut self, builder: BtrBlocksCompressorBuilder) -> Self {
221 self.compressor = CompressorConfig::BtrBlocks(builder);
222 self
223 }
224
225 pub fn with_compressor<C: CompressorPlugin>(mut self, compressor: C) -> Self {
230 self.compressor = CompressorConfig::Opaque(Arc::new(compressor));
231 self
232 }
233
234 pub fn build(self) -> Arc<dyn LayoutStrategy> {
237 let flat: Arc<dyn LayoutStrategy> = if let Some(flat) = self.flat_strategy {
238 flat
239 } else if let Some(allow_encodings) = self.allow_encodings {
240 Arc::new(FlatLayoutStrategy::default().with_allow_encodings(allow_encodings))
241 } else {
242 Arc::new(FlatLayoutStrategy::default())
243 };
244
245 let chunked = ChunkedLayoutStrategy::new(Arc::clone(&flat));
247 let buffered = BufferedStrategy::new(chunked, 2 * ONE_MEG); let data_compressor: Arc<dyn CompressorPlugin> = match &self.compressor {
255 CompressorConfig::BtrBlocks(builder) => Arc::new(
256 builder
257 .clone()
258 .exclude_schemes([IntDictScheme.id()])
259 .build(),
260 ),
261 CompressorConfig::Opaque(compressor) => Arc::clone(compressor),
262 };
263 let compressing = CompressingStrategy::new(buffered, data_compressor);
264
265 let coalescing = RepartitionStrategy::new(
267 compressing,
268 RepartitionWriterOptions {
269 block_size_minimum: ONE_MEG,
276 block_len_multiple: self.row_block_size,
277 block_size_target: Some(ONE_MEG),
278 canonicalize: true,
279 },
280 );
281
282 let stats_compressor: Arc<dyn CompressorPlugin> = match self.compressor {
284 CompressorConfig::BtrBlocks(builder) => Arc::new(builder.build()),
285 CompressorConfig::Opaque(compressor) => compressor,
286 };
287 let compress_then_flat = CompressingStrategy::new(flat, stats_compressor);
288
289 let dict = DictStrategy::new(
291 coalescing.clone(),
292 compress_then_flat.clone(),
293 coalescing,
294 Default::default(),
295 );
296
297 let stats = ZonedStrategy::new(
299 dict,
300 compress_then_flat.clone(),
301 ZonedLayoutOptions {
302 block_size: NonZeroUsize::new(self.row_block_size).vortex_expect("must be non 0"),
303 ..Default::default()
304 },
305 );
306
307 let repartition = RepartitionStrategy::new(
309 stats,
310 RepartitionWriterOptions {
311 block_size_minimum: 0,
313 block_len_multiple: self.row_block_size,
315 block_size_target: None,
316 canonicalize: false,
317 },
318 );
319
320 let validity_strategy = CollectStrategy::new(compress_then_flat);
322
323 let table_strategy = TableStrategy::new(Arc::new(validity_strategy), Arc::new(repartition))
325 .with_field_writers(self.field_writers);
326
327 Arc::new(table_strategy)
328 }
329}