use std::collections::Bound;
use std::fmt::{Debug, Formatter};
use std::ops::{Deref, RangeBounds};
use bytes::{Buf, Bytes};
use vortex_error::{vortex_panic, VortexExpect};
use crate::{Alignment, BufferMut, ByteBuffer};
#[derive(Clone, PartialEq, Eq, PartialOrd, Hash)]
pub struct Buffer<T> {
pub(crate) bytes: Bytes,
pub(crate) length: usize,
pub(crate) alignment: Alignment,
pub(crate) _marker: std::marker::PhantomData<T>,
}
impl<T> Buffer<T> {
pub fn copy_from(values: impl AsRef<[T]>) -> Self {
BufferMut::copy_from(values).freeze()
}
pub fn copy_from_aligned(values: impl AsRef<[T]>, alignment: Alignment) -> Self {
BufferMut::copy_from_aligned(values, alignment).freeze()
}
pub fn empty() -> Self {
BufferMut::empty().freeze()
}
pub fn empty_aligned(alignment: Alignment) -> Self {
BufferMut::empty_aligned(alignment).freeze()
}
pub fn full(item: T, len: usize) -> Self
where
T: Copy,
{
BufferMut::full(item, len).freeze()
}
pub fn from_byte_buffer(buffer: ByteBuffer) -> Self {
Self::from_byte_buffer_aligned(buffer, Alignment::of::<T>())
}
pub fn from_byte_buffer_aligned(buffer: ByteBuffer, alignment: Alignment) -> Self {
Self::from_bytes_aligned(buffer.into_inner(), alignment)
}
pub fn from_bytes_aligned(bytes: Bytes, alignment: Alignment) -> Self {
if !alignment.is_aligned_to(Alignment::of::<T>()) {
vortex_panic!(
"Alignment {} must be compatible with the scalar type's alignment {}",
alignment,
Alignment::of::<T>(),
);
}
if bytes.as_ptr().align_offset(*alignment) != 0 {
vortex_panic!(
"Bytes alignment must align to the scalar type's alignment {}",
Alignment::of::<T>()
);
}
if bytes.len() % size_of::<T>() != 0 {
vortex_panic!(
"Bytes length {} must be a multiple of the scalar type's size {}",
bytes.len(),
size_of::<T>()
);
}
let length = bytes.len() / size_of::<T>();
Self {
bytes,
length,
alignment,
_marker: Default::default(),
}
}
#[inline(always)]
pub fn len(&self) -> usize {
self.length
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.length == 0
}
#[inline(always)]
pub fn alignment(&self) -> Alignment {
self.alignment
}
#[inline(always)]
pub fn as_slice(&self) -> &[T] {
let raw_slice = self.bytes.as_ref();
unsafe { std::slice::from_raw_parts(raw_slice.as_ptr().cast(), self.length) }
}
pub fn iter(&self) -> impl Iterator<Item = &T> + '_ {
self.as_slice().iter()
}
#[inline(always)]
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
self.slice_with_alignment(range, self.alignment)
}
#[inline(always)]
pub fn slice_unaligned(&self, range: impl RangeBounds<usize>) -> Self {
self.slice_with_alignment(range, Alignment::of::<u8>())
}
pub fn slice_with_alignment(
&self,
range: impl RangeBounds<usize>,
alignment: Alignment,
) -> Self {
let len = self.len();
let begin = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).vortex_expect("out of range"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).vortex_expect("out of range"),
Bound::Excluded(&n) => n,
Bound::Unbounded => len,
};
if begin > end {
vortex_panic!(
"range start must not be greater than end: {:?} <= {:?}",
begin,
end
);
}
if end > len {
vortex_panic!("range end out of bounds: {:?} <= {:?}", end, len);
}
if end == begin {
return Self::empty_aligned(alignment);
}
let begin_byte = begin * size_of::<T>();
let end_byte = end * size_of::<T>();
if !begin_byte.is_multiple_of(*alignment) {
vortex_panic!("range start must be aligned to {:?}", alignment);
}
if !end_byte.is_multiple_of(*alignment) {
vortex_panic!("range end must be aligned to {:?}", alignment);
}
if !alignment.is_aligned_to(Alignment::of::<T>()) {
vortex_panic!("Slice alignment must at least align to type T")
}
Self {
bytes: self.bytes.slice(begin_byte..end_byte),
length: end - begin,
alignment,
_marker: Default::default(),
}
}
pub fn into_inner(self) -> Bytes {
self.bytes
}
pub fn into_byte_buffer(self) -> ByteBuffer {
ByteBuffer {
bytes: self.bytes,
length: self.length * size_of::<T>(),
alignment: self.alignment,
_marker: Default::default(),
}
}
pub fn into_mut(self) -> BufferMut<T> {
self.try_into_mut()
.unwrap_or_else(|buffer| BufferMut::<T>::copy_from(&buffer))
}
pub fn try_into_mut(self) -> Result<BufferMut<T>, Self> {
self.bytes
.try_into_mut()
.map(|bytes| BufferMut {
bytes,
length: self.length,
alignment: self.alignment,
_marker: Default::default(),
})
.map_err(|bytes| Self {
bytes,
length: self.length,
alignment: self.alignment,
_marker: Default::default(),
})
}
pub fn aligned(mut self, alignment: Alignment) -> Self {
if self.as_ptr().align_offset(*alignment) == 0 {
self.alignment = alignment;
self
} else {
#[cfg(feature = "warn-copy")]
{
let bt = std::backtrace::Backtrace::capture();
log::warn!(
"Buffer is not aligned to requested alignment {}, copying: {}",
alignment,
bt
)
}
Self::copy_from_aligned(self, alignment)
}
}
}
impl<T> Debug for Buffer<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
const TRUNC_SIZE: usize = 512;
let mut binding = f.debug_struct("Buffer");
let mut fields = binding
.field("length", &self.length)
.field("alignment", &self.alignment);
let mut bytes = self.bytes.clone();
if bytes.len() > TRUNC_SIZE {
fields = fields.field("truncated", &true);
}
bytes.truncate(TRUNC_SIZE);
fields.field("bytes", &bytes).finish()
}
}
impl<T> Deref for Buffer<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T> AsRef<[T]> for Buffer<T> {
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}
impl<T> FromIterator<T> for Buffer<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
BufferMut::from_iter(iter).freeze()
}
}
impl From<Vec<u8>> for ByteBuffer {
fn from(value: Vec<u8>) -> Self {
Self::from(Bytes::from(value))
}
}
impl From<Bytes> for ByteBuffer {
fn from(bytes: Bytes) -> Self {
let length = bytes.len();
Self {
bytes,
length,
alignment: Alignment::of::<u8>(),
_marker: Default::default(),
}
}
}
impl Buf for ByteBuffer {
fn remaining(&self) -> usize {
self.len()
}
fn chunk(&self) -> &[u8] {
self.as_slice()
}
fn advance(&mut self, cnt: usize) {
if !cnt.is_multiple_of(*self.alignment) {
vortex_panic!(
"Cannot advance buffer by {} items, resulting alignment is not {}",
cnt,
self.alignment
);
}
self.bytes.advance(cnt);
self.length -= cnt;
}
}
pub struct BufferIterator<T> {
buffer: Buffer<T>,
index: usize,
}
impl<T: Copy> Iterator for BufferIterator<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
(self.index < self.buffer.len()).then(move || {
let value = self.buffer.as_slice()[self.index];
self.index += 1;
value
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.buffer.len() - self.index;
(remaining, Some(remaining))
}
}
impl<T: Copy> IntoIterator for Buffer<T> {
type Item = T;
type IntoIter = BufferIterator<T>;
fn into_iter(self) -> Self::IntoIter {
BufferIterator {
buffer: self,
index: 0,
}
}
}
impl<T> From<BufferMut<T>> for Buffer<T> {
fn from(value: BufferMut<T>) -> Self {
value.freeze()
}
}
#[cfg(test)]
mod test {
use bytes::Buf;
use crate::{buffer, Alignment, ByteBuffer};
#[test]
fn align() {
let buf = buffer![0u8, 1, 2];
let aligned = buf.aligned(Alignment::new(32));
assert_eq!(aligned.alignment(), Alignment::new(32));
assert_eq!(aligned.as_slice(), &[0, 1, 2]);
}
#[test]
fn slice() {
let buf = buffer![0, 1, 2, 3, 4];
assert_eq!(buf.slice(1..3).as_slice(), &[1, 2]);
assert_eq!(buf.slice(1..=3).as_slice(), &[1, 2, 3]);
}
#[test]
fn slice_unaligned() {
let buf = buffer![0i32, 1, 2, 3, 4].into_byte_buffer();
buf.slice_unaligned(1..2);
}
#[test]
#[should_panic]
fn slice_bad_alignment() {
let buf = buffer![0i32, 1, 2, 3, 4].into_byte_buffer();
buf.slice(1..2);
}
#[test]
fn bytes_buf() {
let mut buf = ByteBuffer::copy_from("helloworld".as_bytes());
assert_eq!(buf.remaining(), 10);
assert_eq!(buf.chunk(), b"helloworld");
Buf::advance(&mut buf, 5);
assert_eq!(buf.remaining(), 5);
assert_eq!(buf.as_slice(), b"world");
assert_eq!(buf.chunk(), b"world");
}
}