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