diff --git a/Cargo.toml b/Cargo.toml index c31d079..6206740 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,3 @@ edition = "2021" num = "0.4" byteorder = "1" libflate = "2" -ordered-float = "3" -thiserror = "1" \ No newline at end of file diff --git a/src/codec.rs b/src/codec.rs index 8205475..826c03b 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -11,46 +11,118 @@ use std::io::Write; use std::str; /// Errors which can occur when decoding a term -#[derive(Debug, thiserror::Error)] +#[derive(Debug)] pub enum DecodeError { - #[error("I/O error")] - Io(#[from] io::Error), + /// I/O error. + Io(io::Error), - #[error("the format version {version} is unsupported")] + /// Unsupported format version. UnsupportedVersion { version: u8 }, - #[error("unknown tag {tag}")] + /// Unknown tag. UnknownTag { tag: u8 }, - #[error("{value} is not a {expected}")] + /// Unexpected type. UnexpectedType { value: Term, expected: String }, - #[error("{value} is out of range {range:?}")] + /// Out of range. OutOfRange { value: i32, range: std::ops::Range, }, - #[error("tried to convert non-finite float")] + /// Non-finite float. NonFiniteFloat, } +impl std::fmt::Display for DecodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Io(error) => write!(f, "I/O error: {error}"), + Self::UnsupportedVersion { version } => { + write!(f, "the format version {version} is unsupported") + } + Self::UnknownTag { tag } => write!(f, "unknown tag {tag}"), + Self::UnexpectedType { value, expected } => write!(f, "{value} is not a {expected}"), + Self::OutOfRange { value, range } => write!(f, "{value} is out of range {range:?}"), + Self::NonFiniteFloat => write!(f, "tried to convert non-finite float"), + } + } +} + +impl std::error::Error for DecodeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let Self::Io(error) = self { + Some(error) + } else { + None + } + } +} + +impl From for DecodeError { + fn from(value: std::io::Error) -> Self { + Self::Io(value) + } +} + /// Errors which can occur when encoding a term -#[derive(Debug, thiserror::Error)] +#[derive(Debug)] pub enum EncodeError { - #[error("I/O error")] - Io(#[from] io::Error), + /// I/O error. + Io(io::Error), - #[error("too long atom name: {} bytes", .0.name.len())] + /// Too long atom name. TooLongAtomName(Atom), - #[error("too large integer value: {} bytes required to encode", .0.value.to_bytes_le().1.len())] + /// Too large integer value. TooLargeInteger(BigInteger), - #[error("too large reference ID: {} bytes required to encode", .0.id.len() * 4)] + /// Too large reference ID. TooLargeReferenceId(Reference), } +impl std::fmt::Display for EncodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Io(error) => write!(f, "I/O error: {error}"), + Self::TooLongAtomName(atom) => { + write!(f, "too long atom name: {} bytes", atom.name.len()) + } + Self::TooLargeInteger(integer) => { + write!( + f, + "too large integer value: {} bytes required to encode", + integer.value.to_bytes_le().1.len() + ) + } + Self::TooLargeReferenceId(reference) => { + write!( + f, + "too large reference ID: {} bytes required to encode", + reference.id.len() * 4 + ) + } + } + } +} + +impl std::error::Error for EncodeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let Self::Io(error) = self { + Some(error) + } else { + None + } + } +} + +impl From for EncodeError { + fn from(value: std::io::Error) -> Self { + Self::Io(value) + } +} + pub type DecodeResult = Result; pub type EncodeResult = Result<(), EncodeError>; diff --git a/src/lib.rs b/src/lib.rs index aec00e8..3e0b45e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -354,7 +354,7 @@ impl From<&FixInteger> for BigInteger { } /// Floating point number -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Float { /// The value of the number pub value: f64, @@ -388,15 +388,17 @@ impl TryFrom for Float { } } } -impl PartialEq for Float { - fn eq(&self, other: &Self) -> bool { - ordered_float::OrderedFloat(self.value) == ordered_float::OrderedFloat(other.value) - } -} impl Eq for Float {} impl std::hash::Hash for Float { fn hash(&self, state: &mut H) { - ordered_float::OrderedFloat(self.value).hash(state); + if self.value.is_nan() { + f64::NAN.to_bits().hash(state); + } else if self.value == 0.0 { + // Positive or negative zero + 0.0f64.to_bits().hash(state); + } else { + self.value.to_bits().hash(state); + } } }