vortex_array/arrays/primitive/vtable/
mod.rs1use vortex_error::VortexResult;
5use vortex_error::vortex_bail;
6use vortex_error::vortex_ensure;
7use vortex_error::vortex_panic;
8
9use crate::ArrayParts;
10use crate::ArrayRef;
11use crate::ExecutionCtx;
12use crate::ExecutionResult;
13use crate::array::Array;
14use crate::array::ArrayView;
15use crate::array::VTable;
16use crate::arrays::primitive::PrimitiveData;
17use crate::buffer::BufferHandle;
18use crate::builders::ArrayBuilder;
19use crate::builders::PrimitiveBuilder;
20use crate::dtype::DType;
21use crate::dtype::PType;
22use crate::match_each_native_ptype;
23use crate::serde::ArrayChildren;
24use crate::validity::Validity;
25mod kernel;
26mod operations;
27mod validity;
28
29use std::hash::Hasher;
30
31use vortex_buffer::Alignment;
32use vortex_session::VortexSession;
33use vortex_session::registry::CachedId;
34
35use crate::EqMode;
36use crate::array::ArrayId;
37use crate::arrays::primitive::array::PrimitiveSlots;
38use crate::arrays::primitive::compute::rules::RULES;
39use crate::hash::ArrayEq;
40use crate::hash::ArrayHash;
41
42pub type PrimitiveArray = Array<Primitive>;
44
45pub(crate) fn initialize(session: &VortexSession) {
46 kernel::initialize(session);
47}
48
49impl ArrayHash for PrimitiveData {
50 fn array_hash<H: Hasher>(&self, state: &mut H, accuracy: EqMode) {
51 self.buffer.array_hash(state, accuracy);
52 }
53}
54
55impl ArrayEq for PrimitiveData {
56 fn array_eq(&self, other: &Self, accuracy: EqMode) -> bool {
57 self.buffer.array_eq(&other.buffer, accuracy)
58 }
59}
60
61impl VTable for Primitive {
62 type TypedArrayData = PrimitiveData;
63
64 type OperationsVTable = Self;
65 type ValidityVTable = Self;
66
67 fn id(&self) -> ArrayId {
68 static ID: CachedId = CachedId::new("vortex.primitive");
69 *ID
70 }
71
72 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
73 1
74 }
75
76 fn buffer(array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
77 match idx {
78 0 => array.buffer_handle().clone(),
79 _ => vortex_panic!("PrimitiveArray buffer index {idx} out of bounds"),
80 }
81 }
82
83 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
84 match idx {
85 0 => Some("values".to_string()),
86 _ => None,
87 }
88 }
89
90 fn with_buffers(
91 &self,
92 array: ArrayView<'_, Self>,
93 buffers: &[BufferHandle],
94 ) -> VortexResult<ArrayParts<Self>> {
95 vortex_ensure!(
96 buffers.len() == 1,
97 "Expected 1 buffer, got {}",
98 buffers.len()
99 );
100 let mut data = array.data().clone();
101 data.buffer = buffers[0].clone();
102 Ok(
103 ArrayParts::new(self.clone(), array.dtype().clone(), array.len(), data)
104 .with_slots(array.slots().iter().cloned().collect()),
105 )
106 }
107
108 fn serialize(
109 _array: ArrayView<'_, Self>,
110 _session: &VortexSession,
111 ) -> VortexResult<Option<Vec<u8>>> {
112 Ok(Some(vec![]))
113 }
114
115 fn validate(
116 &self,
117 data: &PrimitiveData,
118 dtype: &DType,
119 len: usize,
120 slots: &[Option<ArrayRef>],
121 ) -> VortexResult<()> {
122 let DType::Primitive(_, nullability) = dtype else {
123 vortex_bail!("Expected primitive dtype, got {dtype:?}");
124 };
125 vortex_ensure!(
126 data.len() == len,
127 "PrimitiveArray length {} does not match outer length {}",
128 data.len(),
129 len
130 );
131 let validity =
132 crate::array::child_to_validity(slots[PrimitiveSlots::VALIDITY].as_ref(), *nullability);
133 if let Some(validity_len) = validity.maybe_len() {
134 vortex_ensure!(
135 validity_len == len,
136 "PrimitiveArray validity len {} does not match outer length {}",
137 validity_len,
138 len
139 );
140 }
141
142 Ok(())
143 }
144
145 fn deserialize(
146 &self,
147 dtype: &DType,
148 len: usize,
149 metadata: &[u8],
150
151 buffers: &[BufferHandle],
152 children: &dyn ArrayChildren,
153 _session: &VortexSession,
154 ) -> VortexResult<ArrayParts<Self>> {
155 if !metadata.is_empty() {
156 vortex_bail!(
157 "PrimitiveArray expects empty metadata, got {} bytes",
158 metadata.len()
159 );
160 }
161 if buffers.len() != 1 {
162 vortex_bail!("Expected 1 buffer, got {}", buffers.len());
163 }
164 let buffer = buffers[0].clone();
165
166 let validity = if children.is_empty() {
167 Validity::from(dtype.nullability())
168 } else if children.len() == 1 {
169 let validity = children.get(0, &Validity::DTYPE, len)?;
170 Validity::Array(validity)
171 } else {
172 vortex_bail!("Expected 0 or 1 child, got {}", children.len());
173 };
174
175 let ptype = PType::try_from(dtype)?;
176
177 vortex_ensure!(
178 buffer.is_aligned_to(Alignment::new(ptype.byte_width())),
179 "Misaligned buffer cannot be used to build PrimitiveArray of {ptype}"
180 );
181
182 if buffer.len() != ptype.byte_width() * len {
183 vortex_bail!(
184 "Buffer length {} does not match expected length {} for {}, {}",
185 buffer.len(),
186 ptype.byte_width() * len,
187 ptype.byte_width(),
188 len,
189 );
190 }
191
192 vortex_ensure!(
193 buffer.is_aligned_to(Alignment::new(ptype.byte_width())),
194 "PrimitiveArray::build: Buffer (align={}) must be aligned to {}",
195 buffer.alignment(),
196 ptype.byte_width()
197 );
198
199 let slots = PrimitiveData::make_slots(&validity, len);
201 let data = unsafe { PrimitiveData::new_unchecked_from_handle(buffer, ptype, validity) };
202 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
203 }
204
205 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
206 PrimitiveSlots::NAMES[idx].to_string()
207 }
208
209 fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
210 Ok(ExecutionResult::done(array))
211 }
212
213 fn append_to_builder(
214 array: ArrayView<'_, Self>,
215 builder: &mut dyn ArrayBuilder,
216 ctx: &mut ExecutionCtx,
217 ) -> VortexResult<()> {
218 match_each_native_ptype!(array.ptype(), |P| {
219 if let Some(builder) = builder.as_any_mut().downcast_mut::<PrimitiveBuilder<P>>() {
220 return builder.append_primitive_array(&array.into_owned(), ctx);
221 }
222 });
223
224 vortex_bail!("append_to_builder for Primitive requires a matching PrimitiveBuilder");
225 }
226
227 fn reduce_parent(
228 array: ArrayView<'_, Self>,
229 parent: &ArrayRef,
230 child_idx: usize,
231 ) -> VortexResult<Option<ArrayRef>> {
232 RULES.evaluate(array, parent, child_idx)
233 }
234}
235
236#[derive(Clone, Debug)]
237pub struct Primitive;
238
239#[cfg(test)]
240mod tests {
241 use vortex_buffer::ByteBufferMut;
242 use vortex_buffer::buffer;
243 use vortex_error::VortexResult;
244 use vortex_session::registry::ReadContext;
245
246 use crate::ArrayContext;
247 use crate::IntoArray;
248 use crate::VortexSessionExecute;
249 use crate::array_session;
250 use crate::arrays::PrimitiveArray;
251 use crate::assert_arrays_eq;
252 use crate::buffer::BufferHandle;
253 use crate::serde::SerializeOptions;
254 use crate::serde::SerializedArray;
255 use crate::validity::Validity;
256
257 #[test]
258 fn test_nullable_primitive_serde_roundtrip() {
259 let session = array_session();
260 let mut ctx = session.create_execution_ctx();
261 let array = PrimitiveArray::new(
262 buffer![1i32, 2, 3, 4],
263 Validity::from_iter([true, false, true, false]),
264 );
265 let dtype = array.dtype().clone();
266 let len = array.len();
267
268 let array_ctx = ArrayContext::empty();
269 let serialized = array
270 .clone()
271 .into_array()
272 .serialize(&array_ctx, &session, &SerializeOptions::default())
273 .unwrap();
274
275 let mut concat = ByteBufferMut::empty();
276 for buf in serialized {
277 concat.extend_from_slice(buf.as_ref());
278 }
279 let parts = SerializedArray::try_from(concat.freeze()).unwrap();
280 let decoded = parts
281 .decode(&dtype, len, &ReadContext::new(array_ctx.to_ids()), &session)
282 .unwrap();
283
284 assert_arrays_eq!(decoded, array, &mut ctx);
285 }
286
287 #[test]
288 fn test_with_buffers_replaces_primitive_buffer_with_equivalent_contents() -> VortexResult<()> {
289 let session = array_session();
290 let mut ctx = session.create_execution_ctx();
291
292 let array = PrimitiveArray::from_iter([1i32, 2, 3, 4]).into_array();
293 let replacement = BufferHandle::new_host(buffer![1i32, 2, 3, 4].into_byte_buffer());
294 let rewritten = unsafe { array.with_buffers([replacement]) }?;
297 let expected = PrimitiveArray::from_iter([1i32, 2, 3, 4]);
298
299 assert_arrays_eq!(rewritten, expected, &mut ctx);
300 Ok(())
301 }
302
303 #[test]
304 fn test_with_buffers_rejects_length_change() {
305 let array = PrimitiveArray::from_iter([1i32, 2, 3, 4]).into_array();
306 let replacement = BufferHandle::new_host(buffer![10i32, 20, 30].into_byte_buffer());
307
308 assert!(unsafe { array.with_buffers([replacement]) }.is_err());
311 }
312}