use std::mem::size_of;
use std::sync::Arc;
use arrow::array::{
Array, ArrayRef, ArrowPrimitiveType, AsArray, BinaryBuilder, BinaryViewBuilder,
BooleanBufferBuilder, LargeBinaryBuilder, LargeStringBuilder, PrimitiveArray,
StringBuilder, StringViewBuilder,
};
use arrow::buffer::{BooleanBuffer, NullBuffer};
use arrow::datatypes::DataType;
use datafusion_common::{Result, ScalarValue, internal_err};
use datafusion_expr::EmitTo;
pub(crate) trait ValueState: Send + Sync {
fn resize(&mut self, new_size: usize);
fn update(&mut self, group_idx: usize, array: &ArrayRef, idx: usize) -> Result<()>;
fn take(&mut self, emit_to: EmitTo) -> Result<ArrayRef>;
fn size(&self) -> usize;
}
pub(crate) struct PrimitiveValueState<T: ArrowPrimitiveType + Send> {
vals: Vec<T::Native>,
nulls: BooleanBufferBuilder,
data_type: DataType,
}
impl<T: ArrowPrimitiveType + Send> PrimitiveValueState<T> {
pub(crate) fn new(data_type: DataType) -> Self {
Self {
vals: vec![],
nulls: BooleanBufferBuilder::new(0),
data_type,
}
}
}
impl<T: ArrowPrimitiveType + Send> ValueState for PrimitiveValueState<T> {
fn resize(&mut self, new_size: usize) {
self.vals.resize(new_size, T::default_value());
self.nulls.resize(new_size);
}
fn update(&mut self, group_idx: usize, array: &ArrayRef, idx: usize) -> Result<()> {
let array = array.as_primitive::<T>();
self.vals[group_idx] = array.value(idx);
self.nulls.set_bit(group_idx, !array.is_null(idx));
Ok(())
}
fn take(&mut self, emit_to: EmitTo) -> Result<ArrayRef> {
let values = emit_to.take_needed(&mut self.vals);
let null_buf = NullBuffer::new(take_need(&mut self.nulls, emit_to));
let array: PrimitiveArray<T> =
PrimitiveArray::<T>::new(values.into(), Some(null_buf))
.with_data_type(self.data_type.clone());
Ok(Arc::new(array))
}
fn size(&self) -> usize {
self.vals.capacity() * size_of::<T::Native>() + self.nulls.capacity() / 8
}
}
pub(crate) struct BytesValueState {
vals: Vec<Option<Vec<u8>>>,
data_type: DataType,
total_capacity: usize,
}
impl BytesValueState {
pub(crate) fn try_new(data_type: DataType) -> Result<Self> {
if !matches!(
data_type,
DataType::Utf8
| DataType::LargeUtf8
| DataType::Utf8View
| DataType::Binary
| DataType::LargeBinary
| DataType::BinaryView
) {
return internal_err!("BytesValueState does not support {}", data_type);
}
Ok(Self {
vals: vec![],
data_type,
total_capacity: 0,
})
}
}
impl ValueState for BytesValueState {
fn resize(&mut self, new_size: usize) {
if new_size < self.vals.len() {
for v in self.vals[new_size..].iter().flatten() {
self.total_capacity -= v.capacity();
}
}
self.vals.resize(new_size, None);
}
fn update(&mut self, group_idx: usize, array: &ArrayRef, idx: usize) -> Result<()> {
if let Some(v) = &self.vals[group_idx] {
self.total_capacity -= v.capacity();
}
if array.is_null(idx) {
self.vals[group_idx] = None;
} else {
let val = match self.data_type {
DataType::Utf8 => array.as_string::<i32>().value(idx).as_bytes(),
DataType::LargeUtf8 => array.as_string::<i64>().value(idx).as_bytes(),
DataType::Utf8View => array.as_string_view().value(idx).as_bytes(),
DataType::Binary => array.as_binary::<i32>().value(idx),
DataType::LargeBinary => array.as_binary::<i64>().value(idx),
DataType::BinaryView => array.as_binary_view().value(idx),
_ => {
return internal_err!(
"Unsupported data type for BytesValueState: {}",
self.data_type
);
}
};
if let Some(v) = &mut self.vals[group_idx] {
v.clear();
v.extend_from_slice(val);
} else {
let v = val.to_vec();
self.vals[group_idx] = Some(v);
}
self.vals[group_idx]
.as_ref()
.inspect(|x| self.total_capacity += x.capacity());
}
Ok(())
}
fn take(&mut self, emit_to: EmitTo) -> Result<ArrayRef> {
let values = emit_to.take_needed(&mut self.vals);
let (total_len, taken_capacity) = values
.iter()
.flatten()
.fold((0, 0), |(len_acc, cap_acc), v| {
(len_acc + v.len(), cap_acc + v.capacity())
});
self.total_capacity -= taken_capacity;
match self.data_type {
DataType::Utf8 => {
let mut builder = StringBuilder::with_capacity(values.len(), total_len);
for val in values {
match val {
Some(v) => builder.append_value(
unsafe { std::str::from_utf8_unchecked(&v) },
),
None => builder.append_null(),
}
}
Ok(Arc::new(builder.finish()))
}
DataType::LargeUtf8 => {
let mut builder =
LargeStringBuilder::with_capacity(values.len(), total_len);
for val in values {
match val {
Some(v) => builder.append_value(
unsafe { std::str::from_utf8_unchecked(&v) },
),
None => builder.append_null(),
}
}
Ok(Arc::new(builder.finish()))
}
DataType::Utf8View => {
let mut builder = StringViewBuilder::with_capacity(values.len());
for val in values {
match val {
Some(v) => builder.append_value(
unsafe { std::str::from_utf8_unchecked(&v) },
),
None => builder.append_null(),
}
}
Ok(Arc::new(builder.finish()))
}
DataType::Binary => {
let mut builder = BinaryBuilder::with_capacity(values.len(), total_len);
for val in values {
match val {
Some(v) => builder.append_value(&v),
None => builder.append_null(),
}
}
Ok(Arc::new(builder.finish()))
}
DataType::LargeBinary => {
let mut builder =
LargeBinaryBuilder::with_capacity(values.len(), total_len);
for val in values {
match val {
Some(v) => builder.append_value(&v),
None => builder.append_null(),
}
}
Ok(Arc::new(builder.finish()))
}
DataType::BinaryView => {
let mut builder = BinaryViewBuilder::with_capacity(values.len());
for val in values {
match val {
Some(v) => builder.append_value(&v),
None => builder.append_null(),
}
}
Ok(Arc::new(builder.finish()))
}
_ => internal_err!(
"Unsupported data type for BytesValueState: {}",
self.data_type
),
}
}
fn size(&self) -> usize {
self.vals.capacity() * size_of::<Option<Vec<u8>>>() + self.total_capacity
}
}
impl BytesValueState {
#[cfg(test)]
fn total_capacity_calculated(&self) -> usize {
self.vals.iter().flatten().map(|v| v.capacity()).sum()
}
}
pub(crate) struct GenericValueState {
vals: Vec<Option<ScalarValue>>,
data_type: DataType,
total_size: usize,
}
impl GenericValueState {
pub(crate) fn new(data_type: DataType) -> Self {
Self {
vals: vec![],
data_type,
total_size: 0,
}
}
}
impl ValueState for GenericValueState {
fn resize(&mut self, new_size: usize) {
if new_size < self.vals.len() {
for v in self.vals[new_size..].iter().flatten() {
self.total_size -= v.size();
}
}
self.vals.resize(new_size, None);
}
fn update(&mut self, group_idx: usize, array: &ArrayRef, idx: usize) -> Result<()> {
if let Some(v) = &self.vals[group_idx] {
self.total_size -= v.size();
}
let mut scalar = ScalarValue::try_from_array(array, idx)?;
scalar.compact();
self.total_size += scalar.size();
self.vals[group_idx] = Some(scalar);
Ok(())
}
fn take(&mut self, emit_to: EmitTo) -> Result<ArrayRef> {
let taken = emit_to.take_needed(&mut self.vals);
let taken_size: usize = taken.iter().flatten().map(|v| v.size()).sum();
self.total_size -= taken_size;
let default = ScalarValue::try_from(&self.data_type)?;
let scalars = taken
.into_iter()
.map(|opt| opt.unwrap_or_else(|| default.clone()))
.collect::<Vec<_>>();
if scalars.is_empty() {
return Ok(arrow::array::new_empty_array(&self.data_type));
}
ScalarValue::iter_to_array(scalars)
}
fn size(&self) -> usize {
self.vals.capacity() * size_of::<Option<ScalarValue>>() + self.total_size
}
}
pub(crate) fn take_need(
bool_buf_builder: &mut BooleanBufferBuilder,
emit_to: EmitTo,
) -> BooleanBuffer {
let bool_buf = bool_buf_builder.finish();
match emit_to {
EmitTo::All => bool_buf,
EmitTo::First(n) => {
let first_n: BooleanBuffer = bool_buf.slice(0, n);
bool_buf_builder.append_buffer(&bool_buf.slice(n, bool_buf.len() - n));
first_n
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use arrow::array::{
Array, BinaryArray, BinaryViewArray, FixedSizeListArray, Int32Array,
Int32Builder, LargeBinaryArray, LargeListArray, LargeStringArray, ListBuilder,
MapArray, StringArray, StringBuilder, StringViewArray, StructArray,
};
use arrow::buffer::{OffsetBuffer, ScalarBuffer};
use arrow::datatypes::{DataType, Field, Fields};
#[test]
fn test_bytes_value_state_utf8() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::Utf8)?;
state.resize(2);
let array: ArrayRef = Arc::new(StringArray::from(vec![
Some("hello"),
Some("world"),
Some("longer_string_than_hello"),
]));
state.update(0, &array, 0)?; state.update(1, &array, 1)?;
assert_eq!(state.total_capacity, state.total_capacity_calculated());
state.update(0, &array, 2)?;
assert_eq!(state.total_capacity, state.total_capacity_calculated());
let result = state.take(EmitTo::All)?;
let result = result.as_string::<i32>();
assert_eq!(result.len(), 2);
assert_eq!(result.value(0), "longer_string_than_hello");
assert_eq!(result.value(1), "world");
assert_eq!(state.total_capacity, 0);
assert_eq!(state.total_capacity, state.total_capacity_calculated());
Ok(())
}
#[test]
fn test_bytes_value_state_large_utf8() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::LargeUtf8)?;
state.resize(1);
let array: ArrayRef = Arc::new(LargeStringArray::from(vec!["large_utf8"]));
state.update(0, &array, 0)?;
let result = state.take(EmitTo::All)?;
assert_eq!(result.as_string::<i64>().value(0), "large_utf8");
Ok(())
}
#[test]
fn test_bytes_value_state_utf8_view() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::Utf8View)?;
state.resize(1);
let array: ArrayRef = Arc::new(StringViewArray::from(vec!["Utf8View"]));
state.update(0, &array, 0)?;
let result = state.take(EmitTo::All)?;
assert_eq!(result.as_string_view().value(0), "Utf8View");
Ok(())
}
#[test]
fn test_bytes_value_state_binary() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::Binary)?;
state.resize(1);
let array: ArrayRef = Arc::new(BinaryArray::from(vec![b"binary" as &[u8]]));
state.update(0, &array, 0)?;
let result = state.take(EmitTo::All)?;
assert_eq!(result.as_binary::<i32>().value(0), b"binary");
Ok(())
}
#[test]
fn test_bytes_value_state_large_binary() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::LargeBinary)?;
state.resize(1);
let array: ArrayRef =
Arc::new(LargeBinaryArray::from(vec![b"large_binary" as &[u8]]));
state.update(0, &array, 0)?;
let result = state.take(EmitTo::All)?;
assert_eq!(result.as_binary::<i64>().value(0), b"large_binary");
Ok(())
}
#[test]
fn test_bytes_value_state_binary_view() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::BinaryView)?;
state.resize(1);
let data: Vec<Option<&[u8]>> = vec![Some(b"long_binary_value_to_test_view")];
let array: ArrayRef = Arc::new(BinaryViewArray::from(data));
state.update(0, &array, 0)?;
let result = state.take(EmitTo::All)?;
let result = result.as_binary_view();
assert_eq!(result.value(0), b"long_binary_value_to_test_view");
Ok(())
}
#[test]
fn test_bytes_value_state_emit_first() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::Utf8)?;
state.resize(3);
let array: ArrayRef = Arc::new(StringArray::from(vec!["a", "b", "c"]));
state.update(0, &array, 0)?;
state.update(1, &array, 1)?;
state.update(2, &array, 2)?;
let result = state.take(EmitTo::First(2))?;
let result = result.as_string::<i32>();
assert_eq!(result.len(), 2);
assert_eq!(result.value(0), "a");
assert_eq!(result.value(1), "b");
let result = state.take(EmitTo::All)?;
let result = result.as_string::<i32>();
assert_eq!(result.len(), 1);
assert_eq!(result.value(0), "c");
Ok(())
}
#[test]
fn test_bytes_value_state_update_null() -> Result<()> {
let mut state = BytesValueState::try_new(DataType::Utf8)?;
state.resize(1);
let array: ArrayRef = Arc::new(StringArray::from(vec![Some("hello"), None]));
state.update(0, &array, 0)?;
assert_eq!(state.total_capacity, state.total_capacity_calculated());
assert!(state.total_capacity > 0);
state.update(0, &array, 1)?;
assert_eq!(
state.total_capacity,
state.total_capacity_calculated(),
"total_capacity should match calculated capacity after update(NULL)"
);
assert_eq!(state.total_capacity, 0);
Ok(())
}
fn make_list_utf8_array() -> ArrayRef {
let mut builder = ListBuilder::new(StringBuilder::new());
builder.values().append_value("a");
builder.append(true);
builder.values().append_value("b");
builder.values().append_value("c");
builder.append(true);
builder.values().append_value("d");
builder.values().append_value("e");
builder.values().append_value("f");
builder.append(true);
Arc::new(builder.finish())
}
#[test]
fn test_generic_value_state_list_utf8() -> Result<()> {
let list_utf8 =
DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)));
let mut state = GenericValueState::new(list_utf8.clone());
state.resize(2);
let array = make_list_utf8_array();
state.update(0, &array, 0)?;
state.update(1, &array, 1)?;
let size_after_first = state.total_size;
state.update(0, &array, 2)?;
assert!(
state.total_size > 0,
"total_size must remain positive after overwrite"
);
let expected_delta = {
let new_scalar = {
let mut s = ScalarValue::try_from_array(&array, 2)?;
s.compact();
s
};
let old_scalar = {
let mut s = ScalarValue::try_from_array(&array, 0)?;
s.compact();
s
};
new_scalar.size() as isize - old_scalar.size() as isize
};
assert_eq!(
state.total_size as isize - size_after_first as isize,
expected_delta,
"size accounting drifted after overwrite"
);
let result = state.take(EmitTo::All)?;
let result = result.as_list::<i32>();
assert_eq!(result.len(), 2);
let g0 = result.value(0);
let g0 = g0.as_any().downcast_ref::<StringArray>().unwrap();
assert_eq!(g0.value(0), "d");
assert_eq!(g0.value(1), "e");
assert_eq!(g0.value(2), "f");
let g1 = result.value(1);
let g1 = g1.as_any().downcast_ref::<StringArray>().unwrap();
assert_eq!(g1.value(0), "b");
assert_eq!(g1.value(1), "c");
assert_eq!(state.total_size, 0, "state must be fully drained");
Ok(())
}
#[test]
fn test_generic_value_state_struct() -> Result<()> {
let fields = Fields::from(vec![
Field::new("id", DataType::Int32, false),
Field::new("name", DataType::Utf8, false),
]);
let struct_type = DataType::Struct(fields.clone());
let id = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
let name = Arc::new(StringArray::from(vec!["a", "b", "c"])) as ArrayRef;
let struct_array =
Arc::new(StructArray::new(fields, vec![id, name], None)) as ArrayRef;
let mut state = GenericValueState::new(struct_type);
state.resize(2);
state.update(0, &struct_array, 0)?;
state.update(1, &struct_array, 2)?;
let out = state.take(EmitTo::All)?;
let out = out.as_any().downcast_ref::<StructArray>().unwrap();
assert_eq!(out.len(), 2);
let out_id = out.column(0).as_any().downcast_ref::<Int32Array>().unwrap();
let out_name = out
.column(1)
.as_any()
.downcast_ref::<StringArray>()
.unwrap();
assert_eq!(out_id.value(0), 1);
assert_eq!(out_id.value(1), 3);
assert_eq!(out_name.value(0), "a");
assert_eq!(out_name.value(1), "c");
Ok(())
}
#[test]
fn test_generic_value_state_large_list() -> Result<()> {
let field = Arc::new(Field::new("item", DataType::Int32, true));
let large_list_type = DataType::LargeList(Arc::clone(&field));
let values = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
let offsets: OffsetBuffer<i64> =
OffsetBuffer::new(ScalarBuffer::from(vec![0_i64, 2, 5, 6]));
let array = Arc::new(LargeListArray::new(field, offsets, Arc::new(values), None))
as ArrayRef;
let mut state = GenericValueState::new(large_list_type);
state.resize(2);
state.update(0, &array, 0)?; state.update(1, &array, 2)?;
let out = state.take(EmitTo::All)?;
let out = out.as_list::<i64>();
assert_eq!(out.len(), 2);
let g0 = out.value(0);
let g0 = g0.as_any().downcast_ref::<Int32Array>().unwrap();
assert_eq!(g0.len(), 2);
assert_eq!(g0.value(0), 1);
assert_eq!(g0.value(1), 2);
let g1 = out.value(1);
let g1 = g1.as_any().downcast_ref::<Int32Array>().unwrap();
assert_eq!(g1.len(), 1);
assert_eq!(g1.value(0), 6);
Ok(())
}
#[test]
fn test_generic_value_state_fixed_size_list() -> Result<()> {
let field = Arc::new(Field::new("item", DataType::Int32, true));
let fsl_type = DataType::FixedSizeList(Arc::clone(&field), 2);
let values = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
let array = Arc::new(FixedSizeListArray::new(field, 2, Arc::new(values), None))
as ArrayRef;
let mut state = GenericValueState::new(fsl_type);
state.resize(2);
state.update(0, &array, 0)?; state.update(1, &array, 2)?;
let out = state.take(EmitTo::All)?;
let out = out
.as_any()
.downcast_ref::<FixedSizeListArray>()
.expect("emitted FixedSizeListArray");
assert_eq!(out.len(), 2);
let g0 = out.value(0);
let g0 = g0.as_any().downcast_ref::<Int32Array>().unwrap();
assert_eq!(g0.value(0), 1);
assert_eq!(g0.value(1), 2);
let g1 = out.value(1);
let g1 = g1.as_any().downcast_ref::<Int32Array>().unwrap();
assert_eq!(g1.value(0), 5);
assert_eq!(g1.value(1), 6);
Ok(())
}
#[test]
fn test_generic_value_state_map() -> Result<()> {
let keys = Arc::new(StringArray::from(vec!["a", "b", "c"])) as ArrayRef;
let values = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
let entry_fields = Fields::from(vec![
Field::new("keys", DataType::Utf8, false),
Field::new("values", DataType::Int32, true),
]);
let entries = StructArray::new(entry_fields.clone(), vec![keys, values], None);
let offsets: OffsetBuffer<i32> =
OffsetBuffer::new(ScalarBuffer::from(vec![0_i32, 2, 3]));
let map_field =
Arc::new(Field::new("entries", DataType::Struct(entry_fields), false));
let map_array = Arc::new(MapArray::new(
Arc::clone(&map_field),
offsets,
entries,
None,
false,
)) as ArrayRef;
let map_type = DataType::Map(map_field, false);
let mut state = GenericValueState::new(map_type);
state.resize(2);
state.update(0, &map_array, 0)?; state.update(1, &map_array, 1)?;
let out = state.take(EmitTo::All)?;
let out = out.as_any().downcast_ref::<MapArray>().unwrap();
assert_eq!(out.len(), 2);
assert_eq!(out.value_length(0), 2);
assert_eq!(out.value_length(1), 1);
Ok(())
}
#[test]
fn test_generic_value_state_emit_first() -> Result<()> {
let list_utf8 =
DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)));
let mut state = GenericValueState::new(list_utf8);
state.resize(3);
let array = make_list_utf8_array();
state.update(0, &array, 0)?;
state.update(1, &array, 1)?;
state.update(2, &array, 2)?;
let after_all_updates = state.total_size;
assert!(after_all_updates > 0);
let head = state.take(EmitTo::First(2))?;
let head = head.as_list::<i32>();
assert_eq!(head.len(), 2);
let h0 = head.value(0);
let h0 = h0.as_any().downcast_ref::<StringArray>().unwrap();
assert_eq!(h0.value(0), "a");
let h1 = head.value(1);
let h1 = h1.as_any().downcast_ref::<StringArray>().unwrap();
assert_eq!(h1.value(0), "b");
assert_eq!(h1.value(1), "c");
assert!(state.total_size > 0);
assert!(state.total_size < after_all_updates);
let tail = state.take(EmitTo::All)?;
let tail = tail.as_list::<i32>();
assert_eq!(tail.len(), 1);
let t0 = tail.value(0);
let t0 = t0.as_any().downcast_ref::<StringArray>().unwrap();
assert_eq!(t0.value(0), "d");
assert_eq!(t0.value(1), "e");
assert_eq!(t0.value(2), "f");
assert_eq!(state.total_size, 0);
Ok(())
}
#[test]
fn test_generic_value_state_update_null() -> Result<()> {
let mut builder = ListBuilder::new(Int32Builder::new());
builder.values().append_value(1);
builder.values().append_value(2);
builder.append(true);
builder.append(false); let array: ArrayRef = Arc::new(builder.finish());
let list_type = array.data_type().clone();
let mut state = GenericValueState::new(list_type);
state.resize(1);
state.update(0, &array, 0)?;
let size_after_value = state.total_size;
assert!(size_after_value > 0);
state.update(0, &array, 1)?;
let recomputed: usize = state.vals.iter().flatten().map(|v| v.size()).sum();
assert_eq!(
state.total_size, recomputed,
"total_size drifted after null update"
);
let out = state.take(EmitTo::All)?;
let out = out.as_list::<i32>();
assert_eq!(out.len(), 1);
assert!(out.is_null(0));
assert_eq!(state.total_size, 0);
Ok(())
}
#[test]
fn test_generic_value_state_compact_releases_parent_batch() -> Result<()> {
let list_utf8 =
DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)));
let mut state = GenericValueState::new(list_utf8);
state.resize(1);
let array: ArrayRef = make_list_utf8_array();
let source_values_ptr =
array.as_list::<i32>().values().to_data().buffers()[1].as_ptr();
state.update(0, &array, 0)?;
drop(array);
let stored_values_ptr = match state
.vals
.first()
.and_then(|opt| opt.as_ref())
.expect("group 0 should have a stored value")
{
ScalarValue::List(list_arr) => {
list_arr.values().to_data().buffers()[1].as_ptr()
}
other => panic!("expected ScalarValue::List, got {other:?}"),
};
assert_ne!(
source_values_ptr, stored_values_ptr,
"compact() failed: stored ScalarValue still shares the source \
batch's Utf8 value-data buffer, meaning the batch is pinned in \
memory even after the outer ArrayRef is dropped"
);
let out = state.take(EmitTo::All)?;
let out = out.as_list::<i32>();
assert_eq!(out.len(), 1);
let g0 = out.value(0);
let g0 = g0.as_any().downcast_ref::<StringArray>().unwrap();
assert_eq!(g0.value(0), "a");
Ok(())
}
#[test]
fn test_generic_value_state_resize_shrink_recovers_size() -> Result<()> {
let list_utf8 =
DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)));
let mut state = GenericValueState::new(list_utf8);
state.resize(3);
let array = make_list_utf8_array();
state.update(0, &array, 0)?;
state.update(1, &array, 1)?;
state.update(2, &array, 2)?;
let full_size = state.total_size;
assert!(full_size > 0);
state.resize(1);
assert!(state.total_size > 0);
assert!(state.total_size < full_size);
Ok(())
}
}