use std::ops::Deref;
use crate::{ArrowNativeType, OffsetBuffer, OverflowError};
#[derive(Debug)]
pub struct OffsetBufferBuilder<O: ArrowNativeType> {
offsets: Vec<O>,
last_offset: usize,
}
impl<O: ArrowNativeType> OffsetBufferBuilder<O> {
pub fn new(capacity: usize) -> Self {
let mut offsets = Vec::with_capacity(capacity + 1);
offsets.push(O::usize_as(0));
Self {
offsets,
last_offset: 0,
}
}
#[inline]
pub fn push_length(&mut self, length: usize) {
self.try_push_length(length)
.unwrap_or_else(|err| panic!("{err}"))
}
#[inline]
pub fn try_push_length(&mut self, length: usize) -> Result<(), OverflowError> {
self.last_offset = self
.last_offset
.checked_add(length)
.ok_or_else(|| OverflowError::new::<usize>("total length"))?;
self.offsets.push(O::usize_as(self.last_offset));
Ok(())
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.offsets.reserve(additional);
}
pub fn finish(self) -> OffsetBuffer<O> {
self.try_finish().unwrap_or_else(|err| panic!("{err}"))
}
pub fn try_finish(self) -> Result<OffsetBuffer<O>, OverflowError> {
O::from_usize(self.last_offset)
.ok_or_else(|| OverflowError::new::<O>("offset").with_value(self.last_offset))?;
Ok(unsafe { OffsetBuffer::new_unchecked(self.offsets.into()) })
}
pub fn finish_cloned(&self) -> OffsetBuffer<O> {
self.try_finish_cloned()
.unwrap_or_else(|err| panic!("{err}"))
}
pub fn try_finish_cloned(&self) -> Result<OffsetBuffer<O>, OverflowError> {
let cloned = Self {
offsets: self.offsets.clone(),
last_offset: self.last_offset,
};
cloned.try_finish()
}
}
impl<O: ArrowNativeType> Deref for OffsetBufferBuilder<O> {
type Target = [O];
fn deref(&self) -> &Self::Target {
self.offsets.as_ref()
}
}
#[cfg(test)]
mod tests {
#[test]
fn try_finish_overflow() {
let mut builder = OffsetBufferBuilder::<i32>::new(2);
builder.try_push_length(u32::MAX as usize).unwrap();
let expected = "offset overflow: 4294967295 does not fit in i32";
assert_eq!(
builder.try_finish_cloned().unwrap_err().to_string(),
expected
);
assert_eq!(builder.try_finish().unwrap_err().to_string(), expected);
let mut builder = OffsetBufferBuilder::<i32>::new(2);
builder.try_push_length(usize::MAX).unwrap();
assert_eq!(
builder.try_push_length(1).unwrap_err().to_string(),
"total length overflow: does not fit in usize"
);
assert_eq!(builder.len(), 2);
}
use crate::OffsetBufferBuilder;
#[test]
fn test_basic() {
let mut builder = OffsetBufferBuilder::<i32>::new(5);
assert_eq!(builder.len(), 1);
assert_eq!(&*builder, &[0]);
let finished = builder.finish_cloned();
assert_eq!(finished.len(), 1);
assert_eq!(&*finished, &[0]);
builder.push_length(2);
builder.push_length(6);
builder.push_length(0);
builder.push_length(13);
let finished = builder.finish();
assert_eq!(&*finished, &[0, 2, 8, 8, 21]);
}
#[test]
#[should_panic(expected = "overflow")]
fn test_usize_overflow() {
let mut builder = OffsetBufferBuilder::<i32>::new(5);
builder.push_length(1);
builder.push_length(usize::MAX);
builder.finish();
}
#[test]
#[should_panic(expected = "overflow")]
fn test_i32_overflow() {
let mut builder = OffsetBufferBuilder::<i32>::new(5);
builder.push_length(1);
builder.push_length(i32::MAX as usize);
builder.finish();
}
}