//! Simple stack-allocated vector. #![cfg(not(feature = "alloc"))] #![doc(hidden)] use crate::bigint; use core::{cmp, mem, ops, ptr, slice}; /// Simple stack vector implementation. #[derive(Clone)] pub struct StackVec { /// The raw buffer for the elements. data: [mem::MaybeUninit; bigint::BIGINT_LIMBS], /// The number of elements in the array (we never need more than u16::MAX). length: u16, } #[allow(clippy::new_without_default)] impl StackVec { /// Construct an empty vector. #[inline] pub const fn new() -> Self { Self { length: 0, data: [mem::MaybeUninit::uninit(); bigint::BIGINT_LIMBS], } } /// Construct a vector from an existing slice. #[inline] pub fn try_from(x: &[bigint::Limb]) -> Option { let mut vec = Self::new(); vec.try_extend(x)?; Some(vec) } /// Sets the length of a vector. /// /// This will explicitly set the size of the vector, without actually /// modifying its buffers, so it is up to the caller to ensure that the /// vector is actually the specified size. /// /// # Safety /// /// Safe as long as `len` is less than `BIGINT_LIMBS`. #[inline] pub unsafe fn set_len(&mut self, len: usize) { // Constant is `u16::MAX` for older Rustc versions. debug_assert!(len <= 0xffff); debug_assert!(len <= bigint::BIGINT_LIMBS); self.length = len as u16; } /// The number of elements stored in the vector. #[inline] pub const fn len(&self) -> usize { self.length as usize } /// If the vector is empty. #[inline] pub const fn is_empty(&self) -> bool { self.len() == 0 } /// The number of items the vector can hold. #[inline] pub const fn capacity(&self) -> usize { bigint::BIGINT_LIMBS as usize } /// Append an item to the vector, without bounds checking. /// /// # Safety /// /// Safe if `self.len() < self.capacity()`. #[inline] pub unsafe fn push_unchecked(&mut self, value: bigint::Limb) { debug_assert!(self.len() < self.capacity()); // SAFETY: safe, capacity is less than the current size. unsafe { ptr::write(self.as_mut_ptr().add(self.len()), value); self.length += 1; } } /// Append an item to the vector. #[inline] pub fn try_push(&mut self, value: bigint::Limb) -> Option<()> { if self.len() < self.capacity() { // SAFETY: safe, capacity is less than the current size. unsafe { self.push_unchecked(value) }; Some(()) } else { None } } /// Remove an item from the end of a vector, without bounds checking. /// /// # Safety /// /// Safe if `self.len() > 0`. #[inline] pub unsafe fn pop_unchecked(&mut self) -> bigint::Limb { debug_assert!(!self.is_empty()); // SAFETY: safe if `self.length > 0`. // We have a trivial drop and copy, so this is safe. self.length -= 1; unsafe { ptr::read(self.as_mut_ptr().add(self.len())) } } /// Remove an item from the end of the vector and return it, or None if empty. #[inline] pub fn pop(&mut self) -> Option { if self.is_empty() { None } else { // SAFETY: safe, since `self.len() > 0`. unsafe { Some(self.pop_unchecked()) } } } /// Add items from a slice to the vector, without bounds checking. /// /// # Safety /// /// Safe if `self.len() + slc.len() <= self.capacity()`. #[inline] pub unsafe fn extend_unchecked(&mut self, slc: &[bigint::Limb]) { let index = self.len(); let new_len = index + slc.len(); debug_assert!(self.len() + slc.len() <= self.capacity()); let src = slc.as_ptr(); // SAFETY: safe if `self.len() + slc.len() <= self.capacity()`. unsafe { let dst = self.as_mut_ptr().add(index); ptr::copy_nonoverlapping(src, dst, slc.len()); self.set_len(new_len); } } /// Copy elements from a slice and append them to the vector. #[inline] pub fn try_extend(&mut self, slc: &[bigint::Limb]) -> Option<()> { if self.len() + slc.len() <= self.capacity() { // SAFETY: safe, since `self.len() + slc.len() <= self.capacity()`. unsafe { self.extend_unchecked(slc) }; Some(()) } else { None } } /// Truncate vector to new length, dropping any items after `len`. /// /// # Safety /// /// Safe as long as `len <= self.capacity()`. unsafe fn truncate_unchecked(&mut self, len: usize) { debug_assert!(len <= self.capacity()); self.length = len as u16; } /// Resize the buffer, without bounds checking. /// /// # Safety /// /// Safe as long as `len <= self.capacity()`. #[inline] pub unsafe fn resize_unchecked(&mut self, len: usize, value: bigint::Limb) { debug_assert!(len <= self.capacity()); let old_len = self.len(); if len > old_len { // We have a trivial drop, so there's no worry here. // Just, don't set the length until all values have been written, // so we don't accidentally read uninitialized memory. // SAFETY: safe if `len < self.capacity()`. let count = len - old_len; for index in 0..count { unsafe { let dst = self.as_mut_ptr().add(old_len + index); ptr::write(dst, value); } } self.length = len as u16; } else { // SAFETY: safe since `len < self.len()`. unsafe { self.truncate_unchecked(len) }; } } /// Try to resize the buffer. /// /// If the new length is smaller than the current length, truncate /// the input. If it's larger, then append elements to the buffer. #[inline] pub fn try_resize(&mut self, len: usize, value: bigint::Limb) -> Option<()> { if len > self.capacity() { None } else { // SAFETY: safe, since `len <= self.capacity()`. unsafe { self.resize_unchecked(len, value) }; Some(()) } } // HI /// Get the high 64 bits from the vector. #[inline(always)] pub fn hi64(&self) -> (u64, bool) { bigint::hi64(self) } // FROM /// Create StackVec from u64 value. #[inline(always)] pub fn from_u64(x: u64) -> Self { bigint::from_u64(x) } // MATH /// Normalize the integer, so any leading zero values are removed. #[inline] pub fn normalize(&mut self) { bigint::normalize(self) } /// Get if the big integer is normalized. #[inline] pub fn is_normalized(&self) -> bool { bigint::is_normalized(self) } /// AddAssign small integer. #[inline] pub fn add_small(&mut self, y: bigint::Limb) -> Option<()> { bigint::small_add(self, y) } /// MulAssign small integer. #[inline] pub fn mul_small(&mut self, y: bigint::Limb) -> Option<()> { bigint::small_mul(self, y) } } impl PartialEq for StackVec { #[inline] #[allow(clippy::op_ref)] fn eq(&self, other: &Self) -> bool { use core::ops::Deref; self.len() == other.len() && self.deref() == other.deref() } } impl Eq for StackVec { } impl cmp::PartialOrd for StackVec { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(bigint::compare(self, other)) } } impl cmp::Ord for StackVec { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { bigint::compare(self, other) } } impl ops::Deref for StackVec { type Target = [bigint::Limb]; #[inline] fn deref(&self) -> &[bigint::Limb] { // SAFETY: safe since `self.data[..self.len()]` must be initialized // and `self.len() <= self.capacity()`. unsafe { let ptr = self.data.as_ptr() as *const bigint::Limb; slice::from_raw_parts(ptr, self.len()) } } } impl ops::DerefMut for StackVec { #[inline] fn deref_mut(&mut self) -> &mut [bigint::Limb] { // SAFETY: safe since `self.data[..self.len()]` must be initialized // and `self.len() <= self.capacity()`. unsafe { let ptr = self.data.as_mut_ptr() as *mut bigint::Limb; slice::from_raw_parts_mut(ptr, self.len()) } } } impl ops::MulAssign<&[bigint::Limb]> for StackVec { #[inline] fn mul_assign(&mut self, rhs: &[bigint::Limb]) { bigint::large_mul(self, rhs).unwrap(); } }