zlo 0.1.0

A binary serialization/deserialization strategy that uses Serde for transforming structs into very compact bit representations
Documentation
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate zlo;

use serde::Deserializer;
use serde::Serializer;

use zlo::Infinite;
use zlo::serialize;

use std::fmt;

// User input demo that shows how one could encode gamepad input.

// In this implementation axis is simply encoded as two i8s instead of two f32s.
// This incurs constant precision loss to 256 possible values per axis.  An
// alternative way would be to encode its length from 0 to 1, a boolean and just
// one axis.  This approach gives dynamic precision, at small lengths it's most
// precise and drops as length gets bigger.

#[derive(Serialize, Deserialize)]
pub struct TinyUserInput {
    view_angles: [f32; 3],
    #[serde(serialize_with = "serialize_axis", deserialize_with = "deserialize_axis")]
    stick_0: [f32; 2],
    #[serde(serialize_with = "serialize_axis", deserialize_with = "deserialize_axis")]
    stick_1: [f32; 2],
}

#[derive(Serialize, Deserialize)]
pub struct NotTinyUserInput {
    view_angles: [f32; 3],
    stick_0: [f32; 2],
    stick_1: [f32; 2],
}

fn f32_to_i8(x: f32) -> i8 {
    use std::i8::{MIN, MAX};
    assert!(x >= -1.0 && x <= 1.0, "x out of bounds");
    (x * (if x < 0.0 { MIN } else { MAX }) as f32) as i8
}

fn i8_to_f32(x: i8) -> f32 {
    use std::i8::{MIN, MAX};
    x as f32 * if x < 0 { 1.0 / MIN as f32 } else { 1.0 / MAX as f32 }
}

fn serialize_axis<S>(axis: &[f32; 2], serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer
{
    use serde::ser::SerializeSeq;

    let mut seq = serializer.serialize_seq(Some(2))?;
    seq.serialize_element(&f32_to_i8(axis[0]))?;
    seq.serialize_element(&f32_to_i8(axis[1]))?;
    seq.end()
}

fn deserialize_axis<'de, D>(deserializer: D) -> Result<[f32; 2], D::Error>
    where D: Deserializer<'de>
{
    use serde::de::Visitor;
    use serde::de::SeqAccess;
    use serde::de::Error;

    struct AxisVisitor;

    impl<'de> Visitor<'de> for AxisVisitor {
        type Value = [f32; 2];

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            write!(formatter, "an array of two ints")
        }

        fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
            where V: SeqAccess<'de>
        {
            let a: i8 = visitor.next_element()?.ok_or(V::Error::invalid_length(0, &"2"))?;
            let b: i8 = visitor.next_element()?.ok_or(V::Error::invalid_length(1, &"2"))?;

            Ok([i8_to_f32(a), i8_to_f32(b)])
        }
    }

    deserializer.deserialize_seq(AxisVisitor)
}

fn main() {
    let view_angles = [0.0; 3];
    let stick_0 = [1.0, 0.3];
    let stick_1 = [0.0, 0.9];

    let tiny     = TinyUserInput    { view_angles, stick_0, stick_1 };
    let not_tiny = NotTinyUserInput { view_angles, stick_0, stick_1 };

    let a = serialize(&tiny, Infinite).unwrap();
    let b = serialize(&not_tiny, Infinite).unwrap();

    assert_eq!(a.len(), 8);
    assert_eq!(b.len(), 13);
}