use alloc::alloc::{alloc, alloc_zeroed, dealloc, realloc, Layout};
use core::{
borrow::{Borrow, BorrowMut},
cmp::max,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
mem,
num::NonZeroUsize,
ops::{Deref, DerefMut, Index, IndexMut, RangeFull},
ptr,
ptr::NonNull,
};
use awint_core::{Bits, InlAwi};
use const_fn::const_fn;
use crate::awint_internals::*;
union InlOrExt {
_inl: Digit,
_ext: *const Digit,
}
#[repr(C)]
pub struct Awi {
_inl_or_ext: InlOrExt,
_nzbw: NonZeroUsize,
_cap: usize,
_boo: PhantomData<NonNull<Digit>>,
}
unsafe impl Send for Awi {}
unsafe impl Sync for Awi {}
impl<'a> Awi {
#[doc(hidden)]
#[inline]
#[const_fn(cfg(feature = "const_support"))]
pub const unsafe fn inl_from_raw_parts(digit: Digit, nzbw: NonZeroUsize) -> Awi {
debug_assert!(nzbw.get() <= BITS);
Awi {
_inl_or_ext: InlOrExt { _inl: digit },
_nzbw: nzbw,
_cap: 0,
_boo: PhantomData,
}
}
#[doc(hidden)]
#[inline]
#[const_fn(cfg(feature = "const_support"))]
pub const unsafe fn ext_from_raw_parts(
digits: *const Digit,
nzbw: NonZeroUsize,
cap_in_bytes: usize,
) -> Awi {
debug_assert!(cap_in_bytes.checked_mul(8).is_some());
debug_assert!((cap_in_bytes * 8) >= nzbw.get());
Awi {
_inl_or_ext: InlOrExt { _ext: digits },
_nzbw: nzbw,
_cap: cap_in_bytes,
_boo: PhantomData,
}
}
#[inline]
#[const_fn(cfg(feature = "const_support"))]
#[must_use]
const fn internal_as_ref(&'a self) -> &'a Bits {
if self._cap == 0 {
unsafe {
let tmp: &Digit = &self._inl_or_ext._inl;
let tmp: *const Digit = tmp;
let tmp = tmp as *mut Digit;
Bits::from_raw_parts(RawBits::from_raw_parts(
NonNull::new_unchecked(tmp),
self._nzbw,
))
}
} else {
unsafe {
let tmp = self._inl_or_ext._ext;
let tmp = tmp as *mut Digit;
Bits::from_raw_parts(RawBits::from_raw_parts(
NonNull::new_unchecked(tmp),
self._nzbw,
))
}
}
}
#[inline]
#[const_fn(cfg(feature = "const_support"))]
#[must_use]
const fn internal_as_mut(&'a mut self) -> &'a mut Bits {
if self._cap == 0 {
unsafe {
let tmp: &mut Digit = &mut self._inl_or_ext._inl;
let tmp: *const Digit = tmp;
let tmp = tmp as *mut Digit;
Bits::from_raw_parts_mut(RawBits::from_raw_parts(
NonNull::new_unchecked(tmp),
self._nzbw,
))
}
} else {
unsafe {
let tmp = self._inl_or_ext._ext;
let tmp = tmp as *mut Digit;
Bits::from_raw_parts_mut(RawBits::from_raw_parts(
NonNull::new_unchecked(tmp),
self._nzbw,
))
}
}
}
#[inline]
#[const_fn(cfg(feature = "const_support"))]
#[must_use]
pub const fn nzbw(&self) -> NonZeroUsize {
self._nzbw
}
#[inline]
#[const_fn(cfg(feature = "const_support"))]
#[must_use]
pub const fn bw(&self) -> usize {
self._nzbw.get()
}
#[const_fn(cfg(feature = "const_support"))]
#[must_use]
pub const fn capacity(&self) -> NonZeroUsize {
if self._cap == 0 {
unsafe { NonZeroUsize::new_unchecked(BITS) }
} else {
unsafe { NonZeroUsize::new_unchecked(self._cap * 8) }
}
}
#[doc(hidden)]
#[inline]
#[const_fn(cfg(feature = "const_support"))]
#[must_use]
pub const fn layout(&self) -> Option<Layout> {
if self._cap == 0 {
None
} else {
unsafe {
Some(Layout::from_size_align_unchecked(
self._cap,
mem::align_of::<Digit>(),
))
}
}
}
pub fn from_bits(bits: &Bits) -> Awi {
let mut tmp = Awi::zero(bits.nzbw());
tmp.const_as_mut().copy_(bits).unwrap();
tmp
}
pub fn zero(w: NonZeroUsize) -> Self {
if w.get() <= BITS {
unsafe { Awi::inl_from_raw_parts(0, w) }
} else {
unsafe {
let size_in_digits = total_digits(w).get();
let size_in_bytes = size_in_digits * mem::size_of::<Digit>();
let layout =
Layout::from_size_align_unchecked(size_in_bytes, mem::align_of::<Digit>());
let ptr: *mut Digit = alloc_zeroed(layout).cast();
Awi::ext_from_raw_parts(ptr, w, size_in_bytes)
}
}
}
pub fn umax(w: NonZeroUsize) -> Self {
let mut res = if w.get() <= BITS {
unsafe { Awi::inl_from_raw_parts(MAX, w) }
} else {
unsafe {
let size_in_digits = total_digits(w).get();
let size_in_bytes = size_in_digits * mem::size_of::<Digit>();
let layout =
Layout::from_size_align_unchecked(size_in_bytes, mem::align_of::<Digit>());
let ptr: *mut Digit = alloc(layout).cast();
ptr.write_bytes(u8::MAX, size_in_digits);
Awi::ext_from_raw_parts(ptr, w, size_in_bytes)
}
};
res.const_as_mut().clear_unused_bits();
res
}
pub fn imax(w: NonZeroUsize) -> Self {
let mut val = Self::umax(w);
*val.const_as_mut().last_mut() = (MAX >> 1) >> val.unused();
val
}
pub fn imin(w: NonZeroUsize) -> Self {
let mut val = Self::zero(w);
*val.const_as_mut().last_mut() = (IDigit::MIN as Digit) >> val.unused();
val
}
pub fn uone(w: NonZeroUsize) -> Self {
let mut val = Self::zero(w);
*val.const_as_mut().first_mut() = 1;
val
}
pub fn from_bits_with_capacity(bits: &Bits, min_capacity: NonZeroUsize) -> Awi {
let mut tmp = Awi::zero_with_capacity(bits.nzbw(), min_capacity);
tmp.const_as_mut().copy_(bits).unwrap();
tmp
}
pub fn zero_with_capacity(w: NonZeroUsize, min_capacity: NonZeroUsize) -> Self {
let min_capacity = max(w, min_capacity);
if min_capacity.get() <= BITS {
unsafe { Awi::inl_from_raw_parts(0, w) }
} else {
unsafe {
let size_in_digits = total_digits(min_capacity).get();
let size_in_bytes = size_in_digits * mem::size_of::<Digit>();
let layout =
Layout::from_size_align_unchecked(size_in_bytes, mem::align_of::<Digit>());
let ptr: *mut Digit = alloc_zeroed(layout).cast();
Awi::ext_from_raw_parts(ptr, w, size_in_bytes)
}
}
}
pub fn umax_with_capacity(w: NonZeroUsize, min_capacity: NonZeroUsize) -> Self {
let min_capacity = max(w, min_capacity);
let mut res = if min_capacity.get() <= BITS {
unsafe { Awi::inl_from_raw_parts(MAX, w) }
} else {
unsafe {
let size_in_digits = total_digits(min_capacity).get();
let size_in_bytes = size_in_digits * mem::size_of::<Digit>();
let layout =
Layout::from_size_align_unchecked(size_in_bytes, mem::align_of::<Digit>());
let ptr: *mut Digit = alloc(layout).cast();
ptr.write_bytes(u8::MAX, size_in_digits);
Awi::ext_from_raw_parts(ptr, w, size_in_bytes)
}
};
res.const_as_mut().clear_unused_bits();
res
}
pub fn imax_with_capacity(w: NonZeroUsize, min_capacity: NonZeroUsize) -> Self {
let mut val = Self::umax_with_capacity(w, min_capacity);
*val.const_as_mut().last_mut() = (MAX >> 1) >> val.unused();
val
}
pub fn imin_with_capacity(w: NonZeroUsize, min_capacity: NonZeroUsize) -> Self {
let mut val = Self::zero_with_capacity(w, min_capacity);
*val.const_as_mut().last_mut() = (IDigit::MIN as Digit) >> val.unused();
val
}
pub fn uone_with_capacity(w: NonZeroUsize, min_capacity: NonZeroUsize) -> Self {
let mut val = Self::zero_with_capacity(w, min_capacity);
*val.const_as_mut().first_mut() = 1;
val
}
unsafe fn internal_capacity_change(&mut self, min_new_capacity: NonZeroUsize, init: bool) {
if min_new_capacity.get() <= BITS {
if let Some(layout) = self.layout() {
unsafe {
dealloc(self._inl_or_ext._ext as *mut u8, layout);
self._cap = 0;
}
} } else if self._cap == 0 {
let size_in_digits = total_digits(min_new_capacity).get();
let size_in_bytes = size_in_digits * mem::size_of::<Digit>();
unsafe {
let layout =
Layout::from_size_align_unchecked(size_in_bytes, mem::align_of::<Digit>());
let ptr: *mut Digit = if init {
let ptr = alloc(layout);
ptr.write_bytes(u8::MAX, size_in_bytes);
ptr
} else {
alloc_zeroed(layout)
}
.cast();
self._inl_or_ext._ext = ptr;
self._cap = size_in_bytes;
}
} else {
let size_in_digits = total_digits(min_new_capacity).get();
let size_in_bytes = size_in_digits * mem::size_of::<Digit>();
if size_in_bytes != self._cap {
unsafe {
let old_ptr = self._inl_or_ext._ext as *mut u8;
let old_size_in_bytes = self._cap;
let old_layout = Layout::from_size_align_unchecked(
old_size_in_bytes,
mem::align_of::<Digit>(),
);
let new_ptr: *mut Digit = realloc(old_ptr, old_layout, size_in_bytes).cast();
self._inl_or_ext._ext = new_ptr;
self._cap = size_in_bytes;
if size_in_bytes > old_size_in_bytes {
let start_ptr = (new_ptr as *mut u8).add(old_size_in_bytes);
if init {
start_ptr.write_bytes(u8::MAX, size_in_bytes - old_size_in_bytes);
} else {
start_ptr.write_bytes(0u8, size_in_bytes - old_size_in_bytes);
}
}
}
} }
}
pub fn reserve(&mut self, additional: usize) {
let new_cap = self
.capacity()
.get()
.checked_add(additional)
.expect("new capacity exceeds `usize::MAX`");
let old_digit = if self._cap == 0 {
Some(unsafe { self._inl_or_ext._inl })
} else {
None
};
unsafe {
self.internal_capacity_change(NonZeroUsize::new(new_cap).unwrap(), false);
}
if let Some(old_digit) = old_digit {
if self._cap != 0 {
unsafe {
let ptr = self._inl_or_ext._ext as *mut Digit;
ptr.write(old_digit);
}
}
}
}
pub fn shrink_to(&mut self, min_capacity: NonZeroUsize) {
let new_cap = max(self._nzbw, min_capacity);
let old_digit = self.to_digit();
let old_internal = self._cap == 0;
unsafe {
self.internal_capacity_change(new_cap, false);
}
if (!old_internal) && (self._cap == 0) {
self._inl_or_ext._inl = old_digit;
} else if old_internal && (self._cap != 0) {
unsafe {
let ptr = self._inl_or_ext._ext as *mut Digit;
ptr.write(old_digit);
}
}
}
pub fn shrink_to_fit(&mut self) {
let old_digit = if self._cap != 0 {
Some(unsafe {
let ptr = self._inl_or_ext._ext as *mut Digit;
ptr.read()
})
} else {
None
};
unsafe {
self.internal_capacity_change(self._nzbw, false);
}
if let Some(old_digit) = old_digit {
if self._cap == 0 {
self._inl_or_ext._inl = old_digit;
}
}
}
fn internal_resize(&mut self, new_nzbw: NonZeroUsize, init: bool) {
let old_capacity = self.capacity();
if new_nzbw <= old_capacity {
self._nzbw = new_nzbw;
} else if self._cap == 0 {
let old_digit = unsafe { self._inl_or_ext._inl };
let minimum = max(
old_capacity
.checked_next_power_of_two()
.expect("reallocation failure"),
new_nzbw,
);
unsafe {
self.internal_capacity_change(minimum, init);
self._nzbw = new_nzbw;
let ptr = self._inl_or_ext._ext as *mut Digit;
ptr.write(old_digit);
}
} else {
let minimum = max(
old_capacity
.checked_next_power_of_two()
.expect("reallocation failure"),
new_nzbw,
);
unsafe {
self.internal_capacity_change(minimum, init);
self._nzbw = new_nzbw;
}
}
}
pub fn resize(&mut self, new_bitwidth: NonZeroUsize, extension: bool) {
let original_bw = self.bw();
self.internal_resize(new_bitwidth, extension);
if new_bitwidth.get() > original_bw {
unsafe {
let original_extra = extra_u(original_bw);
let start = if original_extra != 0 {
if extension {
*self.get_unchecked_mut(digits_u(original_bw)) |= MAX << original_extra;
}
digits_u(original_bw).wrapping_add(1)
} else {
digits_u(original_bw)
};
let end = self.total_digits();
self.digit_set(extension, start..end, extension)
}
}
self.clear_unused_bits();
}
pub fn zero_resize(&mut self, new_bitwidth: NonZeroUsize) -> bool {
let overflow = if new_bitwidth.get() < self.bw() {
unsafe {
if (extra(new_bitwidth) != 0)
&& ((self.get_unchecked(digits(new_bitwidth)) >> extra(new_bitwidth)) != 0)
{
true
} else {
let mut overflow = false;
const_for!(i in {total_digits(new_bitwidth).get()..self.total_digits()} {
if self.get_unchecked(i) != 0 {
overflow = true;
break
}
});
overflow
}
}
} else {
false
};
self.resize(new_bitwidth, false);
overflow
}
pub fn sign_resize(&mut self, new_bitwidth: NonZeroUsize) -> bool {
let old_msb = self.msb();
let old_len = self.total_digits();
let old_extra = self.extra();
let new_len = total_digits(new_bitwidth).get();
let new_extra = extra(new_bitwidth);
let mut overflow = false;
if new_bitwidth.get() < self.bw() {
unsafe {
if old_msb {
if new_len == old_len {
if old_extra != 0 {
let expected = (MAX >> (BITS - old_extra)) & (MAX << new_extra);
if (self.last() & expected) != expected {
overflow = true;
}
} else {
let expected = MAX << new_extra;
if (self.last() & expected) != expected {
overflow = true;
}
}
self.resize(new_bitwidth, old_msb);
if !self.msb() {
overflow = true;
}
overflow
} else {
if new_extra != 0 {
let expected = MAX << new_extra;
if (self.get_unchecked(new_len - 1) & expected) != expected {
overflow = true;
}
}
if !overflow {
const_for!(i in {new_len..(old_len - 1)} {
if self.get_unchecked(i) != MAX {
overflow = true;
}
});
}
if old_extra != 0 {
let expected = MAX >> (BITS - old_extra);
if (self.last() & expected) != expected {
overflow = true;
}
} else if self.last() != MAX {
overflow = true;
}
self.resize(new_bitwidth, old_msb);
if !self.msb() {
overflow = true;
}
overflow
}
} else {
if (new_extra != 0) && ((self.get_unchecked(new_len - 1) >> new_extra) != 0) {
overflow = true;
} else {
const_for!(i in {new_len..old_len} {
if self.get_unchecked(i) != 0 {
overflow = true;
break
}
});
}
self.resize(new_bitwidth, old_msb);
if self.msb() {
overflow = true;
}
overflow
}
}
} else {
self.resize(new_bitwidth, old_msb);
false
}
}
#[doc(hidden)]
pub fn panicking_zero(w: usize) -> Self {
Self::zero(NonZeroUsize::new(w).unwrap())
}
#[doc(hidden)]
pub fn panicking_umax(w: usize) -> Self {
Self::umax(NonZeroUsize::new(w).unwrap())
}
#[doc(hidden)]
pub fn panicking_imax(w: usize) -> Self {
Self::imax(NonZeroUsize::new(w).unwrap())
}
#[doc(hidden)]
pub fn panicking_imin(w: usize) -> Self {
Self::imin(NonZeroUsize::new(w).unwrap())
}
#[doc(hidden)]
pub fn panicking_uone(w: usize) -> Self {
Self::uone(NonZeroUsize::new(w).unwrap())
}
}
impl Drop for Awi {
fn drop(&mut self) {
if let Some(layout) = self.layout() {
unsafe {
dealloc(self._inl_or_ext._ext as *mut u8, layout);
}
}
}
}
impl Clone for Awi {
fn clone(&self) -> Awi {
if self._cap == 0 {
unsafe {
Awi::inl_from_raw_parts(self._inl_or_ext._inl, self._nzbw)
}
} else if self._nzbw.get() <= BITS {
unsafe {
let digit = self.internal_as_ref().to_digit();
Awi::inl_from_raw_parts(digit, self._nzbw)
}
} else {
unsafe {
let size_in_digits = total_digits(self._nzbw).get();
let size_in_bytes = size_in_digits * mem::size_of::<Digit>();
let layout =
Layout::from_size_align_unchecked(size_in_bytes, mem::align_of::<Digit>());
let dst: *mut Digit = alloc(layout).cast();
ptr::copy_nonoverlapping(self._inl_or_ext._ext, dst, size_in_digits);
Awi::ext_from_raw_parts(dst, self._nzbw, size_in_bytes)
}
}
}
}
impl PartialEq for Awi {
fn eq(&self, rhs: &Self) -> bool {
self.as_ref() == rhs.as_ref()
}
}
impl Eq for Awi {}
#[cfg(feature = "zeroize_support")]
impl zeroize::Zeroize for Awi {
fn zeroize(&mut self) {
self.as_mut().zeroize()
}
}
macro_rules! impl_fmt {
($($ty:ident)*) => {
$(
impl fmt::$ty for Awi {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::$ty::fmt(self.as_ref(), f)
}
}
)*
};
}
impl_fmt!(Debug Display LowerHex UpperHex Octal Binary);
impl Hash for Awi {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
impl Deref for Awi {
type Target = Bits;
#[inline]
fn deref(&self) -> &Self::Target {
self.internal_as_ref()
}
}
impl DerefMut for Awi {
#[inline]
fn deref_mut(&mut self) -> &mut Bits {
self.internal_as_mut()
}
}
impl Index<RangeFull> for Awi {
type Output = Bits;
#[inline]
fn index(&self, _i: RangeFull) -> &Bits {
self
}
}
impl Borrow<Bits> for Awi {
#[inline]
fn borrow(&self) -> &Bits {
self
}
}
impl AsRef<Bits> for Awi {
#[inline]
fn as_ref(&self) -> &Bits {
self
}
}
impl IndexMut<RangeFull> for Awi {
#[inline]
fn index_mut(&mut self, _i: RangeFull) -> &mut Bits {
self
}
}
impl BorrowMut<Bits> for Awi {
#[inline]
fn borrow_mut(&mut self) -> &mut Bits {
self
}
}
impl AsMut<Bits> for Awi {
#[inline]
fn as_mut(&mut self) -> &mut Bits {
self
}
}
impl From<&Bits> for Awi {
fn from(bits: &Bits) -> Awi {
let mut tmp = Awi::zero(bits.nzbw());
tmp.const_as_mut().copy_(bits).unwrap();
tmp
}
}
impl<const BW: usize, const LEN: usize> From<InlAwi<BW, LEN>> for Awi {
fn from(awi: InlAwi<BW, LEN>) -> Awi {
let mut tmp = Awi::zero(awi.nzbw());
tmp.const_as_mut().copy_(&awi).unwrap();
tmp
}
}
macro_rules! awi_from_ty {
($($ty:ident $from:ident $assign:ident);*;) => {
$(
pub fn $from(x: $ty) -> Self {
let mut tmp = Awi::zero(bw($ty::BITS as usize));
tmp.$assign(x);
tmp
}
)*
};
}
impl Awi {
awi_from_ty!(
u8 from_u8 u8_;
u16 from_u16 u16_;
u32 from_u32 u32_;
u64 from_u64 u64_;
u128 from_u128 u128_;
usize from_usize usize_;
i8 from_i8 i8_;
i16 from_i16 i16_;
i32 from_i32 i32_;
i64 from_i64 i64_;
i128 from_i128 i128_;
isize from_isize isize_;
);
pub fn from_bool(x: bool) -> Self {
let mut tmp = Awi::zero(bw(1));
tmp.bool_(x);
tmp
}
pub fn from_digit(x: Digit) -> Self {
let mut tmp = Awi::zero(bw(BITS));
tmp.digit_(x);
tmp
}
}
impl From<bool> for Awi {
fn from(x: bool) -> Awi {
let mut tmp = Awi::zero(bw(1));
tmp.bool_(x);
tmp
}
}
macro_rules! awi_from {
($($ty:ident, $assign:ident);*;) => {
$(
impl From<$ty> for Awi {
fn from(x: $ty) -> Self {
let mut tmp = Awi::zero(bw($ty::BITS as usize));
tmp.$assign(x);
tmp
}
}
)*
};
}
awi_from!(
u8, u8_;
u16, u16_;
u32, u32_;
u64, u64_;
u128, u128_;
usize, usize_;
i8, i8_;
i16, i16_;
i32, i32_;
i64, i64_;
i128, i128_;
isize, isize_;
);