1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use itertools::Itertools;
8use smallvec::smallvec;
9use vortex_buffer::Alignment;
10use vortex_buffer::BitBufferMut;
11use vortex_buffer::Buffer;
12use vortex_buffer::BufferMut;
13use vortex_buffer::ByteBuffer;
14use vortex_error::VortexExpect;
15use vortex_error::VortexResult;
16use vortex_error::vortex_ensure;
17use vortex_error::vortex_panic;
18
19use crate::ArrayRef;
20use crate::ArraySlots;
21use crate::ExecutionCtx;
22use crate::IntoArray;
23use crate::array::Array;
24use crate::array::ArrayParts;
25use crate::array::TypedArrayRef;
26use crate::array::child_to_validity;
27use crate::array::validity_to_child;
28use crate::array_slots;
29use crate::arrays::Decimal;
30use crate::arrays::DecimalArray;
31use crate::arrays::PrimitiveArray;
32use crate::arrays::primitive::PrimitiveArrayExt;
33use crate::buffer::BufferHandle;
34use crate::dtype::BigCast;
35use crate::dtype::DType;
36use crate::dtype::DecimalDType;
37use crate::dtype::DecimalType;
38use crate::dtype::IntegerPType;
39use crate::dtype::NativeDecimalType;
40use crate::dtype::Nullability;
41use crate::match_each_decimal_value_type;
42use crate::match_each_unsigned_integer_ptype;
43use crate::patches::Patches;
44use crate::validity::Validity;
45
46#[array_slots(Decimal)]
47pub struct DecimalSlots {
48 pub validity: Option<ArrayRef>,
50}
51
52#[derive(Clone, Debug)]
110pub struct DecimalData {
111 pub(super) decimal_dtype: DecimalDType,
112 pub(super) values: BufferHandle,
113 pub(super) values_type: DecimalType,
114}
115
116impl Display for DecimalData {
117 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
118 write!(
119 f,
120 "decimal_dtype: {}, values_type: {}",
121 self.decimal_dtype, self.values_type
122 )
123 }
124}
125
126pub struct DecimalDataParts {
127 pub decimal_dtype: DecimalDType,
128 pub values: BufferHandle,
129 pub values_type: DecimalType,
130 pub validity: Validity,
131}
132
133pub trait DecimalArrayExt: TypedArrayRef<Decimal> {
134 fn decimal_dtype(&self) -> DecimalDType {
135 match self.as_ref().dtype() {
136 DType::Decimal(decimal_dtype, _) => *decimal_dtype,
137 _ => unreachable!("DecimalArrayExt requires a decimal dtype"),
138 }
139 }
140
141 fn nullability(&self) -> Nullability {
142 match self.as_ref().dtype() {
143 DType::Decimal(_, nullability) => *nullability,
144 _ => unreachable!("DecimalArrayExt requires a decimal dtype"),
145 }
146 }
147
148 fn validity_child(&self) -> Option<&ArrayRef> {
149 self.as_ref().slots()[DecimalSlots::VALIDITY].as_ref()
150 }
151
152 fn validity(&self) -> Validity {
153 child_to_validity(
154 self.as_ref().slots()[DecimalSlots::VALIDITY].as_ref(),
155 self.nullability(),
156 )
157 }
158
159 fn values_type(&self) -> DecimalType {
160 self.values_type
161 }
162
163 fn precision(&self) -> u8 {
164 self.decimal_dtype().precision()
165 }
166
167 fn scale(&self) -> i8 {
168 self.decimal_dtype().scale()
169 }
170
171 fn buffer_handle(&self) -> &BufferHandle {
172 &self.values
173 }
174
175 fn buffer<T: NativeDecimalType>(&self) -> Buffer<T> {
176 DecimalData::buffer::<T>(self)
177 }
178}
179impl<T: TypedArrayRef<Decimal>> DecimalArrayExt for T {}
180
181impl DecimalData {
182 pub(super) fn make_slots(validity: &Validity, len: usize) -> ArraySlots {
184 smallvec![validity_to_child(validity, len)]
185 }
186
187 pub fn new<T: NativeDecimalType>(buffer: Buffer<T>, decimal_dtype: DecimalDType) -> Self {
194 Self::try_new(buffer, decimal_dtype).vortex_expect("DecimalArray construction failed")
195 }
196
197 pub fn new_handle(
205 values: BufferHandle,
206 values_type: DecimalType,
207 decimal_dtype: DecimalDType,
208 ) -> Self {
209 Self::try_new_handle(values, values_type, decimal_dtype)
210 .vortex_expect("DecimalArray construction failed")
211 }
212
213 pub fn try_new<T: NativeDecimalType>(
222 buffer: Buffer<T>,
223 decimal_dtype: DecimalDType,
224 ) -> VortexResult<Self> {
225 let values = BufferHandle::new_host(buffer.into_byte_buffer());
226 let values_type = T::DECIMAL_TYPE;
227
228 Self::try_new_handle(values, values_type, decimal_dtype)
229 }
230
231 pub fn try_new_handle(
239 values: BufferHandle,
240 values_type: DecimalType,
241 decimal_dtype: DecimalDType,
242 ) -> VortexResult<Self> {
243 Self::validate(&values, values_type)?;
244
245 Ok(unsafe { Self::new_unchecked_handle(values, values_type, decimal_dtype) })
247 }
248
249 pub unsafe fn new_unchecked<T: NativeDecimalType>(
263 buffer: Buffer<T>,
264 decimal_dtype: DecimalDType,
265 ) -> Self {
266 unsafe {
268 Self::new_unchecked_handle(
269 BufferHandle::new_host(buffer.into_byte_buffer()),
270 T::DECIMAL_TYPE,
271 decimal_dtype,
272 )
273 }
274 }
275
276 pub unsafe fn new_unchecked_handle(
286 values: BufferHandle,
287 values_type: DecimalType,
288 decimal_dtype: DecimalDType,
289 ) -> Self {
290 Self {
291 decimal_dtype,
292 values,
293 values_type,
294 }
295 }
296
297 fn validate(buffer: &BufferHandle, values_type: DecimalType) -> VortexResult<()> {
301 let byte_width = values_type.byte_width();
302 vortex_ensure!(
303 buffer.len().is_multiple_of(byte_width),
304 InvalidArgument: "decimal buffer size {} is not divisible by element width {}",
305 buffer.len(),
306 byte_width,
307 );
308 match_each_decimal_value_type!(values_type, |D| {
309 vortex_ensure!(
310 buffer.is_aligned_to(Alignment::of::<D>()),
311 InvalidArgument: "decimal buffer alignment {:?} is invalid for values type {:?}",
312 buffer.alignment(),
313 D::DECIMAL_TYPE,
314 );
315 Ok::<(), vortex_error::VortexError>(())
316 })?;
317 Ok(())
318 }
319
320 pub unsafe fn new_unchecked_from_byte_buffer(
330 byte_buffer: ByteBuffer,
331 values_type: DecimalType,
332 decimal_dtype: DecimalDType,
333 ) -> Self {
334 unsafe {
336 Self::new_unchecked_handle(
337 BufferHandle::new_host(byte_buffer),
338 values_type,
339 decimal_dtype,
340 )
341 }
342 }
343
344 pub fn len(&self) -> usize {
346 self.values.len() / self.values_type.byte_width()
347 }
348
349 pub fn is_empty(&self) -> bool {
351 self.len() == 0
352 }
353
354 pub fn buffer_handle(&self) -> &BufferHandle {
356 &self.values
357 }
358
359 pub fn buffer<T: NativeDecimalType>(&self) -> Buffer<T> {
360 if self.values_type != T::DECIMAL_TYPE {
361 vortex_panic!(
362 "Cannot extract Buffer<{:?}> for DecimalArray with values_type {:?}",
363 T::DECIMAL_TYPE,
364 self.values_type,
365 );
366 }
367 Buffer::<T>::from_byte_buffer(self.values.as_host().clone())
368 }
369
370 pub fn values_type(&self) -> DecimalType {
372 self.values_type
373 }
374
375 pub fn decimal_dtype(&self) -> DecimalDType {
377 self.decimal_dtype
378 }
379
380 pub fn precision(&self) -> u8 {
381 self.decimal_dtype.precision()
382 }
383
384 pub fn scale(&self) -> i8 {
385 self.decimal_dtype.scale()
386 }
387}
388
389impl Array<Decimal> {
390 pub fn into_data_parts(self) -> DecimalDataParts {
391 let validity = DecimalArrayExt::validity(&self);
392 let decimal_dtype = DecimalArrayExt::decimal_dtype(&self);
393 let data = self.into_data();
394 DecimalDataParts {
395 decimal_dtype,
396 values: data.values,
397 values_type: data.values_type,
398 validity,
399 }
400 }
401}
402
403impl Array<Decimal> {
404 pub fn new<T: NativeDecimalType>(
406 buffer: Buffer<T>,
407 decimal_dtype: DecimalDType,
408 validity: Validity,
409 ) -> Self {
410 Self::try_new(buffer, decimal_dtype, validity)
411 .vortex_expect("DecimalArray construction failed")
412 }
413
414 pub unsafe fn new_unchecked<T: NativeDecimalType>(
420 buffer: Buffer<T>,
421 decimal_dtype: DecimalDType,
422 validity: Validity,
423 ) -> Self {
424 let dtype = DType::Decimal(decimal_dtype, validity.nullability());
425 let len = buffer.len();
426 let slots = DecimalData::make_slots(&validity, len);
427 let data = unsafe { DecimalData::new_unchecked(buffer, decimal_dtype) };
428 unsafe {
429 Array::from_parts_unchecked(
430 ArrayParts::new(Decimal, dtype, len, data).with_slots(slots),
431 )
432 }
433 }
434
435 pub fn try_new<T: NativeDecimalType>(
437 buffer: Buffer<T>,
438 decimal_dtype: DecimalDType,
439 validity: Validity,
440 ) -> VortexResult<Self> {
441 let dtype = DType::Decimal(decimal_dtype, validity.nullability());
442 let len = buffer.len();
443 let slots = DecimalData::make_slots(&validity, len);
444 let data = DecimalData::try_new(buffer, decimal_dtype)?;
445 Array::try_from_parts(ArrayParts::new(Decimal, dtype, len, data).with_slots(slots))
446 }
447
448 #[expect(
450 clippy::same_name_method,
451 reason = "intentionally named from_iter like Iterator::from_iter"
452 )]
453 pub fn from_iter<T: NativeDecimalType, I: IntoIterator<Item = T>>(
454 iter: I,
455 decimal_dtype: DecimalDType,
456 ) -> Self {
457 Self::new(
458 BufferMut::from_iter(iter).freeze(),
459 decimal_dtype,
460 Validity::NonNullable,
461 )
462 }
463
464 pub fn from_option_iter<T: NativeDecimalType, I: IntoIterator<Item = Option<T>>>(
466 iter: I,
467 decimal_dtype: DecimalDType,
468 ) -> Self {
469 let iter = iter.into_iter();
470 let mut values = BufferMut::with_capacity(iter.size_hint().0);
471 let mut validity = BitBufferMut::with_capacity(values.capacity());
472
473 for value in iter {
474 match value {
475 Some(value) => {
476 values.push(value);
477 validity.append(true);
478 }
479 None => {
480 values.push(T::default());
481 validity.append(false);
482 }
483 }
484 }
485
486 Self::new(
487 values.freeze(),
488 decimal_dtype,
489 Validity::from(validity.freeze()),
490 )
491 }
492
493 pub fn new_handle(
495 values: BufferHandle,
496 values_type: DecimalType,
497 decimal_dtype: DecimalDType,
498 validity: Validity,
499 ) -> Self {
500 Self::try_new_handle(values, values_type, decimal_dtype, validity)
501 .vortex_expect("DecimalArray construction failed")
502 }
503
504 pub fn try_new_handle(
506 values: BufferHandle,
507 values_type: DecimalType,
508 decimal_dtype: DecimalDType,
509 validity: Validity,
510 ) -> VortexResult<Self> {
511 let dtype = DType::Decimal(decimal_dtype, validity.nullability());
512 let len = values.len() / values_type.byte_width();
513 let slots = DecimalData::make_slots(&validity, len);
514 let data = DecimalData::try_new_handle(values, values_type, decimal_dtype)?;
515 Array::try_from_parts(ArrayParts::new(Decimal, dtype, len, data).with_slots(slots))
516 }
517
518 pub unsafe fn new_unchecked_handle(
524 values: BufferHandle,
525 values_type: DecimalType,
526 decimal_dtype: DecimalDType,
527 validity: Validity,
528 ) -> Self {
529 let dtype = DType::Decimal(decimal_dtype, validity.nullability());
530 let len = values.len() / values_type.byte_width();
531 let slots = DecimalData::make_slots(&validity, len);
532 let data = unsafe { DecimalData::new_unchecked_handle(values, values_type, decimal_dtype) };
533 unsafe {
534 Array::from_parts_unchecked(
535 ArrayParts::new(Decimal, dtype, len, data).with_slots(slots),
536 )
537 }
538 }
539
540 #[expect(
541 clippy::cognitive_complexity,
542 reason = "patching depends on both patch and value physical types"
543 )]
544 pub fn patch(self, patches: &Patches, ctx: &mut ExecutionCtx) -> VortexResult<Self> {
545 let offset = patches.offset();
546 let dtype = self.dtype().clone();
547 let len = self.len();
548 let patch_indices = patches.indices().clone().execute::<PrimitiveArray>(ctx)?;
549 let patch_values = patches.values().clone().execute::<DecimalArray>(ctx)?;
550
551 let patch_validity = patch_values.validity()?;
552 let patched_validity = self.validity()?.patch(
553 self.len(),
554 offset,
555 &patch_indices.clone().into_array(),
556 &patch_validity,
557 ctx,
558 )?;
559 assert_eq!(self.decimal_dtype(), patch_values.decimal_dtype());
560
561 let data = self.into_data();
562 let patch_indices_unsigned =
565 patch_indices.reinterpret_cast(patch_indices.ptype().to_unsigned());
566 let data = match_each_unsigned_integer_ptype!(patch_indices_unsigned.ptype(), |I| {
567 let patch_indices = patch_indices_unsigned.as_slice::<I>();
568 match_each_decimal_value_type!(patch_values.values_type(), |PatchDVT| {
569 let patch_values = patch_values.buffer::<PatchDVT>();
570 match_each_decimal_value_type!(data.values_type(), |ValuesDVT| {
571 let buffer = data.buffer::<ValuesDVT>().into_mut();
572 patch_typed(
573 buffer,
574 data.decimal_dtype(),
575 patch_indices,
576 offset,
577 patch_values,
578 )
579 })
580 })
581 });
582 let slots = DecimalData::make_slots(&patched_validity, len);
583 Ok(unsafe {
584 Array::from_parts_unchecked(
585 ArrayParts::new(Decimal, dtype, len, data).with_slots(slots),
586 )
587 })
588 }
589}
590
591fn patch_typed<I, ValuesDVT, PatchDVT>(
592 mut buffer: BufferMut<ValuesDVT>,
593 decimal_dtype: DecimalDType,
594 patch_indices: &[I],
595 patch_indices_offset: usize,
596 patch_values: Buffer<PatchDVT>,
597) -> DecimalData
598where
599 I: IntegerPType,
600 PatchDVT: NativeDecimalType,
601 ValuesDVT: NativeDecimalType,
602{
603 if !ValuesDVT::DECIMAL_TYPE.is_compatible_decimal_value_type(decimal_dtype) {
604 vortex_panic!(
605 "patch_typed: {:?} cannot represent every value in {}.",
606 ValuesDVT::DECIMAL_TYPE,
607 decimal_dtype
608 )
609 }
610
611 for (idx, value) in patch_indices.iter().zip_eq(patch_values.into_iter()) {
612 buffer[idx.as_() - patch_indices_offset] = <ValuesDVT as BigCast>::from(value).vortex_expect(
613 "values of a given DecimalDType are representable in all compatible NativeDecimalType",
614 );
615 }
616
617 DecimalData::new(buffer.freeze(), decimal_dtype)
618}