1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::Result;
use crate::serialize::{Serialize, SerializeInto, generic_serialize_into};

use crate::crypto::sexp::{Sexp, String_};

impl Serialize for Sexp {
    fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> {
        match self {
            Sexp::String(ref s) => s.serialize(o),
            Sexp::List(ref l) => {
                write!(o, "(")?;
                for sexp in l {
                    sexp.serialize(o)?;
                }
                write!(o, ")")?;
                Ok(())
            },
        }
    }
}

impl SerializeInto for Sexp {
    fn serialized_len(&self) -> usize {
        match self {
            Sexp::String(ref s) => s.serialized_len(),
            Sexp::List(ref l) =>
                2 + l.iter().map(|s| s.serialized_len()).sum::<usize>(),
        }
    }

    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
        generic_serialize_into(self, buf)
    }
}

impl Serialize for String_ {
    fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> {
        if let Some(display) = self.display_hint() {
            write!(o, "[{}:", display.len())?;
            o.write_all(display)?;
            write!(o, "]")?;
        }
        write!(o, "{}:", self.len())?;
        o.write_all(self)?;
        Ok(())
    }
}

/// Computes the length of the size tag for a given string length.
fn size_tag_len(len: usize) -> usize {
    // Compute log10(self.len()).
    let mut l = len;
    let mut digits = 0;
    while l > 0 {
        l /= 10;
        digits += 1;
    }

    std::cmp::max(1, digits) // 0 takes up 1 char, too.
}

impl SerializeInto for String_ {
    fn serialized_len(&self) -> usize {
        self.display_hint()
            .map(|d| size_tag_len(d.len()) + 3 + d.len()).unwrap_or(0)
            + size_tag_len(self.len()) + 1 + self.len()
    }

    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
        generic_serialize_into(self, buf)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sexp() {
        assert_eq!(
            &Sexp::List(vec![]).to_vec().unwrap(),
            b"()");
        assert_eq!(
            &Sexp::List(vec![Sexp::String(b"hi"[..].into()),
                            Sexp::String(b"ho"[..].into()),
            ]).to_vec().unwrap(),
            b"(2:hi2:ho)");
        assert_eq!(
            &Sexp::List(vec![
                Sexp::String(b"hi"[..].into()),
                Sexp::String(String_::with_display_hint(b"ho".to_vec(),
                                                        b"fancy".to_vec())),
            ]).to_vec().unwrap(),
            b"(2:hi[5:fancy]2:ho)");
        assert_eq!(
            &Sexp::List(vec![
                Sexp::String(b"hi"[..].into()),
                Sexp::List(vec![
                    Sexp::String(b"ha"[..].into()),
                    Sexp::String(b"ho"[..].into()),
                ]),
            ]).to_vec().unwrap(),
            b"(2:hi(2:ha2:ho))");
        assert_eq!(
            &Sexp::List(vec![
                Sexp::String(b"sig-val"[..].into()),
                Sexp::List(vec![
                    Sexp::String(b"rsa"[..].into()),
                    Sexp::List(vec![
                        Sexp::String(b"s"[..].into()),
                        Sexp::String(b"abc"[..].into()),
                    ]),
                ]),
            ]).to_vec().unwrap(),
            b"(7:sig-val(3:rsa(1:s3:abc)))");
    }

    #[test]
    fn string() {
        assert_eq!(&String_::new(b"hi".to_vec()).to_vec().unwrap(),
                   b"2:hi");
        assert_eq!(&String_::with_display_hint(b"hi".to_vec(),
                                               b"fancy".to_vec())
                   .to_vec().unwrap(),
                   b"[5:fancy]2:hi");
    }
}