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_error::vortex_bail;
38use vortex_mask::Mask;
39
40use crate::ArrayRef;
41use crate::ExecutionCtx;
42use crate::array::ArrayView;
43use crate::arrays::List;
44use crate::arrays::ListView;
45use crate::canonical::Canonical;
46use crate::dtype::DType;
47use crate::match_each_decimal_value_type;
48use crate::match_each_native_ptype;
49use crate::memory::HostAllocatorRef;
50use crate::scalar::Scalar;
51
52mod lazy_null_builder;
53pub(crate) use lazy_null_builder::LazyBitBufferBuilder;
54
55mod bool;
56mod decimal;
57pub mod dict;
58mod extension;
59mod fixed_size_list;
60mod list;
61mod listview;
62mod null;
63mod primitive;
64mod struct_;
65mod varbinview;
66
67pub use bool::*;
68pub use decimal::*;
69pub use extension::*;
70pub use fixed_size_list::*;
71pub use list::*;
72pub use listview::*;
73pub use null::*;
74pub use primitive::*;
75pub use struct_::*;
76pub use varbinview::*;
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    /// Appends the values of a [`List`]-encoded `array` to this builder.
200    ///
201    /// Only list-typed builders support this; the default implementation returns an error. List
202    /// encodings dispatch through this method because the concrete list builders are generic over
203    /// their offset/size integer types, which cannot be named through a `dyn ArrayBuilder`.
204    fn append_list_array(
205        &mut self,
206        array: ArrayView<'_, List>,
207        _ctx: &mut ExecutionCtx,
208    ) -> VortexResult<()> {
209        vortex_bail!(
210            "cannot append a List array of dtype {} to a {} builder",
211            array.dtype(),
212            self.dtype()
213        )
214    }
215
216    /// Appends the values of a [`ListView`]-encoded `array` to this builder.
217    ///
218    /// See [`append_list_array`](Self::append_list_array); this is the same hook for the canonical
219    /// [`ListViewArray`](crate::arrays::ListViewArray) encoding.
220    fn append_listview_array(
221        &mut self,
222        array: ArrayView<'_, ListView>,
223        _ctx: &mut ExecutionCtx,
224    ) -> VortexResult<()> {
225        vortex_bail!(
226            "cannot append a ListView array of dtype {} to a {} builder",
227            array.dtype(),
228            self.dtype()
229        )
230    }
231}
232
233/// Construct a new canonical builder for the given [`DType`].
234///
235///
236/// # Example
237///
238/// ```
239/// use vortex_array::builders::{builder_with_capacity, ArrayBuilder};
240/// use vortex_array::dtype::{DType, Nullability};
241/// use vortex_array::{VortexSessionExecute, array_session};
242///
243/// // Create a new builder for string data.
244/// let mut builder = builder_with_capacity(&DType::Utf8(Nullability::NonNullable), 4);
245///
246/// builder.append_scalar(&"a".into()).unwrap();
247/// builder.append_scalar(&"b".into()).unwrap();
248/// builder.append_scalar(&"c".into()).unwrap();
249/// builder.append_scalar(&"d".into()).unwrap();
250///
251/// let strings = builder.finish();
252/// let mut ctx = array_session().create_execution_ctx();
253///
254/// assert_eq!(strings.execute_scalar(0, &mut ctx).unwrap(), "a".into());
255/// assert_eq!(strings.execute_scalar(1, &mut ctx).unwrap(), "b".into());
256/// assert_eq!(strings.execute_scalar(2, &mut ctx).unwrap(), "c".into());
257/// assert_eq!(strings.execute_scalar(3, &mut ctx).unwrap(), "d".into());
258/// ```
259pub fn builder_with_capacity(dtype: &DType, capacity: usize) -> Box<dyn ArrayBuilder> {
260    match dtype {
261        DType::Null => Box::new(NullBuilder::new()),
262        DType::Bool(n) => Box::new(BoolBuilder::with_capacity(*n, capacity)),
263        DType::Primitive(ptype, n) => {
264            match_each_native_ptype!(ptype, |P| {
265                Box::new(PrimitiveBuilder::<P>::with_capacity(*n, capacity))
266            })
267        }
268        DType::Decimal(decimal_type, n) => {
269            match_each_decimal_value_type!(
270                DecimalType::smallest_decimal_value_type(decimal_type),
271                |D| {
272                    Box::new(DecimalBuilder::with_capacity::<D>(
273                        capacity,
274                        *decimal_type,
275                        *n,
276                    ))
277                }
278            )
279        }
280        DType::Utf8(n) => Box::new(VarBinViewBuilder::with_capacity(DType::Utf8(*n), capacity)),
281        DType::Binary(n) => Box::new(VarBinViewBuilder::with_capacity(
282            DType::Binary(*n),
283            capacity,
284        )),
285        DType::List(dtype, n) => Box::new(ListViewBuilder::<u64, u64>::with_capacity(
286            Arc::clone(dtype),
287            *n,
288            2 * capacity, // Arbitrarily choose 2 times the `offsets` capacity here.
289            capacity,
290        )),
291        DType::FixedSizeList(elem_dtype, list_size, null) => {
292            Box::new(FixedSizeListBuilder::with_capacity(
293                Arc::clone(elem_dtype),
294                *list_size,
295                *null,
296                capacity,
297            ))
298        }
299        DType::Struct(struct_dtype, n) => Box::new(StructBuilder::with_capacity(
300            struct_dtype.clone(),
301            *n,
302            capacity,
303        )),
304        DType::Union(..) => todo!("TODO(connor)[Union]: unimplemented"),
305        DType::Variant(_) => {
306            unimplemented!()
307        }
308        DType::Extension(ext_dtype) => {
309            Box::new(ExtensionBuilder::with_capacity(ext_dtype.clone(), capacity))
310        }
311    }
312}
313
314/// Construct a new canonical builder for the given [`DType`] using a host
315/// [`crate::memory::HostAllocator`].
316pub fn builder_with_capacity_in(
317    allocator: HostAllocatorRef,
318    dtype: &DType,
319    capacity: usize,
320) -> Box<dyn ArrayBuilder> {
321    let _allocator = allocator;
322    builder_with_capacity(dtype, capacity)
323}