use crate::GenIndex;
use core::{
cmp::Ordering,
hash::{Hash, Hasher},
};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct IndexF64(f64);
const MAX_SAFE_GENERATION: u32 = (1 << 21) - 1;
const MAX_SAFE_VALUE: u64 = (1 << 53) - 1;
impl GenIndex for IndexF64 {
type Index = u32;
type Generation = u32;
#[inline]
fn max_generation() -> Self::Generation {
MAX_SAFE_GENERATION
}
#[inline]
fn from_raw_parts(index: Self::Index, generation: Self::Generation) -> Self {
Self(index as f64 + (((generation & MAX_SAFE_GENERATION) as u64) << 32) as f64)
}
#[inline]
fn index(&self) -> Self::Index {
(self.0 as u64 & (u32::MAX as u64)) as u32
}
#[inline]
fn generation(&self) -> Self::Generation {
((self.0 as u64) >> 32) as u32
}
}
impl Hash for IndexF64 {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0 as u64).hash(state);
}
}
impl From<IndexF64> for (u32, u32) {
#[inline]
fn from(idx: IndexF64) -> Self {
(idx.index(), idx.generation())
}
}
impl From<(u32, u32)> for IndexF64 {
#[inline]
fn from((index, generation): (u32, u32)) -> Self {
IndexF64::from_raw_parts(index, generation)
}
}
impl From<IndexF64> for f64 {
#[inline]
fn from(idx: IndexF64) -> Self {
idx.0
}
}
impl From<f64> for IndexF64 {
#[inline]
fn from(value: f64) -> Self {
let value = (value as u64) & MAX_SAFE_VALUE;
IndexF64(value as f64)
}
}
impl PartialOrd for IndexF64 {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.0 == other.0 {
Some(Ordering::Equal)
} else {
match self.index().cmp(&other.index()) {
Ordering::Equal => None,
ordering => Some(ordering),
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{GenIndex, IndexF64};
#[test]
fn test_create() {
let index: IndexF64 = IndexF64::from_raw_parts(0, 0);
assert_eq!(index, IndexF64::default());
let index: IndexF64 = (2, 3).into();
assert_eq!((index.index(), index.generation()), index.into());
assert_eq!(((3u64 << 32) | 2) as f64, Into::<f64>::into(index));
assert_eq!(Into::<IndexF64>::into(((3u64 << 32) | 2) as f64 + 0.5), index);
}
#[test]
fn test_cmp() {
assert!(IndexF64::from_raw_parts(1, 1) < IndexF64::from_raw_parts(2, 1));
assert!(IndexF64::from_raw_parts(1, 3) < IndexF64::from_raw_parts(2, 1));
assert_eq!(IndexF64::from_raw_parts(1, 3), IndexF64::from_raw_parts(1, 3));
assert_ne!(IndexF64::from_raw_parts(1, 3), IndexF64::from_raw_parts(1, 2));
assert!(!(IndexF64::from_raw_parts(1, 3) < IndexF64::from_raw_parts(1, 4)));
assert!(!(IndexF64::from_raw_parts(1, 4) < IndexF64::from_raw_parts(1, 3)));
}
#[cfg(feature = "serde")]
#[test]
fn test_deserialize() {
use serde_json::{json, Value};
let expected_index = IndexF64::from_raw_parts(123, 456);
let json: Value = json!((456u64 << 32 | 123) as f64);
let index: IndexF64 = serde_json::from_value(json).unwrap();
assert_eq!(index, expected_index);
}
#[cfg(feature = "serde")]
#[test]
fn test_serialize() {
use crate::{GenIndex, IndexF64};
use serde_json::{json, Value};
let index = IndexF64::from_raw_parts(123, 456);
let expected_json: Value = json!((456u64 << 32 | 123) as f64);
let json: Value = serde_json::to_value(index).unwrap();
assert_eq!(json, expected_json);
}
}