#![cfg_attr(not(any(test, doc)), no_std)]
#![cfg_attr(feature = "allocator_api", feature(allocator_api))]
#![cfg_attr(feature = "use_unstable_apis", feature(slice_range, try_reserve_kind))]
#![deny(unsafe_op_in_unsafe_fn)]
extern crate alloc;
mod collect;
mod error;
mod set_len_on_drop;
use alloc::{collections::TryReserveError, vec::Vec};
use set_len_on_drop::SetLenOnDrop;
#[cfg(feature = "allocator_api")]
use core::alloc::Allocator;
pub use collect::TryCollect;
pub use error::alloc_error;
#[doc(hidden)]
pub mod alloc_usings {
pub use alloc::{alloc::Layout, boxed::Box, collections::TryReserveError, vec::Vec};
}
pub trait FallibleVec<T>: Sized {
fn try_extend<I: IntoIterator<Item = T>>(&mut self, iter: I) -> Result<(), TryReserveError>;
fn try_push(&mut self, item: T) -> Result<(), TryReserveError>;
fn try_insert(&mut self, index: usize, element: T) -> Result<(), TryReserveError>;
fn try_resize_with<F: FnMut() -> T>(
&mut self,
new_len: usize,
f: F,
) -> Result<(), TryReserveError>;
#[cfg(all(feature = "allocator_api", feature = "use_unstable_apis"))]
fn try_splice_in<I: IntoIterator<Item = T>, A: Allocator>(
&mut self,
range: impl core::ops::RangeBounds<usize>,
replace_with: I,
alloc: A,
) -> Result<(), TryReserveError>;
fn try_extend_from_slice(&mut self, slice: &[T]) -> Result<(), TryReserveError>
where
T: Clone;
fn try_resize(&mut self, new_len: usize, item: T) -> Result<(), TryReserveError>
where
T: Clone;
}
macro_rules! impl_trait_for_vec {
{ impl $trait:ident $impl:tt } => {
#[cfg(not(feature = "allocator_api"))]
impl<T> $trait<T> for Vec<T> $impl
#[cfg(feature = "allocator_api")]
impl<T, A: Allocator> $trait<T> for Vec<T, A> $impl
}
}
impl_trait_for_vec! {
impl FallibleVec {
fn try_extend<I: IntoIterator<Item = T>>(&mut self, iter: I) -> Result<(), TryReserveError> {
let iter = iter.into_iter();
let (low_bound, _upper_bound) = iter.size_hint();
self.try_reserve(low_bound)?;
for item in iter {
self.try_push(item)?;
}
Ok(())
}
fn try_extend_from_slice(&mut self, slice: &[T]) -> Result<(), TryReserveError>
where
T: Clone,
{
self.try_reserve(slice.len())?;
let ptr = self.as_mut_ptr();
let mut local_len = SetLenOnDrop::new(self);
for item in slice.iter() {
unsafe {
ptr.add(local_len.current_len()).write(item.clone());
}
local_len.increment_len(1);
}
Ok(())
}
fn try_push(&mut self, item: T) -> Result<(), TryReserveError> {
self.try_reserve(1)?;
unsafe {
self.as_mut_ptr().add(self.len()).write(item);
self.set_len(self.len() + 1);
}
Ok(())
}
fn try_insert(&mut self, index: usize, element: T) -> Result<(), TryReserveError> {
self.move_tail(index, 1)?;
unsafe {
self.as_mut_ptr().add(index).write(element);
self.set_len(self.len() + 1);
}
Ok(())
}
fn try_resize(&mut self, new_len: usize, item: T) -> Result<(), TryReserveError>
where
T: Clone,
{
#[allow(clippy::comparison_chain)]
if new_len < self.len() {
self.truncate(new_len);
} else if new_len > self.len() {
self.try_reserve(new_len - self.len())?;
let ptr = self.as_mut_ptr();
let mut local_len = SetLenOnDrop::new(self);
loop {
unsafe {
ptr.add(local_len.current_len()).write(item.clone());
}
local_len.increment_len(1);
if local_len.current_len() == new_len {
break;
}
}
}
Ok(())
}
fn try_resize_with<F: FnMut() -> T>(
&mut self,
new_len: usize,
mut f: F,
) -> Result<(), TryReserveError> {
#[allow(clippy::comparison_chain)]
if new_len < self.len() {
self.truncate(new_len);
} else if new_len > self.len() {
self.try_reserve(new_len - self.len())?;
let ptr = self.as_mut_ptr();
let mut local_len = SetLenOnDrop::new(self);
loop {
let item = f();
unsafe {
ptr.add(local_len.current_len()).write(item);
}
local_len.increment_len(1);
if local_len.current_len() == new_len {
break;
}
}
}
Ok(())
}
#[cfg(all(feature = "allocator_api", feature = "use_unstable_apis"))]
fn try_splice_in<I: IntoIterator<Item = T>, ATemp: Allocator>(
&mut self,
range: impl core::ops::RangeBounds<usize>,
replace_with: I,
alloc: ATemp,
) -> Result<(), TryReserveError> {
let mut replace_with = replace_with.into_iter();
let core::ops::Range {
start: mut index,
end,
} = core::slice::range(range, ..self.len());
while index < end {
if let Some(item) = replace_with.next() {
self[index] = item;
index += 1;
} else {
self.drain(index..end);
return Ok(());
}
}
let (lower_bound, ..) = replace_with.size_hint();
if lower_bound > 0 {
self.move_tail(index, lower_bound)?;
let after_splice = self.len() - index;
unsafe {
self.set_len(index);
}
{
let ptr = self.as_mut_ptr();
let mut local_len = SetLenOnDrop::new(self);
loop {
unsafe {
ptr.add(local_len.current_len())
.write(replace_with.next().unwrap());
}
local_len.increment_len(1);
if local_len.current_len() == index + lower_bound {
break;
}
}
}
index += lower_bound;
unsafe {
self.set_len(self.len() + after_splice);
}
}
let remainder = replace_with.try_collect_in(alloc)?;
if !remainder.is_empty() {
self.move_tail(index, remainder.len())?;
unsafe {
self.set_len(self.len() + remainder.len());
}
let ptr = unsafe { self.as_mut_ptr().add(index) };
for (i, item) in remainder.into_iter().enumerate() {
unsafe { ptr.add(i).write(item) };
}
}
Ok(())
}
}
}
#[macro_export]
macro_rules! try_vec {
() => (
core::result::Result::Ok::<Vec<_>, $crate::alloc_usings::TryReserveError>(
$crate::alloc_usings::Vec::new())
);
($elem:expr; $n:expr) => (
$crate::try_new_repeat_item($elem, $n)
);
($($x:expr),+ $(,)?) => ({
let values = [$($x),+];
let layout = $crate::alloc_usings::Layout::for_value(&values);
$crate::alloc_usings::Box::try_new(values)
.map(|b| <[_]>::into_vec(b))
.map_err::<$crate::alloc_usings::TryReserveError, _>(|_| $crate::alloc_error(layout))
});
}
#[macro_export]
#[cfg(feature = "allocator_api")]
macro_rules! try_vec_in {
($allocator:expr) => (
core::result::Result::Ok::<Vec<_, _>, $crate::alloc_usings::TryReserveError>(
$crate::alloc_usings::Vec::new_in($allocator))
);
($elem:expr; $n:expr => $allocator:expr) => (
$crate::try_new_repeat_item_in($elem, $n, $allocator)
);
($($x:expr),+ $(,)? => $allocator:expr) => ({
let values = [$($x),+];
let layout = $crate::alloc_usings::Layout::for_value(&values);
$crate::alloc_usings::Box::try_new_in(values, $allocator)
.map(|b| <[_]>::into_vec(b))
.map_err::<$crate::alloc_usings::TryReserveError, _>(|_| $crate::alloc_error(layout))
});
}
#[cfg(feature = "allocator_api")]
pub fn try_with_capacity_in<T, A: Allocator>(
size: usize,
alloc: A,
) -> Result<Vec<T, A>, TryReserveError> {
let mut vec: Vec<T, A> = Vec::new_in(alloc);
vec.try_reserve(size)?;
Ok(vec)
}
pub fn try_with_capacity<T>(size: usize) -> Result<Vec<T>, TryReserveError> {
let mut vec: Vec<T> = Vec::new();
vec.try_reserve(size)?;
Ok(vec)
}
#[doc(hidden)]
#[cfg(feature = "allocator_api")]
pub fn try_new_repeat_item_in<T: Clone, A: Allocator>(
item: T,
size: usize,
alloc: A,
) -> Result<Vec<T, A>, TryReserveError> {
Vec::new_in(alloc).try_new_repeat_item_internal(item, size)
}
#[doc(hidden)]
pub fn try_new_repeat_item<T: Clone>(item: T, size: usize) -> Result<Vec<T>, TryReserveError> {
Vec::new().try_new_repeat_item_internal(item, size)
}
trait ImplementationDetails<T>: Sized {
fn try_new_repeat_item_internal(self, item: T, size: usize) -> Result<Self, TryReserveError>
where
T: Clone;
fn move_tail(&mut self, index: usize, by: usize) -> Result<(), TryReserveError>;
}
impl_trait_for_vec! {
impl ImplementationDetails {
#[inline]
fn try_new_repeat_item_internal(
mut self,
item: T,
size: usize,
) -> Result<Self, TryReserveError>
where T: Clone {
if size > 0 {
self.try_reserve(size)?;
let ptr = self.as_mut_ptr();
let mut local_len = SetLenOnDrop::new(&mut self);
loop {
unsafe {
ptr.add(local_len.current_len()).write(item.clone());
}
local_len.increment_len(1);
if local_len.current_len() == size {
break;
}
}
}
Ok(self)
}
fn move_tail(
&mut self,
index: usize,
by: usize,
) -> Result<(), TryReserveError> {
self.try_reserve(by)?;
let source = unsafe { self.as_ptr().add(index) };
let destination = unsafe { self.as_mut_ptr().add(index + by) };
unsafe {
core::ptr::copy(source, destination, self.len() - index);
}
Ok(())
}
}
}
#[cfg(test)]
pub mod tests;