Skip to main content

vortex_array/builders/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Builders for Vortex arrays.
5//!
6//! Every logical type in Vortex has a canonical (uncompressed) in-memory encoding. This module
7//! provides pre-allocated builders to construct new canonical arrays.
8//!
9//! ## Example:
10//!
11//! ```
12//! use vortex_array::builders::{builder_with_capacity, ArrayBuilder};
13//! use vortex_array::dtype::{DType, Nullability};
14//! use vortex_array::{VortexSessionExecute, array_session};
15//!
16//! // Create a new builder for string data.
17//! let mut builder = builder_with_capacity(&DType::Utf8(Nullability::NonNullable), 4);
18//!
19//! builder.append_scalar(&"a".into()).unwrap();
20//! builder.append_scalar(&"b".into()).unwrap();
21//! builder.append_scalar(&"c".into()).unwrap();
22//! builder.append_scalar(&"d".into()).unwrap();
23//!
24//! let strings = builder.finish();
25//! let mut ctx = array_session().create_execution_ctx();
26//!
27//! assert_eq!(strings.execute_scalar(0, &mut ctx).unwrap(), "a".into());
28//! assert_eq!(strings.execute_scalar(1, &mut ctx).unwrap(), "b".into());
29//! assert_eq!(strings.execute_scalar(2, &mut ctx).unwrap(), "c".into());
30//! assert_eq!(strings.execute_scalar(3, &mut ctx).unwrap(), "d".into());
31//! ```
32
33use std::any::Any;
34use std::sync::Arc;
35
36use vortex_error::VortexResult;
37use vortex_mask::Mask;
38
39use crate::ArrayRef;
40use crate::ExecutionCtx;
41use crate::canonical::Canonical;
42use crate::dtype::DType;
43use crate::match_each_decimal_value_type;
44use crate::match_each_native_ptype;
45use crate::memory::HostAllocatorRef;
46use crate::scalar::Scalar;
47
48mod lazy_null_builder;
49pub(crate) use lazy_null_builder::LazyBitBufferBuilder;
50
51mod bool;
52mod decimal;
53pub mod dict;
54mod extension;
55mod fixed_size_list;
56mod list;
57mod listview;
58mod map;
59mod null;
60mod primitive;
61mod struct_;
62mod varbinview;
63
64pub use bool::*;
65pub use decimal::*;
66pub use extension::*;
67pub use fixed_size_list::*;
68pub use list::*;
69pub use listview::*;
70pub use map::*;
71pub use null::*;
72pub use primitive::*;
73pub use struct_::*;
74pub use varbinview::*;
75
76pub use crate::arrays::varbin::builder::VarBinBuilder;
77
78#[cfg(test)]
79mod tests;
80
81/// The default capacity for builders.
82///
83/// This is equal to the default capacity for Arrow Arrays.
84pub const DEFAULT_BUILDER_CAPACITY: usize = 1024;
85
86pub trait ArrayBuilder: Send {
87    fn as_any(&self) -> &dyn Any;
88
89    fn as_any_mut(&mut self) -> &mut dyn Any;
90
91    fn dtype(&self) -> &DType;
92
93    fn len(&self) -> usize;
94
95    fn is_empty(&self) -> bool {
96        self.len() == 0
97    }
98
99    /// Append a "zero" value to the array.
100    ///
101    /// Zero values are generally determined by [`Scalar::default_value`].
102    fn append_zero(&mut self) {
103        self.append_zeros(1)
104    }
105
106    /// Appends n "zero" values to the array.
107    ///
108    /// Zero values are generally determined by [`Scalar::default_value`].
109    fn append_zeros(&mut self, n: usize);
110
111    /// Append a "null" value to the array.
112    ///
113    /// Implementors should panic if this method is called on a non-nullable [`ArrayBuilder`].
114    fn append_null(&mut self) {
115        self.append_nulls(1)
116    }
117
118    /// The inner part of `append_nulls`.
119    ///
120    /// # Safety
121    ///
122    /// The array builder must be nullable.
123    unsafe fn append_nulls_unchecked(&mut self, n: usize);
124
125    /// Appends n "null" values to the array.
126    ///
127    /// Implementors should panic if this method is called on a non-nullable [`ArrayBuilder`].
128    fn append_nulls(&mut self, n: usize) {
129        assert!(
130            self.dtype().is_nullable(),
131            "tried to append {n} nulls to a non-nullable array builder"
132        );
133
134        // SAFETY: We check above that the array builder is nullable.
135        unsafe {
136            self.append_nulls_unchecked(n);
137        }
138    }
139
140    /// Appends a default value to the array.
141    fn append_default(&mut self) {
142        self.append_defaults(1)
143    }
144
145    /// Appends n default values to the array.
146    ///
147    /// If the array builder is nullable, then this has the behavior of `self.append_nulls(n)`.
148    /// If the array builder is non-nullable, then it has the behavior of `self.append_zeros(n)`.
149    fn append_defaults(&mut self, n: usize) {
150        if self.dtype().is_nullable() {
151            self.append_nulls(n);
152        } else {
153            self.append_zeros(n);
154        }
155    }
156
157    /// A generic function to append a scalar to the builder.
158    fn append_scalar(&mut self, scalar: &Scalar) -> VortexResult<()>;
159
160    /// Allocate space for extra `additional` items
161    fn reserve_exact(&mut self, additional: usize);
162
163    /// Override builders validity with the one provided.
164    ///
165    /// Note that this will have no effect on the final array if the array builder is non-nullable.
166    fn set_validity(&mut self, validity: Mask) {
167        if !self.dtype().is_nullable() {
168            return;
169        }
170        assert_eq!(self.len(), validity.len());
171        unsafe { self.set_validity_unchecked(validity) }
172    }
173
174    /// override validity with the one provided, without checking lengths
175    ///
176    /// # Safety
177    ///
178    /// Given validity must have an equal length to [`self.len()`](Self::len).
179    unsafe fn set_validity_unchecked(&mut self, validity: Mask);
180
181    /// Constructs an Array from the builder components.
182    ///
183    /// # Panics
184    ///
185    /// This function may panic if the builder's methods are called with invalid arguments. If only
186    /// the methods on this interface are used, the builder should not panic. However, specific
187    /// builders have interfaces that may be misused. For example, if the number of values in a
188    /// [PrimitiveBuilder]'s [vortex_buffer::BufferMut] does not match the number of validity bits,
189    /// the PrimitiveBuilder's [Self::finish] will panic.
190    fn finish(&mut self) -> ArrayRef;
191
192    /// Constructs a canonical array directly from the builder.
193    ///
194    /// This method provides a default implementation that creates an [`ArrayRef`] via `finish` and
195    /// then converts it to canonical form. Specific builders can override this with optimized
196    /// implementations that avoid the intermediate [`ArrayRef`] creation.
197    fn finish_into_canonical(&mut self, ctx: &mut ExecutionCtx) -> Canonical;
198}
199
200/// Matches a `&mut dyn ArrayBuilder` against every concrete list builder type, i.e. every
201/// [`ListBuilder`]`<O>` and [`ListViewBuilder`]`<O, S>` instantiation over the
202/// [`OffsetBuilderPType`](crate::dtype::OffsetBuilderPType) offset/size types (`u32`, `u64`, `i32`,
203/// `i64`).
204///
205/// Binds the downcast builder as `$builder` and evaluates `$body` with it, yielding
206/// `Some($body)`; yields `None` when the builder is not a list builder. List encodings dispatch
207/// through this matcher because the concrete list builders are generic over their offset/size
208/// integer types, which cannot be named through a `dyn ArrayBuilder`. The matcher is exhaustive
209/// because `OffsetBuilderPType` is sealed, so no other instantiations can be constructed.
210#[macro_export]
211macro_rules! match_each_list_builder {
212    ($dyn_builder:expr, | $builder:ident | $body:expr) => {{
213        let __dyn_builder: &mut dyn $crate::builders::ArrayBuilder = $dyn_builder;
214        match $crate::__match_each_list_builder!(
215            __dyn_builder,
216            $builder,
217            $body,
218            [u32, u64, i32, i64]
219        ) {
220            ::core::option::Option::Some(__result) => ::core::option::Option::Some(__result),
221            ::core::option::Option::None => $crate::__match_each_listview_builder!(
222                __dyn_builder,
223                $builder,
224                $body,
225                [u32, u64, i32, i64]
226            ),
227        }
228    }};
229}
230
231#[doc(hidden)]
232#[macro_export]
233macro_rules! __match_each_list_builder {
234    ($target:ident, $builder:ident, $body:expr, []) => {
235        ::core::option::Option::None
236    };
237    ($target:ident, $builder:ident, $body:expr, [$offset:ty $(, $rest:ty)*]) => {
238        if let ::core::option::Option::Some($builder) =
239            $crate::builders::ArrayBuilder::as_any_mut($target)
240                .downcast_mut::<$crate::builders::ListBuilder<$offset>>()
241        {
242            ::core::option::Option::Some($body)
243        } else {
244            $crate::__match_each_list_builder!($target, $builder, $body, [$($rest),*])
245        }
246    };
247}
248
249#[doc(hidden)]
250#[macro_export]
251macro_rules! __match_each_listview_builder {
252    ($target:ident, $builder:ident, $body:expr, []) => {
253        ::core::option::Option::None
254    };
255    ($target:ident, $builder:ident, $body:expr, [$offset:ty $(, $rest:ty)*]) => {
256        match $crate::__match_each_listview_builder_size!(
257            $target,
258            $builder,
259            $body,
260            $offset,
261            [u32, u64, i32, i64]
262        ) {
263            ::core::option::Option::Some(__result) => ::core::option::Option::Some(__result),
264            ::core::option::Option::None => $crate::__match_each_listview_builder!(
265                $target,
266                $builder,
267                $body,
268                [$($rest),*]
269            ),
270        }
271    };
272}
273
274#[doc(hidden)]
275#[macro_export]
276macro_rules! __match_each_listview_builder_size {
277    ($target:ident, $builder:ident, $body:expr, $offset:ty, []) => {
278        ::core::option::Option::None
279    };
280    ($target:ident, $builder:ident, $body:expr, $offset:ty, [$size:ty $(, $rest:ty)*]) => {
281        if let ::core::option::Option::Some($builder) =
282            $crate::builders::ArrayBuilder::as_any_mut($target)
283                .downcast_mut::<$crate::builders::ListViewBuilder<$offset, $size>>()
284        {
285            ::core::option::Option::Some($body)
286        } else {
287            $crate::__match_each_listview_builder_size!(
288                $target, $builder, $body, $offset, [$($rest),*]
289            )
290        }
291    };
292}
293
294/// Matches a `&mut dyn ArrayBuilder` against every concrete map builder type.
295///
296/// Binds the downcast builder as `$builder` and evaluates `$body` with it, yielding
297/// `Some($body)`; yields `None` when the builder is not a map builder.
298#[macro_export]
299macro_rules! match_each_map_builder {
300    ($dyn_builder:expr, | $builder:ident | $body:expr) => {{
301        let __dyn_builder: &mut dyn $crate::builders::ArrayBuilder = $dyn_builder;
302        $crate::__match_each_map_builder!(__dyn_builder, $builder, $body, [u32, u64, i32, i64])
303    }};
304}
305
306#[doc(hidden)]
307#[macro_export]
308macro_rules! __match_each_map_builder {
309    ($target:ident, $builder:ident, $body:expr, []) => {
310        ::core::option::Option::None
311    };
312    ($target:ident, $builder:ident, $body:expr, [$offset:ty $(, $rest:ty)*]) => {
313        match $crate::__match_each_map_builder_size!(
314            $target,
315            $builder,
316            $body,
317            $offset,
318            [u32, u64, i32, i64]
319        ) {
320            ::core::option::Option::Some(__result) => ::core::option::Option::Some(__result),
321            ::core::option::Option::None => $crate::__match_each_map_builder!(
322                $target,
323                $builder,
324                $body,
325                [$($rest),*]
326            ),
327        }
328    };
329}
330
331#[doc(hidden)]
332#[macro_export]
333macro_rules! __match_each_map_builder_size {
334    ($target:ident, $builder:ident, $body:expr, $offset:ty, []) => {
335        ::core::option::Option::None
336    };
337    ($target:ident, $builder:ident, $body:expr, $offset:ty, [$size:ty $(, $rest:ty)*]) => {
338        if let ::core::option::Option::Some($builder) =
339            $crate::builders::ArrayBuilder::as_any_mut($target)
340                .downcast_mut::<$crate::builders::MapBuilder<$offset, $size>>()
341        {
342            ::core::option::Option::Some($body)
343        } else {
344            $crate::__match_each_map_builder_size!(
345                $target, $builder, $body, $offset, [$($rest),*]
346            )
347        }
348    };
349}
350
351/// Construct a new canonical builder for the given [`DType`].
352///
353///
354/// # Example
355///
356/// ```
357/// use vortex_array::builders::{builder_with_capacity, ArrayBuilder};
358/// use vortex_array::dtype::{DType, Nullability};
359/// use vortex_array::{VortexSessionExecute, array_session};
360///
361/// // Create a new builder for string data.
362/// let mut builder = builder_with_capacity(&DType::Utf8(Nullability::NonNullable), 4);
363///
364/// builder.append_scalar(&"a".into()).unwrap();
365/// builder.append_scalar(&"b".into()).unwrap();
366/// builder.append_scalar(&"c".into()).unwrap();
367/// builder.append_scalar(&"d".into()).unwrap();
368///
369/// let strings = builder.finish();
370/// let mut ctx = array_session().create_execution_ctx();
371///
372/// assert_eq!(strings.execute_scalar(0, &mut ctx).unwrap(), "a".into());
373/// assert_eq!(strings.execute_scalar(1, &mut ctx).unwrap(), "b".into());
374/// assert_eq!(strings.execute_scalar(2, &mut ctx).unwrap(), "c".into());
375/// assert_eq!(strings.execute_scalar(3, &mut ctx).unwrap(), "d".into());
376/// ```
377pub fn builder_with_capacity(dtype: &DType, capacity: usize) -> Box<dyn ArrayBuilder> {
378    match dtype {
379        DType::Null => Box::new(NullBuilder::new()),
380        DType::Bool(n) => Box::new(BoolBuilder::with_capacity(*n, capacity)),
381        DType::Primitive(ptype, n) => {
382            match_each_native_ptype!(ptype, |P| {
383                Box::new(PrimitiveBuilder::<P>::with_capacity(*n, capacity))
384            })
385        }
386        DType::Decimal(decimal_type, n) => {
387            match_each_decimal_value_type!(
388                DecimalType::smallest_decimal_value_type(decimal_type),
389                |D| {
390                    Box::new(DecimalBuilder::with_capacity::<D>(
391                        capacity,
392                        *decimal_type,
393                        *n,
394                    ))
395                }
396            )
397        }
398        DType::Utf8(n) => Box::new(VarBinViewBuilder::with_capacity(DType::Utf8(*n), capacity)),
399        DType::Binary(n) => Box::new(VarBinViewBuilder::with_capacity(
400            DType::Binary(*n),
401            capacity,
402        )),
403        DType::List(dtype, n) => Box::new(ListViewBuilder::<u64, u64>::with_capacity(
404            Arc::clone(dtype),
405            *n,
406            2 * capacity, // Arbitrarily choose 2 times the `offsets` capacity here.
407            capacity,
408        )),
409        DType::Map(map_dtype, nullability) => Box::new(MapBuilder::<u64, u64>::with_capacity(
410            map_dtype.clone(),
411            *nullability,
412            capacity,
413        )),
414        DType::FixedSizeList(elem_dtype, list_size, null) => {
415            Box::new(FixedSizeListBuilder::with_capacity(
416                Arc::clone(elem_dtype),
417                *list_size,
418                *null,
419                capacity,
420            ))
421        }
422        DType::Struct(struct_dtype, n) => Box::new(StructBuilder::with_capacity(
423            struct_dtype.clone(),
424            *n,
425            capacity,
426        )),
427        DType::Union(..) => todo!("TODO(connor)[Union]: unimplemented"),
428        DType::Variant(_) => {
429            unimplemented!()
430        }
431        DType::Extension(ext_dtype) => {
432            Box::new(ExtensionBuilder::with_capacity(ext_dtype.clone(), capacity))
433        }
434    }
435}
436
437/// Construct a new canonical builder for the given [`DType`] using a host
438/// [`crate::memory::HostAllocator`].
439pub fn builder_with_capacity_in(
440    allocator: HostAllocatorRef,
441    dtype: &DType,
442    capacity: usize,
443) -> Box<dyn ArrayBuilder> {
444    let _allocator = allocator;
445    builder_with_capacity(dtype, capacity)
446}