1use std::sync::Arc;
7use std::sync::LazyLock;
8
9use vortex_alp::ALP;
10use vortex_alp::ALPRD;
13use vortex_array::arrays::Bool;
14use vortex_array::arrays::Chunked;
15use vortex_array::arrays::Constant;
16use vortex_array::arrays::Decimal;
17use vortex_array::arrays::Dict;
18use vortex_array::arrays::Extension;
19use vortex_array::arrays::FixedSizeList;
20use vortex_array::arrays::List;
21use vortex_array::arrays::ListView;
22use vortex_array::arrays::Masked;
23use vortex_array::arrays::Null;
24use vortex_array::arrays::Primitive;
25use vortex_array::arrays::Struct;
26use vortex_array::arrays::VarBin;
27use vortex_array::arrays::VarBinView;
28use vortex_array::dtype::FieldPath;
29use vortex_array::session::ArrayRegistry;
30use vortex_array::session::ArraySession;
31#[cfg(feature = "zstd")]
32use vortex_btrblocks::BtrBlocksCompressorBuilder;
33#[cfg(feature = "zstd")]
34use vortex_btrblocks::FloatCode;
35#[cfg(feature = "zstd")]
36use vortex_btrblocks::IntCode;
37#[cfg(feature = "zstd")]
38use vortex_btrblocks::StringCode;
39use vortex_bytebool::ByteBool;
40use vortex_datetime_parts::DateTimeParts;
41use vortex_decimal_byte_parts::DecimalByteParts;
42use vortex_fastlanes::BitPacked;
43use vortex_fastlanes::Delta;
44use vortex_fastlanes::FoR;
45use vortex_fastlanes::RLE;
46use vortex_fsst::FSST;
47use vortex_layout::LayoutStrategy;
48use vortex_layout::layouts::buffered::BufferedStrategy;
49use vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy;
50use vortex_layout::layouts::collect::CollectStrategy;
51use vortex_layout::layouts::compressed::CompressingStrategy;
52use vortex_layout::layouts::compressed::CompressorPlugin;
53use vortex_layout::layouts::dict::writer::DictStrategy;
54use vortex_layout::layouts::flat::writer::FlatLayoutStrategy;
55use vortex_layout::layouts::repartition::RepartitionStrategy;
56use vortex_layout::layouts::repartition::RepartitionWriterOptions;
57use vortex_layout::layouts::table::TableStrategy;
58use vortex_layout::layouts::zoned::writer::ZonedLayoutOptions;
59use vortex_layout::layouts::zoned::writer::ZonedStrategy;
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_zigzag::ZigZag;
66#[cfg(feature = "zstd")]
67use vortex_zstd::Zstd;
68#[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
69use vortex_zstd::ZstdBuffers;
70
71const ONE_MEG: u64 = 1 << 20;
72
73pub static ALLOWED_ENCODINGS: LazyLock<ArrayRegistry> = LazyLock::new(|| {
78 let session = ArraySession::empty();
79
80 session.register(Null);
82 session.register(Bool);
83 session.register(Primitive);
84 session.register(Decimal);
85 session.register(VarBin);
86 session.register(VarBinView);
87 session.register(List);
88 session.register(ListView);
89 session.register(FixedSizeList);
90 session.register(Struct);
91 session.register(Extension);
92 session.register(Chunked);
93 session.register(Constant);
94 session.register(Masked);
95 session.register(Dict);
96
97 session.register(ALP);
99 session.register(ALPRD);
100 session.register(BitPacked);
101 session.register(ByteBool);
102 session.register(DateTimeParts);
103 session.register(DecimalByteParts);
104 session.register(Delta);
105 session.register(FoR);
106 session.register(FSST);
107 session.register(Pco);
108 session.register(RLE);
109 session.register(RunEnd);
110 session.register(Sequence);
111 session.register(Sparse);
112 session.register(ZigZag);
113
114 #[cfg(feature = "zstd")]
115 session.register(Zstd);
116 #[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
117 session.register(ZstdBuffers);
118
119 session.registry().clone()
120});
121
122pub struct WriteStrategyBuilder {
128 compressor: Option<Arc<dyn CompressorPlugin>>,
129 row_block_size: usize,
130 field_writers: HashMap<FieldPath, Arc<dyn LayoutStrategy>>,
131 allow_encodings: Option<ArrayRegistry>,
132 flat_strategy: Option<Arc<dyn LayoutStrategy>>,
133}
134
135impl Default for WriteStrategyBuilder {
136 fn default() -> Self {
139 Self {
140 compressor: None,
141 row_block_size: 8192,
142 field_writers: HashMap::new(),
143 allow_encodings: Some(ALLOWED_ENCODINGS.clone()),
144 flat_strategy: None,
145 }
146 }
147}
148
149impl WriteStrategyBuilder {
150 pub fn with_compressor<C: CompressorPlugin>(mut self, compressor: C) -> Self {
155 self.compressor = Some(Arc::new(compressor));
156 self
157 }
158
159 pub fn with_row_block_size(mut self, row_block_size: usize) -> Self {
161 self.row_block_size = row_block_size;
162 self
163 }
164
165 pub fn with_field_writer(
168 mut self,
169 field: impl Into<FieldPath>,
170 writer: Arc<dyn LayoutStrategy>,
171 ) -> Self {
172 self.field_writers.insert(field.into(), writer);
173 self
174 }
175
176 pub fn with_allow_encodings(mut self, allow_encodings: ArrayRegistry) -> Self {
178 self.allow_encodings = Some(allow_encodings);
179 self
180 }
181
182 pub fn with_flat_strategy(mut self, flat: Arc<dyn LayoutStrategy>) -> Self {
187 self.flat_strategy = Some(flat);
188 self
189 }
190
191 #[cfg(feature = "zstd")]
198 pub fn with_cuda_compatible_encodings(mut self) -> Self {
199 let mut builder = BtrBlocksCompressorBuilder::default()
200 .exclude_int([IntCode::Sparse, IntCode::Rle])
201 .exclude_float([FloatCode::Rle, FloatCode::Sparse])
202 .exclude_string([StringCode::Dict, StringCode::Fsst]);
203
204 #[cfg(feature = "unstable_encodings")]
205 {
206 builder = builder.include_string([StringCode::ZstdBuffers]);
207 }
208 #[cfg(not(feature = "unstable_encodings"))]
209 {
210 builder = builder.include_string([StringCode::Zstd]);
211 }
212
213 self.compressor = Some(Arc::new(builder.build()));
214 self
215 }
216
217 #[cfg(feature = "zstd")]
223 pub fn with_compact_encodings(mut self) -> Self {
224 let btrblocks = BtrBlocksCompressorBuilder::default()
225 .include_string([StringCode::Zstd])
226 .include_int([IntCode::Pco])
227 .include_float([FloatCode::Pco])
228 .build();
229
230 self.compressor = Some(Arc::new(btrblocks));
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(flat.clone());
247 let buffered = BufferedStrategy::new(chunked, 2 * ONE_MEG); let compressing = if let Some(ref compressor) = self.compressor {
251 CompressingStrategy::new_opaque(buffered, compressor.clone())
252 } else {
253 CompressingStrategy::new_btrblocks(buffered, true)
254 };
255
256 let coalescing = RepartitionStrategy::new(
258 compressing,
259 RepartitionWriterOptions {
260 block_size_minimum: ONE_MEG,
267 block_len_multiple: self.row_block_size,
268 block_size_target: Some(ONE_MEG),
269 canonicalize: true,
270 },
271 );
272
273 let compress_then_flat = if let Some(ref compressor) = self.compressor {
275 CompressingStrategy::new_opaque(flat, compressor.clone())
276 } else {
277 CompressingStrategy::new_btrblocks(flat, false)
278 };
279
280 let dict = DictStrategy::new(
282 coalescing.clone(),
283 compress_then_flat.clone(),
284 coalescing,
285 Default::default(),
286 );
287
288 let stats = ZonedStrategy::new(
290 dict,
291 compress_then_flat.clone(),
292 ZonedLayoutOptions {
293 block_size: self.row_block_size,
294 ..Default::default()
295 },
296 );
297
298 let repartition = RepartitionStrategy::new(
300 stats,
301 RepartitionWriterOptions {
302 block_size_minimum: 0,
304 block_len_multiple: self.row_block_size,
306 block_size_target: None,
307 canonicalize: false,
308 },
309 );
310
311 let validity_strategy = CollectStrategy::new(compress_then_flat);
313
314 let table_strategy = TableStrategy::new(Arc::new(validity_strategy), Arc::new(repartition))
316 .with_field_writers(self.field_writers);
317
318 Arc::new(table_strategy)
319 }
320}