use std::alloc::{Layout, LayoutError};
use std::cmp::Ordering;
use std::fmt::{self, Formatter};
use std::hash::Hasher;
use std::ptr::copy_nonoverlapping;
use crate::alloc::{alloc_infallible, dealloc_infallible};
use crate::number::INumber;
use crate::thin::{ThinMut, ThinMutExt, ThinRef, ThinRefExt};
use super::{
number_cmp, Destructured, DestructuredMut, DestructuredRef, IValue, NumVal, ReprTag, ValueRepr,
ValueType,
};
#[repr(C)]
#[repr(align(8))]
struct Header {
exp: i32,
meta: u32,
}
const NEGATIVE: u32 = 1 << 31;
const DECIMAL_POINT: u32 = 1 << 30;
const LEN_MASK: u32 = DECIMAL_POINT - 1;
const MAX_LIMBS: usize = LEN_MASK as usize;
trait HeaderRef<'a>: ThinRefExt<'a, Header> {
fn len(&self) -> usize {
(self.meta & LEN_MASK) as usize
}
fn negative(&self) -> bool {
self.meta & NEGATIVE != 0
}
fn has_decimal_point(&self) -> bool {
self.meta & DECIMAL_POINT != 0
}
fn limbs_ptr(&self) -> *const u64 {
unsafe { self.ptr().add(1).cast::<u64>() }
}
fn limbs(&self) -> &'a [u64] {
unsafe { std::slice::from_raw_parts(self.limbs_ptr(), self.len()) }
}
}
trait HeaderMut<'a>: ThinMutExt<'a, Header> {
fn limbs_ptr_mut(mut self) -> *mut u64 {
unsafe { self.ptr_mut().add(1).cast::<u64>() }
}
}
impl<'a, T: ThinRefExt<'a, Header>> HeaderRef<'a> for T {}
impl<'a, T: ThinMutExt<'a, Header>> HeaderMut<'a> for T {}
pub(crate) struct DecimalRepr;
impl DecimalRepr {
fn layout(len: usize) -> Result<Layout, LayoutError> {
Ok(Layout::new::<Header>()
.extend(Layout::array::<u64>(len)?)?
.0
.pad_to_align())
}
pub(crate) fn store(
negative: bool,
magnitude: &[u64],
exp: i32,
has_decimal_point: bool,
) -> IValue {
assert!(magnitude.len() <= MAX_LIMBS, "decimal mantissa too large");
debug_assert!(
!super::bigint::is_zero(magnitude)
&& super::bigint::rem_small(magnitude, super::bigint::TEN) != 0,
"a stored decimal's magnitude is canonical"
);
let meta = magnitude.len() as u32
| if negative { NEGATIVE } else { 0 }
| if has_decimal_point { DECIMAL_POINT } else { 0 };
unsafe {
let ptr = alloc_infallible(Self::layout(magnitude.len()).unwrap()).cast::<Header>();
ptr.write(Header { exp, meta });
let hd = ThinMut::new(ptr);
copy_nonoverlapping(magnitude.as_ptr(), hd.limbs_ptr_mut(), magnitude.len());
IValue::new_ptr(ReprTag::NumberDecimal, ptr.cast())
}
}
unsafe fn header(v: &IValue) -> ThinRef<'_, Header> {
ThinRef::new(v.ptr().cast())
}
unsafe fn num_val(v: &IValue) -> NumVal<'_> {
let hd = Self::header(v);
NumVal::from_big(hd.negative(), hd.limbs(), hd.exp)
}
}
impl ValueRepr for DecimalRepr {
fn value_type(&self, _v: &IValue) -> ValueType {
ValueType::Number
}
unsafe fn clone(&self, v: &IValue) -> IValue {
let hd = Self::header(v);
Self::store(hd.negative(), hd.limbs(), hd.exp, hd.has_decimal_point())
}
unsafe fn drop(&self, v: &mut IValue) {
let layout = Self::layout(Self::header(v).len()).unwrap();
dealloc_infallible(v.ptr(), layout);
}
unsafe fn hash(&self, v: &IValue, state: &mut dyn Hasher) {
Self::num_val(v).hash(state);
}
unsafe fn eq(&self, a: &IValue, b: &IValue) -> bool {
number_cmp(Self::num_val(a), b) == Some(Ordering::Equal)
}
unsafe fn partial_cmp(&self, a: &IValue, b: &IValue) -> Option<Ordering> {
number_cmp(Self::num_val(a), b)
}
unsafe fn debug(&self, v: &IValue, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", Self::num_val(v))
}
fn destructure(&self, v: IValue) -> Destructured {
Destructured::Number(INumber(v))
}
unsafe fn destructure_ref<'a>(&self, v: &'a IValue) -> DestructuredRef<'a> {
DestructuredRef::Number(v.as_number_unchecked())
}
unsafe fn destructure_mut<'a>(&self, v: &'a mut IValue) -> DestructuredMut<'a> {
DestructuredMut::Number(v.as_number_unchecked_mut())
}
unsafe fn num_val<'a>(&self, v: &'a IValue) -> Option<NumVal<'a>> {
Some(Self::num_val(v))
}
fn has_decimal_point(&self, v: &IValue) -> bool {
unsafe { Self::header(v) }.has_decimal_point()
}
}