diff --git a/.gitignore b/.gitignore index 7ae52d1..7eca8a9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .vscode/ /target Cargo.lock +#winmerge +**/*.bak \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index c89d4b8..8fe5730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,21 @@ readme = "README.md" repository = "https://github.com/stencillogic/astro-float" [dependencies] -astro-float-num = { version = "0.3.6", default-features = false } -astro-float-macro = "0.4.5" +astro-float-num = {path = "astro-float-num",default-features = false } #{ version = "0.3.6", default-features = false } +astro-float-macro = {path = "astro-float-macro" } #"0.4.5" [features] default = ["std", "random", "serde"] std = ["astro-float-num/std"] random = ["astro-float-num/random"] serde = ["astro-float-num/serde"] +rkyv = ["astro-float-num/rkyv"] [dev-dependencies] +astro-float-num = { path = "astro-float-num",default-features = false , features = [ "rkyv" ] } trybuild = "1.0" rusty-hook = "0.11.2" +rkyv = "0.8" #couldn't run tests in astro-num on msvc(mpfr won't build) [workspace] members = [ diff --git a/astro-float-num/Cargo.toml b/astro-float-num/Cargo.toml index d31c677..6700043 100644 --- a/astro-float-num/Cargo.toml +++ b/astro-float-num/Cargo.toml @@ -23,9 +23,11 @@ serde = { version = "1.0.147", optional = true } rand = { version = "0.8.5", optional = true } lazy_static = { version = "1.4.0", default-features = false, features = [] } itertools = { version = "0.10.3", default-features = false, features = [] } +rkyv = { version = "0.8", features = [ "bytecheck" ] , optional = true } [features] default = ["std", "random", "serde"] std = [] random = ["dep:rand"] serde = ["dep:serde"] +rkyv = ["dep:rkyv"] \ No newline at end of file diff --git a/astro-float-num/src/common/buf.rs b/astro-float-num/src/common/buf.rs index 844d8e5..9459b8a 100644 --- a/astro-float-num/src/common/buf.rs +++ b/astro-float-num/src/common/buf.rs @@ -15,8 +15,12 @@ use crate::common::util::shift_slice_right; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Serialize, Deserialize}; + /// Buffer for holding mantissa gidits. #[derive(Debug, Hash)] +#[cfg_attr(feature = "rkyv", derive(Archive, Serialize, Deserialize))] pub struct WordBuf { inner: Vec, } diff --git a/astro-float-num/src/defs.rs b/astro-float-num/src/defs.rs index 022a91f..0a69250 100644 --- a/astro-float-num/src/defs.rs +++ b/astro-float-num/src/defs.rs @@ -8,6 +8,9 @@ use std::collections::TryReserveError; #[cfg(not(feature = "std"))] use alloc::collections::TryReserveError; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Serialize, Deserialize}; + /// A word. #[cfg(not(target_arch = "x86"))] pub type Word = u64; @@ -79,6 +82,87 @@ pub enum Sign { Pos = 1, } +#[cfg(feature = "rkyv")] +mod rkyv_impl { + use super::Sign; + use rkyv::{ + bytecheck::{CheckBytes, InvalidEnumDiscriminantError, Verify}, + primitive::ArchivedI16, + rancor::{fail, Fallible, Source}, + traits::NoUndef, + Archive, Deserialize, Place, Portable, Serialize, + }; + // Hand-written archived enum + #[derive(CheckBytes, Portable)] + #[bytecheck(crate = rkyv::bytecheck, verify)] + #[repr(C)] + pub struct ArchivedSign(ArchivedI16); + + // Implementation detail: `ArchivedMyEnum` has no undef bytes + unsafe impl NoUndef for ArchivedSign {} + + impl ArchivedSign { + // Internal fallible conversion back to the original enum + fn try_to_native(&self) -> Option { + Some(match self.0.to_native() { + -1 => Sign::Neg, + 1 => Sign::Pos, + _ => return None, + }) + } + + // Public infallible conversion back to the original enum + pub fn to_native(&self) -> Sign { + unsafe { self.try_to_native().unwrap_unchecked() } + } + } + + unsafe impl Verify for ArchivedSign + where + C::Error: Source, + { + // verify runs after all of the fields have been checked + fn verify(&self, _: &mut C) -> Result<(), C::Error> { + // Use the internal conversion to try to convert back + if self.try_to_native().is_none() { + // Return an error if it fails (i.e. the discriminant did not match + // any valid discriminants) + fail!(InvalidEnumDiscriminantError { + enum_name: "ArchivedSign", + invalid_discriminant: self.0.to_native(), + }) + } + Ok(()) + } + } + + impl Archive for Sign { + type Archived = ArchivedSign; + type Resolver = (); + + fn resolve(&self, _: Self::Resolver, out: Place) { + // Convert Sign -> i16 -> ArchivedI16 and write to `out` + out.write(ArchivedSign((*self as i16).into())); + } + } + + // Serialization is a no-op because there's no out-of-line data + impl Serialize for Sign { + fn serialize(&self, _: &mut S) -> Result::Error> { + Ok(()) + } + } + + // Deserialization just calls the public conversion and returns the result + impl Deserialize for ArchivedSign { + fn deserialize(&self, _: &mut D) -> Result::Error> { + Ok(self.to_native()) + } + } +} +#[cfg(feature = "rkyv")] +pub use rkyv_impl::*; + impl Sign { /// Changes the sign to the opposite. pub fn invert(&self) -> Self { @@ -106,6 +190,7 @@ impl Sign { /// Possible errors. #[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "rkyv", derive(Archive, Serialize, Deserialize))] pub enum Error { /// The exponent value becomes greater than the upper limit of the range of exponent values. ExponentOverflow(Sign), diff --git a/astro-float-num/src/ext.rs b/astro-float-num/src/ext.rs index 60822c9..2b367c1 100644 --- a/astro-float-num/src/ext.rs +++ b/astro-float-num/src/ext.rs @@ -13,6 +13,9 @@ use crate::Word; use core::num::FpCategory; use lazy_static::lazy_static; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Serialize, Deserialize}; + #[cfg(feature = "std")] use core::fmt::Write; @@ -45,11 +48,13 @@ lazy_static! { /// A floating point number of arbitrary precision. #[derive(Debug)] +#[cfg_attr(feature = "rkyv", derive(Archive, Serialize, Deserialize))] pub struct BigFloat { inner: Flavor, } #[derive(Debug)] +#[cfg_attr(feature = "rkyv", derive(Archive, Serialize, Deserialize))] enum Flavor { Value(BigFloatNumber), NaN(Option), diff --git a/astro-float-num/src/mantissa/mantissa.rs b/astro-float-num/src/mantissa/mantissa.rs index 352ea0d..158d693 100644 --- a/astro-float-num/src/mantissa/mantissa.rs +++ b/astro-float-num/src/mantissa/mantissa.rs @@ -22,8 +22,12 @@ use crate::mantissa::util::RightShiftedSlice; use core::mem::size_of; use itertools::izip; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Serialize, Deserialize}; + /// Mantissa representation. #[derive(Debug, Hash)] +#[cfg_attr(feature = "rkyv", derive(Archive, Serialize, Deserialize))] pub struct Mantissa { m: WordBuf, n: usize, // number of bits, 0 is for number 0 diff --git a/astro-float-num/src/num.rs b/astro-float-num/src/num.rs index f477fb9..5a1d8f0 100644 --- a/astro-float-num/src/num.rs +++ b/astro-float-num/src/num.rs @@ -15,8 +15,12 @@ use crate::defs::WORD_BIT_SIZE; use crate::defs::WORD_SIGNIFICANT_BIT; use crate::mantissa::Mantissa; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Serialize, Deserialize}; + /// A finite floating point number with mantissa of an arbitrary size, an exponent, and the sign. #[derive(Debug, Hash)] +#[cfg_attr(feature = "rkyv", derive(Archive, Serialize, Deserialize))] pub(crate) struct BigFloatNumber { e: Exponent, s: Sign, diff --git a/tests/mod.rs b/tests/mod.rs index a5df2b2..5e15ca7 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -1,5 +1,9 @@ // Additional tests of the library. +#[cfg(feature = "rkyv")] +#[cfg(test)] +mod rkyv; + use astro_float_macro::expr; use astro_float_num::{ ctx::Context, BigFloat, Consts, Radix, RoundingMode, Sign, EXPONENT_MAX, EXPONENT_MIN, diff --git a/tests/rkyv.rs b/tests/rkyv.rs new file mode 100644 index 0000000..8ad4636 --- /dev/null +++ b/tests/rkyv.rs @@ -0,0 +1,33 @@ + +#[cfg(test)] +mod tests { + use rkyv::{ + to_bytes, + deserialize, + rancor::Error, + access_unchecked, + Archive, + }; + + use astro_float_num::{ + BigFloat, + //ArchivedBigFloat + }; + + #[test] + fn to_from_rkyv() { + let zero = &BigFloat::new(0); + let bytes = to_bytes::(zero).unwrap(); + let archived = + unsafe { access_unchecked::<::Archived>(&bytes) }; + let bf : BigFloat = deserialize::(archived).unwrap(); + assert_eq!(bf,zero); + + let bf = BigFloat::from_f32(0.3, 64 + 1); + let bytes = to_bytes::(&bf).unwrap(); + let archived = + unsafe { access_unchecked::<::Archived>(&bytes) }; + let bf2 : BigFloat = deserialize::(archived).unwrap(); + assert_eq!(bf,bf2); + } +}