New concise encoding
This commit is contained in:
parent
abc27adea4
commit
52f6bd177c
3 changed files with 88 additions and 67 deletions
|
@ -2,7 +2,7 @@
|
||||||
name = "nettext"
|
name = "nettext"
|
||||||
description = "A text-based data format for cryptographic network protocols"
|
description = "A text-based data format for cryptographic network protocols"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -262,6 +262,13 @@ impl<'a> Term<'a> {
|
||||||
pub fn encode_string(self) -> String {
|
pub fn encode_string(self) -> String {
|
||||||
unsafe { String::from_utf8_unchecked(self.encode()) }
|
unsafe { String::from_utf8_unchecked(self.encode()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate the concise nettext representation of a term
|
||||||
|
pub fn encode_concise(self) -> Vec<u8> {
|
||||||
|
let mut buf = Vec::with_capacity(128);
|
||||||
|
self.0.encode_concise_aux(&mut buf);
|
||||||
|
buf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> T<'a> {
|
impl<'a> T<'a> {
|
||||||
|
@ -339,6 +346,46 @@ impl<'a> T<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encode_concise_aux(self, buf: &mut Vec<u8>) {
|
||||||
|
match self {
|
||||||
|
T::Str(s) => buf.extend_from_slice(s),
|
||||||
|
T::OwnedStr(s) => buf.extend_from_slice(&s),
|
||||||
|
T::Dict(mut d) => {
|
||||||
|
buf.push(b'{');
|
||||||
|
let mut keys = d.keys().cloned().collect::<Vec<_>>();
|
||||||
|
keys.sort();
|
||||||
|
for (i, k) in keys.into_iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
buf.push(b',');
|
||||||
|
}
|
||||||
|
let v = d.remove(&k).unwrap();
|
||||||
|
buf.extend_from_slice(k.borrow());
|
||||||
|
buf.push(b'=');
|
||||||
|
v.encode_concise_aux(buf);
|
||||||
|
}
|
||||||
|
buf.push(b'}');
|
||||||
|
}
|
||||||
|
T::List(l) => {
|
||||||
|
buf.push(b'[');
|
||||||
|
for (i,item) in l.into_iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
buf.push(b',');
|
||||||
|
}
|
||||||
|
item.encode_concise_aux(buf);
|
||||||
|
}
|
||||||
|
buf.push(b']');
|
||||||
|
}
|
||||||
|
T::Seq(l) => {
|
||||||
|
for (i, v) in l.into_iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
buf.push(b' ');
|
||||||
|
}
|
||||||
|
v.encode_concise_aux(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -363,6 +410,7 @@ mod tests {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
])
|
])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let expected = "HELLO alexhelloworld [
|
let expected = "HELLO alexhelloworld [
|
||||||
dude,
|
dude,
|
||||||
why,
|
why,
|
||||||
|
@ -371,9 +419,27 @@ mod tests {
|
||||||
from = jxx,
|
from = jxx,
|
||||||
subject = hello,
|
subject = hello,
|
||||||
}";
|
}";
|
||||||
let enc = input.encode();
|
assert_eq!(debug(&input.encode()), expected);
|
||||||
eprintln!("{}", debug(&enc));
|
}
|
||||||
eprintln!("{}", expected);
|
|
||||||
assert_eq!(debug(&enc), expected);
|
#[test]
|
||||||
|
fn complex1_concise() {
|
||||||
|
let input = seq([
|
||||||
|
string("HELLO").unwrap(),
|
||||||
|
string("alexhelloworld").unwrap(),
|
||||||
|
list([
|
||||||
|
string("dude").unwrap(),
|
||||||
|
string("why").unwrap(),
|
||||||
|
]),
|
||||||
|
dict([
|
||||||
|
("from", string("jxx").unwrap()),
|
||||||
|
("subject", string("hello").unwrap()),
|
||||||
|
("data", raw(b"{ f1 = plop, f2 = kuko }").unwrap()),
|
||||||
|
])
|
||||||
|
.unwrap(),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
let expected_concise = "HELLO alexhelloworld [dude,why] {data={ f1 = plop, f2 = kuko },from=jxx,subject=hello}";
|
||||||
|
assert_eq!(debug(&input.encode_concise()), expected_concise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,20 @@ mod tests {
|
||||||
|
|
||||||
fn test_bidir<T: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug>(
|
fn test_bidir<T: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug>(
|
||||||
input: T,
|
input: T,
|
||||||
expected: &str,
|
expected_concise: &str,
|
||||||
) {
|
) {
|
||||||
eprintln!("Expecting: {}", expected);
|
eprintln!("Expecting: {}", expected_concise);
|
||||||
|
|
||||||
let ser = to_bytes(&input).unwrap();
|
let ser = to_bytes(&input).unwrap();
|
||||||
let ser = debug(&ser);
|
let ser = debug(&ser);
|
||||||
eprintln!("Serialized: {}", ser);
|
eprintln!("Serialized: {}", ser);
|
||||||
assert_eq!(ser, expected);
|
|
||||||
assert_eq!(from_bytes::<T>(ser.as_bytes()).unwrap(), input);
|
assert_eq!(from_bytes::<T>(ser.as_bytes()).unwrap(), input);
|
||||||
|
|
||||||
|
let ser_concise = to_term(&input).unwrap().encode_concise();
|
||||||
|
let ser_concise = debug(&ser_concise);
|
||||||
|
eprintln!("Serialized (concise): {}", ser_concise);
|
||||||
|
assert_eq!(ser_concise, expected_concise);
|
||||||
|
assert_eq!(from_bytes::<T>(ser_concise.as_bytes()).unwrap(), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -39,13 +45,7 @@ mod tests {
|
||||||
int: 1,
|
int: 1,
|
||||||
seq: vec!["a".to_string(), "b".to_string()],
|
seq: vec!["a".to_string(), "b".to_string()],
|
||||||
};
|
};
|
||||||
let expected = r#"Test {
|
let expected = r#"Test {int=1,seq=[YQ,Yg]}"#;
|
||||||
int = 1,
|
|
||||||
seq = [
|
|
||||||
YQ,
|
|
||||||
Yg,
|
|
||||||
],
|
|
||||||
}"#;
|
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
|
|
||||||
let input = vec![
|
let input = vec![
|
||||||
|
@ -58,22 +58,7 @@ mod tests {
|
||||||
seq: vec!["c".to_string(), "d".to_string()],
|
seq: vec!["c".to_string(), "d".to_string()],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let expected = r#"[
|
let expected = r#"[Test {int=1,seq=[YQ,Yg]},Test {int=2,seq=[Yw,ZA]}]"#;
|
||||||
Test {
|
|
||||||
int = 1,
|
|
||||||
seq = [
|
|
||||||
YQ,
|
|
||||||
Yg,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Test {
|
|
||||||
int = 2,
|
|
||||||
seq = [
|
|
||||||
Yw,
|
|
||||||
ZA,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]"#;
|
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +85,7 @@ mod tests {
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
|
|
||||||
let input = E::Struct { a: 1 };
|
let input = E::Struct { a: 1 };
|
||||||
let expected = r#"E.Struct { a = 1 }"#;
|
let expected = r#"E.Struct {a=1}"#;
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
|
|
||||||
let input = vec![
|
let input = vec![
|
||||||
|
@ -112,14 +97,7 @@ mod tests {
|
||||||
E::Tuple(3, 2),
|
E::Tuple(3, 2),
|
||||||
];
|
];
|
||||||
let expected =
|
let expected =
|
||||||
r#"[
|
r#"[E.Unit,E.Unit,E.Newtype 1,E.Tuple 1 2,E.Struct {a=1},E.Tuple 3 2]"#;
|
||||||
E.Unit,
|
|
||||||
E.Unit,
|
|
||||||
E.Newtype 1,
|
|
||||||
E.Tuple 1 2,
|
|
||||||
E.Struct { a = 1 },
|
|
||||||
E.Tuple 3 2,
|
|
||||||
]"#;
|
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,24 +111,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list() {
|
fn test_list() {
|
||||||
let input = vec![1, 2, 3, 4];
|
let input = vec![1, 2, 3, 4];
|
||||||
let expected = r#"[
|
let expected = r#"[1,2,3,4]"#;
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
]"#;
|
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_seqlist() {
|
fn test_seqlist() {
|
||||||
let input = vec![(1, 2), (2, 3), (3, 4), (5, 6)];
|
let input = vec![(1, 2), (2, 3), (3, 4), (5, 6)];
|
||||||
let expected = r#"[
|
let expected = r#"[1 2,2 3,3 4,5 6]"#;
|
||||||
1 2,
|
|
||||||
2 3,
|
|
||||||
3 4,
|
|
||||||
5 6,
|
|
||||||
]"#;
|
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,26 +127,13 @@ mod tests {
|
||||||
let mut input = HashMap::new();
|
let mut input = HashMap::new();
|
||||||
input.insert("hello".to_string(), "world".to_string());
|
input.insert("hello".to_string(), "world".to_string());
|
||||||
input.insert("dont".to_string(), "panic".to_string());
|
input.insert("dont".to_string(), "panic".to_string());
|
||||||
let expected = r#"{
|
let expected = r#"{ZG9udA=cGFuaWM,aGVsbG8=d29ybGQ}"#;
|
||||||
ZG9udA = cGFuaWM,
|
|
||||||
aGVsbG8 = d29ybGQ,
|
|
||||||
}"#;
|
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
|
|
||||||
let mut input = HashMap::new();
|
let mut input = HashMap::new();
|
||||||
input.insert(12, vec![42, 125]);
|
input.insert(12, vec![42, 125]);
|
||||||
input.insert(33, vec![19, 22, 21]);
|
input.insert(33, vec![19, 22, 21]);
|
||||||
let expected = r#"{
|
let expected = r#"{12=[42,125],33=[19,22,21]}"#;
|
||||||
12 = [
|
|
||||||
42,
|
|
||||||
125,
|
|
||||||
],
|
|
||||||
33 = [
|
|
||||||
19,
|
|
||||||
22,
|
|
||||||
21,
|
|
||||||
],
|
|
||||||
}"#;
|
|
||||||
test_bidir(input, expected);
|
test_bidir(input, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue