diff --git a/.build.yml b/.build.yml index c9ec2e5..65cd319 100644 --- a/.build.yml +++ b/.build.yml @@ -3,11 +3,14 @@ packages: - rustup sources: - https://git.sr.ht/~swaits/vivaldi-nc +artifacts: + - vivaldi-nc/tarpaulin-report.html tasks: - setup: | rustup toolchain install nightly stable cd vivaldi-nc/ rustup run stable cargo fetch + cargo install cargo-tarpaulin -f - format: | rustup default stable cd vivaldi-nc/ @@ -16,31 +19,30 @@ tasks: rustup default stable cd vivaldi-nc/ cargo clippy --verbose + - coverage: | + rustup default stable + cd vivaldi-nc/ + cargo tarpaulin --verbose --all-features --out Html - stable: | rustup default stable cd vivaldi-nc/ cargo build --verbose - cargo test --verbose --no-default-features --features f32 - cargo test --verbose --no-default-features --features f64 + cargo test --verbose --all-features - nightly: | rustup default nightly cd vivaldi-nc/ cargo clippy --verbose ||: cargo build --verbose ||: - cargo test --verbose --no-default-features --features f32 ||: - cargo test --verbose --no-default-features --features f64 ||: + cargo test --verbose --all-features ||: - bench: | rustup default stable cd vivaldi-nc/ - cargo bench --verbose --no-default-features --features f32 - cargo bench --verbose --no-default-features --features f64 + cargo bench --verbose --all-features - examples: | rustup default stable cd vivaldi-nc/ - cargo run --verbose --no-default-features --features f32 --example planetlab - cargo run --verbose --no-default-features --features f64 --example planetlab - cargo run --verbose --no-default-features --features f32 --example planetlab --release - cargo run --verbose --no-default-features --features f64 --example planetlab --release + cargo run --verbose --all-features --example planetlab + cargo run --verbose --all-features --example planetlab --release - docs: | cd vivaldi-nc/ rustup run stable cargo doc --no-deps diff --git a/.gitignore b/.gitignore index 09766cd..2b28fc4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /flamegraph.svg /target Cargo.lock +/tarpaulin-report.html diff --git a/Cargo.toml b/Cargo.toml index ac784f6..5fbd7f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,8 @@ exclude = [ ] [features] -default = ["f32"] +default = [] f32 = [] -f64 = [] [profile.bench] debug = true diff --git a/README.md b/README.md index d0c5dc7..1eb3d3e 100644 --- a/README.md +++ b/README.md @@ -112,11 +112,13 @@ usage examples. ### Cargo Features -By default, the internal data structures and operations all use `f32`. If you want to use `f64`, you can enable it as a cargo feature: +By default, the internal data structures and operations all use `f64`, which is +slightly faster on modern architectures. If you want to use `f32` instead, you +can enable it as a cargo feature: ```toml [dependencies] -vivaldi-nc = { version = "0.5.2", features = ["f64"] } +vivaldi-nc = { version = "(version)", features = ["f32"] } ``` ### Examples diff --git a/examples/planetlab.rs b/examples/planetlab.rs index a594020..1b8ed14 100644 --- a/examples/planetlab.rs +++ b/examples/planetlab.rs @@ -11,7 +11,7 @@ type FloatType = f32; /// `FloatType` is a type alias for either `f32` or `f64` depending on cargo features -#[cfg(feature = "f64")] +#[cfg(not(feature = "f32"))] type FloatType = f64; use std::{ diff --git a/src/height_vector.rs b/src/height_vector.rs index d1a4cad..1e40f96 100644 --- a/src/height_vector.rs +++ b/src/height_vector.rs @@ -58,7 +58,7 @@ use crate::vector::{self, Vector}; type FloatType = f32; /// `FloatType` is a type alias for either `f32` or `f64` depending on cargo features -#[cfg(feature = "f64")] +#[cfg(not(feature = "f32"))] type FloatType = f64; // @@ -122,15 +122,16 @@ impl HeightVector { /// by the inveerse of its length. pub(crate) fn normalized(&self) -> Self { let len = self.len(); - if len < FloatType::EPSILON { + // scale it by the inverse of its magnitude like any normal vector + let ret = Self { + position: self.position / len, + height: self.height / len, + }; + if ret.is_valid() { + ret + } else { // if we have a bad vector, generate a new random vector Self::new() - } else { - // otherwise scale it by the inverse of its magnitude like any normal vector - Self { - position: self.position / len, - height: self.height / len, - } } } @@ -377,4 +378,36 @@ mod tests { let t = serde_json::to_string(&a); assert_eq!(t.as_ref().unwrap(), s); } + + #[test] + fn test_sub_invalid() { + let valid = HeightVector::<2>::from(([1.0, 2.0], 3.0)); + let mut invalid = HeightVector::<2>::from(([1.0, 2.0], 3.0)); + // force invalidity here - because `::from()` actually also catches this + invalid.height = 1.0 / 0.0; + let result = valid - invalid; + assert_approx_eq!(result.len(), 1.0); + } + + #[test] + fn test_add_invalid() { + let valid = HeightVector::<2>::from(([1.0, 2.0], 3.0)); + let mut invalid = HeightVector::<2>::from(([1.0 / 0.0, 2.0], 3.0)); + + // make sure `from()` caught and replaced the invalid vec + assert_approx_eq!(invalid.len(), 1.0); + + // force invalidity here - because `::from()` actually also catches this + invalid.height = 1.0 / 0.0; + let result = valid + invalid; + assert_approx_eq!(result.len(), 1.0); + } + + #[test] + fn test_zero_norm() { + let a = HeightVector::<2>::from(([0.0, 0.0], 0.0)); + let b = a.normalized(); + let len = b.len(); + assert_approx_eq!(len, 1.0); + } } diff --git a/src/lib.rs b/src/lib.rs index bdbd99c..14e1c9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,10 +108,6 @@ mod height_vector; mod vector; -// make sure we don't have conflicting features -#[cfg(all(feature = "f32", feature = "f64"))] -compile_error!("feature \"f32\" and feature \"f64\" cannot be enabled at the same time"); - // publish our interface pub mod network_coordinate; pub use network_coordinate::NetworkCoordinate; diff --git a/src/network_coordinate.rs b/src/network_coordinate.rs index 8c899a5..6b747e3 100644 --- a/src/network_coordinate.rs +++ b/src/network_coordinate.rs @@ -14,7 +14,7 @@ use crate::height_vector::HeightVector; #[cfg(feature = "f32")] type FloatType = f32; -#[cfg(feature = "f64")] +#[cfg(not(feature = "f32"))] type FloatType = f64; // @@ -127,7 +127,7 @@ impl NetworkCoordinate { #[cfg(feature = "f32")] return Duration::from_secs_f32((self.heightvec - rhs.heightvec).len() / 1000.0); - #[cfg(feature = "f64")] + #[cfg(not(feature = "f32"))] return Duration::from_secs_f64((self.heightvec - rhs.heightvec).len() / 1000.0); } @@ -195,14 +195,17 @@ impl NetworkCoordinate { #[cfg(feature = "f32")] let rtt_estimated_ms = self.estimated_rtt(rhs).as_secs_f32() * 1000.0; - #[cfg(feature = "f64")] + #[cfg(not(feature = "f32"))] let rtt_ms = rtt.as_secs_f64() * 1000.0; - #[cfg(feature = "f64")] + #[cfg(not(feature = "f32"))] let rtt_estimated_ms = self.estimated_rtt(rhs).as_secs_f64() * 1000.0; // rtt needs to be positive if rtt_ms < 0.0 { - return self; + // Note: `rtt` is guaranteed to be positive because `Duration` enforces it. + // If this panics, something changed where `Duration` now allows for negative + // values. + unreachable!(); } // Sample weight balances local and remote error. (1) @@ -233,6 +236,10 @@ impl NetworkCoordinate { // error if self.heightvec.is_invalid() { *self = Self::new(); + + // We should never get here because the call to `normalized()` above should catch any + // invalid `heightvec` + unreachable!(); } // return reference to updated self @@ -376,4 +383,11 @@ mod tests { let estimate = a.estimated_rtt(&b); assert_approx_eq!(estimate.as_secs_f32(), 0.080_099); } + + #[test] + fn test_error_getter() { + let s = "{\"position\":[1.5,0.5,2.0],\"height\":25.0,\"error\":1.0}"; + let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap(); + assert_approx_eq!(a.error(), 1.0); + } }