feat: add serde

Adds serialization/deserialization support via serde and serde_with.

Also:

- remove `license-file` in Cargo.toml (superfluous)
- update README with Getting Started section
- add `serde` and `serde_with` dependencies to README
- add tests to `HeightVector`
- tweak assertion in `NetworkCoordinate`
- refactor `Vector` to be a struct witn an `inner` instead of a tuple struct so that we can use `serde_as` (from `serde_with`) to help us derive SerDe implementations for the const generic sized array
- moved trait requirements (which are getting long) into where clauses

Signed-off-by: Stephen Waits <steve@waits.net>
This commit is contained in:
swaits committed 2023-02-04 09:42:46 -07:00
1 parent 9265b5fa28
commit b3147dfad7
5 files changed
+213 -34

No files matched your search

+66 -1
View File
@@ -45,6 +45,7 @@
use std::ops::{Add, Mul, Sub};
use nanorand::{Rng, WyRand};
use serde::{Deserialize, Serialize};
use crate::vector::Vector;
@@ -61,7 +62,7 @@ use crate::vector::Vector;
/// ## Generic Parameters
///
/// - `N`: the dimensionality of the vector portion (i.e. non-height) of the Vivaldi height vector
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub(crate) struct HeightVector<const N: usize>(Vector<f32, N>, f32);
//
@@ -160,4 +161,68 @@ mod tests {
let a = HeightVector::<3>::from(([1.0, 2.0, 3.0], 4.0));
assert_approx_eq!(a.len(), 7.741_657, 0.00001);
}
#[test]
fn test_new() {
// new gives us a random unit length vec
let a = HeightVector::<3>::new();
assert_approx_eq!(a.len(), 1.0);
}
#[test]
fn test_default() {
// default() should give us a zero vec
let a = HeightVector::<3>::default();
assert_approx_eq!(a.len(), 0.0);
}
#[test]
fn test_from() {
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
assert_eq!(a.0[0], 1.0);
assert_eq!(a.0[1], 2.0);
assert_eq!(a.1, 3.0);
}
#[test]
fn test_add() {
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
let b = HeightVector::<2>::from(([3.0, 2.0], 1.0));
let c = a + b;
assert_eq!(c.0[0], 4.0);
assert_eq!(c.0[1], 4.0);
assert_eq!(c.1, 4.0);
}
#[test]
fn test_sub() {
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
let b = HeightVector::<2>::from(([3.0, 2.0], 1.0));
let c = a - b;
assert_eq!(c.0[0], -2.0);
assert_eq!(c.0[1], 0.0);
assert_eq!(c.1, 4.0);
}
#[test]
fn test_mul_scalar() {
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0)) * 10.0;
assert_eq!(a.0[0], 10.0);
assert_eq!(a.0[1], 20.0);
assert_eq!(a.1, 30.0);
}
#[test]
fn test_serde() {
// start with JSON, deserialize it
let s = "[{\"inner\":[1.0,2.0,3.0]},4.0]";
let a: HeightVector<3> = serde_json::from_str(s).unwrap();
// make sure it's the right length and works like we expect a normal NC
assert_approx_eq!(a.len(), 7.741_657, 0.001);
// serialize it into a new JSON string and make sure it matches the original
let t = serde_json::to_string(&a);
assert_eq!(t.as_ref().unwrap(), s);
}
}
+20 -2
View File
@@ -27,6 +27,8 @@
use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::height_vector::HeightVector;
//
@@ -47,7 +49,7 @@ const MIN_ERROR: f32 = f32::EPSILON;
// **** Structs ****
//
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct NetworkCoordinate<const N: usize> {
position: HeightVector<N>,
error: f32,
@@ -274,6 +276,22 @@ mod tests {
(slc.error.powf(2.0) + nyc.error.powf(2.0) + lax.error.powf(2.0) + mad.error.powf(2.0))
.sqrt();
println!("error = {error}");
assert!(error < 0.07);
assert!(error < 0.2);
}
#[test]
fn test_serde() {
// start with JSON, deserialize it
let s = "{\"position\":[{\"inner\":[1.5,0.5,2.0]},0.1],\"error\":1.0}";
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap();
// make sure it's the right length and works like we expect a normal NC
assert_approx_eq!(a.position.len(), 2.649_509, 0.001);
assert_eq!(a.error, 1.0);
assert_eq!(a.estimated_rtt(&a).as_millis(), 0);
// serialize it into a new JSON string and make sure it matches the original
let t = serde_json::to_string(&a);
assert_eq!(t.as_ref().unwrap(), s);
}
}
+60 -17
View File
@@ -4,6 +4,8 @@
//! Vivaldi `HeightVector`.
use num_traits::Float;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::ops::{Add, Div, Index, Mul, Sub};
@@ -16,14 +18,24 @@ use std::ops::{Add, Div, Index, Mul, Sub};
/// ## Generic parameters:
/// - `T`: the type to use, must satisfy the `Float` trait (i.e. `f32` or `f64`)
/// - `N`: the number of dimensions (i.e. 2 for 2D vectors, 3 for 3D, etc)
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct Vector<T: Float, const N: usize>([T; N]);
#[serde_as]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct Vector<T, const N: usize>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
#[serde_as(as = "[_; N]")]
inner: [T; N],
}
//
// **** Implementations ****
//
impl<T: Float, const N: usize> Vector<T, N> {
impl<T, const N: usize> Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
/// Create a new Zero vector.
pub(crate) fn new() -> Self {
Self::default()
@@ -32,8 +44,8 @@ impl<T: Float, const N: usize> Vector<T, N> {
/// Compute magnitude of vector.
pub(crate) fn len(&self) -> T {
(0..N)
.map(|i| self.0[i] * self.0[i])
.fold(T::zero(), |acc, x| acc + x)
.map(|i| self.inner[i] * self.inner[i])
.fold(T::zero(), |sum_sq, x| sum_sq + x)
.sqrt()
}
}
@@ -42,21 +54,32 @@ impl<T: Float, const N: usize> Vector<T, N> {
// **** Trait Implementations ****
//
impl<T: Float, const N: usize> Default for Vector<T, N> {
impl<T, const N: usize> Default for Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
/// A default vector is all zeroes
fn default() -> Self {
Self([T::zero(); N])
Self {
inner: [T::zero(); N],
}
}
}
impl<T: Float, const N: usize> From<[T; N]> for Vector<T, N> {
impl<T, const N: usize> From<[T; N]> for Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
/// Create a new Vector from an array of type T, N dimensions.
fn from(value: [T; N]) -> Self {
Self(value)
Self { inner: value }
}
}
impl<T: Float, const N: usize> Add for Vector<T, N> {
impl<T, const N: usize> Add for Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
type Output = Self;
/// Vector+Vector addition.
@@ -69,7 +92,10 @@ impl<T: Float, const N: usize> Add for Vector<T, N> {
}
}
impl<T: Float, const N: usize> Sub for Vector<T, N> {
impl<T, const N: usize> Sub for Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
type Output = Self;
/// Vector-Vector subtraction.
@@ -82,38 +108,47 @@ impl<T: Float, const N: usize> Sub for Vector<T, N> {
}
}
impl<T: Float, const N: usize> Mul<T> for Vector<T, N> {
impl<T, const N: usize> Mul<T> for Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
type Output = Self;
/// Vector*scalar multiplication.
fn mul(self, rhs: T) -> Self::Output {
let mut ret = Self::Output::new();
for i in 0..N {
ret.0[i] = self.0[i] * rhs
ret.inner[i] = self.inner[i] * rhs
}
ret
}
}
impl<T: Float, const N: usize> Div<T> for Vector<T, N> {
impl<T, const N: usize> Div<T> for Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
type Output = Self;
/// Vector/scalar division.
fn div(self, rhs: T) -> Self::Output {
let mut ret = Self::Output::new();
for i in 0..N {
ret.0[i] = self.0[i] / rhs
ret.inner[i] = self.inner[i] / rhs
}
ret
}
}
impl<T: Float, const N: usize> Index<usize> for Vector<T, N> {
impl<T, const N: usize> Index<usize> for Vector<T, N>
where
T: Float + Serialize + for<'d> Deserialize<'d>,
{
type Output = T;
/// Nicer indexing [] for read only references.
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
&self.inner[index]
}
}
@@ -183,4 +218,12 @@ mod tests {
let b = a * 10.0;
assert_approx_eq!(b.len(), 37.416_57, 0.0001);
}
#[test]
fn test_serde() {
let a = Vector::<f32, 3>::from([1.0, 2.0, 3.0]);
let s = serde_json::to_string(&a);
assert_eq!(s.as_ref().unwrap(), "{\"inner\":[1.0,2.0,3.0]}");
assert!(s.is_ok());
}
}