use std::sync::Arc;
use vortex_error::VortexResult;
use crate::ExecutionCtx;
use crate::array::ArrayView;
use crate::arrays::PrimitiveArray;
use crate::arrays::VarBin;
use crate::arrays::VarBinViewArray;
use crate::arrays::varbinview::build_views::MAX_BUFFER_LEN;
use crate::arrays::varbinview::build_views::build_views_from_offsets;
use crate::match_each_integer_ptype;
pub(crate) fn varbin_to_canonical(
array: ArrayView<'_, VarBin>,
ctx: &mut ExecutionCtx,
) -> VortexResult<VarBinViewArray> {
let parts = array.into_owned().into_data_parts();
let offsets = parts.offsets.execute::<PrimitiveArray>(ctx)?;
let (buffers, views) = match_each_integer_ptype!(offsets.ptype(), |P| {
build_views_from_offsets(
0,
MAX_BUFFER_LEN,
parts.bytes.unwrap_host(),
offsets.as_slice::<P>(),
)
});
Ok(unsafe {
VarBinViewArray::new_unchecked(views, Arc::from(buffers), parts.dtype, parts.validity)
})
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use vortex_error::VortexResult;
use crate::IntoArray;
use crate::VortexSessionExecute;
use crate::array_session;
use crate::arrays::ChunkedArray;
use crate::arrays::VarBinArray;
use crate::arrays::VarBinViewArray;
use crate::arrays::varbin::builder::VarBinBuilder;
use crate::assert_arrays_eq;
use crate::builders::VarBinViewBuilder;
use crate::dtype::DType;
use crate::dtype::Nullability;
#[rstest]
#[case(DType::Utf8(Nullability::Nullable))]
#[case(DType::Binary(Nullability::Nullable))]
fn test_canonical_varbin_sliced(#[case] dtype: DType) {
let mut varbin = VarBinBuilder::<i32>::with_capacity(dtype.clone(), 10);
varbin.push_null();
varbin.push_null();
varbin.append_value("123456789012".as_bytes());
varbin.append_value("1234567890123".as_bytes());
let varbin = varbin.finish_into_varbin();
let varbin = varbin.slice(1..4).unwrap();
let mut ctx = array_session().create_execution_ctx();
let canonical = varbin.execute::<VarBinViewArray>(&mut ctx).unwrap();
assert_eq!(canonical.dtype(), &dtype);
assert!(
!canonical
.is_valid(0, &mut array_session().create_execution_ctx())
.unwrap()
);
assert!(canonical.views()[1].is_inlined());
assert_eq!(canonical.bytes_at(1).as_slice(), "123456789012".as_bytes());
assert!(!canonical.views()[2].is_inlined());
assert_eq!(canonical.bytes_at(2).as_slice(), "1234567890123".as_bytes());
}
#[rstest]
#[case(DType::Utf8(Nullability::NonNullable))]
#[case(DType::Binary(Nullability::NonNullable))]
fn test_canonical_varbin_unsliced(#[case] dtype: DType) {
let mut ctx = array_session().create_execution_ctx();
let varbin = VarBinArray::from_iter_nonnull(["foo", "bar", "baz"], dtype.clone());
let canonical = varbin
.as_array()
.clone()
.execute::<VarBinViewArray>(&mut ctx)
.unwrap();
let expected = match dtype {
DType::Utf8(_) => VarBinViewArray::from_iter_str(["foo", "bar", "baz"]),
_ => VarBinViewArray::from_iter_bin(["foo", "bar", "baz"]),
};
assert_arrays_eq!(canonical, expected, &mut ctx);
}
#[rstest]
#[case(DType::Utf8(Nullability::Nullable))]
#[case(DType::Binary(Nullability::Nullable))]
fn append_varbin_to_varbinview_builder(#[case] dtype: DType) -> VortexResult<()> {
let mut ctx = array_session().create_execution_ctx();
let long = "a value long enough that its view has to reference a buffer";
let longer = "another value long enough that its view has to reference a buffer";
let first = VarBinArray::from_iter([Some("short"), None, Some(long)], dtype.clone());
let second = VarBinArray::from_iter([Some(longer), Some("tiny"), None], dtype.clone());
let view = VarBinViewArray::from_iter([Some(long), None], dtype.clone());
let mut builder = VarBinViewBuilder::with_capacity(dtype.clone(), 8);
first
.as_array()
.clone()
.append_to_builder(&mut builder, &mut ctx)?;
builder.append_value(longer);
second
.as_array()
.clone()
.append_to_builder(&mut builder, &mut ctx)?;
view.clone()
.into_array()
.append_to_builder(&mut builder, &mut ctx)?;
let expected = ChunkedArray::try_new(
vec![
first.as_array().clone(),
VarBinViewArray::from_iter([Some(longer)], dtype.clone()).into_array(),
second.as_array().clone(),
view.into_array(),
],
dtype,
)?;
assert_arrays_eq!(builder.finish_into_varbinview(), expected, &mut ctx);
Ok(())
}
#[test]
fn append_varbin_to_a_compacting_builder_still_compacts() -> VortexResult<()> {
let mut ctx = array_session().create_execution_ctx();
let dtype = DType::Utf8(Nullability::NonNullable);
let array = VarBinArray::from_iter_nonnull(["short", "tiny", "small"], dtype.clone());
let mut builder = VarBinViewBuilder::with_compaction(dtype, 4, 1.0);
array
.as_array()
.clone()
.append_to_builder(&mut builder, &mut ctx)?;
let compacted = builder.finish_into_varbinview();
assert!(
compacted
.data_buffers()
.iter()
.all(|buffer| buffer.is_empty()),
"a fully-inlined append should not retain any value bytes"
);
assert_arrays_eq!(compacted, array.as_array().clone(), &mut ctx);
Ok(())
}
#[test]
fn test_canonical_varbin_empty() {
let varbin =
VarBinArray::from_iter_nonnull([] as [&str; 0], DType::Utf8(Nullability::NonNullable));
let mut ctx = array_session().create_execution_ctx();
let canonical = varbin
.as_array()
.clone()
.execute::<VarBinViewArray>(&mut ctx)
.unwrap();
assert_eq!(canonical.len(), 0);
}
}