1use std::fmt::Debug;
5use std::iter;
6
7mod accessor;
8
9use arrow_buffer::BooleanBufferBuilder;
10use vortex_buffer::{Alignment, Buffer, BufferMut, ByteBuffer, ByteBufferMut};
11use vortex_dtype::{DType, NativePType, Nullability, PType, match_each_native_ptype};
12use vortex_error::{VortexResult, vortex_panic};
13
14use crate::builders::ArrayBuilder;
15use crate::stats::{ArrayStats, StatsSetRef};
16use crate::validity::Validity;
17use crate::{Array, ArrayRef, Canonical, EncodingId, EncodingRef, IntoArray, vtable};
18
19mod compute;
20mod native_value;
21mod ops;
22mod patch;
23mod serde;
24mod top_value;
25
26pub use compute::{IS_CONST_LANE_WIDTH, compute_is_constant};
27pub use native_value::NativeValue;
28
29use crate::vtable::{
30 ArrayVTable, CanonicalVTable, NotSupported, VTable, ValidityHelper,
31 ValidityVTableFromValidityHelper,
32};
33
34vtable!(Primitive);
35
36impl VTable for PrimitiveVTable {
37 type Array = PrimitiveArray;
38 type Encoding = PrimitiveEncoding;
39
40 type ArrayVTable = Self;
41 type CanonicalVTable = Self;
42 type OperationsVTable = Self;
43 type ValidityVTable = ValidityVTableFromValidityHelper;
44 type VisitorVTable = Self;
45 type ComputeVTable = NotSupported;
46 type EncodeVTable = NotSupported;
47 type SerdeVTable = Self;
48
49 fn id(_encoding: &Self::Encoding) -> EncodingId {
50 EncodingId::new_ref("vortex.primitive")
51 }
52
53 fn encoding(_array: &Self::Array) -> EncodingRef {
54 EncodingRef::new_ref(PrimitiveEncoding.as_ref())
55 }
56}
57
58#[derive(Clone, Debug)]
90pub struct PrimitiveArray {
91 dtype: DType,
92 buffer: ByteBuffer,
93 validity: Validity,
94 stats_set: ArrayStats,
95}
96
97#[derive(Clone, Debug)]
98pub struct PrimitiveEncoding;
99
100impl PrimitiveArray {
101 pub fn new<T: NativePType>(buffer: impl Into<Buffer<T>>, validity: Validity) -> Self {
102 let buffer = buffer.into();
103 if let Some(len) = validity.maybe_len()
104 && buffer.len() != len
105 {
106 vortex_panic!(
107 "Buffer and validity length mismatch: buffer={}, validity={}",
108 buffer.len(),
109 len
110 );
111 }
112
113 Self {
114 dtype: DType::Primitive(T::PTYPE, validity.nullability()),
115 buffer: buffer.into_byte_buffer(),
116 validity,
117 stats_set: Default::default(),
118 }
119 }
120
121 pub fn empty<T: NativePType>(nullability: Nullability) -> Self {
122 Self::new(Buffer::<T>::empty(), nullability.into())
123 }
124
125 pub fn from_byte_buffer(buffer: ByteBuffer, ptype: PType, validity: Validity) -> Self {
126 match_each_native_ptype!(ptype, |T| {
127 Self::new::<T>(Buffer::from_byte_buffer(buffer), validity)
128 })
129 }
130
131 pub fn from_option_iter<T: NativePType, I: IntoIterator<Item = Option<T>>>(iter: I) -> Self {
134 let iter = iter.into_iter();
135 let mut values = BufferMut::with_capacity(iter.size_hint().0);
136 let mut validity = BooleanBufferBuilder::new(values.capacity());
137
138 for i in iter {
139 match i {
140 None => {
141 validity.append(false);
142 values.push(T::default());
143 }
144 Some(e) => {
145 validity.append(true);
146 values.push(e);
147 }
148 }
149 }
150 Self::new(values.freeze(), Validity::from(validity.finish()))
151 }
152
153 pub fn from_values_byte_buffer(
155 valid_elems_buffer: ByteBuffer,
156 ptype: PType,
157 validity: Validity,
158 n_rows: usize,
159 ) -> VortexResult<Self> {
160 let byte_width = ptype.byte_width();
161 let alignment = Alignment::new(byte_width);
162 let buffer = match &validity {
163 Validity::AllValid | Validity::NonNullable => valid_elems_buffer.aligned(alignment),
164 Validity::AllInvalid => ByteBuffer::zeroed_aligned(n_rows * byte_width, alignment),
165 Validity::Array(is_valid) => {
166 let bool_array = is_valid.to_canonical()?.into_bool()?;
167 let bool_buffer = bool_array.boolean_buffer();
168 let mut bytes = ByteBufferMut::zeroed_aligned(n_rows * byte_width, alignment);
169 for (i, valid_i) in bool_buffer.set_indices().enumerate() {
170 bytes[valid_i * byte_width..(valid_i + 1) * byte_width]
171 .copy_from_slice(&valid_elems_buffer[i * byte_width..(i + 1) * byte_width])
172 }
173 bytes.freeze()
174 }
175 };
176
177 Ok(Self::from_byte_buffer(buffer, ptype, validity))
178 }
179
180 pub fn ptype(&self) -> PType {
181 self.dtype().as_ptype()
182 }
183
184 pub fn byte_buffer(&self) -> &ByteBuffer {
185 &self.buffer
186 }
187
188 pub fn into_byte_buffer(self) -> ByteBuffer {
189 self.buffer
190 }
191
192 pub fn buffer<T: NativePType>(&self) -> Buffer<T> {
193 if T::PTYPE != self.ptype() {
194 vortex_panic!(
195 "Attempted to get buffer of type {} from array of type {}",
196 T::PTYPE,
197 self.ptype()
198 )
199 }
200 Buffer::from_byte_buffer(self.byte_buffer().clone())
201 }
202
203 pub fn into_buffer<T: NativePType>(self) -> Buffer<T> {
204 if T::PTYPE != self.ptype() {
205 vortex_panic!(
206 "Attempted to get buffer of type {} from array of type {}",
207 T::PTYPE,
208 self.ptype()
209 )
210 }
211 Buffer::from_byte_buffer(self.buffer)
212 }
213
214 pub fn into_buffer_mut<T: NativePType>(self) -> BufferMut<T> {
217 if T::PTYPE != self.ptype() {
218 vortex_panic!(
219 "Attempted to get buffer_mut of type {} from array of type {}",
220 T::PTYPE,
221 self.ptype()
222 )
223 }
224 self.into_buffer()
225 .try_into_mut()
226 .unwrap_or_else(|buffer| BufferMut::<T>::copy_from(&buffer))
227 }
228
229 #[allow(clippy::panic_in_result_fn)]
231 pub fn try_into_buffer_mut<T: NativePType>(self) -> Result<BufferMut<T>, PrimitiveArray> {
232 if T::PTYPE != self.ptype() {
233 vortex_panic!(
234 "Attempted to get buffer_mut of type {} from array of type {}",
235 T::PTYPE,
236 self.ptype()
237 )
238 }
239 let validity = self.validity().clone();
240 Buffer::<T>::from_byte_buffer(self.into_byte_buffer())
241 .try_into_mut()
242 .map_err(|buffer| PrimitiveArray::new(buffer, validity))
243 }
244
245 pub fn map_each<T, R, F>(self, f: F) -> PrimitiveArray
252 where
253 T: NativePType,
254 R: NativePType,
255 F: FnMut(T) -> R,
256 {
257 let validity = self.validity().clone();
258 let buffer = match self.try_into_buffer_mut() {
259 Ok(buffer_mut) => buffer_mut.map_each(f),
260 Err(parray) => BufferMut::<R>::from_iter(parray.buffer::<T>().iter().copied().map(f)),
261 };
262 PrimitiveArray::new(buffer.freeze(), validity)
263 }
264
265 pub fn map_each_with_validity<T, R, F>(self, f: F) -> VortexResult<PrimitiveArray>
270 where
271 T: NativePType,
272 R: NativePType,
273 F: FnMut((T, bool)) -> R,
274 {
275 let validity = self.validity();
276
277 let buf_iter = self.buffer::<T>().into_iter();
278
279 let buffer = match &validity {
280 Validity::NonNullable | Validity::AllValid => {
281 BufferMut::<R>::from_iter(buf_iter.zip(iter::repeat(true)).map(f))
282 }
283 Validity::AllInvalid => {
284 BufferMut::<R>::from_iter(buf_iter.zip(iter::repeat(false)).map(f))
285 }
286 Validity::Array(val) => {
287 let val = val.to_canonical()?.into_bool()?;
288 BufferMut::<R>::from_iter(buf_iter.zip(val.boolean_buffer()).map(f))
289 }
290 };
291 Ok(PrimitiveArray::new(buffer.freeze(), validity.clone()))
292 }
293
294 pub fn as_slice<T: NativePType>(&self) -> &[T] {
298 if T::PTYPE != self.ptype() {
299 vortex_panic!(
300 "Attempted to get slice of type {} from array of type {}",
301 T::PTYPE,
302 self.ptype()
303 )
304 }
305 let raw_slice = self.byte_buffer().as_ptr();
306 unsafe {
308 std::slice::from_raw_parts(raw_slice.cast(), self.byte_buffer().len() / size_of::<T>())
309 }
310 }
311
312 pub fn reinterpret_cast(&self, ptype: PType) -> Self {
313 if self.ptype() == ptype {
314 return self.clone();
315 }
316
317 assert_eq!(
318 self.ptype().byte_width(),
319 ptype.byte_width(),
320 "can't reinterpret cast between integers of two different widths"
321 );
322
323 PrimitiveArray::from_byte_buffer(self.byte_buffer().clone(), ptype, self.validity().clone())
324 }
325}
326
327impl ArrayVTable<PrimitiveVTable> for PrimitiveVTable {
328 fn len(array: &PrimitiveArray) -> usize {
329 array.byte_buffer().len() / array.ptype().byte_width()
330 }
331
332 fn dtype(array: &PrimitiveArray) -> &DType {
333 &array.dtype
334 }
335
336 fn stats(array: &PrimitiveArray) -> StatsSetRef<'_> {
337 array.stats_set.to_ref(array.as_ref())
338 }
339}
340
341impl ValidityHelper for PrimitiveArray {
342 fn validity(&self) -> &Validity {
343 &self.validity
344 }
345}
346
347impl<T: NativePType> FromIterator<T> for PrimitiveArray {
348 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
349 let values = BufferMut::from_iter(iter);
350 PrimitiveArray::new(values.freeze(), Validity::NonNullable)
351 }
352}
353
354impl<T: NativePType> IntoArray for Buffer<T> {
355 fn into_array(self) -> ArrayRef {
356 PrimitiveArray::new(self, Validity::NonNullable).into_array()
357 }
358}
359
360impl<T: NativePType> IntoArray for BufferMut<T> {
361 fn into_array(self) -> ArrayRef {
362 self.freeze().into_array()
363 }
364}
365
366impl CanonicalVTable<PrimitiveVTable> for PrimitiveVTable {
367 fn canonicalize(array: &PrimitiveArray) -> VortexResult<Canonical> {
368 Ok(Canonical::Primitive(array.clone()))
369 }
370
371 fn append_to_builder(
372 array: &PrimitiveArray,
373 builder: &mut dyn ArrayBuilder,
374 ) -> VortexResult<()> {
375 builder.extend_from_array(array.as_ref())
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use vortex_buffer::buffer;
382 use vortex_scalar::PValue;
383
384 use crate::arrays::{BoolArray, PrimitiveArray};
385 use crate::compute::conformance::filter::test_filter_conformance;
386 use crate::compute::conformance::mask::test_mask_conformance;
387 use crate::compute::conformance::search_sorted::rstest_reuse::apply;
388 use crate::compute::conformance::search_sorted::{search_sorted_conformance, *};
389 use crate::search_sorted::{SearchResult, SearchSorted, SearchSortedSide};
390 use crate::validity::Validity;
391 use crate::{ArrayRef, IntoArray};
392
393 #[apply(search_sorted_conformance)]
394 fn test_search_sorted_primitive(
395 #[case] array: ArrayRef,
396 #[case] value: i32,
397 #[case] side: SearchSortedSide,
398 #[case] expected: SearchResult,
399 ) {
400 let res = array
401 .as_primitive_typed()
402 .search_sorted(&Some(PValue::from(value)), side);
403 assert_eq!(res, expected);
404 }
405
406 #[test]
407 fn test_mask_primitive_array() {
408 test_mask_conformance(
409 PrimitiveArray::new(buffer![0, 1, 2, 3, 4], Validity::NonNullable).as_ref(),
410 );
411 test_mask_conformance(
412 PrimitiveArray::new(buffer![0, 1, 2, 3, 4], Validity::AllValid).as_ref(),
413 );
414 test_mask_conformance(
415 PrimitiveArray::new(buffer![0, 1, 2, 3, 4], Validity::AllInvalid).as_ref(),
416 );
417 test_mask_conformance(
418 PrimitiveArray::new(
419 buffer![0, 1, 2, 3, 4],
420 Validity::Array(
421 BoolArray::from_iter([true, false, true, false, true]).into_array(),
422 ),
423 )
424 .as_ref(),
425 );
426 }
427
428 #[test]
429 fn test_filter_primitive_array() {
430 test_filter_conformance(
432 PrimitiveArray::new(buffer![42i32], Validity::NonNullable).as_ref(),
433 );
434 test_filter_conformance(PrimitiveArray::new(buffer![0, 1], Validity::NonNullable).as_ref());
435 test_filter_conformance(
436 PrimitiveArray::new(buffer![0, 1, 2, 3, 4], Validity::NonNullable).as_ref(),
437 );
438 test_filter_conformance(
439 PrimitiveArray::new(buffer![0, 1, 2, 3, 4, 5, 6, 7], Validity::NonNullable).as_ref(),
440 );
441
442 test_filter_conformance(
444 PrimitiveArray::new(buffer![0, 1, 2, 3, 4], Validity::AllValid).as_ref(),
445 );
446 test_filter_conformance(
447 PrimitiveArray::new(
448 buffer![0, 1, 2, 3, 4, 5],
449 Validity::Array(
450 BoolArray::from_iter([true, false, true, false, true, true]).into_array(),
451 ),
452 )
453 .as_ref(),
454 );
455 }
456}