1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use smallvec::smallvec;
8use vortex_buffer::Alignment;
9use vortex_buffer::Buffer;
10use vortex_buffer::BufferMut;
11use vortex_buffer::ByteBuffer;
12use vortex_buffer::ByteBufferMut;
13use vortex_error::VortexExpect;
14use vortex_error::VortexResult;
15use vortex_error::vortex_err;
16use vortex_error::vortex_panic;
17
18use crate::ArraySlots;
19use crate::ExecutionCtx;
20use crate::array::Array;
21use crate::array::ArrayParts;
22use crate::array::TypedArrayRef;
23use crate::arrays::BoolArray;
24use crate::arrays::Primitive;
25use crate::arrays::PrimitiveArray;
26use crate::dtype::DType;
27use crate::dtype::NativePType;
28use crate::dtype::Nullability;
29use crate::dtype::PType;
30use crate::match_each_native_ptype;
31use crate::validity::Validity;
32
33mod cast;
34mod conversion;
35mod patch;
36mod top_value;
37
38pub use patch::chunk_range;
39pub use patch::patch_chunk;
40
41use crate::ArrayRef;
42use crate::aggregate_fn::NumericalAggregateOpts;
43use crate::aggregate_fn::fns::min_max::min_max;
44use crate::array::child_to_validity;
45use crate::array::validity_to_child;
46use crate::array_slots;
47use crate::arrays::bool::BoolArrayExt;
48use crate::buffer::BufferHandle;
49use crate::builtins::ArrayBuiltins;
50
51#[array_slots(Primitive)]
52pub struct PrimitiveSlots {
53 #[slot(0)]
55 pub validity: Option<ArrayRef>,
56}
57
58#[derive(Clone, Debug)]
91pub struct PrimitiveData {
92 pub(super) ptype: PType,
93 pub(super) buffer: BufferHandle,
94}
95
96impl Display for PrimitiveData {
97 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
98 write!(f, "ptype: {}", self.ptype)
99 }
100}
101
102pub struct PrimitiveDataParts {
103 pub ptype: PType,
104 pub buffer: BufferHandle,
105 pub validity: Validity,
106}
107
108pub trait PrimitiveArrayExt: TypedArrayRef<Primitive> {
109 fn ptype(&self) -> PType {
110 match self.as_ref().dtype() {
111 DType::Primitive(ptype, _) => *ptype,
112 _ => unreachable!("PrimitiveArrayExt requires a primitive dtype"),
113 }
114 }
115
116 fn nullability(&self) -> Nullability {
117 match self.as_ref().dtype() {
118 DType::Primitive(_, nullability) => *nullability,
119 _ => unreachable!("PrimitiveArrayExt requires a primitive dtype"),
120 }
121 }
122
123 fn validity_child(&self) -> Option<&ArrayRef> {
124 self.as_ref().slots()[PrimitiveSlots::VALIDITY].as_ref()
125 }
126
127 fn validity(&self) -> Validity {
128 child_to_validity(
129 self.as_ref().slots()[PrimitiveSlots::VALIDITY].as_ref(),
130 self.nullability(),
131 )
132 }
133
134 fn buffer_handle(&self) -> &BufferHandle {
135 &self.buffer
136 }
137
138 fn reinterpret_cast(&self, ptype: PType) -> PrimitiveArray {
139 if self.ptype() == ptype {
140 return self.to_owned();
141 }
142
143 assert_eq!(
144 self.ptype().byte_width(),
145 ptype.byte_width(),
146 "can't reinterpret cast between integers of two different widths"
147 );
148
149 PrimitiveArray::from_buffer_handle(
150 self.buffer_handle().clone(),
151 ptype,
152 PrimitiveArrayExt::validity(self),
153 )
154 }
155
156 fn narrow(&self, ctx: &mut ExecutionCtx) -> VortexResult<PrimitiveArray> {
158 if !self.ptype().is_int() {
159 return Ok(self.to_owned());
160 }
161
162 let Some(min_max) = min_max(self.as_ref(), ctx, NumericalAggregateOpts::default())? else {
163 return Ok(PrimitiveArray::new(
164 Buffer::<u8>::zeroed(self.len()),
165 PrimitiveArrayExt::validity(self),
166 ));
167 };
168
169 let Ok(min) = min_max
172 .min
173 .cast(&PType::I64.into())
174 .and_then(|s| i64::try_from(&s))
175 else {
176 return Ok(self.to_owned());
177 };
178 let Ok(max) = min_max
179 .max
180 .cast(&PType::I64.into())
181 .and_then(|s| i64::try_from(&s))
182 else {
183 return Ok(self.to_owned());
184 };
185
186 let nullability = self.as_ref().dtype().nullability();
187
188 if min < 0 || max < 0 {
189 if min >= i8::MIN as i64 && max <= i8::MAX as i64 {
191 let result = self
192 .as_ref()
193 .cast(DType::Primitive(PType::I8, nullability))?
194 .execute::<PrimitiveArray>(ctx)?;
195 return Ok(result);
196 }
197
198 if min >= i16::MIN as i64 && max <= i16::MAX as i64 {
199 let result = self
200 .as_ref()
201 .cast(DType::Primitive(PType::I16, nullability))?
202 .execute::<PrimitiveArray>(ctx)?;
203 return Ok(result);
204 }
205
206 if min >= i32::MIN as i64 && max <= i32::MAX as i64 {
207 let result = self
208 .as_ref()
209 .cast(DType::Primitive(PType::I32, nullability))?
210 .execute::<PrimitiveArray>(ctx)?;
211 return Ok(result);
212 }
213 } else {
214 if max <= u8::MAX as i64 {
216 let result = self
217 .as_ref()
218 .cast(DType::Primitive(PType::U8, nullability))?
219 .execute::<PrimitiveArray>(ctx)?;
220 return Ok(result);
221 }
222
223 if max <= u16::MAX as i64 {
224 let result = self
225 .as_ref()
226 .cast(DType::Primitive(PType::U16, nullability))?
227 .execute::<PrimitiveArray>(ctx)?;
228 return Ok(result);
229 }
230
231 if max <= u32::MAX as i64 {
232 let result = self
233 .as_ref()
234 .cast(DType::Primitive(PType::U32, nullability))?
235 .execute::<PrimitiveArray>(ctx)?;
236 return Ok(result);
237 }
238 }
239
240 Ok(self.to_owned())
241 }
242}
243impl<T: TypedArrayRef<Primitive>> PrimitiveArrayExt for T {}
244
245impl PrimitiveData {
247 pub(super) fn make_slots(validity: &Validity, len: usize) -> ArraySlots {
249 smallvec![validity_to_child(validity, len)]
250 }
251
252 pub unsafe fn new_unchecked_from_handle(
259 handle: BufferHandle,
260 ptype: PType,
261 _validity: Validity,
262 ) -> Self {
263 Self {
264 ptype,
265 buffer: handle,
266 }
267 }
268
269 pub fn new<T: NativePType>(buffer: impl Into<Buffer<T>>, validity: Validity) -> Self {
276 let buffer = buffer.into();
277 Self::try_new(buffer, validity).vortex_expect("PrimitiveArray construction failed")
278 }
279
280 #[inline]
289 pub fn try_new<T: NativePType>(buffer: Buffer<T>, validity: Validity) -> VortexResult<Self> {
290 Self::validate(&buffer, &validity)?;
291
292 Ok(unsafe { Self::new_unchecked(buffer, validity) })
294 }
295
296 #[inline]
309 pub unsafe fn new_unchecked<T: NativePType>(buffer: Buffer<T>, _validity: Validity) -> Self {
310 #[cfg(debug_assertions)]
311 Self::validate(&buffer, &_validity)
312 .vortex_expect("[Debug Assertion]: Invalid `PrimitiveArray` parameters");
313
314 Self {
315 ptype: T::PTYPE,
316 buffer: BufferHandle::new_host(buffer.into_byte_buffer()),
317 }
318 }
319
320 #[inline]
324 pub fn validate<T: NativePType>(buffer: &Buffer<T>, validity: &Validity) -> VortexResult<()> {
325 if let Some(len) = validity.maybe_len()
326 && buffer.len() != len
327 {
328 return Err(vortex_err!(
329 InvalidArgument:
330 "Buffer and validity length mismatch: buffer={}, validity={}",
331 buffer.len(),
332 len
333 ));
334 }
335 Ok(())
336 }
337
338 pub fn empty<T: NativePType>(nullability: Nullability) -> Self {
339 Self::new(Buffer::<T>::empty(), nullability.into())
340 }
341}
342
343impl Array<Primitive> {
344 pub fn empty<T: NativePType>(nullability: Nullability) -> Self {
345 let dtype = DType::Primitive(T::PTYPE, nullability);
346 let len = 0;
347 let data = PrimitiveData::empty::<T>(nullability);
348 let slots = PrimitiveData::make_slots(&Validity::from(nullability), len);
349 unsafe { Array::from_parts_unchecked(ArrayParts::new(Primitive, dtype, len, data, slots)) }
350 }
351
352 pub fn new<T: NativePType>(buffer: impl Into<Buffer<T>>, validity: Validity) -> Self {
358 let buffer = buffer.into();
359 let dtype = DType::Primitive(T::PTYPE, validity.nullability());
360 let len = buffer.len();
361 let slots = PrimitiveData::make_slots(&validity, len);
362 let data = PrimitiveData::new(buffer, validity);
363 unsafe { Array::from_parts_unchecked(ArrayParts::new(Primitive, dtype, len, data, slots)) }
364 }
365
366 pub fn try_new<T: NativePType>(buffer: Buffer<T>, validity: Validity) -> VortexResult<Self> {
368 let dtype = DType::Primitive(T::PTYPE, validity.nullability());
369 let len = buffer.len();
370 let slots = PrimitiveData::make_slots(&validity, len);
371 let data = PrimitiveData::try_new(buffer, validity)?;
372 Ok(unsafe {
373 Array::from_parts_unchecked(ArrayParts::new(Primitive, dtype, len, data, slots))
374 })
375 }
376
377 pub unsafe fn new_unchecked<T: NativePType>(buffer: Buffer<T>, validity: Validity) -> Self {
383 let dtype = DType::Primitive(T::PTYPE, validity.nullability());
384 let len = buffer.len();
385 let slots = PrimitiveData::make_slots(&validity, len);
386 let data = unsafe { PrimitiveData::new_unchecked(buffer, validity) };
387 unsafe { Array::from_parts_unchecked(ArrayParts::new(Primitive, dtype, len, data, slots)) }
388 }
389
390 pub unsafe fn new_unchecked_from_handle(
396 handle: BufferHandle,
397 ptype: PType,
398 validity: Validity,
399 ) -> Self {
400 let dtype = DType::Primitive(ptype, validity.nullability());
401 let len = handle.len() / ptype.byte_width();
402 let slots = PrimitiveData::make_slots(&validity, len);
403 let data = unsafe { PrimitiveData::new_unchecked_from_handle(handle, ptype, validity) };
404 unsafe { Array::from_parts_unchecked(ArrayParts::new(Primitive, dtype, len, data, slots)) }
405 }
406
407 pub fn from_buffer_handle(handle: BufferHandle, ptype: PType, validity: Validity) -> Self {
409 let dtype = DType::Primitive(ptype, validity.nullability());
410 let len = handle.len() / ptype.byte_width();
411 let slots = PrimitiveData::make_slots(&validity, len);
412 let data = PrimitiveData::from_buffer_handle(handle, ptype, validity);
413 Array::try_from_parts(ArrayParts::new(Primitive, dtype, len, data, slots))
414 .vortex_expect("PrimitiveData is always valid")
415 }
416
417 pub fn from_byte_buffer(buffer: ByteBuffer, ptype: PType, validity: Validity) -> Self {
419 let dtype = DType::Primitive(ptype, validity.nullability());
420 let len = buffer.len() / ptype.byte_width();
421 let slots = PrimitiveData::make_slots(&validity, len);
422 let data = PrimitiveData::from_byte_buffer(buffer, ptype, validity);
423 unsafe { Array::from_parts_unchecked(ArrayParts::new(Primitive, dtype, len, data, slots)) }
424 }
425
426 pub fn from_values_byte_buffer(
428 valid_elems_buffer: ByteBuffer,
429 ptype: PType,
430 validity: Validity,
431 n_rows: usize,
432 ctx: &mut ExecutionCtx,
433 ) -> Self {
434 let dtype = DType::Primitive(ptype, validity.nullability());
435 let len = n_rows;
436 let slots = PrimitiveData::make_slots(&validity, len);
437 let data = PrimitiveData::from_values_byte_buffer(
438 valid_elems_buffer,
439 ptype,
440 validity,
441 n_rows,
442 ctx,
443 );
444 unsafe { Array::from_parts_unchecked(ArrayParts::new(Primitive, dtype, len, data, slots)) }
445 }
446
447 pub fn validate<T: NativePType>(buffer: &Buffer<T>, validity: &Validity) -> VortexResult<()> {
449 PrimitiveData::validate(buffer, validity)
450 }
451
452 pub fn into_data_parts(self) -> PrimitiveDataParts {
453 let validity = PrimitiveArrayExt::validity(&self);
454 let ptype = PrimitiveArrayExt::ptype(&self);
455 let data = self.into_data();
456 PrimitiveDataParts {
457 ptype,
458 buffer: data.buffer,
459 validity,
460 }
461 }
462}
463
464impl PrimitiveData {
465 pub fn len(&self) -> usize {
466 self.buffer.len() / self.ptype.byte_width()
467 }
468
469 pub fn is_empty(&self) -> bool {
471 self.buffer.is_empty()
472 }
473
474 pub fn ptype(&self) -> PType {
475 self.ptype
476 }
477
478 pub fn buffer_handle(&self) -> &BufferHandle {
480 &self.buffer
481 }
482
483 pub fn from_buffer_handle(handle: BufferHandle, ptype: PType, _validity: Validity) -> Self {
484 Self {
485 ptype,
486 buffer: handle,
487 }
488 }
489
490 pub fn from_byte_buffer(buffer: ByteBuffer, ptype: PType, validity: Validity) -> Self {
491 match_each_native_ptype!(ptype, |T| {
492 Self::new::<T>(Buffer::from_byte_buffer(buffer), validity)
493 })
494 }
495
496 pub fn from_values_byte_buffer(
498 valid_elems_buffer: ByteBuffer,
499 ptype: PType,
500 validity: Validity,
501 n_rows: usize,
502 ctx: &mut ExecutionCtx,
503 ) -> Self {
504 let byte_width = ptype.byte_width();
505 let alignment = Alignment::new(byte_width);
506 let buffer = match &validity {
507 Validity::AllValid | Validity::NonNullable => valid_elems_buffer.aligned(alignment),
508 Validity::AllInvalid => ByteBuffer::zeroed_aligned(n_rows * byte_width, alignment),
509 Validity::Array(is_valid) => {
510 let bool_array = is_valid
511 .clone()
512 .execute::<BoolArray>(ctx)
513 .vortex_expect("must be a bool array");
514 let bool_buffer = bool_array.bit_buffer_view();
515 let mut bytes = ByteBufferMut::zeroed_aligned(n_rows * byte_width, alignment);
516 for (i, valid_i) in bool_buffer.set_indices().enumerate() {
517 bytes[valid_i * byte_width..(valid_i + 1) * byte_width]
518 .copy_from_slice(&valid_elems_buffer[i * byte_width..(i + 1) * byte_width])
519 }
520 bytes.freeze()
521 }
522 };
523
524 Self::from_byte_buffer(buffer, ptype, validity)
525 }
526
527 pub fn to_buffer<T: NativePType>(&self) -> Buffer<T> {
531 if T::PTYPE != self.ptype() {
532 vortex_panic!(
533 "Attempted to get buffer of type {} from array of type {}",
534 T::PTYPE,
535 self.ptype()
536 )
537 }
538 Buffer::from_byte_buffer(self.buffer_handle().to_host_sync())
539 }
540
541 pub fn into_buffer<T: NativePType>(self) -> Buffer<T> {
543 if T::PTYPE != self.ptype() {
544 vortex_panic!(
545 "Attempted to get buffer of type {} from array of type {}",
546 T::PTYPE,
547 self.ptype()
548 )
549 }
550 Buffer::from_byte_buffer(self.buffer.into_host_sync())
551 }
552
553 pub fn into_buffer_mut<T: NativePType>(self) -> BufferMut<T> {
556 self.try_into_buffer_mut()
557 .unwrap_or_else(|buffer| BufferMut::<T>::copy_from(&buffer))
558 }
559
560 pub fn try_into_buffer_mut<T: NativePType>(self) -> Result<BufferMut<T>, Buffer<T>> {
565 if T::PTYPE != self.ptype() {
566 vortex_panic!(
567 "Attempted to get buffer_mut of type {} from array of type {}",
568 T::PTYPE,
569 self.ptype()
570 )
571 }
572 let buffer = Buffer::<T>::from_byte_buffer(self.buffer.into_host_sync());
573 buffer.try_into_mut()
574 }
575}