use super::InProgressArray;
use crate::filter::FilterPredicate;
use arrow_array::builder::FixedSizeBinaryBuilder;
use arrow_array::cast::AsArray;
use arrow_array::{Array, ArrayRef};
use arrow_schema::ArrowError;
use std::sync::Arc;
#[derive(Debug)]
pub(crate) struct InProgressFixedSizeBinaryArray {
source: Option<ArrayRef>,
value_length: i32,
batch_size: usize,
builder: Option<FixedSizeBinaryBuilder>,
}
impl InProgressFixedSizeBinaryArray {
pub(crate) fn new(value_length: i32, batch_size: usize) -> Self {
Self {
source: None,
value_length,
batch_size,
builder: None,
}
}
fn ensure_builder(&mut self) -> &mut FixedSizeBinaryBuilder {
self.builder.get_or_insert_with(|| {
FixedSizeBinaryBuilder::with_capacity(self.batch_size, self.value_length)
})
}
}
impl InProgressArray for InProgressFixedSizeBinaryArray {
fn set_source(&mut self, source: Option<ArrayRef>) {
self.source = source;
}
fn copy_rows(&mut self, offset: usize, len: usize) -> Result<(), ArrowError> {
let source = self.source.as_ref().ok_or_else(|| {
ArrowError::InvalidArgumentError(
"Internal Error: InProgressFixedSizeBinaryArray: source not set".to_string(),
)
})?;
let sliced = source.as_fixed_size_binary().slice(offset, len);
self.ensure_builder().append_array(&sliced)?;
Ok(())
}
fn copy_rows_by_filter_from(
&mut self,
source: ArrayRef,
filter: &FilterPredicate,
) -> Result<(), ArrowError> {
let filtered = filter.filter(source.as_ref())?;
if !filtered.is_empty() {
self.ensure_builder()
.append_array(filtered.as_fixed_size_binary())?;
}
Ok(())
}
fn finish(&mut self) -> Result<ArrayRef, ArrowError> {
let mut b = self
.builder
.take()
.unwrap_or_else(|| FixedSizeBinaryBuilder::new(self.value_length));
Ok(Arc::new(b.finish()))
}
fn size(&self) -> usize {
self.builder.as_ref().map_or(0, |b| b.capacity())
+ self
.source
.as_ref()
.map_or(0, |a| a.get_array_memory_size())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::filter::FilterBuilder;
use arrow_array::{BooleanArray, FixedSizeBinaryArray};
fn make_fsb(value_length: i32, data: &[Option<&[u8]>]) -> FixedSizeBinaryArray {
let mut b = FixedSizeBinaryBuilder::with_capacity(data.len(), value_length);
for v in data {
match v {
Some(bytes) => b.append_value(bytes).unwrap(),
None => b.append_null(),
}
}
b.finish()
}
#[test]
fn test_roundtrip_with_nulls() {
let source = Arc::new(make_fsb(4, &[Some(b"abcd"), None, Some(b"ijkl")])) as ArrayRef;
let mut coalescer = InProgressFixedSizeBinaryArray::new(4, 8);
coalescer.set_source(Some(Arc::clone(&source)));
coalescer.copy_rows(0, 3).unwrap();
let output = coalescer.finish().unwrap();
let output = output.as_fixed_size_binary();
assert_eq!(output.len(), 3);
assert_eq!(output.value(0), b"abcd");
assert!(output.is_null(1));
assert_eq!(output.value(2), b"ijkl");
}
#[test]
fn test_offset_copy() {
let source =
Arc::new(make_fsb(4, &[Some(b"aaaa"), Some(b"bbbb"), Some(b"cccc")])) as ArrayRef;
let mut coalescer = InProgressFixedSizeBinaryArray::new(4, 8);
coalescer.set_source(Some(Arc::clone(&source)));
coalescer.copy_rows(1, 2).unwrap();
let output = coalescer.finish().unwrap();
let output = output.as_fixed_size_binary();
assert_eq!(output.len(), 2);
assert_eq!(output.value(0), b"bbbb");
assert_eq!(output.value(1), b"cccc");
}
#[test]
fn test_finish_preserves_value_length() {
let mut coalescer = InProgressFixedSizeBinaryArray::new(16, 8);
let output = coalescer.finish().unwrap();
let output = output.as_fixed_size_binary();
assert_eq!(output.len(), 0);
assert_eq!(output.value_length(), 16);
}
#[test]
fn test_filter_path() {
let source = Arc::new(make_fsb(
4,
&[Some(b"aaaa"), Some(b"bbbb"), Some(b"cccc"), Some(b"dddd")],
)) as ArrayRef;
let filter_mask = BooleanArray::from(vec![true, false, true, false]);
let predicate = FilterBuilder::new(&filter_mask).build();
let mut coalescer = InProgressFixedSizeBinaryArray::new(4, 8);
coalescer
.copy_rows_by_filter_from(source, &predicate)
.unwrap();
let output = coalescer.finish().unwrap();
let output = output.as_fixed_size_binary();
assert_eq!(output.len(), 2);
assert_eq!(output.value(0), b"aaaa");
assert_eq!(output.value(1), b"cccc");
}
}