diff --git a/.travis.yml b/.travis.yml index 83b26ad2..ac5dcd74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,9 +23,10 @@ script: - cargo build --verbose - cargo build --verbose --features arbitrary - cargo build --verbose --features serde-serialize - - cargo test --verbose --features "arbitrary debug serde-serialize" + - cargo build --verbose --features abomonation-serialize + - cargo test --verbose --features "arbitrary serde-serialize abomonation-serialize" - cd nalgebra-lapack; cargo test --verbose env: matrix: - - CARGO_FEATURE_SYSTEM_NETLIB=1 CARGO_FEATURE_EXCLUDE_LAPACKE=1 CARGO_FEATURE_EXCLUDE_CBLAS=1 + - CARGO_FEATURE_SYSTEM_NETLIB=1 CARGO_FEATURE_EXCLUDE_LAPACKE=1 CARGO_FEATURE_EXCLUDE_CBLAS=1 \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index bf8235b8..7551baae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ path = "src/lib.rs" [features] arbitrary = [ "quickcheck" ] serde-serialize = [ "serde", "serde_derive", "num-complex/serde" ] +abomonation-serialize = [ "abomonation" ] debug = [ ] [dependencies] @@ -31,6 +32,7 @@ alga = "0.5" matrixmultiply = "0.1" serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } +abomonation = { version = "0.4", optional = true } [dependencies.quickcheck] optional = true diff --git a/Makefile b/Makefile index ad1fafcc..5af4b5a7 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ all: cargo check --features "debug arbitrary serde-serialize" doc: - cargo doc --no-deps --features "debug arbitrary serde-serialize" + cargo doc --no-deps --features "debug arbitrary serde-serialize abomonation" bench: cargo bench test: - cargo test --features "debug arbitrary serde-serialize" + cargo test --features "debug arbitrary serde-serialize abomonation-serialize" diff --git a/src/core/matrix.rs b/src/core/matrix.rs index 984dfd3d..4b1f7deb 100644 --- a/src/core/matrix.rs +++ b/src/core/matrix.rs @@ -11,6 +11,9 @@ use approx::ApproxEq; #[cfg(feature = "serde-serialize")] use serde::{Serialize, Serializer, Deserialize, Deserializer}; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use alga::general::{Ring, Real}; use core::{Scalar, DefaultAllocator, Unit, VectorN, MatrixMN}; @@ -99,6 +102,21 @@ impl<'de, N, R, C, S> Deserialize<'de> for Matrix } } +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Matrix { + unsafe fn entomb(&self, writer: &mut Vec) { + self.data.entomb(writer) + } + + unsafe fn embalm(&mut self) { + self.data.embalm() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.data.exhume(bytes) + } +} + impl Matrix { /// Creates a new matrix with the given data without statically checking that the matrix /// dimension matches the storage dimension. diff --git a/src/core/matrix_array.rs b/src/core/matrix_array.rs index cccb91c0..5ebec52a 100644 --- a/src/core/matrix_array.rs +++ b/src/core/matrix_array.rs @@ -13,6 +13,9 @@ use std::mem; #[cfg(feature = "serde-serialize")] use std::marker::PhantomData; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use typenum::Prod; use generic_array::{ArrayLength, GenericArray}; @@ -319,3 +322,36 @@ where N: Scalar + Deserialize<'a>, } } } + +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for MatrixArray + where R: DimName, + C: DimName, + R::Value: Mul, + Prod: ArrayLength, + N: Abomonation +{ + unsafe fn entomb(&self, writer: &mut Vec) { + for element in self.data.as_slice() { + element.entomb(writer); + } + } + + unsafe fn embalm(&mut self) { + for element in self.data.as_mut_slice() { + element.embalm(); + } + } + + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + for element in self.data.as_mut_slice() { + let temp = bytes; + bytes = if let Some(remainder) = element.exhume(temp) { + remainder + } else { + return None; + } + } + Some(bytes) + } +} diff --git a/src/core/matrix_vec.rs b/src/core/matrix_vec.rs index bfaccf8d..260f79ce 100644 --- a/src/core/matrix_vec.rs +++ b/src/core/matrix_vec.rs @@ -6,6 +6,9 @@ use core::storage::{Storage, StorageMut, Owned, ContiguousStorage, ContiguousSto use core::allocator::Allocator; use core::default_allocator::DefaultAllocator; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + /* * * Storage. @@ -213,6 +216,21 @@ unsafe impl StorageMut for MatrixVec Abomonation for MatrixVec { + unsafe fn entomb(&self, writer: &mut Vec) { + self.data.entomb(writer) + } + + unsafe fn embalm(&mut self) { + self.data.embalm() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.data.exhume(bytes) + } +} + unsafe impl ContiguousStorage for MatrixVec where DefaultAllocator: Allocator { } diff --git a/src/core/unit.rs b/src/core/unit.rs index 6051165d..0d8035ef 100644 --- a/src/core/unit.rs +++ b/src/core/unit.rs @@ -5,6 +5,9 @@ use approx::ApproxEq; #[cfg(feature = "serde-serialize")] use serde::{Serialize, Serializer, Deserialize, Deserializer}; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use alga::general::SubsetOf; use alga::linear::NormedSpace; @@ -36,6 +39,21 @@ impl<'de, T: Deserialize<'de>> Deserialize<'de> for Unit { } } +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Unit { + unsafe fn entomb(&self, writer: &mut Vec) { + self.value.entomb(writer); + } + + unsafe fn embalm(&mut self) { + self.value.embalm(); + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.value.exhume(bytes) + } +} + impl Unit { /// Normalize the given value and return it wrapped on a `Unit` structure. #[inline] diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index fd60a16d..397aabfe 100644 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -6,6 +6,9 @@ use approx::ApproxEq; #[cfg(feature = "serde-serialize")] use serde; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use alga::general::{Real, SubsetOf}; use alga::linear::Rotation; @@ -42,6 +45,30 @@ pub struct Isometry _noconstruct: PhantomData } +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Isometry + where N: Real, + D: DimName, + R: Abomonation, + Translation: Abomonation, + DefaultAllocator: Allocator +{ + unsafe fn entomb(&self, writer: &mut Vec) { + self.rotation.entomb(writer); + self.translation.entomb(writer); + } + + unsafe fn embalm(&mut self) { + self.rotation.embalm(); + self.translation.embalm(); + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.rotation.exhume(bytes) + .and_then(|bytes| self.translation.exhume(bytes)) + } +} + impl hash::Hash for Isometry where DefaultAllocator: Allocator, Owned: hash::Hash { @@ -66,6 +93,7 @@ impl> + Clone> Clone for Isometry>> Isometry where DefaultAllocator: Allocator { + /// Creates a new isometry from its rotational and translational parts. #[inline] pub fn from_parts(translation: Translation, rotation: R) -> Isometry { diff --git a/src/geometry/point.rs b/src/geometry/point.rs index 8b7aea7c..9fe9b1a5 100644 --- a/src/geometry/point.rs +++ b/src/geometry/point.rs @@ -7,6 +7,9 @@ use approx::ApproxEq; #[cfg(feature = "serde-serialize")] use serde; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use core::{DefaultAllocator, Scalar, VectorN}; use core::iter::{MatrixIter, MatrixIterMut}; use core::dimension::{DimName, DimNameSum, DimNameAdd, U1}; @@ -66,6 +69,27 @@ where DefaultAllocator: Allocator, } } + +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Point + where N: Scalar, + D: DimName, + VectorN: Abomonation, + DefaultAllocator: Allocator +{ + unsafe fn entomb(&self, writer: &mut Vec) { + self.coords.entomb(writer) + } + + unsafe fn embalm(&mut self) { + self.coords.embalm() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.coords.exhume(bytes) + } +} + impl Point where DefaultAllocator: Allocator { diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index 2d9abad0..6a59329b 100644 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -8,6 +8,9 @@ use serde; #[cfg(feature = "serde-serialize")] use core::storage::Owned; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use alga::general::Real; use core::{Unit, Vector3, Vector4, MatrixSlice, MatrixSliceMut, SquareMatrix, MatrixN}; @@ -25,9 +28,25 @@ pub struct Quaternion { pub coords: Vector4 } -impl Eq for Quaternion { +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Quaternion + where Vector4: Abomonation +{ + unsafe fn entomb(&self, writer: &mut Vec) { + self.coords.entomb(writer) + } + + unsafe fn embalm(&mut self) { + self.coords.embalm() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.coords.exhume(bytes) + } } +impl Eq for Quaternion { } + impl PartialEq for Quaternion { fn eq(&self, rhs: &Self) -> bool { self.coords == rhs.coords || diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs index c04e1eb1..3a598cca 100644 --- a/src/geometry/rotation.rs +++ b/src/geometry/rotation.rs @@ -9,6 +9,9 @@ use serde; #[cfg(feature = "serde-serialize")] use core::storage::Owned; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use alga::general::Real; use core::{DefaultAllocator, Scalar, MatrixN}; @@ -45,6 +48,26 @@ impl Clone for Rotation } } +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Rotation + where N: Scalar, + D: DimName, + MatrixN: Abomonation, + DefaultAllocator: Allocator +{ + unsafe fn entomb(&self, writer: &mut Vec) { + self.matrix.entomb(writer) + } + + unsafe fn embalm(&mut self) { + self.matrix.embalm() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.matrix.exhume(bytes) + } +} + #[cfg(feature = "serde-serialize")] impl serde::Serialize for Rotation where DefaultAllocator: Allocator, diff --git a/src/geometry/similarity.rs b/src/geometry/similarity.rs index 00d3220a..cc146918 100644 --- a/src/geometry/similarity.rs +++ b/src/geometry/similarity.rs @@ -5,6 +5,9 @@ use approx::ApproxEq; #[cfg(feature = "serde-serialize")] use serde; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use alga::general::{Real, SubsetOf}; use alga::linear::Rotation; @@ -15,6 +18,7 @@ use core::allocator::Allocator; use geometry::{Point, Translation, Isometry}; + /// A similarity, i.e., an uniform scaling, followed by a rotation, followed by a translation. #[repr(C)] #[derive(Debug)] @@ -38,6 +42,24 @@ pub struct Similarity scaling: N } +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Similarity + where Isometry: Abomonation, + DefaultAllocator: Allocator +{ + unsafe fn entomb(&self, writer: &mut Vec) { + self.isometry.entomb(writer) + } + + unsafe fn embalm(&mut self) { + self.isometry.embalm() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.isometry.exhume(bytes) + } +} + impl hash::Hash for Similarity where DefaultAllocator: Allocator, Owned: hash::Hash { diff --git a/src/geometry/translation.rs b/src/geometry/translation.rs index 3b8261aa..385e00da 100644 --- a/src/geometry/translation.rs +++ b/src/geometry/translation.rs @@ -6,6 +6,9 @@ use approx::ApproxEq; #[cfg(feature = "serde-serialize")] use serde; +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + use alga::general::{Real, ClosedNeg}; use core::{DefaultAllocator, Scalar, MatrixN, VectorN}; @@ -44,6 +47,26 @@ impl Clone for Translation } } +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Translation + where N: Scalar, + D: DimName, + VectorN: Abomonation, + DefaultAllocator: Allocator +{ + unsafe fn entomb(&self, writer: &mut Vec) { + self.vector.entomb(writer) + } + + unsafe fn embalm(&mut self) { + self.vector.embalm() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.vector.exhume(bytes) + } +} + #[cfg(feature = "serde-serialize")] impl serde::Serialize for Translation where DefaultAllocator: Allocator, diff --git a/src/lib.rs b/src/lib.rs index 095c21f1..b5e5e3b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,11 +90,16 @@ an optimized set of tools for computer graphics and physics. Those features incl #[cfg(feature = "arbitrary")] extern crate quickcheck; + #[cfg(feature = "serde")] extern crate serde; #[cfg(feature = "serde")] #[macro_use] extern crate serde_derive; + +#[cfg(feature = "abomonation")] +extern crate abomonation; + extern crate num_traits as num; extern crate num_complex; extern crate rand; diff --git a/tests/core/abomonation.rs b/tests/core/abomonation.rs new file mode 100644 index 00000000..ac53716b --- /dev/null +++ b/tests/core/abomonation.rs @@ -0,0 +1,51 @@ +use rand::random; +use abomonation::{Abomonation, encode, decode}; +use na::{ + DMatrix, Matrix3x4, Point3, Translation3, Rotation3, Isometry3, Quaternion, + IsometryMatrix3, Similarity3, SimilarityMatrix3 +}; + +#[test] +fn abomonate_dmatrix() { + assert_encode_and_decode(DMatrix::::new_random(3, 5)); +} + +macro_rules! test_abomonation( + ($($test: ident, $ty: ty);* $(;)*) => {$( + #[test] + fn $test() { + assert_encode_and_decode(random::<$ty>()); + } + )*} +); + +test_abomonation! { + abomonate_matrix3x4, Matrix3x4; + abomonate_point3, Point3; + abomonate_translation3, Translation3; + abomonate_rotation3, Rotation3; + abomonate_isometry3, Isometry3; + abomonate_isometry_matrix3, IsometryMatrix3; + abomonate_similarity3, Similarity3; + abomonate_similarity_matrix3, SimilarityMatrix3; + abomonate_quaternion, Quaternion; +} + +fn assert_encode_and_decode(original_data: T) { + use std::mem::drop; + + // Hold on to a clone for later comparison + let data = original_data.clone(); + + // Encode + let mut bytes = Vec::new(); + unsafe { encode(&original_data, &mut bytes); } + + // Drop the original, so that dangling pointers are revealed by the test + drop(original_data); + + if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { + assert!(result == &data); + assert!(rest.len() == 0, "binary data was not decoded completely"); + } +} diff --git a/tests/core/mod.rs b/tests/core/mod.rs index aa73d361..71d09757 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -4,3 +4,4 @@ mod matrix; mod matrix_slice; mod blas; mod serde; +mod abomonation; diff --git a/tests/geometry/quaternion.rs b/tests/geometry/quaternion.rs index eb3a42f2..3000fa9c 100644 --- a/tests/geometry/quaternion.rs +++ b/tests/geometry/quaternion.rs @@ -83,7 +83,7 @@ quickcheck!( /* * - * Quaterion * Vector == RotationBase * Vector + * Quaterion * Vector == Rotation * Vector * */ fn unit_quaternion_mul_vector(q: UnitQuaternion, v: Vector3, p: Point3) -> bool { diff --git a/tests/geometry/rotation.rs b/tests/geometry/rotation.rs index d5fa782c..263577ce 100644 --- a/tests/geometry/rotation.rs +++ b/tests/geometry/rotation.rs @@ -79,7 +79,7 @@ quickcheck!( /* * - * RotationBase matrix between vectors. + * Rotation matrix between vectors. * */ fn rotation_between_is_anticommutative_2(a: Vector2, b: Vector2) -> bool { @@ -132,7 +132,7 @@ quickcheck!( /* * - * RotationBase construction. + * Rotation construction. * */ fn new_rotation_2(angle: f64) -> bool { @@ -159,7 +159,7 @@ quickcheck!( /* * - * RotationBase pow. + * Rotation pow. * */ fn powf_rotation_2(angle: f64, pow: f64) -> bool { diff --git a/tests/geometry/unit_complex.rs b/tests/geometry/unit_complex.rs index ff8d4a66..a5147690 100644 --- a/tests/geometry/unit_complex.rs +++ b/tests/geometry/unit_complex.rs @@ -60,7 +60,7 @@ quickcheck!( /* * - * Quaterion * Vector == RotationBase * Vector + * Quaterion * Vector == Rotation * Vector * */ fn unit_complex_mul_vector(c: UnitComplex, v: Vector2, p: Point2) -> bool { diff --git a/tests/lib.rs b/tests/lib.rs index 5e111eb0..9a1174e7 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -5,6 +5,7 @@ extern crate quickcheck; extern crate approx; extern crate num_traits as num; extern crate serde_json; +extern crate abomonation; extern crate rand; extern crate alga; extern crate nalgebra as na;