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}