Skip to content

Commit 3611c22

Browse files
author
Ruslan Piasetskyi
committed
serializable-state-derive: initial commit
1 parent afc029e commit 3611c22

File tree

6 files changed

+307
-0
lines changed

6 files changed

+307
-0
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crypto-common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ categories = ["cryptography", "no-std"]
1414
[dependencies]
1515
generic-array = { version = "0.14.6", features = ["more_lengths"] }
1616
typenum = "1.14" # earlier versions of typenum don't satisfy the 'static bound on U* types
17+
serializable-state-derive = { path = "serializable-state-derive"}
1718

1819
# optional dependencies
1920
rand_core = { version = "0.6", optional = true }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "serializable-state-derive"
3+
description = "Macro implementation of #[derive(SerializableState)]"
4+
version = "0.1.0"
5+
authors = ["Ruslan Piasetskyi <ruslan.piasetskyi@gmail.com>"]
6+
license = "MIT OR Apache-2.0"
7+
edition = "2018"
8+
documentation = "https://docs.rs/serializable-state-derive"
9+
repository = "https://github.com/RustCrypto/traits"
10+
keywords = ["serialization", "no_std", "derive"]
11+
categories = ["cryptography", "no-std"]
12+
13+
[lib]
14+
proc-macro = true
15+
16+
[dependencies]
17+
syn = "1.0.100"
18+
quote = "1.0.21"
19+
proc-macro2 = "1.0.43"
20+
21+
[dev-dependencies]
22+
crypto-common = { version = "0.1.6", path = ".." }
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use proc_macro2::{TokenStream, Ident};
2+
use quote::{quote, quote_spanned, format_ident};
3+
use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Fields, Generics, punctuated::Iter, Field, Index};
4+
5+
#[proc_macro_derive(SerializableState)]
6+
pub fn derive_serializable_state(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7+
let input = parse_macro_input!(input as DeriveInput);
8+
let struct_name = input.ident;
9+
let serialized_state_size = generate_serializable_state_size(&input.data);
10+
let serialize_logic = generate_serialize_logic(&input.data);
11+
let deserialize_logic = generate_deserialize_logic(&input.data);
12+
13+
check_generics(&input.generics);
14+
15+
let expanded = quote! {
16+
impl crypto_common::SerializableState for #struct_name {
17+
type SerializedStateSize = #serialized_state_size;
18+
19+
fn serialize(&self) -> crypto_common::SerializedState<Self> {
20+
use crypto_common::generic_array::sequence::Concat;
21+
use crypto_common::SerializableState;
22+
23+
#serialize_logic
24+
}
25+
26+
fn deserialize(_serialized_state: &crypto_common::SerializedState<Self>) -> Result<Self, crypto_common::DeserializeStateError> {
27+
use crypto_common::generic_array::sequence::Split;
28+
use crypto_common::SerializableState;
29+
30+
#deserialize_logic
31+
}
32+
}
33+
};
34+
35+
proc_macro::TokenStream::from(expanded)
36+
}
37+
38+
fn check_generics(generics: &Generics) {
39+
if generics.params.iter().next().is_some() {
40+
panic!("Generics are not supported yet. Please implement SerializableState on your own.")
41+
}
42+
}
43+
44+
fn generate_serializable_state_size(data: &Data) -> TokenStream {
45+
match *data {
46+
Data::Struct(ref data) => match data.fields {
47+
Fields::Named(ref fields) => serializable_state_size_from_fields(fields.named.iter()),
48+
Fields::Unnamed(ref fields) => serializable_state_size_from_fields(fields.unnamed.iter()),
49+
Fields::Unit => quote! { crypto_common::typenum::U0 }
50+
},
51+
Data::Enum(_) | Data::Union(_) => unimplemented!(),
52+
}
53+
}
54+
55+
fn generate_serialize_logic(data: &Data) -> TokenStream {
56+
match *data {
57+
Data::Struct(ref data) => match data.fields {
58+
Fields::Named(ref fields) => serialize_logic_from_fields(fields.named.iter()),
59+
Fields::Unnamed(ref fields) => serialize_logic_from_fields(fields.unnamed.iter()),
60+
Fields::Unit => quote! { crypto_common::SerializedState::<Self>::default() }
61+
},
62+
Data::Enum(_) | Data::Union(_) => unimplemented!(),
63+
}
64+
}
65+
66+
fn generate_deserialize_logic(data: &Data) -> TokenStream {
67+
match *data {
68+
Data::Struct(ref data) => match data.fields {
69+
Fields::Named(ref fields) => deserialize_logic_from_fields(fields.named.iter(), true),
70+
Fields::Unnamed(ref fields) => deserialize_logic_from_fields(fields.unnamed.iter(), false),
71+
Fields::Unit => quote! { Ok(Self {}) }
72+
},
73+
Data::Enum(_) | Data::Union(_) => unimplemented!(),
74+
}
75+
}
76+
77+
fn serializable_state_size_from_fields(mut fields: Iter<Field>) -> TokenStream {
78+
match fields.next() {
79+
None => quote! { crypto_common::typenum::U0 },
80+
Some(first) => {
81+
let ty = &first.ty;
82+
let mut size = quote_spanned! { first.span() => <#ty as crypto_common::SerializableState>::SerializedStateSize };
83+
fields.for_each(|field| {
84+
let ty = &field.ty;
85+
size = quote_spanned! {
86+
field.span() => crypto_common::typenum::Sum<<#ty as crypto_common::SerializableState>::SerializedStateSize, #size>
87+
};
88+
});
89+
size
90+
}
91+
}
92+
}
93+
94+
fn serialize_logic_from_fields(mut fields: Iter<Field>) -> TokenStream {
95+
match fields.next() {
96+
None => quote! { crypto_common::SerializedState::<Self>::default() },
97+
Some(first) => {
98+
let field_name = get_field_name(0, &first.ident, None);
99+
let mut code = quote! { self.#field_name.serialize() };
100+
fields.enumerate().for_each(|(i, field)| {
101+
let field_name = get_field_name(i + 1, &field.ident, None);
102+
code = quote_spanned! { field.span() => #code.concat(self.#field_name.serialize()) };
103+
});
104+
code
105+
}
106+
}
107+
}
108+
109+
fn deserialize_logic_from_fields(fields: Iter<Field>, named: bool) -> TokenStream {
110+
let mut skip_first = fields.clone();
111+
match skip_first.next() {
112+
None => quote! { Ok(Self {}) },
113+
Some(first) => {
114+
let mut code = quote!();
115+
fields.enumerate().for_each(|(i, field)| {
116+
let field_name = get_field_name(i, &field.ident, Some("serialized_"));
117+
let ty = &field.ty;
118+
code = quote_spanned! {
119+
field.span() =>
120+
#code
121+
let (#field_name, _serialized_state) = Split::<_, <#ty as SerializableState>::SerializedStateSize>::split(_serialized_state);
122+
let #field_name = <#ty>::deserialize(#field_name)?;
123+
};
124+
});
125+
let first = get_field_name(0, &first.ident, Some("serialized_"));
126+
let recurce = skip_first.enumerate().map(|(i, field)| {
127+
let field_name = get_field_name(i + 1, &field.ident, Some("serialized_"));
128+
quote_spanned! { field.span() => #field_name }
129+
});
130+
131+
let construction = if named {
132+
quote! { Self { #first #(, #recurce)* } }
133+
} else {
134+
quote! { Self ( #first #(, #recurce)* ) }
135+
};
136+
137+
quote! {
138+
#code
139+
140+
Ok(#construction)
141+
}
142+
}
143+
}
144+
}
145+
146+
fn get_field_name(i: usize, ident: &Option<Ident>, unnamed_prefix: Option<&str>) -> TokenStream {
147+
match ident {
148+
Some(ident) => quote!{ #ident },
149+
None => {
150+
match unnamed_prefix {
151+
None => {
152+
let index = Index::from(i);
153+
quote! { #index }
154+
},
155+
Some(unnamed_prefix) => {
156+
let ident = format_ident!("{}{}", unnamed_prefix, i);
157+
quote! { #ident }
158+
}
159+
}
160+
}
161+
}
162+
}
163+
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use crypto_common::SerializableState;
2+
3+
macro_rules! serialization_test {
4+
($name:ident, $type: ty, $obj: expr, $serialized_state: expr) => {
5+
#[test]
6+
fn $name() {
7+
let obj = $obj;
8+
9+
let serialized_state = obj.serialize();
10+
assert_eq!(serialized_state.as_slice(), $serialized_state);
11+
12+
let deserialized_obj = <$type>::deserialize(&serialized_state).unwrap();
13+
assert_eq!(deserialized_obj, obj);
14+
}
15+
};
16+
}
17+
18+
#[derive(SerializableState, PartialEq, Debug)]
19+
struct StructWithNamedFields {
20+
a: u8,
21+
b: u64,
22+
c: [u16; 3],
23+
}
24+
25+
serialization_test!(
26+
struct_with_named_fields_serialization_test,
27+
StructWithNamedFields,
28+
StructWithNamedFields {
29+
a: 0x42,
30+
b: 0x1122334455667788,
31+
c: [0xAABB, 0xCCDD, 0xEEFF],
32+
},
33+
&[0x42, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xBB, 0xAA, 0xDD, 0xCC, 0xFF, 0xEE]
34+
);
35+
36+
#[derive(SerializableState, PartialEq, Debug)]
37+
struct StructWithZeroNamedFields {}
38+
39+
serialization_test!(
40+
struct_with_zero_named_fields_serialization_test,
41+
StructWithZeroNamedFields,
42+
StructWithZeroNamedFields {},
43+
&[]
44+
);
45+
46+
#[derive(SerializableState, PartialEq, Debug)]
47+
struct StructWithUnnamedFields([u8; 5], u32);
48+
49+
serialization_test!(
50+
struct_with_unnamed_fields_serialization_test,
51+
StructWithUnnamedFields,
52+
StructWithUnnamedFields([0x11, 0x22, 0x33, 0x44, 0x55], 0xAABBCCDD),
53+
&[0x11, 0x22, 0x33, 0x44, 0x55, 0xDD, 0xCC, 0xBB, 0xAA]
54+
);
55+
56+
#[derive(SerializableState, PartialEq, Debug)]
57+
struct StructWithZeroUnnamedFields();
58+
59+
serialization_test!(
60+
struct_with_zero_unnamed_fields_serialization_test,
61+
StructWithZeroUnnamedFields,
62+
StructWithZeroUnnamedFields(),
63+
&[]
64+
);
65+
66+
#[derive(SerializableState, PartialEq, Debug)]
67+
struct UnitStruct;
68+
69+
serialization_test!(unit_struct_serialization_test, UnitStruct, UnitStruct, &[]);

crypto-common/src/serializable_state.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use generic_array::{
44
ArrayLength, GenericArray,
55
};
66

7+
pub use generic_array::typenum;
8+
pub use serializable_state_derive::*;
9+
710
/// Serialized internal state.
811
pub type SerializedState<T> = GenericArray<u8, <T as SerializableState>::SerializedStateSize>;
912

@@ -81,6 +84,27 @@ macro_rules! impl_serializable_state_u8_array {
8184
};
8285
}
8386

87+
// Implementation for sizes that are not supported in GenericArray
88+
// https://github.com/fizyk20/generic-array/blob/0.14.4/src/impls.rs#L183
89+
macro_rules! impl_serializable_state_u8_array_unsupported_size {
90+
($n: ty) => {
91+
impl SerializableState for [u8; <$n>::USIZE] {
92+
type SerializedStateSize = $n;
93+
94+
fn serialize(&self) -> SerializedState<Self> {
95+
SerializedState::<Self>::clone_from_slice(self)
96+
}
97+
98+
fn deserialize(
99+
serialized_state: &SerializedState<Self>,
100+
) -> Result<Self, DeserializeStateError> {
101+
use core::convert::TryFrom;
102+
Ok(Self::try_from(serialized_state.as_slice()).unwrap())
103+
}
104+
}
105+
};
106+
}
107+
84108
macro_rules! impl_serializable_state_type_array {
85109
($type: ty, $type_size: ty, $n: ty) => {
86110
impl SerializableState for [$type; <$n>::USIZE] {
@@ -126,6 +150,18 @@ macro_rules! impl_serializable_state_arrays {
126150
}
127151
}
128152

153+
macro_rules! impl_serializable_state_arrays_unsupported_size {
154+
($($n: ty),*) => {
155+
$(
156+
impl_serializable_state_u8_array_unsupported_size!($n);
157+
impl_serializable_state_type_array!(u16, U2, $n);
158+
impl_serializable_state_type_array!(u32, U4, $n);
159+
impl_serializable_state_type_array!(u64, U8, $n);
160+
impl_serializable_state_type_array!(u128, U16, $n);
161+
)*
162+
}
163+
}
164+
129165
impl_serializable_state_arrays! {
130166
generic_array::typenum::U1,
131167
generic_array::typenum::U2,
@@ -205,3 +241,9 @@ impl_serializable_state_arrays! {
205241
generic_array::typenum::U1000,
206242
generic_array::typenum::U1024
207243
}
244+
245+
impl_serializable_state_arrays_unsupported_size! {
246+
generic_array::typenum::U112,
247+
generic_array::typenum::U184,
248+
generic_array::typenum::U248
249+
}

0 commit comments

Comments
 (0)