vortex_vector/decimal/
generic.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Definition and implementation of [`DVector<D>`].
5
6use std::fmt::Debug;
7use std::ops::RangeBounds;
8
9use vortex_buffer::Buffer;
10use vortex_dtype::{NativeDecimalType, PrecisionScale};
11use vortex_error::{VortexExpect, VortexResult, vortex_bail};
12use vortex_mask::Mask;
13
14use crate::decimal::{DScalar, DVectorMut};
15use crate::{Scalar, VectorOps};
16
17/// An immutable vector of generic decimal values.
18///
19/// `D` is bound by [`NativeDecimalType`], which can be one of the native integer types (`i8`,
20/// `i16`, `i32`, `i64`, `i128`) or `i256`. `D` is used to store the decimal values.
21///
22/// The decimal vector maintains a [`PrecisionScale<D>`] that defines the precision (total number of
23/// digits) and scale (digits after the decimal point) for all values in the vector.
24#[derive(Debug, Clone)]
25pub struct DVector<D> {
26    /// The precision and scale of each decimal in the decimal vector.
27    pub(super) ps: PrecisionScale<D>,
28    /// The buffer representing the vector decimal elements.
29    pub(super) elements: Buffer<D>,
30    /// The validity mask (where `true` represents an element is **not** null).
31    pub(super) validity: Mask,
32}
33
34impl<D: NativeDecimalType> DVector<D> {
35    /// Creates a new [`DVector<D>`] from the given [`PrecisionScale`], elements buffer, and
36    /// validity mask.
37    ///
38    /// # Panics
39    ///
40    /// Panics if:
41    ///
42    /// - The lengths of the `elements` and `validity` do not match.
43    /// - Any of the elements are out of bounds for the given [`PrecisionScale`].
44    pub fn new(ps: PrecisionScale<D>, elements: Buffer<D>, validity: Mask) -> Self {
45        Self::try_new(ps, elements, validity).vortex_expect("Failed to create `DVector`")
46    }
47
48    /// Tries to create a new [`DVector<D>`] from the given [`PrecisionScale`], elements buffer, and
49    /// validity mask.
50    ///
51    /// # Errors
52    ///
53    /// Returns an error if:
54    ///
55    /// - The lengths of the `elements` and `validity` do not match.
56    /// - Any of the elements are out of bounds for the given [`PrecisionScale`].
57    pub fn try_new(
58        ps: PrecisionScale<D>,
59        elements: Buffer<D>,
60        validity: Mask,
61    ) -> VortexResult<Self> {
62        if elements.len() != validity.len() {
63            vortex_bail!(
64                "Elements length {} does not match validity length {}",
65                elements.len(),
66                validity.len()
67            );
68        }
69
70        // We assert that each element is within bounds for the given precision/scale.
71        if !elements.iter().all(|e| ps.is_valid(*e)) {
72            vortex_bail!(
73                "One or more elements are out of bounds for precision {} and scale {}",
74                ps.precision(),
75                ps.scale()
76            );
77        }
78
79        Ok(Self {
80            ps,
81            elements,
82            validity,
83        })
84    }
85
86    /// Creates a new [`DVector<D>`] from the given [`PrecisionScale`], elements buffer, and
87    /// validity mask, _without_ validation.
88    ///
89    /// # Safety
90    ///
91    /// The caller must ensure:
92    ///
93    /// - The lengths of the elements and validity are equal.
94    /// - All elements are in bounds for the given [`PrecisionScale`].
95    pub unsafe fn new_unchecked(
96        ps: PrecisionScale<D>,
97        elements: Buffer<D>,
98        validity: Mask,
99    ) -> Self {
100        if cfg!(debug_assertions) {
101            Self::try_new(ps, elements, validity).vortex_expect("Failed to create `DVector`")
102        } else {
103            Self {
104                ps,
105                elements,
106                validity,
107            }
108        }
109    }
110
111    /// Decomposes the decimal vector into its constituent parts ([`PrecisionScale`], decimal
112    /// buffer, and validity).
113    pub fn into_parts(self) -> (PrecisionScale<D>, Buffer<D>, Mask) {
114        (self.ps, self.elements, self.validity)
115    }
116
117    /// Get the precision/scale of the decimal vector.
118    pub fn precision_scale(&self) -> PrecisionScale<D> {
119        self.ps
120    }
121
122    /// Returns the precision of the decimal vector.
123    pub fn precision(&self) -> u8 {
124        self.ps.precision()
125    }
126
127    /// Returns the scale of the decimal vector.
128    pub fn scale(&self) -> i8 {
129        self.ps.scale()
130    }
131
132    /// Returns a reference to the underlying elements buffer containing the decimal data.
133    pub fn elements(&self) -> &Buffer<D> {
134        &self.elements
135    }
136
137    /// Gets a nullable element at the given index, panicking on out-of-bounds.
138    ///
139    /// If the element at the given index is null, returns `None`. Otherwise, returns `Some(x)`,
140    /// where `x: D`.
141    ///
142    /// Note that this `get` method is different from the standard library [`slice::get`], which
143    /// returns `None` if the index is out of bounds. This method will panic if the index is out of
144    /// bounds, and return `None` if the elements is null.
145    ///
146    /// # Panics
147    ///
148    /// Panics if the index is out of bounds.
149    pub fn get(&self, index: usize) -> Option<&D> {
150        self.validity.value(index).then(|| &self.elements[index])
151    }
152}
153
154impl<D: NativeDecimalType> AsRef<[D]> for DVector<D> {
155    fn as_ref(&self) -> &[D] {
156        &self.elements
157    }
158}
159
160impl<D: NativeDecimalType> VectorOps for DVector<D> {
161    type Mutable = DVectorMut<D>;
162
163    fn len(&self) -> usize {
164        self.elements.len()
165    }
166
167    fn validity(&self) -> &Mask {
168        &self.validity
169    }
170
171    fn scalar_at(&self, index: usize) -> Scalar {
172        assert!(index < self.len());
173
174        let is_valid = self.validity.value(index);
175        let value = is_valid.then(|| self.elements[index]);
176
177        // SAFETY: We have already checked the validity on construction of the vector
178        Scalar::Decimal(unsafe { DScalar::<D>::new_unchecked(self.ps, value) }.into())
179    }
180
181    fn slice(&self, range: impl RangeBounds<usize> + Clone + Debug) -> Self {
182        let elements = self.elements.slice(range.clone());
183        let validity = self.validity.slice(range);
184        Self {
185            ps: self.ps,
186            elements,
187            validity,
188        }
189    }
190
191    fn try_into_mut(self) -> Result<DVectorMut<D>, Self> {
192        let elements = match self.elements.try_into_mut() {
193            Ok(elements) => elements,
194            Err(elements) => {
195                return Err(Self {
196                    ps: self.ps,
197                    elements,
198                    validity: self.validity,
199                });
200            }
201        };
202
203        match self.validity.try_into_mut() {
204            Ok(validity_mut) => Ok(DVectorMut {
205                ps: self.ps,
206                elements,
207                validity: validity_mut,
208            }),
209            Err(validity) => Err(Self {
210                ps: self.ps,
211                elements: elements.freeze(),
212                validity,
213            }),
214        }
215    }
216
217    fn into_mut(self) -> DVectorMut<D> {
218        DVectorMut {
219            ps: self.ps,
220            elements: self.elements.into_mut(),
221            validity: self.validity.into_mut(),
222        }
223    }
224}