use std::borrow::Cow;
use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
use std::mem::{self, ManuallyDrop, MaybeUninit};
use std::fmt::{self, Debug, LowerHex, UpperHex};
use std::ops::{Bound, Range, RangeBounds};
use std::alloc::{self, Layout};
use std::ptr::{self, NonNull};
use std::num::NonZero;
use std::marker::PhantomData;
use std::{slice, str};
use crate::error::{Error, ErrorKind};
use crate::value::DevEnum;
use crate::dev::DevState;
#[repr(C)]
struct HeapHeader {
count: AtomicUsize,
cap: usize,
side: AtomicPtr<SideHeader>,
}
#[repr(C)]
struct SideHeader {
next: *mut SideHeader,
size: usize,
}
const ALIGN: usize = 8;
type AllocResult<T> = Result<T, Option<Layout>>;
fn infallible<T>(res: AllocResult<T>) -> T {
match res {
Ok(value) => value,
Err(Some(layout)) => alloc::handle_alloc_error(layout),
Err(None) => panic!("capacity overflow"),
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
struct Heap(NonNull<HeapHeader>);
#[repr(C, align(8))]
struct EmptyHeapHeader(HeapHeader);
static EMPTY_HEAP_HEADER: EmptyHeapHeader = EmptyHeapHeader(HeapHeader {
count: AtomicUsize::new(usize::MAX),
cap: 0,
side: AtomicPtr::new(ptr::null_mut()),
});
impl Heap {
const EMPTY: Self = Self(unsafe { NonNull::new_unchecked((&raw const EMPTY_HEAP_HEADER.0).cast_mut()) });
#[inline]
fn is_immortal(self) -> bool {
self.0 == Self::EMPTY.0
}
fn layout(cap: usize) -> Option<Layout> {
cap.checked_mul(ALIGN)
.and_then(|size| size.checked_add(mem::size_of::<HeapHeader>()))
.and_then(|size| Layout::from_size_align(size, ALIGN).ok())
}
fn alloc(len: usize) -> AllocResult<Self> {
let cap = len.div_ceil(ALIGN);
let layout = Self::layout(cap).ok_or(None)?;
let ptr = unsafe { alloc::alloc_zeroed(layout) };
let ptr = NonNull::new(ptr.cast::<HeapHeader>()).ok_or(Some(layout))?;
unsafe { ptr.write(HeapHeader { count: AtomicUsize::new(1), cap, side: AtomicPtr::new(ptr::null_mut()) }) };
Ok(Self(ptr))
}
#[inline]
fn data(self) -> *mut u8 {
unsafe { self.0.as_ptr().add(1).cast::<u8>() }
}
#[inline]
fn capacity(self) -> usize {
unsafe { (*self.0.as_ptr()).cap * ALIGN }
}
#[inline]
fn header(&self) -> &HeapHeader {
unsafe { self.0.as_ref() }
}
#[inline]
fn is_unique(self) -> bool {
self.header().count.load(Ordering::Acquire) == 1
}
#[inline]
fn acquire(self) {
if self.is_immortal() {
return;
}
let old = self.header().count.fetch_add(1, Ordering::Relaxed);
if old > isize::MAX as usize {
std::process::abort();
}
}
#[inline]
unsafe fn release(self) {
if self.is_immortal() || self.header().count.fetch_sub(1, Ordering::Release) != 1 {
return;
}
atomic::fence(Ordering::Acquire);
unsafe { self.free() }
}
#[cold]
unsafe fn free(self) {
let header = self.0.as_ptr();
unsafe {
self.free_sides();
alloc::dealloc(header.cast(), Self::layout((*header).cap).unwrap_unchecked());
}
}
fn side_layout(size: usize) -> Option<Layout> {
size.checked_add(mem::size_of::<SideHeader>())
.and_then(|size| Layout::from_size_align(size, ALIGN).ok())
}
fn push_side(self, side: DetachedSide) {
debug_assert!(!self.is_immortal(), "nothing is attached to the empty storage");
let side = ManuallyDrop::new(side).0;
let head = &self.header().side;
let mut next = head.load(Ordering::Relaxed);
loop {
unsafe { (*side.as_ptr()).next = next };
match head.compare_exchange_weak(next, side.as_ptr(), Ordering::Release, Ordering::Relaxed) {
Ok(_) => break,
Err(current) => next = current,
}
}
}
unsafe fn free_sides(self) {
let mut side = self.header().side.swap(ptr::null_mut(), Ordering::Relaxed);
while !side.is_null() {
unsafe {
let next = (*side).next;
alloc::dealloc(side.cast(), Self::side_layout((*side).size).unwrap_unchecked());
side = next;
}
}
}
#[cold]
unsafe fn grow(&mut self, capacity: usize) -> AllocResult<()> {
let old_cap = self.header().cap;
let cap = capacity.div_ceil(ALIGN).max(old_cap * 2);
let old_layout = unsafe { Self::layout(old_cap).unwrap_unchecked() };
let new_layout = Self::layout(cap).ok_or(None)?;
let ptr = unsafe { alloc::realloc(self.0.as_ptr().cast(), old_layout, new_layout.size()) };
self.0 = NonNull::new(ptr.cast::<HeapHeader>()).ok_or(Some(new_layout))?;
unsafe {
(*self.0.as_ptr()).cap = cap;
self.data().add(old_cap * ALIGN).write_bytes(0, (cap - old_cap) * ALIGN);
}
Ok(())
}
}
struct DetachedSide(NonNull<SideHeader>);
impl DetachedSide {
fn new(size: usize) -> AllocResult<Self> {
let layout = Heap::side_layout(size).ok_or(None)?;
let side = NonNull::new(unsafe { alloc::alloc(layout) }.cast::<SideHeader>()).ok_or(Some(layout))?;
unsafe { side.write(SideHeader { next: ptr::null_mut(), size }) };
Ok(Self(side))
}
#[inline]
fn payload(&self) -> NonNull<u8> {
unsafe { self.0.add(1).cast::<u8>() }
}
}
impl Drop for DetachedSide {
fn drop(&mut self) {
unsafe { alloc::dealloc(self.0.as_ptr().cast(), Heap::side_layout((*self.0.as_ptr()).size).unwrap_unchecked()) }
}
}
struct HeapRef {
inner: Heap,
offset: usize,
}
impl HeapRef {
const EMPTY: Self = Self {
inner: Heap::EMPTY,
offset: 0,
};
fn copy(bytes: &[u8], capacity: usize) -> AllocResult<Self> {
let storage = Heap::alloc(bytes.len().max(capacity))?;
unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), storage.data(), bytes.len()) };
Ok(Self { inner: storage, offset: 0 })
}
#[inline]
fn ptr(&self) -> *mut u8 {
unsafe { self.inner.data().add(self.offset) }
}
#[inline]
fn clone_at(&self, count: usize) -> Self {
self.inner.acquire();
Self { inner: self.inner, offset: self.offset + count }
}
#[inline]
fn is_unique(&self) -> bool {
self.inner.is_unique()
}
#[inline]
fn is_writable(&self) -> bool {
self.inner.is_immortal() || self.inner.is_unique()
}
}
impl Clone for HeapRef {
#[inline]
fn clone(&self) -> Self {
self.clone_at(0)
}
}
impl Drop for HeapRef {
#[inline]
fn drop(&mut self) {
unsafe { self.inner.release() }
}
}
pub unsafe trait Spans: Copy + Send + Sync + 'static {
const EMPTY: Self;
unsafe fn span(self, index: usize) -> (usize, usize);
unsafe fn advance(self, count: usize) -> (usize, Self);
}
pub struct PackedSpans<T>(PhantomData<fn() -> T>);
impl<T> PackedSpans<T> {
#[inline]
const fn new() -> Self {
Self(PhantomData)
}
}
impl<T> Clone for PackedSpans<T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for PackedSpans<T> {}
unsafe impl<T: 'static> Spans for PackedSpans<T> {
const EMPTY: Self = Self::new();
#[inline]
unsafe fn span(self, index: usize) -> (usize, usize) {
(index * mem::size_of::<T>(), mem::size_of::<T>())
}
#[inline]
unsafe fn advance(self, count: usize) -> (usize, Self) {
(count * mem::size_of::<T>(), self)
}
}
#[derive(Clone, Copy)]
pub struct TableSpans(NonNull<Range<u32>>);
unsafe impl Send for TableSpans {}
unsafe impl Sync for TableSpans {}
impl TableSpans {
#[inline]
fn single(len: usize) -> Self {
debug_assert!(len <= isize::MAX as usize);
Self(NonNull::without_provenance(unsafe { NonZero::new_unchecked(len << 1 | 1) }))
}
#[inline]
fn single_len(self) -> Option<usize> {
let addr = self.0.addr().get();
(addr & 1 != 0).then_some(addr >> 1)
}
}
unsafe impl Spans for TableSpans {
const EMPTY: Self = Self(NonNull::dangling());
#[inline]
unsafe fn span(self, index: usize) -> (usize, usize) {
if let Some(len) = self.single_len() {
return (0, len);
}
let Range { start, end } = unsafe { self.0.add(index).read() };
(start as usize, (end - start) as usize)
}
#[inline]
unsafe fn advance(self, count: usize) -> (usize, Self) {
if let Some(len) = self.single_len() {
return if count == 0 { (0, self) } else { (len, Self::EMPTY) };
}
(0, Self(unsafe { self.0.add(count) }))
}
}
pub unsafe trait Element: Send + Sync + 'static {
type Ref<'a>: Copy + Debug;
type Spans: Spans;
const INLINE_FIXED: bool;
fn validate(bytes: &[u8]) -> bool;
unsafe fn read<'a>(ptr: *const u8, len: usize) -> Self::Ref<'a>
where
Self: 'a;
fn fmt_row<'a, I>(values: I, f: &mut fmt::Formatter<'_>) -> fmt::Result
where
I: Iterator<Item = Self::Ref<'a>>,
Self: 'a,
{
for (index, value) in values.enumerate() {
if index != 0 {
f.write_str(", ")?;
}
Debug::fmt(&value, f)?;
}
Ok(())
}
}
pub unsafe trait PackedElement: Element<Spans = PackedSpans<Self>> + Copy {
fn validate_all(bytes: &[u8]) -> bool;
}
const fn inline_fixed<T>() -> bool {
assert!(mem::size_of::<T>() > 0, "a packed type is not zero-sized");
assert!(mem::size_of::<T>() <= mem::size_of::<Repr>(), "a packed type fits in place of a storage");
assert!(mem::align_of::<T>() <= ALIGN, "a packed type is aligned on at most 8 octets");
true
}
macro_rules! impl_packed_element {
($($ty:ty $(where |$octet:ident| $valid:expr)? $(, debug $debug:path)?);* $(;)?) => { $(
// SAFETY: The type has no padding, and is read from the octets validated.
unsafe impl Element for $ty {
type Ref<'a> = $ty;
type Spans = PackedSpans<$ty>;
const INLINE_FIXED: bool = inline_fixed::<$ty>();
#[inline]
fn validate(bytes: &[u8]) -> bool {
bytes.len() == mem::size_of::<$ty>() && Self::validate_all(bytes)
}
#[inline]
unsafe fn read<'a>(ptr: *const u8, _len: usize) -> $ty
where
$ty: 'a,
{
unsafe { ptr.cast::<$ty>().read() }
}
$(
#[inline]
fn fmt_row<'a, I>(values: I, f: &mut fmt::Formatter<'_>) -> fmt::Result
where
I: Iterator<Item = $ty>,
$ty: 'a,
{
$debug(values, f)
}
)?
}
unsafe impl PackedElement for $ty {
#[inline]
fn validate_all(_bytes: &[u8]) -> bool {
true $(&& _bytes.iter().all(|&$octet| $valid))?
}
}
)* };
}
impl_packed_element!(
u8, debug fmt_octets;
i8; u16; i16; u32; i32; u64; i64; f32; f64; DevEnum;
bool where |octet| octet <= 1;
DevState where |octet| octet <= DevState::Unknown as u8;
);
fn fmt_octets(octets: impl Iterator<Item = u8>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ascii = false;
for (index, byte) in octets.enumerate() {
if index != 0 && !ascii {
f.write_str(" ")?;
}
if byte.is_ascii_alphanumeric() || matches!(byte, b' ' | b':' | b'/' | b'.' | b'-' | b'_') {
if !ascii {
f.write_str("'")?;
ascii = true;
}
write!(f, "{}", byte as char)?;
} else {
if ascii {
f.write_str("' ")?;
ascii = false;
}
write!(f, "{byte:02X}")?;
}
}
if ascii {
f.write_str("'")?;
}
Ok(())
}
fn fmt_hex<T: PackedElement>(octets: &[u8], f: &mut fmt::Formatter<'_>, upper: bool) -> fmt::Result {
let size = mem::size_of::<T>();
for (index, &byte) in octets.iter().enumerate() {
if size > 1 && index != 0 && index % size == 0 {
f.write_str(" ")?;
}
if upper { write!(f, "{byte:02X}")? } else { write!(f, "{byte:02x}")? }
}
Ok(())
}
unsafe impl Element for str {
type Ref<'a> = &'a str;
type Spans = TableSpans;
const INLINE_FIXED: bool = false;
#[inline]
fn validate(bytes: &[u8]) -> bool {
str::from_utf8(bytes).is_ok()
}
#[inline]
unsafe fn read<'a>(ptr: *const u8, len: usize) -> &'a str
where
str: 'a,
{
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
}
}
#[repr(C)]
union Repr {
raw: ManuallyDrop<HeapRef>,
inline: [MaybeUninit<u64>; mem::size_of::<HeapRef>() / 8],
}
pub struct Buf<T: Element + ?Sized, const DIM: usize> {
repr: Repr,
spans: T::Spans,
dim: [usize; DIM],
_t: PhantomData<*const T>,
}
unsafe impl<T: Element + ?Sized, const DIM: usize> Send for Buf<T, DIM> {}
unsafe impl<T: Element + ?Sized, const DIM: usize> Sync for Buf<T, DIM> {}
#[inline]
fn checked_len(dim: &[usize]) -> Option<usize> {
dim.iter().try_fold(1usize, |len, &d| len.checked_mul(d))
}
#[track_caller]
fn range_bounds<R: RangeBounds<usize>>(range: R, len: usize) -> Range<usize> {
let start = match range.start_bound() {
Bound::Included(&start) => start,
Bound::Excluded(&start) => start.checked_add(1).expect("range start overflow"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&end) => end.checked_add(1).expect("range end overflow"),
Bound::Excluded(&end) => end,
Bound::Unbounded => len,
};
assert!(start <= end, "slice index starts at {start} but ends at {end}");
assert!(end <= len, "range end index {end} out of range for length {len}");
start..end
}
impl<T: Element + ?Sized, const DIM: usize> Buf<T, DIM> {
const INLINE: bool = DIM == 0 && T::INLINE_FIXED;
#[inline]
pub const fn new() -> Self {
const { assert!(DIM >= 1, "a scalar always holds one value") };
let raw = ManuallyDrop::new(HeapRef::EMPTY);
Self {
repr: Repr { raw },
spans: T::Spans::EMPTY,
dim: [0; DIM],
_t: PhantomData,
}
}
#[inline]
unsafe fn from_raw(raw: HeapRef, spans: T::Spans, dim: [usize; DIM]) -> Self {
debug_assert!(!Self::INLINE);
Self {
repr: Repr { raw: ManuallyDrop::new(raw) },
spans,
dim,
_t: PhantomData,
}
}
#[inline]
fn raw(&self) -> &HeapRef {
debug_assert!(!Self::INLINE);
unsafe { &self.repr.raw }
}
#[inline]
fn raw_mut(&mut self) -> &mut HeapRef {
debug_assert!(!Self::INLINE);
unsafe { &mut self.repr.raw }
}
#[inline]
fn into_raw(self) -> HeapRef {
let this = ManuallyDrop::new(self);
unsafe { ptr::read(this.raw()) }
}
#[inline]
fn base(&self) -> *const u8 {
if Self::INLINE {
ptr::addr_of!(self.repr).cast::<u8>()
} else {
self.raw().ptr()
}
}
#[inline]
fn base_mut(&mut self) -> *mut u8 {
if Self::INLINE {
ptr::addr_of_mut!(self.repr).cast::<u8>()
} else {
self.raw().ptr()
}
}
#[inline]
unsafe fn read(&self, index: usize) -> T::Ref<'_> {
unsafe {
let (offset, len) = self.spans.span(index);
T::read(self.base().add(offset), len)
}
}
#[inline]
pub fn dim(&self) -> [usize; DIM] {
self.dim
}
#[inline]
pub fn len(&self) -> usize {
self.dim.iter().product()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn raw_index(&self, pos: [usize; DIM]) -> Option<usize> {
let mut index = 0;
let mut stride = 1;
for (&coord, &dim) in pos.iter().zip(&self.dim) {
if coord >= dim {
return None;
}
index += coord * stride;
stride *= dim;
}
Some(index)
}
#[inline]
pub fn get(&self, pos: [usize; DIM]) -> Option<T::Ref<'_>> {
let index = self.raw_index(pos)?;
Some(unsafe { self.read(index) })
}
pub fn get_scalar(&self, pos: [usize; DIM]) -> Option<Buf<T, 0>> {
let index = self.raw_index(pos)?;
if Buf::<T, 0>::INLINE {
let mut repr = Repr { inline: [MaybeUninit::uninit(); _] };
unsafe {
let (offset, len) = self.spans.span(index);
ptr::copy_nonoverlapping(self.base().add(offset), ptr::addr_of_mut!(repr).cast::<u8>(), len);
}
Some(Buf { repr, spans: self.spans, dim: [], _t: PhantomData })
} else {
unsafe {
let (octets, spans) = self.spans.advance(index);
Some(Buf::from_raw(self.raw().clone_at(octets), spans, []))
}
}
}
#[inline]
pub fn get_raw(&self, index: usize) -> Option<T::Ref<'_>> {
(index < self.len()).then(|| unsafe { self.read(index) })
}
#[inline]
pub fn iter(&self) -> impl ExactSizeIterator<Item = T::Ref<'_>> + '_ {
(0..self.len()).map(move |index| unsafe { self.read(index) })
}
#[track_caller]
pub fn slice<R: RangeBounds<usize>>(&self, range: R) -> Self {
const { assert!(DIM >= 1, "a scalar cannot be sliced") };
let Range { start, end } = range_bounds(range, self.dim[DIM - 1]);
let stride: usize = self.dim[..DIM - 1].iter().product();
let (octets, spans) = unsafe { self.spans.advance(start * stride) };
let mut dim = self.dim;
dim[DIM - 1] = end - start;
unsafe { Self::from_raw(self.raw().clone_at(octets), spans, dim) }
}
#[track_caller]
pub fn split_to(&mut self, at: usize) -> Self {
let front = self.slice(..at);
*self = self.slice(at..);
front
}
#[track_caller]
pub fn split_off(&mut self, at: usize) -> Self {
let back = self.slice(at..);
*self = self.slice(..at);
back
}
pub fn reshape<const D: usize>(self, dim: [usize; D]) -> Option<Buf<T, D>> {
if checked_len(&dim)? != self.len() {
return None;
}
let spans = self.spans;
Some(match (Self::INLINE, Buf::<T, D>::INLINE) {
(false, false) => unsafe { Buf::from_raw(self.into_raw(), spans, dim) },
(true, true) => Buf { repr: Repr { inline: unsafe { self.repr.inline } }, spans, dim, _t: PhantomData },
(true, false) => unsafe {
let (offset, len) = spans.span(0);
let bytes = slice::from_raw_parts(self.base().add(offset), len);
Buf::from_raw(infallible(HeapRef::copy(bytes, len)), spans, dim)
},
(false, true) => {
let raw = self.into_raw();
let mut repr = Repr { inline: [MaybeUninit::uninit(); _] };
unsafe {
let (offset, len) = spans.span(0);
ptr::copy_nonoverlapping(raw.ptr().add(offset), ptr::addr_of_mut!(repr).cast::<u8>(), len);
}
Buf { repr, spans, dim, _t: PhantomData }
}
})
}
#[inline]
pub fn into_scalar(self) -> Option<Buf<T, 0>> {
self.reshape([])
}
#[inline]
pub fn is_unique(&self) -> bool {
Self::INLINE || self.raw().is_unique()
}
#[inline]
fn is_writable(&self) -> bool {
Self::INLINE || self.raw().is_writable()
}
}
impl<T: Element + ?Sized> Buf<T, 1> {
#[inline]
pub fn truncate(&mut self, len: usize) {
self.dim[0] = self.dim[0].min(len);
}
}
impl<T: Element + ?Sized> Buf<T, 0> {
#[inline]
pub fn value(&self) -> T::Ref<'_> {
unsafe { self.read(0) }
}
}
impl<T: Element + ?Sized, const DIM: usize> Clone for Buf<T, DIM> {
#[inline]
fn clone(&self) -> Self {
let repr = if Self::INLINE {
Repr { inline: unsafe { self.repr.inline } }
} else {
Repr { raw: ManuallyDrop::new(self.raw().clone()) }
};
Self { repr, spans: self.spans, dim: self.dim, _t: PhantomData }
}
}
impl<T: Element + ?Sized, const DIM: usize> Drop for Buf<T, DIM> {
#[inline]
fn drop(&mut self) {
if !Self::INLINE {
unsafe { ManuallyDrop::drop(&mut self.repr.raw) }
}
}
}
impl<T: Element + ?Sized, const DIM: usize> Default for Buf<T, DIM> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: Element + ?Sized, const DIM: usize> PartialEq for Buf<T, DIM>
where
for<'a> T::Ref<'a>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.dim == other.dim && self.iter().eq(other.iter())
}
}
impl<T: Element + ?Sized, const DIM: usize> Eq for Buf<T, DIM>
where
for<'a> T::Ref<'a>: Eq,
{}
fn fmt_buf<'a, T, I>(f: &mut fmt::Formatter<'_>, name: &str, dim: &[usize], mut values: I) -> fmt::Result
where
T: Element + ?Sized + 'a,
I: Iterator<Item = T::Ref<'a>>,
{
f.write_str(name)?;
if dim.is_empty() {
f.write_str("(")?;
T::fmt_row(values, f)?;
return f.write_str(")");
}
f.write_str("<")?;
for (index, d) in dim.iter().enumerate() {
if index != 0 {
f.write_str(", ")?;
}
write!(f, "{d}")?;
}
f.write_str(">")?;
fmt_level::<T, I>(f, dim, 0, &mut values)
}
fn fmt_level<'a, T, I>(f: &mut fmt::Formatter<'_>, dim: &[usize], depth: usize, values: &mut I) -> fmt::Result
where
T: Element + ?Sized + 'a,
I: Iterator<Item = T::Ref<'a>>,
{
const INDENT: usize = 4;
let (&len, inner) = dim.split_last().expect("at least one dimension");
f.write_str("[")?;
if inner.is_empty() {
T::fmt_row(values.by_ref().take(len), f)?;
} else {
let pretty = f.alternate();
for index in 0..len {
if pretty {
write!(f, "\n{:1$}", "", (depth + 1) * INDENT)?;
} else if index != 0 {
f.write_str(", ")?;
}
fmt_level::<T, I>(f, inner, depth + 1, values)?;
if pretty {
f.write_str(",")?;
}
}
if pretty && len != 0 {
write!(f, "\n{:1$}", "", depth * INDENT)?;
}
}
f.write_str("]")
}
impl<T: Element + ?Sized, const DIM: usize> Debug for Buf<T, DIM> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_buf::<T, _>(f, "Buf", &self.dim, self.iter())
}
}
impl<T: PackedElement> UpperHex for Buf<T, 1> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex::<T>(self.as_bytes(), f, true)
}
}
impl<T: PackedElement> LowerHex for Buf<T, 1> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex::<T>(self.as_bytes(), f, false)
}
}
impl<T: PackedElement, const DIM: usize> Buf<T, DIM> {
#[inline]
fn check_packed(bytes: &[u8], dim: [usize; DIM]) -> Option<bool> {
let size = checked_len(&dim)?.checked_mul(mem::size_of::<T>())?;
if bytes.len() != size || !T::validate_all(bytes) {
return None;
}
Some(!Self::INLINE && bytes.as_ptr().addr().is_multiple_of(mem::align_of::<T>()))
}
unsafe fn copy_from_bytes(bytes: &[u8], dim: [usize; DIM]) -> Self {
if Self::INLINE {
let mut repr = Repr { inline: [MaybeUninit::uninit(); _] };
unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), ptr::addr_of_mut!(repr).cast::<u8>(), bytes.len()) };
Self { repr, spans: PackedSpans::new(), dim, _t: PhantomData }
} else if bytes.is_empty() {
unsafe { Self::from_raw(HeapRef::EMPTY, PackedSpans::new(), dim) }
} else {
unsafe { Self::from_raw(infallible(HeapRef::copy(bytes, bytes.len())), PackedSpans::new(), dim) }
}
}
pub fn from_slice(values: &[T], dim: [usize; DIM]) -> Option<Self> {
if checked_len(&dim)? != values.len() {
return None;
}
let bytes = unsafe { slice::from_raw_parts(values.as_ptr().cast::<u8>(), mem::size_of_val(values)) };
Some(unsafe { Self::copy_from_bytes(bytes, dim) })
}
#[inline]
pub fn zeroed(dim: [usize; DIM]) -> Self {
BufMut::zeroed(dim).freeze()
}
#[inline]
pub fn as_ptr(&self) -> *const T {
self.base().cast::<T>()
}
#[inline]
pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.base(), self.len() * mem::size_of::<T>()) }
}
pub fn cast<U: PackedElement, const D: usize>(self, dim: [usize; D]) -> Result<Buf<U, D>, Self> {
let Some(share) = Buf::<U, D>::check_packed(self.as_bytes(), dim) else {
return Err(self);
};
Ok(unsafe {
if share && !Self::INLINE {
Buf::from_raw(self.into_raw(), PackedSpans::new(), dim)
} else {
Buf::copy_from_bytes(self.as_bytes(), dim)
}
})
}
pub fn into_bytes(self) -> Buf<u8, 1> {
let len = self.as_bytes().len();
match self.cast([len]) {
Ok(bytes) => bytes,
Err(_) => unreachable!("every octet is a valid one"),
}
}
#[inline]
pub fn get_mut(&mut self) -> Option<&mut BufMut<T, DIM>> {
self.is_writable().then(|| unsafe { &mut *(self as *mut Self).cast::<BufMut<T, DIM>>() })
}
#[inline]
pub fn make_mut(&mut self) -> &mut BufMut<T, DIM> {
if !self.is_writable() {
*self = unsafe { Self::copy_from_bytes(self.as_bytes(), self.dim) };
}
unsafe { &mut *(self as *mut Self).cast::<BufMut<T, DIM>>() }
}
#[inline]
pub fn try_into_mut(self) -> Result<BufMut<T, DIM>, Self> {
if self.is_writable() {
Ok(BufMut(self))
} else {
Err(self)
}
}
#[inline]
pub fn into_mut(mut self) -> BufMut<T, DIM> {
self.make_mut();
BufMut(self)
}
#[track_caller]
pub fn slice_ref(&self, subset: &[T]) -> Self {
const { assert!(DIM >= 1, "a scalar cannot be sliced") };
let size = mem::size_of::<T>();
let offset = subset.as_ptr().addr().wrapping_sub(self.base().addr());
let (start, len) = (offset / size, self.len());
assert!(offset.is_multiple_of(size) && start <= len && subset.len() <= len - start,
"the values at {:p} are not within the buffer at {:p}, of {len} values",
subset.as_ptr(),
self.base());
let stride: usize = self.dim[..DIM - 1].iter().product();
if stride == 0 {
return self.slice(..0);
}
assert!(start.is_multiple_of(stride) && subset.len().is_multiple_of(stride),
"the values {start}..{} are not whole rows of {stride} values",
start + subset.len());
self.slice(start / stride..(start + subset.len()) / stride)
}
}
impl<T: PackedElement> From<T> for Buf<T, 0> {
#[inline]
fn from(value: T) -> Self {
Self::from_slice(slice::from_ref(&value), []).expect("a scalar holds one value")
}
}
impl<T: PackedElement> From<&[T]> for Buf<T, 1> {
#[inline]
fn from(values: &[T]) -> Self {
Self::from_slice(values, [values.len()]).expect("same length")
}
}
impl<T: PackedElement> From<Vec<T>> for Buf<T, 1> {
#[inline]
fn from(values: Vec<T>) -> Self {
Self::from(values.as_slice())
}
}
impl<T: PackedElement> From<Box<[T]>> for Buf<T, 1> {
#[inline]
fn from(values: Box<[T]>) -> Self {
Self::from(&*values)
}
}
impl<T: PackedElement, const N: usize> From<[T; N]> for Buf<T, 1> {
#[inline]
fn from(values: [T; N]) -> Self {
Self::from(values.as_slice())
}
}
impl<T: PackedElement, const DIM: usize> From<BufMut<T, DIM>> for Buf<T, DIM> {
#[inline]
fn from(buf: BufMut<T, DIM>) -> Self {
buf.freeze()
}
}
impl<T: Element<Spans = TableSpans> + ?Sized, const DIM: usize> Buf<T, DIM> {
fn with_spans<I, F>(bytes: Buf<u8, 1>, spans: I, dim: [usize; DIM], mut check: F) -> Result<Self, Buf<u8, 1>>
where
I: IntoIterator<Item = Range<u32>>,
F: FnMut(&[u8], &Range<u32>) -> bool,
{
let Some(len) = checked_len(&dim) else {
return Err(bytes);
};
let mut spans = spans.into_iter();
if len == 0 {
if spans.next().is_some() {
return Err(bytes);
}
return Ok(unsafe { Self::from_raw(bytes.into_raw(), TableSpans::EMPTY, dim) });
}
if len == 1 {
let Some(span) = spans.next().filter(|span| check(bytes.as_slice(), span)) else {
return Err(bytes);
};
if spans.next().is_some() {
return Err(bytes);
}
let mut raw = bytes.into_raw();
raw.offset += span.start as usize;
return Ok(unsafe { Self::from_raw(raw, TableSpans::single(span.len()), dim) });
}
let Some(size) = len.checked_mul(mem::size_of::<Range<u32>>()) else {
return Err(bytes);
};
let Ok(side) = DetachedSide::new(size) else {
return Err(bytes);
};
let table = side.payload().cast::<Range<u32>>();
for index in 0..len {
let Some(span) = spans.next().filter(|span| check(bytes.as_slice(), span)) else {
return Err(bytes);
};
unsafe { table.add(index).write(span) };
}
if spans.next().is_some() {
return Err(bytes);
}
let mut raw = bytes.into_raw();
if raw.inner.is_immortal() {
raw = HeapRef { inner: infallible(Heap::alloc(0)), offset: 0 };
}
raw.inner.push_side(side);
Ok(unsafe { Self::from_raw(raw, TableSpans(table), dim) })
}
}
impl<const DIM: usize> Buf<str, DIM> {
pub fn from_spans<I>(bytes: Buf<u8, 1>, spans: I, dim: [usize; DIM]) -> Result<Self, Buf<u8, 1>>
where
I: IntoIterator<Item = Range<u32>>,
{
Self::with_spans(bytes, spans, dim, |bytes, span| {
bytes.get(span.start as usize..span.end as usize).is_some_and(str::validate)
})
}
#[inline]
#[track_caller]
pub unsafe fn from_spans_unchecked<I>(bytes: Buf<u8, 1>, spans: I, dim: [usize; DIM]) -> Self
where
I: IntoIterator<Item = Range<u32>>,
{
Self::with_spans(bytes, spans, dim, |_, _| true)
.unwrap_or_else(|_| panic!("no table of as many spans as the dimensions {dim:?} say"))
}
}
impl Buf<str, 1> {
pub fn from_strs<I>(values: I) -> Option<Self>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let mut values = values.into_iter();
let Some(first) = values.next() else {
return Some(unsafe { Self::from_raw(HeapRef::EMPTY, TableSpans::EMPTY, [0]) });
};
let Some(second) = values.next() else {
let value = first.as_ref();
let raw = if value.is_empty() { HeapRef::EMPTY } else { infallible(HeapRef::copy(value.as_bytes(), 0)) };
return Some(unsafe { Self::from_raw(raw, TableSpans::single(value.len()), [1]) });
};
let mut buf = BufMut::<u8, 1>::new();
let mut spans = Vec::with_capacity(values.size_hint().0.saturating_add(2));
for value in [first, second].into_iter().chain(values) {
let start = u32::try_from(buf.len()).ok()?;
buf.extend_from_slice(value.as_ref().as_bytes());
spans.push(start..u32::try_from(buf.len()).ok()?);
}
let len = spans.len();
Some(unsafe { Self::from_spans_unchecked(buf.freeze(), spans, [len]) })
}
}
impl From<&str> for Buf<str, 0> {
#[inline]
fn from(value: &str) -> Self {
Buf::<str, 1>::from_strs([value])
.and_then(Buf::into_scalar)
.expect("a single string always makes a scalar")
}
}
impl From<Cow<'_, str>> for Buf<str, 0> {
#[inline]
fn from(value: Cow<'_, str>) -> Self {
Self::from(&*value)
}
}
impl From<String> for Buf<str, 0> {
#[inline]
fn from(value: String) -> Self {
Self::from(value.as_str())
}
}
impl<S: AsRef<str>> From<&[S]> for Buf<str, 1> {
#[inline]
fn from(values: &[S]) -> Self {
Self::from_strs(values).expect("strings of more than 4 GiB")
}
}
#[repr(transparent)]
pub struct BufMut<T: PackedElement, const DIM: usize>(Buf<T, DIM>);
impl<T: PackedElement, const DIM: usize> BufMut<T, DIM> {
pub fn zeroed(dim: [usize; DIM]) -> Self {
let size = checked_len(&dim)
.and_then(|len| len.checked_mul(mem::size_of::<T>()))
.expect("capacity overflow");
if Buf::<T, DIM>::INLINE || size == 0 {
return Self(unsafe { Buf::copy_from_bytes(&[0; mem::size_of::<Repr>()][..size], dim) });
}
let raw = HeapRef { inner: infallible(Heap::alloc(size)), offset: 0 };
Self(unsafe { Buf::from_raw(raw, PackedSpans::new(), dim) })
}
#[inline]
pub fn from_slice(values: &[T], dim: [usize; DIM]) -> Option<Self> {
Buf::from_slice(values, dim).map(Self)
}
#[inline]
pub fn dim(&self) -> [usize; DIM] {
self.0.dim
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn as_ptr(&self) -> *const T {
self.0.as_ptr()
}
#[inline]
pub fn as_slice(&self) -> &[T] {
self.0.as_slice()
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
let len = self.len();
unsafe { slice::from_raw_parts_mut(self.0.base_mut().cast::<T>(), len) }
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub fn freeze(self) -> Buf<T, DIM> {
self.0
}
}
impl<T: PackedElement> BufMut<T, 1> {
#[inline]
pub const fn new() -> Self {
Self(Buf::new())
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
let mut buf = Self::new();
buf.reserve(capacity);
buf
}
#[inline]
pub fn capacity(&self) -> usize {
let raw = self.0.raw();
(raw.inner.capacity() - raw.offset) / mem::size_of::<T>()
}
#[inline]
fn ptr_at(&self, index: usize) -> *mut T {
debug_assert!(index <= self.capacity());
unsafe { self.0.raw().ptr().cast::<T>().add(index) }
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
infallible(self.alloc_reserve(additional))
}
pub fn try_reserve(&mut self, additional: usize) -> Result<(), Error> {
self.alloc_reserve(additional).map_err(|_| Error::new(ErrorKind::Internal)
.with_description(format!("Failed to allocate room for {additional} more values behind {}", self.len())))
}
fn alloc_reserve(&mut self, additional: usize) -> AllocResult<()> {
let len = self.len();
let raw = self.0.raw_mut();
let end = len.checked_add(additional)
.and_then(|len| len.checked_mul(mem::size_of::<T>()))
.and_then(|size| raw.offset.checked_add(size))
.ok_or(None)?;
if end <= raw.inner.capacity() {
return Ok(());
}
if raw.inner.is_immortal() {
raw.inner = Heap::alloc(end)?;
} else {
unsafe { raw.inner.grow(end)? };
}
Ok(())
}
#[inline]
pub fn push(&mut self, value: T) {
self.reserve(1);
unsafe { self.ptr_at(self.len()).write(value) };
self.0.dim[0] += 1;
}
pub fn extend_from_slice(&mut self, values: &[T]) {
self.reserve(values.len());
unsafe { ptr::copy_nonoverlapping(values.as_ptr(), self.ptr_at(self.len()), values.len()) };
self.0.dim[0] += values.len();
}
pub fn resize(&mut self, len: usize, value: T) {
let old_len = self.len();
if len > old_len {
let bytes = unsafe { slice::from_raw_parts(ptr::from_ref(&value).cast::<u8>(), mem::size_of::<T>()) };
let end = if bytes.iter().all(|&octet| octet == 0) {
let raw = self.0.raw();
len.min((raw.inner.capacity() - raw.offset).div_ceil(mem::size_of::<T>()))
} else {
len
};
self.reserve(len - old_len);
for index in old_len..end {
unsafe { self.ptr_at(index).write(value) };
}
}
self.0.dim[0] = len;
}
#[inline]
pub fn truncate(&mut self, len: usize) {
self.0.truncate(len);
}
pub fn clear(&mut self) {
let raw = self.0.raw_mut();
if !raw.inner.is_immortal() {
unsafe { raw.inner.free_sides() };
}
raw.offset = 0;
self.0.dim[0] = 0;
}
}
impl BufMut<u8, 1> {
pub fn from_hex(s: &str) -> Option<Self> {
if !s.len().is_multiple_of(2) {
return None;
}
let mut buf = Self::with_capacity(s.len() / 2);
for hex in s.as_bytes().as_chunks::<2>().0 {
let hex = str::from_utf8(hex).ok()?;
buf.push(u8::from_str_radix(hex, 16).ok()?);
}
Some(buf)
}
pub fn set_len(&mut self, len: usize) {
self.reserve(len.saturating_sub(self.len()));
self.0.dim[0] = len;
}
}
impl<T: PackedElement, const DIM: usize> Clone for BufMut<T, DIM> {
#[inline]
fn clone(&self) -> Self {
Self(unsafe { Buf::copy_from_bytes(self.as_bytes(), self.dim()) })
}
}
impl<T: PackedElement> Default for BufMut<T, 1> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: PackedElement + PartialEq, const DIM: usize> PartialEq for BufMut<T, DIM> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.dim() == other.dim() && self.as_slice() == other.as_slice()
}
}
impl<T: PackedElement + Eq, const DIM: usize> Eq for BufMut<T, DIM> {}
impl<T: PackedElement> From<&[T]> for BufMut<T, 1> {
#[inline]
fn from(values: &[T]) -> Self {
Self(Buf::from(values))
}
}
impl<T: PackedElement> From<Vec<T>> for BufMut<T, 1> {
#[inline]
fn from(values: Vec<T>) -> Self {
Self::from(values.as_slice())
}
}
impl<T: PackedElement, const DIM: usize> Debug for BufMut<T, DIM> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_buf::<T, _>(f, "BufMut", &self.0.dim, self.0.iter())
}
}
impl<T: PackedElement> UpperHex for BufMut<T, 1> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex::<T>(self.as_bytes(), f, true)
}
}
impl<T: PackedElement> LowerHex for BufMut<T, 1> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex::<T>(self.as_bytes(), f, false)
}
}
#[cfg(test)]
#[allow(clippy::single_range_in_vec_init)]
mod tests {
use super::*;
#[test]
fn scalar() {
let scalar = Buf::<f64, 0>::from(1.5);
assert_eq!(scalar.value(), 1.5);
assert_eq!(scalar.get([]), Some(1.5));
assert_eq!(scalar.len(), 1);
let clone = scalar.clone();
assert!(scalar.is_unique() && clone.is_unique());
}
#[test]
fn one_dim() {
let buf = Buf::<i32, 1>::from(vec![1, 2, 3]);
assert_eq!(buf.dim(), [3]);
assert_eq!(buf.get([1]), Some(2));
assert_eq!(buf.get([3]), None);
assert_eq!(buf.as_slice(), &[1, 2, 3]);
assert_eq!(buf.iter().sum::<i32>(), 6);
}
#[test]
fn two_dim() {
let buf = Buf::<u8, 2>::from_slice(&[1, 2, 3, 4, 5, 6], [3, 2]).unwrap();
assert_eq!(buf.get([0, 1]), Some(4));
assert_eq!(buf.get([2, 1]), Some(6));
assert_eq!(buf.get([3, 0]), None);
assert_eq!(buf.raw_index([1, 1]), Some(4));
assert_eq!(buf.get_raw(4), Some(5));
assert!(Buf::<u8, 2>::from_slice(&[1, 2, 3], [2, 2]).is_none());
let flat = buf.reshape([6]).unwrap();
assert_eq!(flat.as_slice(), &[1, 2, 3, 4, 5, 6]);
}
#[test]
fn empty() {
const EMPTY: Buf<u32, 2> = Buf::new();
const EMPTY_MUT: BufMut<u8, 1> = BufMut::new();
assert_eq!(EMPTY.dim(), [0, 0]);
assert!(!EMPTY.is_unique());
let mut values = Buf::<u32, 1>::from(vec![]);
assert_eq!(values.as_ptr().cast::<u8>(), EMPTY_MUT.as_ptr());
assert_eq!(values.make_mut().as_mut_slice(), &mut []);
assert!(Buf::<str, 1>::from(&[] as &[&str]).iter().next().is_none());
let strings = Buf::<str, 1>::from_spans(values.into_bytes(), [0..0, 0..0], [2]).unwrap();
assert_eq!(strings.iter().collect::<Vec<_>>(), ["", ""]);
let mut buf = EMPTY_MUT;
assert_eq!(buf.capacity(), 0);
buf.clear();
buf.extend_from_slice(&[]);
assert_eq!(buf.as_ptr(), EMPTY_MUT.as_ptr());
buf.extend_from_slice(&[1, 2]);
assert_ne!(buf.as_ptr(), EMPTY_MUT.as_ptr());
let mut shared = EMPTY.reshape::<1>([0]).unwrap();
shared.get_mut().unwrap().resize(3, 7);
assert_eq!(shared.as_slice(), [7; 3]);
}
#[test]
fn writable_clone() {
let mut buf = BufMut::from(&[1u16, 2][..]);
let mut copy = buf.clone();
assert_ne!(copy.as_ptr(), buf.as_ptr());
copy.as_mut_slice()[0] = 0;
buf.push(3);
assert_eq!((buf.as_slice(), copy.as_slice()), (&[1, 2, 3][..], &[0, 2][..]));
assert!(buf.freeze().is_unique() && copy.freeze().is_unique());
}
#[test]
fn slicing_by_ref() {
let values = Buf::<u16, 1>::from(vec![1, 2, 3, 4]);
let part = values.slice_ref(&values.as_slice()[1..3]);
assert_eq!(part.as_slice(), &[2, 3]);
assert_eq!(part.as_ptr(), values.as_slice()[1..].as_ptr());
let rows = values.reshape([2, 2]).unwrap();
assert_eq!(rows.slice_ref(&rows.as_slice()[2..]).dim(), [2, 1]);
let res = std::panic::catch_unwind(|| rows.slice_ref(&rows.as_slice()[1..3]));
assert!(res.is_err());
let other = [3, 4];
let res = std::panic::catch_unwind(|| rows.slice_ref(&other));
assert!(res.is_err());
}
#[test]
fn octets_by_ref() {
let buf = Buf::from(&b"hello world"[..]);
let part = buf.slice_ref(&buf.as_slice()[6..]);
assert_eq!(part.as_slice(), b"world");
assert!(buf.slice_ref(&buf.as_slice()[11..]).is_empty());
}
#[test]
fn slicing() {
let buf = Buf::<u8, 2>::from_slice(&[1, 2, 3, 4, 5, 6], [2, 3]).unwrap();
let rows = buf.slice(1..);
assert_eq!(rows.dim(), [2, 2]);
assert_eq!(rows.as_slice(), &[3, 4, 5, 6]);
assert!(!buf.is_unique());
assert_eq!(rows.slice(..1).as_slice(), &[3, 4]);
}
#[test]
fn scalar_shapes() {
let scalar = Buf::<u16, 0>::from(7);
let square = scalar.clone().reshape([1, 1]).unwrap();
assert_eq!(square.as_slice(), &[7]);
assert!(scalar.clone().reshape([2]).is_none());
assert_eq!(square.into_scalar().unwrap(), scalar);
assert_eq!(scalar.clone().into_scalar().unwrap(), scalar);
let string = Buf::<str, 0>::from("ab");
let strings = string.clone().reshape([1]).unwrap();
assert!(!string.is_unique());
assert_eq!(strings.into_scalar().unwrap().value(), "ab");
}
#[test]
fn splitting() {
let mut buf = Buf::from(&b"headtail"[..]);
let head = buf.split_to(4);
assert_eq!((head.as_slice(), buf.as_slice()), (&b"head"[..], &b"tail"[..]));
let tail = buf.split_off(2);
assert_eq!((buf.as_slice(), tail.as_slice()), (&b"ta"[..], &b"il"[..]));
assert!(!head.is_unique());
let mut rows = Buf::<u8, 2>::from_slice(&[1, 2, 3, 4, 5, 6], [2, 3]).unwrap();
assert_eq!(rows.split_to(1).as_slice(), &[1, 2]);
assert_eq!(rows.dim(), [2, 2]);
}
#[test]
#[should_panic(expected = "out of range")]
fn slice_out_of_range() {
Buf::<u8, 1>::from(vec![1, 2]).slice(1..3);
}
#[test]
fn scalar_mutation() {
let mut scalar = Buf::<i32, 0>::from(1);
scalar.make_mut().as_mut_slice()[0] = 2;
assert_eq!(scalar.value(), 2);
assert_eq!(BufMut::<i32, 0>::zeroed([]).freeze().value(), 0);
}
#[test]
fn scalar_extraction() {
let values = Buf::<f64, 1>::from(vec![1.5, 2.5]);
assert_eq!(values.slice(1..).into_scalar().unwrap().value(), 2.5);
assert!(values.is_unique());
assert!(values.clone().into_scalar().is_none());
let strings = Buf::<str, 1>::from(&["ab", "cd"][..]);
let scalar = strings.slice(1..).into_scalar().unwrap();
assert_eq!(scalar.value(), "cd");
assert!(!strings.is_unique());
assert_eq!(strings.get_scalar([0]).unwrap().value(), "ab");
assert!(strings.get_scalar([2]).is_none());
let image = Buf::<u8, 2>::from_slice(&[1, 2, 3, 4, 5, 6], [2, 3]).unwrap();
assert_eq!(image.get_scalar([1, 2]).unwrap().value(), 6);
assert_eq!(Buf::<u16, 0>::from(7).get_scalar([]).unwrap().value(), 7);
}
#[test]
fn copy_on_write() {
let mut values = Buf::<u16, 1>::from(vec![1, 2, 3, 4]);
let part = values.slice(1..3);
values.make_mut().as_mut_slice()[1] = 9;
assert_eq!(values.as_slice(), &[1, 9, 3, 4]);
assert_eq!(part.as_slice(), &[2, 3]);
assert!(values.is_unique());
let ptr = values.as_ptr();
values.make_mut().as_mut_slice()[0] = 0;
assert_eq!(values.as_ptr(), ptr);
let rest = Buf::from(vec![0u16; 64]).slice(1..);
let part = rest.slice(..2).into_mut();
assert_ne!(part.as_ptr(), rest.as_ptr());
assert!(part.capacity() < rest.len());
}
#[test]
fn copy_on_write_growth() {
let mut buf = Buf::from(&b"head"[..]);
let head = buf.clone();
buf.make_mut().extend_from_slice(b" tail");
assert_eq!((buf.as_slice(), head.as_slice()), (&b"head tail"[..], &b"head"[..]));
}
#[test]
fn growth() {
let mut buf = BufMut::<u32, 1>::with_capacity(4);
buf.extend_from_slice(&[1, 2]);
buf.resize(5, 0);
buf.extend_from_slice(&[7; 100]);
buf.push(8);
assert_eq!(buf.len(), 106);
assert_eq!(buf.as_slice()[..6], [1, 2, 0, 0, 0, 7]);
assert_eq!(buf.as_slice()[105], 8);
buf.truncate(2);
assert_eq!(buf.as_slice(), &[1, 2]);
}
#[test]
fn length() {
let mut buf = BufMut::from(&[0xAA; 8][..]);
buf.truncate(4);
buf.set_len(12);
assert_eq!(buf.as_slice(), [&[0xAA; 8][..], &[0; 4]].concat());
buf.truncate(4);
buf.resize(8, 0);
assert_eq!(buf.as_slice(), [0xAA, 0xAA, 0xAA, 0xAA, 0, 0, 0, 0]);
}
#[test]
fn zero_resize() {
let mut buf = BufMut::from(&[0xAAAAu16; 4][..]);
buf.clear();
buf.resize(100, 0);
assert_eq!(buf.as_slice(), [0; 100]);
buf.clear();
buf.resize(2, 0xBBBB);
buf.resize(3, 0);
assert_eq!(buf.as_slice(), [0xBBBB, 0xBBBB, 0]);
}
#[test]
fn reservation() {
let mut buf = BufMut::from(&[1u16, 2][..]);
assert!(buf.try_reserve(usize::MAX).is_err());
assert!(buf.try_reserve(isize::MAX as usize).is_err());
assert_eq!(buf.as_slice(), [1, 2]);
buf.reserve(64);
assert!(buf.capacity() >= 66);
}
#[test]
fn recycling() {
let mut buf = BufMut::from(&b"first"[..]);
let ptr = buf.as_ptr();
buf.clear();
buf.extend_from_slice(b"second");
assert_eq!(buf.as_ptr(), ptr);
let buf = buf.freeze();
let first = Buf::<str, 0>::from_spans(buf.slice(..3), [0..3], []).unwrap();
let buf = buf.try_into_mut().unwrap_err();
drop(first);
let mut buf = buf.try_into_mut().unwrap();
buf.clear();
assert_eq!(buf.as_ptr(), ptr);
}
#[test]
fn unique_buffer() {
let buf = Buf::from(&[1u8, 2, 3][..]);
let ptr = buf.as_ptr();
let part = buf.slice(1..);
let mut buf = buf.try_into_mut().unwrap_err();
assert!(buf.get_mut().is_none());
drop(part);
buf.get_mut().unwrap().truncate(1);
let buf = buf.try_into_mut().unwrap();
assert_eq!(buf.as_ptr(), ptr);
assert_eq!(buf.as_slice(), [1]);
}
#[test]
fn packed_cast() {
let buf = Buf::<u32, 1>::from(vec![1, 2, 3, 4]).into_bytes();
let aligned = buf.slice(8..).cast::<u32, 1>([2]).unwrap();
assert_eq!(aligned.as_slice(), &[3, 4]);
assert!(!aligned.is_unique());
let unaligned = buf.slice(1..9).cast::<u32, 1>([2]).unwrap();
assert!(unaligned.is_unique());
let buf = buf.cast::<u32, 1>([3]).unwrap_err();
assert!(buf.slice(..2).cast::<u16, 0>([]).unwrap().is_unique());
let halves = aligned.cast::<u16, 2>([2, 2]).unwrap();
assert_eq!(halves.cast::<u64, 0>([]).unwrap().value(), 3 | 4 << 32);
}
#[test]
fn storage_alignment() {
let buf = Buf::from(&[0u8; 16][..]);
assert_eq!(buf.as_ptr().addr() % ALIGN, 0);
assert!(!buf.clone().cast::<f64, 1>([2]).unwrap().is_unique());
}
#[test]
fn invalid_values() {
let buf = Buf::from(&[0u8, 1, 2][..]);
assert!(buf.slice(..2).cast::<bool, 1>([2]).is_ok());
assert!(buf.cast::<bool, 1>([3]).is_err());
}
#[test]
fn octets_round_trip() {
let values = Buf::<u16, 1>::from(vec![1, 2]);
assert_eq!(values.as_bytes(), [1u16.to_ne_bytes(), 2u16.to_ne_bytes()].concat());
let buf = values.into_bytes();
assert_eq!(buf.cast::<u16, 1>([2]).unwrap().as_slice(), &[1, 2]);
}
#[test]
fn strings() {
let strings = Buf::<str, 1>::from(&["ab", "", "cde"][..]);
assert_eq!(strings.get([2]), Some("cde"));
assert_eq!(strings.iter().collect::<Vec<_>>(), ["ab", "", "cde"]);
assert_eq!(strings.slice(1..).get([1]), Some("cde"));
assert_eq!(Buf::<str, 0>::from("scalar").value(), "scalar");
}
#[test]
fn strings_from_iter() {
let strings = Buf::<str, 1>::from_strs(["ab", "", "cde"].into_iter().filter(|_| true)).unwrap();
assert_eq!(strings.dim(), [3]);
assert_eq!(strings.iter().collect::<Vec<_>>(), ["ab", "", "cde"]);
let empty = Buf::<str, 1>::from_strs(std::iter::empty::<&str>()).unwrap();
assert!(empty.is_empty());
}
#[test]
fn string_spans() {
let msg = Buf::from(&b"..hello world"[..]);
let strings = Buf::<str, 1>::from_spans(msg.slice(2..), [0..5, 6..11], [2]).unwrap();
drop(msg);
assert_eq!(strings.iter().collect::<Vec<_>>(), ["hello", "world"]);
let buf = Buf::from(&[b'a', 0xff][..]);
let buf = Buf::<str, 1>::from_spans(buf, [0..3], [1]).unwrap_err();
let buf = Buf::<str, 1>::from_spans(buf, [1..2], [1]).unwrap_err();
#[allow(clippy::reversed_empty_ranges)]
let buf = Buf::<str, 1>::from_spans(buf, [1..0], [1]).unwrap_err();
assert!(Buf::<str, 0>::from_spans(buf, [0..1], []).is_ok());
}
#[test]
fn single_string() {
let msg = Buf::from(&b"..hello world"[..]);
let hello = Buf::<str, 0>::from_spans(msg.slice(2..), [0..5], []).unwrap();
let world = Buf::<str, 1>::from_spans(msg.slice(2..), [6..11], [1]).unwrap();
drop(msg);
assert_eq!(hello.value(), "hello");
assert_eq!(world.iter().collect::<Vec<_>>(), ["world"]);
assert_eq!(world.get_scalar([0]).unwrap().value(), "world");
assert!(world.slice(1..).is_empty());
assert_eq!(world.slice(..1).reshape([1, 1]).unwrap().get([0, 0]), Some("world"));
assert_eq!(world.into_scalar().unwrap().value(), "world");
let buf = Buf::from(&b"ab"[..]);
let buf = Buf::<str, 0>::from_spans(buf, [0..1, 1..2], []).unwrap_err();
assert!(Buf::<str, 0>::from_spans(buf, [], []).is_err());
let empty = Buf::<str, 0>::from("");
assert_eq!(empty.value(), "");
assert!(!empty.is_unique());
}
#[test]
fn span_iterator() {
let msg = Buf::from(&b"a,bc,def"[..]);
let spans = msg.as_slice().split(|&b| b == b',').scan(0, |start, part| {
let span = *start..*start + part.len() as u32;
*start = span.end + 1;
Some(span)
}).collect::<Vec<_>>();
let strings = Buf::<str, 1>::from_spans(msg.clone(), spans.clone(), [3]).unwrap();
assert_eq!(strings.iter().collect::<Vec<_>>(), ["a", "bc", "def"]);
let msg = Buf::<str, 1>::from_spans(msg, spans.clone(), [2]).unwrap_err();
let msg = Buf::<str, 1>::from_spans(msg, spans.clone(), [4]).unwrap_err();
let msg = Buf::<str, 1>::from_spans(msg, spans.clone(), [usize::MAX]).unwrap_err();
assert!(Buf::<str, 1>::from_spans(msg, [], [0]).is_ok());
}
#[test]
fn thread_sharing() {
let values = Buf::<f64, 1>::from(vec![1.0, 2.0]);
let sum = std::thread::spawn({
let values = values.clone();
move || values.iter().sum::<f64>()
});
assert_eq!(sum.join().unwrap(), 3.0);
}
#[test]
fn states() {
let buf = Buf::from(&[DevState::Moving as u8, 200][..]);
let buf = buf.cast::<DevState, 1>([2]).unwrap_err();
assert_eq!(buf.slice(..1).cast::<DevState, 0>([]).unwrap().value(), DevState::Moving);
}
#[test]
fn equality() {
let values = Buf::<i32, 1>::from([1, 2, 3, 4]);
assert_eq!(values.slice(2..), Buf::from([3, 4]));
assert_ne!(values.clone().reshape([2, 2]).unwrap(), Buf::from_slice(&[1, 2, 3, 4], [4, 1]).unwrap());
}
#[test]
fn debug() {
assert_eq!(format!("{:?}", Buf::<u16, 1>::from([7; 3])), "Buf<3>[7, 7, 7]");
assert_eq!(format!("{:?}", Buf::<u16, 0>::from(7)), "Buf(7)");
let rows = Buf::<i32, 2>::from_slice(&[1, 2, 3, 4, 5, 6], [3, 2]).unwrap();
assert_eq!(format!("{rows:?}"), "Buf<3, 2>[[1, 2, 3], [4, 5, 6]]");
assert_eq!(format!("{rows:#?}"), "Buf<3, 2>[\n [1, 2, 3],\n [4, 5, 6],\n]");
assert_eq!(format!("{:?}", BufMut::<u8, 2>::zeroed([2, 0])), "BufMut<2, 0>[]");
assert_eq!(format!("{:?}", Buf::<u8, 2>::new().reshape([0, 2]).unwrap()), "Buf<0, 2>[[], []]");
assert_eq!(format!("{:?}", Buf::<str, 1>::from(&["a", "b"][..])), r#"Buf<2>["a", "b"]"#);
}
#[test]
fn octets_debug() {
assert_eq!(format!("{:?}", Buf::from(&b"\x01\x02GIOP\x00"[..])), "Buf<7>[01 02 'GIOP' 00]");
assert_eq!(format!("{:?}", BufMut::<u8, 1>::from(vec![0xAB; 20])), format!("BufMut<20>[{}AB]", "AB ".repeat(19)));
let rows = Buf::<u8, 2>::from_slice(b"ab\x01\x02", [2, 2]).unwrap();
assert_eq!(format!("{rows:?}"), "Buf<2, 2>[['ab'], [01 02]]");
}
#[test]
fn hex() {
let buf = Buf::from(&[0x01, 0xAB, 0x00][..]);
assert_eq!(format!("{buf:X}"), "01AB00");
assert_eq!(format!("{buf:x}"), "01ab00");
assert_eq!(format!("{:X}", buf.clone().into_mut()), "01AB00");
assert_eq!(BufMut::from_hex("01AB00").unwrap().freeze(), buf);
assert_eq!(BufMut::from_hex("01ab00").unwrap().freeze(), buf);
assert!(BufMut::from_hex("01A").is_none());
assert!(BufMut::from_hex("0G").is_none());
let words = Buf::<u16, 1>::from([0x0102, 0xABCD]);
let expected = if cfg!(target_endian = "little") { "0201 cdab" } else { "0102 abcd" };
assert_eq!(format!("{words:x}"), expected);
assert_eq!(format!("{:X}", Buf::<u32, 1>::new()), "");
}
#[test]
fn sizes() {
assert_eq!(mem::size_of::<Buf<f64, 0>>(), 16);
assert_eq!(mem::size_of::<Buf<u8, 1>>(), 24);
assert_eq!(mem::size_of::<Buf<f64, 2>>(), 32);
assert_eq!(mem::size_of::<Buf<str, 1>>(), 32);
assert_eq!(mem::size_of::<BufMut<u8, 1>>(), 24);
}
}