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