1use std::fmt::Debug;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::hash::Hasher;
8
9use vortex_array::Array;
10use vortex_array::ArrayEq;
11use vortex_array::ArrayHash;
12use vortex_array::ArrayId;
13use vortex_array::ArrayParts;
14use vortex_array::ArrayRef;
15use vortex_array::ArraySlots;
16use vortex_array::ArrayView;
17use vortex_array::EqMode;
18use vortex_array::ExecutionCtx;
19use vortex_array::ExecutionResult;
20use vortex_array::IntoArray;
21use vortex_array::TypedArrayRef;
22use vortex_array::arrays::BoolArray;
23use vortex_array::buffer::BufferHandle;
24use vortex_array::dtype::DType;
25use vortex_array::scalar::Scalar;
26use vortex_array::serde::ArrayChildren;
27use vortex_array::validity::Validity;
28use vortex_array::vtable::OperationsVTable;
29use vortex_array::vtable::VTable;
30use vortex_array::vtable::ValidityVTable;
31use vortex_array::vtable::child_to_validity;
32use vortex_array::vtable::validity_to_child;
33use vortex_buffer::BitBufferMut;
34use vortex_buffer::ByteBuffer;
35use vortex_error::VortexResult;
36use vortex_error::vortex_bail;
37use vortex_error::vortex_ensure;
38use vortex_error::vortex_panic;
39use vortex_session::VortexSession;
40use vortex_session::registry::CachedId;
41
42pub type ByteBoolArray = Array<ByteBool>;
44
45impl ArrayHash for ByteBoolData {
46 fn array_hash<H: Hasher>(&self, state: &mut H, accuracy: EqMode) {
47 self.buffer.array_hash(state, accuracy);
48 }
49}
50
51impl ArrayEq for ByteBoolData {
52 fn array_eq(&self, other: &Self, accuracy: EqMode) -> bool {
53 self.buffer.array_eq(&other.buffer, accuracy)
54 }
55}
56
57impl VTable for ByteBool {
58 type TypedArrayData = ByteBoolData;
59
60 type OperationsVTable = Self;
61 type ValidityVTable = Self;
62
63 fn id(&self) -> ArrayId {
64 static ID: CachedId = CachedId::new("vortex.bytebool");
65 *ID
66 }
67
68 fn validate(
69 &self,
70 data: &Self::TypedArrayData,
71 dtype: &DType,
72 len: usize,
73 slots: &[Option<ArrayRef>],
74 ) -> VortexResult<()> {
75 let validity = child_to_validity(slots[VALIDITY_SLOT].as_ref(), dtype.nullability());
76 ByteBoolData::validate(data.buffer(), &validity, dtype, len)
77 }
78
79 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
80 1
81 }
82
83 fn buffer(array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
84 match idx {
85 0 => array.buffer().clone(),
86 _ => vortex_panic!("ByteBoolArray buffer index {idx} out of bounds"),
87 }
88 }
89
90 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
91 match idx {
92 0 => Some("values".to_string()),
93 _ => vortex_panic!("ByteBoolArray buffer_name index {idx} out of bounds"),
94 }
95 }
96
97 fn with_buffers(
98 &self,
99 array: ArrayView<'_, Self>,
100 buffers: &[BufferHandle],
101 ) -> VortexResult<ArrayParts<Self>> {
102 vortex_ensure!(
103 buffers.len() == 1,
104 "Expected 1 buffer, got {}",
105 buffers.len()
106 );
107 let data = ByteBoolData::new(buffers[0].clone());
108 Ok(
109 ArrayParts::new(self.clone(), array.dtype().clone(), array.len(), data)
110 .with_slots(array.slots().iter().cloned().collect()),
111 )
112 }
113
114 fn serialize(
115 _array: ArrayView<'_, Self>,
116 _session: &VortexSession,
117 ) -> VortexResult<Option<Vec<u8>>> {
118 Ok(Some(vec![]))
119 }
120
121 fn deserialize(
122 &self,
123 dtype: &DType,
124 len: usize,
125 metadata: &[u8],
126 buffers: &[BufferHandle],
127 children: &dyn ArrayChildren,
128 _session: &VortexSession,
129 ) -> VortexResult<ArrayParts<Self>> {
130 if !metadata.is_empty() {
131 vortex_bail!(
132 "ByteBoolArray expects empty metadata, got {} bytes",
133 metadata.len()
134 );
135 }
136 let validity = if children.is_empty() {
137 Validity::from(dtype.nullability())
138 } else if children.len() == 1 {
139 let validity = children.get(0, &Validity::DTYPE, len)?;
140 Validity::Array(validity)
141 } else {
142 vortex_bail!("Expected 0 or 1 child, got {}", children.len());
143 };
144
145 if buffers.len() != 1 {
146 vortex_bail!("Expected 1 buffer, got {}", buffers.len());
147 }
148 let buffer = buffers[0].clone();
149
150 let data = ByteBoolData::new(buffer);
151 let slots = ByteBoolData::make_slots(&validity, len);
152 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
153 }
154
155 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
156 SLOT_NAMES[idx].to_string()
157 }
158
159 fn reduce_parent(
160 array: ArrayView<'_, Self>,
161 parent: &ArrayRef,
162 child_idx: usize,
163 ) -> VortexResult<Option<ArrayRef>> {
164 crate::rules::RULES.evaluate(array, parent, child_idx)
165 }
166
167 fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
168 let boolean_buffer = BitBufferMut::from(array.truthy_bytes()).freeze();
170 let validity = array.validity()?;
171 Ok(ExecutionResult::done(
172 BoolArray::new(boolean_buffer, validity).into_array(),
173 ))
174 }
175}
176
177pub(super) const VALIDITY_SLOT: usize = 0;
179pub(super) const NUM_SLOTS: usize = 1;
180pub(super) const SLOT_NAMES: [&str; NUM_SLOTS] = ["validity"];
181
182#[derive(Clone, Debug)]
183pub struct ByteBoolData {
184 buffer: BufferHandle,
185}
186
187impl Display for ByteBoolData {
188 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
189 Ok(())
190 }
191}
192
193pub trait ByteBoolArrayExt: TypedArrayRef<ByteBool> {
194 fn validity(&self) -> Validity {
195 child_to_validity(
196 self.as_ref().slots()[VALIDITY_SLOT].as_ref(),
197 self.as_ref().dtype().nullability(),
198 )
199 }
200}
201
202impl<T: TypedArrayRef<ByteBool>> ByteBoolArrayExt for T {}
203
204#[derive(Clone, Debug)]
205pub struct ByteBool;
206
207impl ByteBool {
208 pub fn new(buffer: BufferHandle, validity: Validity) -> ByteBoolArray {
209 if let Some(len) = validity.maybe_len() {
210 assert_eq!(
211 buffer.len(),
212 len,
213 "ByteBool validity and bytes must have same length"
214 );
215 }
216 let dtype = DType::Bool(validity.nullability());
217
218 let slots = ByteBoolData::make_slots(&validity, buffer.len());
219 let data = ByteBoolData::new(buffer);
220 let len = data.len();
221 unsafe {
222 Array::from_parts_unchecked(
223 ArrayParts::new(ByteBool, dtype, len, data).with_slots(slots),
224 )
225 }
226 }
227
228 pub fn from_vec<V: Into<Validity>>(data: Vec<bool>, validity: V) -> ByteBoolArray {
230 let validity = validity.into();
231 let bytes: Vec<u8> = data.into_iter().map(|b| b as u8).collect();
233 let handle = BufferHandle::new_host(ByteBuffer::from(bytes));
234 ByteBool::new(handle, validity)
235 }
236
237 pub fn from_option_vec(data: Vec<Option<bool>>) -> ByteBoolArray {
239 let validity = Validity::from_iter(data.iter().map(|v| v.is_some()));
240 let bytes: Vec<u8> = data
242 .into_iter()
243 .map(|b| b.unwrap_or_default() as u8)
244 .collect();
245 let handle = BufferHandle::new_host(ByteBuffer::from(bytes));
246 ByteBool::new(handle, validity)
247 }
248}
249
250impl ByteBoolData {
251 pub fn validate(
252 buffer: &BufferHandle,
253 validity: &Validity,
254 dtype: &DType,
255 len: usize,
256 ) -> VortexResult<()> {
257 let expected_dtype = DType::Bool(validity.nullability());
258 vortex_ensure!(
259 dtype == &expected_dtype,
260 "expected dtype {expected_dtype}, got {dtype}"
261 );
262 vortex_ensure!(
263 buffer.len() == len,
264 "expected len {len}, got {}",
265 buffer.len()
266 );
267 if let Some(vlen) = validity.maybe_len() {
268 vortex_ensure!(vlen == len, "expected validity len {len}, got {vlen}");
269 }
270 Ok(())
271 }
272
273 fn make_slots(validity: &Validity, len: usize) -> ArraySlots {
274 vec![validity_to_child(validity, len)].into()
275 }
276
277 pub fn new(buffer: BufferHandle) -> Self {
278 Self { buffer }
279 }
280
281 pub fn len(&self) -> usize {
283 self.buffer.len()
284 }
285
286 pub fn is_empty(&self) -> bool {
288 self.buffer.len() == 0
289 }
290
291 pub fn buffer(&self) -> &BufferHandle {
292 &self.buffer
293 }
294
295 pub fn truthy_bytes(&self) -> &[u8] {
299 self.buffer().as_host().as_slice()
300 }
301}
302
303impl ValidityVTable<ByteBool> for ByteBool {
304 fn validity(array: ArrayView<'_, ByteBool>) -> VortexResult<Validity> {
305 Ok(ByteBoolArrayExt::validity(&array))
306 }
307}
308
309impl OperationsVTable<ByteBool> for ByteBool {
310 fn scalar_at(
311 array: ArrayView<'_, ByteBool>,
312 index: usize,
313 _ctx: &mut ExecutionCtx,
314 ) -> VortexResult<Scalar> {
315 Ok(Scalar::bool(
316 array.buffer.as_host()[index] == 1,
317 array.dtype().nullability(),
318 ))
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use std::sync::LazyLock;
325
326 use vortex_array::ArrayContext;
327 use vortex_array::IntoArray;
328 use vortex_array::VortexSessionExecute;
329 use vortex_array::assert_arrays_eq;
330 use vortex_array::serde::SerializeOptions;
331 use vortex_array::serde::SerializedArray;
332 use vortex_array::session::ArraySessionExt;
333 use vortex_buffer::ByteBufferMut;
334 use vortex_session::registry::ReadContext;
335
336 use super::*;
337
338 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
339 let session = vortex_array::array_session();
340 crate::initialize(&session);
341 session
342 });
343
344 #[test]
345 fn test_validity_construction() {
346 let v = vec![true, false];
347 let v_len = v.len();
348
349 let arr = ByteBool::from_vec(v, Validity::AllValid);
350 assert_eq!(v_len, arr.len());
351
352 let mut ctx = SESSION.create_execution_ctx();
353 for idx in 0..arr.len() {
354 assert!(arr.is_valid(idx, &mut ctx).unwrap());
355 }
356
357 let v = vec![Some(true), None, Some(false)];
358 let arr = ByteBool::from_option_vec(v);
359 assert!(arr.is_valid(0, &mut ctx).unwrap());
360 assert!(!arr.is_valid(1, &mut ctx).unwrap());
361 assert!(arr.is_valid(2, &mut ctx).unwrap());
362 assert_eq!(arr.len(), 3);
363
364 let v: Vec<Option<bool>> = vec![None, None];
365 let v_len = v.len();
366
367 let arr = ByteBool::from_option_vec(v);
368 assert_eq!(v_len, arr.len());
369
370 for idx in 0..arr.len() {
371 assert!(!arr.is_valid(idx, &mut ctx).unwrap());
372 }
373 assert_eq!(arr.len(), 2);
374 }
375
376 #[test]
377 fn test_nullable_bytebool_serde_roundtrip() {
378 let array = ByteBool::from_option_vec(vec![Some(true), None, Some(false), None]);
379 let dtype = array.dtype().clone();
380 let len = array.len();
381 let session = vortex_array::array_session();
382 session.arrays().register(ByteBool);
383
384 let ctx = ArrayContext::empty();
385 let serialized = array
386 .clone()
387 .into_array()
388 .serialize(&ctx, &session, &SerializeOptions::default())
389 .unwrap();
390
391 let mut concat = ByteBufferMut::empty();
392 for buf in serialized {
393 concat.extend_from_slice(buf.as_ref());
394 }
395
396 let parts = SerializedArray::try_from(concat.freeze()).unwrap();
397 let decoded = parts
398 .decode(&dtype, len, &ReadContext::new(ctx.to_ids()), &session)
399 .unwrap();
400
401 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
402 }
403}