use libduckdb_sys::duckdb_vector;
use crate::vector::complex::{ListVector, MapVector, StructVector};
use crate::vector::VectorWriter;
pub const MAX_LIST_CHILD_CAPACITY: u64 = 1 << 37;
#[allow(
clippy::cast_possible_truncation,
reason = "the branch above proves the value fits"
)]
const MAX_CHILD_CAPACITY_USIZE: usize = if MAX_LIST_CHILD_CAPACITY > usize::MAX as u64 {
usize::MAX
} else {
MAX_LIST_CHILD_CAPACITY as usize
};
#[derive(Debug)]
pub struct ListBuilder {
vector: duckdb_vector,
written: usize,
reserved: usize,
overflowed: bool,
}
impl ListBuilder {
#[must_use]
pub const unsafe fn new(vector: duckdb_vector) -> Self {
Self {
vector,
written: 0,
reserved: 0,
overflowed: false,
}
}
#[must_use]
#[inline]
pub const fn element_count(&self) -> usize {
self.written
}
#[must_use]
#[inline]
pub const fn overflowed(&self) -> bool {
self.overflowed
}
unsafe fn ensure_capacity(&mut self, capacity: usize) -> bool {
if self.overflowed {
return false;
}
if capacity as u64 > MAX_LIST_CHILD_CAPACITY {
self.overflowed = true;
return false;
}
if capacity > self.reserved {
let target = capacity.next_power_of_two().min(MAX_CHILD_CAPACITY_USIZE);
unsafe { ListVector::reserve(self.vector, target) };
self.reserved = target;
}
true
}
pub unsafe fn push_row<F>(&mut self, row_idx: usize, len: usize, write: F)
where
F: FnOnce(&mut VectorWriter, usize),
{
let base = self.written;
if !unsafe { self.ensure_capacity(base.saturating_add(len)) } {
return;
}
if len > 0 {
let mut writer = unsafe { ListVector::child_writer(self.vector) };
write(&mut writer, base);
}
unsafe { ListVector::set_entry(self.vector, row_idx, base as u64, len as u64) };
self.written = base + len;
}
pub unsafe fn push_map_row<F>(&mut self, row_idx: usize, len: usize, write: F)
where
F: FnOnce(&mut VectorWriter, &mut VectorWriter, usize),
{
let base = self.written;
if !unsafe { self.ensure_capacity(base.saturating_add(len)) } {
return;
}
if len > 0 {
let (mut keys, mut values) = unsafe {
let struct_child = MapVector::struct_child(self.vector);
(
VectorWriter::from_vector(StructVector::get_child(struct_child, 0)),
VectorWriter::from_vector(StructVector::get_child(struct_child, 1)),
)
};
write(&mut keys, &mut values, base);
}
unsafe { ListVector::set_entry(self.vector, row_idx, base as u64, len as u64) };
self.written = base + len;
}
pub unsafe fn finish(self) {
unsafe { ListVector::set_size(self.vector, self.written) };
}
}
#[cfg(test)]
mod tests {
use super::{MAX_CHILD_CAPACITY_USIZE, MAX_LIST_CHILD_CAPACITY};
#[test]
fn max_capacity_matches_duckdbs_constant() {
assert_eq!(MAX_LIST_CHILD_CAPACITY, 137_438_953_472_u64);
assert_eq!(
MAX_CHILD_CAPACITY_USIZE,
usize::try_from(MAX_LIST_CHILD_CAPACITY).unwrap_or(usize::MAX)
);
}
}