Skip to main content

reifydb_core/value/column/buffer/
scatter.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::fmt::Debug;
5
6use reifydb_value::{
7	util::bitvec::BitVec,
8	value::{
9		Value,
10		container::{
11			bool::BoolContainer, number::NumberContainer, temporal::TemporalContainer, uuid::UuidContainer,
12		},
13		date::Date,
14		datetime::DateTime,
15		duration::Duration,
16		is::{IsNumber, IsTemporal, IsUuid},
17		time::Time,
18		uuid::{Uuid4, Uuid7},
19	},
20};
21
22use crate::value::column::ColumnBuffer;
23
24impl ColumnBuffer {
25	pub fn scatter_merge(
26		&self,
27		other: &ColumnBuffer,
28		then_mask: &BitVec,
29		else_mask: &BitVec,
30		total_len: usize,
31	) -> ColumnBuffer {
32		if let (
33			ColumnBuffer::Option {
34				inner: a_inner,
35				bitvec: a_bv,
36			},
37			ColumnBuffer::Option {
38				inner: b_inner,
39				bitvec: b_bv,
40			},
41		) = (self, other)
42		{
43			let merged_inner = a_inner.scatter_merge(b_inner, then_mask, else_mask, total_len);
44			let merged_bv = merge_validity_bitvecs(a_bv, b_bv, then_mask, else_mask, total_len);
45			return match merged_inner {
46				ColumnBuffer::Option {
47					inner: nested_inner,
48					bitvec: nested_bv,
49				} => ColumnBuffer::Option {
50					inner: nested_inner,
51					bitvec: merged_bv.and(&nested_bv),
52				},
53				inner => ColumnBuffer::Option {
54					inner: Box::new(inner),
55					bitvec: merged_bv,
56				},
57			};
58		}
59
60		if let Some(result) = scatter_merge_typed(self, other, then_mask, else_mask, total_len) {
61			return result;
62		}
63
64		scatter_merge_generic(self, other, then_mask, else_mask, total_len)
65	}
66}
67
68fn merge_validity_bitvecs(
69	then_bv: &BitVec,
70	else_bv: &BitVec,
71	then_mask: &BitVec,
72	else_mask: &BitVec,
73	total_len: usize,
74) -> BitVec {
75	let mut out = BitVec::with_capacity(total_len);
76	for i in 0..total_len {
77		let bit = if then_mask.get(i) {
78			i < then_bv.len() && then_bv.get(i)
79		} else if else_mask.get(i) {
80			i < else_bv.len() && else_bv.get(i)
81		} else {
82			false
83		};
84		out.push(bit);
85	}
86	out
87}
88
89fn scatter_merge_generic(
90	self_col: &ColumnBuffer,
91	other: &ColumnBuffer,
92	then_mask: &BitVec,
93	else_mask: &BitVec,
94	total_len: usize,
95) -> ColumnBuffer {
96	let result_type = self_col.get_type();
97	let mut data = ColumnBuffer::with_capacity(result_type.clone(), total_len);
98	for i in 0..total_len {
99		if then_mask.get(i) {
100			data.push_value(self_col.get_value(i));
101		} else if else_mask.get(i) {
102			data.push_value(other.get_value(i));
103		} else {
104			data.push_value(Value::none_of(result_type.clone()));
105		}
106	}
107	data
108}
109
110fn scatter_merge_typed(
111	self_col: &ColumnBuffer,
112	other: &ColumnBuffer,
113	then_mask: &BitVec,
114	else_mask: &BitVec,
115	total_len: usize,
116) -> Option<ColumnBuffer> {
117	macro_rules! number_kernel {
118		($variant:ident, $t:ty) => {
119			if let (ColumnBuffer::$variant(a), ColumnBuffer::$variant(b)) = (self_col, other) {
120				let (data, validity) = number_scatter::<$t>(a, b, then_mask, else_mask, total_len);
121				let inner = ColumnBuffer::$variant(NumberContainer::new(data));
122				return Some(finalize(inner, validity));
123			}
124		};
125	}
126	macro_rules! temporal_kernel {
127		($variant:ident, $t:ty) => {
128			if let (ColumnBuffer::$variant(a), ColumnBuffer::$variant(b)) = (self_col, other) {
129				let (data, validity) = temporal_scatter::<$t>(a, b, then_mask, else_mask, total_len);
130				let inner = ColumnBuffer::$variant(TemporalContainer::new(data));
131				return Some(finalize(inner, validity));
132			}
133		};
134	}
135	macro_rules! uuid_kernel {
136		($variant:ident, $t:ty) => {
137			if let (ColumnBuffer::$variant(a), ColumnBuffer::$variant(b)) = (self_col, other) {
138				let (data, validity) = uuid_scatter::<$t>(a, b, then_mask, else_mask, total_len);
139				let inner = ColumnBuffer::$variant(UuidContainer::new(data));
140				return Some(finalize(inner, validity));
141			}
142		};
143	}
144
145	if let (ColumnBuffer::Bool(a), ColumnBuffer::Bool(b)) = (self_col, other) {
146		let (data, validity) = bool_scatter(a, b, then_mask, else_mask, total_len);
147		let inner = ColumnBuffer::Bool(BoolContainer::from_parts(data));
148		return Some(finalize(inner, validity));
149	}
150
151	number_kernel!(Float4, f32);
152	number_kernel!(Float8, f64);
153	number_kernel!(Int1, i8);
154	number_kernel!(Int2, i16);
155	number_kernel!(Int4, i32);
156	number_kernel!(Int8, i64);
157	number_kernel!(Int16, i128);
158	number_kernel!(Uint1, u8);
159	number_kernel!(Uint2, u16);
160	number_kernel!(Uint4, u32);
161	number_kernel!(Uint8, u64);
162	number_kernel!(Uint16, u128);
163
164	temporal_kernel!(Date, Date);
165	temporal_kernel!(DateTime, DateTime);
166	temporal_kernel!(Time, Time);
167	temporal_kernel!(Duration, Duration);
168
169	uuid_kernel!(Uuid4, Uuid4);
170	uuid_kernel!(Uuid7, Uuid7);
171
172	None
173}
174
175fn finalize(inner: ColumnBuffer, validity: Option<BitVec>) -> ColumnBuffer {
176	match validity {
177		Some(bv) => ColumnBuffer::Option {
178			inner: Box::new(inner),
179			bitvec: bv,
180		},
181		None => inner,
182	}
183}
184
185fn bool_scatter(
186	a: &BoolContainer,
187	b: &BoolContainer,
188	then_mask: &BitVec,
189	else_mask: &BitVec,
190	total_len: usize,
191) -> (BitVec, Option<BitVec>) {
192	let a_data = a.data();
193	let b_data = b.data();
194	let mut out = BitVec::with_capacity(total_len);
195	let mut validity: Option<BitVec> = None;
196	for i in 0..total_len {
197		let in_then = then_mask.get(i);
198		let in_else = !in_then && else_mask.get(i);
199		let bit = if in_then && i < a_data.len() {
200			a_data.get(i)
201		} else if in_else && i < b_data.len() {
202			b_data.get(i)
203		} else {
204			false
205		};
206		out.push(bit);
207		if !in_then && !in_else {
208			let v = validity.get_or_insert_with(|| {
209				let mut bv = BitVec::with_capacity(total_len);
210				for _ in 0..i {
211					bv.push(true);
212				}
213				bv
214			});
215			v.push(false);
216		} else if let Some(v) = validity.as_mut() {
217			v.push(true);
218		}
219	}
220	(out, validity)
221}
222
223fn number_scatter<T>(
224	a: &NumberContainer<T>,
225	b: &NumberContainer<T>,
226	then_mask: &BitVec,
227	else_mask: &BitVec,
228	total_len: usize,
229) -> (Vec<T>, Option<BitVec>)
230where
231	T: IsNumber + Clone + Default + Debug,
232{
233	let a_data = a.data();
234	let b_data = b.data();
235	let mut out: Vec<T> = Vec::with_capacity(total_len);
236	let mut validity: Option<BitVec> = None;
237	for i in 0..total_len {
238		let in_then = then_mask.get(i);
239		let in_else = !in_then && else_mask.get(i);
240		let value = if in_then {
241			a_data.get(i).cloned().unwrap_or_default()
242		} else if in_else {
243			b_data.get(i).cloned().unwrap_or_default()
244		} else {
245			T::default()
246		};
247		out.push(value);
248		if !in_then && !in_else {
249			let v = validity.get_or_insert_with(|| {
250				let mut bv = BitVec::with_capacity(total_len);
251				for _ in 0..i {
252					bv.push(true);
253				}
254				bv
255			});
256			v.push(false);
257		} else if let Some(v) = validity.as_mut() {
258			v.push(true);
259		}
260	}
261	(out, validity)
262}
263
264fn temporal_scatter<T>(
265	a: &TemporalContainer<T>,
266	b: &TemporalContainer<T>,
267	then_mask: &BitVec,
268	else_mask: &BitVec,
269	total_len: usize,
270) -> (Vec<T>, Option<BitVec>)
271where
272	T: IsTemporal + Clone + Default + Debug,
273{
274	let a_data = a.data();
275	let b_data = b.data();
276	let mut out: Vec<T> = Vec::with_capacity(total_len);
277	let mut validity: Option<BitVec> = None;
278	for i in 0..total_len {
279		let in_then = then_mask.get(i);
280		let in_else = !in_then && else_mask.get(i);
281		let value = if in_then {
282			a_data.get(i).cloned().unwrap_or_default()
283		} else if in_else {
284			b_data.get(i).cloned().unwrap_or_default()
285		} else {
286			T::default()
287		};
288		out.push(value);
289		if !in_then && !in_else {
290			let v = validity.get_or_insert_with(|| {
291				let mut bv = BitVec::with_capacity(total_len);
292				for _ in 0..i {
293					bv.push(true);
294				}
295				bv
296			});
297			v.push(false);
298		} else if let Some(v) = validity.as_mut() {
299			v.push(true);
300		}
301	}
302	(out, validity)
303}
304
305fn uuid_scatter<T>(
306	a: &UuidContainer<T>,
307	b: &UuidContainer<T>,
308	then_mask: &BitVec,
309	else_mask: &BitVec,
310	total_len: usize,
311) -> (Vec<T>, Option<BitVec>)
312where
313	T: IsUuid + Clone + Default + Debug,
314{
315	let a_data = a.data();
316	let b_data = b.data();
317	let mut out: Vec<T> = Vec::with_capacity(total_len);
318	let mut validity: Option<BitVec> = None;
319	for i in 0..total_len {
320		let in_then = then_mask.get(i);
321		let in_else = !in_then && else_mask.get(i);
322		let value = if in_then {
323			a_data.get(i).cloned().unwrap_or_default()
324		} else if in_else {
325			b_data.get(i).cloned().unwrap_or_default()
326		} else {
327			T::default()
328		};
329		out.push(value);
330		if !in_then && !in_else {
331			let v = validity.get_or_insert_with(|| {
332				let mut bv = BitVec::with_capacity(total_len);
333				for _ in 0..i {
334					bv.push(true);
335				}
336				bv
337			});
338			v.push(false);
339		} else if let Some(v) = validity.as_mut() {
340			v.push(true);
341		}
342	}
343	(out, validity)
344}
345
346#[cfg(test)]
347mod tests {
348	use reifydb_value::{
349		util::bitvec::BitVec,
350		value::{Value, value_type::ValueType},
351	};
352
353	use crate::value::column::ColumnBuffer;
354
355	#[test]
356	fn scatter_merge_all_mapped_int4() {
357		let a = ColumnBuffer::int4([10, 20, 30, 40]);
358		let b = ColumnBuffer::int4([90, 80, 70, 60]);
359		let then_mask = BitVec::from_slice(&[true, false, true, false]);
360		let else_mask = BitVec::from_slice(&[false, true, false, true]);
361
362		let merged = a.scatter_merge(&b, &then_mask, &else_mask, 4);
363		assert!(matches!(merged, ColumnBuffer::Int4(_)));
364		assert_eq!(merged.get_value(0), Value::Int4(10));
365		assert_eq!(merged.get_value(1), Value::Int4(80));
366		assert_eq!(merged.get_value(2), Value::Int4(30));
367		assert_eq!(merged.get_value(3), Value::Int4(60));
368	}
369
370	#[test]
371	fn scatter_merge_unmapped_promotes_to_option() {
372		let a = ColumnBuffer::int4([10, 20, 30]);
373		let b = ColumnBuffer::int4([90, 80, 70]);
374		// Row 1 is in neither mask, so it must come out as none.
375		let then_mask = BitVec::from_slice(&[true, false, true]);
376		let else_mask = BitVec::from_slice(&[false, false, false]);
377
378		let merged = a.scatter_merge(&b, &then_mask, &else_mask, 3);
379		assert!(matches!(merged, ColumnBuffer::Option { .. }));
380		assert_eq!(merged.get_value(0), Value::Int4(10));
381		assert_eq!(merged.get_value(1), Value::none_of(ValueType::Int4));
382		assert_eq!(merged.get_value(2), Value::Int4(30));
383	}
384
385	#[test]
386	fn scatter_merge_bool_all_mapped() {
387		let a = ColumnBuffer::bool([true, true, false, false]);
388		let b = ColumnBuffer::bool([false, false, true, true]);
389		let then_mask = BitVec::from_slice(&[true, false, true, false]);
390		let else_mask = BitVec::from_slice(&[false, true, false, true]);
391
392		let merged = a.scatter_merge(&b, &then_mask, &else_mask, 4);
393		assert!(matches!(merged, ColumnBuffer::Bool(_)));
394		assert_eq!(merged.get_value(0), Value::Boolean(true));
395		assert_eq!(merged.get_value(1), Value::Boolean(false));
396		assert_eq!(merged.get_value(2), Value::Boolean(false));
397		assert_eq!(merged.get_value(3), Value::Boolean(true));
398	}
399
400	#[test]
401	fn scatter_merge_utf8_uses_generic_fallback() {
402		let a = ColumnBuffer::utf8(["a", "b", "c"]);
403		let b = ColumnBuffer::utf8(["x", "y", "z"]);
404		let then_mask = BitVec::from_slice(&[true, false, true]);
405		let else_mask = BitVec::from_slice(&[false, true, false]);
406
407		let merged = a.scatter_merge(&b, &then_mask, &else_mask, 3);
408		assert_eq!(merged.get_value(0), Value::Utf8("a".to_string()));
409		assert_eq!(merged.get_value(1), Value::Utf8("y".to_string()));
410		assert_eq!(merged.get_value(2), Value::Utf8("c".to_string()));
411	}
412}