Implement macros for untagged collections

This commit is contained in:
Nicolas Stalder
2021-02-18 03:27:03 +01:00
parent b38c0dcd9f
commit e790468cf6
7 changed files with 127 additions and 38 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
profile: minimal
- name: Build Documentation
run: cargo doc --no-deps && scripts/make-toplevel-index.sh
run: cargo doc --features derive && scripts/make-toplevel-index.sh
- name: Deploy Docs
uses: peaceiris/actions-gh-pages@v3
+1 -1
View File
@@ -6,7 +6,7 @@ license = "Apache-2.0 OR MIT"
edition = "2018"
description = "Encoding and decoding of SIMPLE-TLV as described in ISO 7816-4, without allocations."
categories = ["cryptography", "data-structures", "encoding", "no-std"]
keywords = ["crypto"]
keywords = ["crypto", "no_std", "serialization"]
readme = "README.md"
[dependencies]
+27 -15
View File
@@ -3,7 +3,7 @@ use quote::{quote, ToTokens};
use syn::{Attribute, DataStruct, Field, Ident};
use synstructure::Structure;
use crate::{extract_attrs, FieldAttrs};
use crate::{extract_attrs_optional_tag, FieldAttrs};
/// Derive Decodable on a struct
pub(crate) struct DeriveDecodableStruct {
@@ -17,7 +17,7 @@ pub(crate) struct DeriveDecodableStruct {
impl DeriveDecodableStruct {
pub fn derive(s: Structure<'_>, data: &DataStruct, name: &Ident, attrs: &[Attribute]) -> TokenStream {
let (tag, _) = extract_attrs(name, attrs);
let (tag, _) = extract_attrs_optional_tag(name, attrs);
let mut state = Self {
decode_fields: TokenStream::new(),
@@ -56,26 +56,38 @@ impl DeriveDecodableStruct {
}
/// Finish deriving a struct
fn finish(self, s: &Structure<'_>, tag: u8) -> TokenStream {
fn finish(self, s: &Structure<'_>, tag: Option<u8>) -> TokenStream {
let decode_fields = self.decode_fields;
let decode_result = self.decode_result;
s.gen_impl(quote! {
gen impl<'a> core::convert::TryFrom<simple_tlv::TaggedSlice<'a>> for @Self {
type Error = simple_tlv::Error;
if let Some(tag) = tag {
s.gen_impl(quote! {
gen impl<'a> core::convert::TryFrom<simple_tlv::TaggedSlice<'a>> for @Self {
type Error = simple_tlv::Error;
fn try_from(tagged_slice: simple_tlv::TaggedSlice<'a>) -> simple_tlv::Result<Self> {
use core::convert::TryInto;
tagged_slice.tag().assert_eq(simple_tlv::Tag::try_from(#tag).unwrap())?;
tagged_slice.decode_nested(|decoder| {
#decode_fields
fn try_from(tagged_slice: simple_tlv::TaggedSlice<'a>) -> simple_tlv::Result<Self> {
use core::convert::TryInto;
tagged_slice.tag().assert_eq(simple_tlv::Tag::try_from(#tag).unwrap())?;
tagged_slice.decode_nested(|decoder| {
#decode_fields
Ok(Self { #decode_result })
})
Ok(Self { #decode_result })
})
}
}
}
})
})
} else {
s.gen_impl(quote! {
gen impl<'a> simple_tlv::Decodable<'a> for @Self {
fn decode(decoder: &mut simple_tlv::Decoder<'a>) -> simple_tlv::Result<Self> {
use core::convert::{TryFrom, TryInto};
#decode_fields
Ok(Self { #decode_result })
}
}
})
}
}
}
+46 -19
View File
@@ -3,7 +3,7 @@ use quote::{quote, ToTokens};
use syn::{Attribute, DataStruct, Field, Ident};
use synstructure::Structure;
use crate::{extract_attrs, FieldAttrs};
use crate::{extract_attrs_optional_tag, FieldAttrs};
/// Derive Encodable on a struct
pub(crate) struct DeriveEncodableStruct {
@@ -14,7 +14,7 @@ pub(crate) struct DeriveEncodableStruct {
impl DeriveEncodableStruct {
pub fn derive(s: Structure<'_>, data: &DataStruct, name: &Ident, attrs: &[Attribute]) -> TokenStream {
let (tag, _) = extract_attrs(name, attrs);
let (tag, _) = extract_attrs_optional_tag(name, attrs);
let mut state = Self {
encode_fields: TokenStream::new(),
@@ -46,30 +46,57 @@ impl DeriveEncodableStruct {
}
/// Finish deriving a struct
fn finish(self, s: &Structure<'_>, tag: u8) -> TokenStream {
fn finish(self, s: &Structure<'_>, tag: Option<u8>) -> TokenStream {
let encode_fields = self.encode_fields;
s.gen_impl(quote! {
gen impl simple_tlv::Tagged for @Self {
fn tag() -> simple_tlv::Tag {
// TODO(nickray): FIXME FIXME
use core::convert::TryFrom;
simple_tlv::Tag::try_from(#tag).unwrap()
if let Some(tag) = tag {
s.gen_impl(quote! {
gen impl simple_tlv::Tagged for @Self {
fn tag() -> simple_tlv::Tag {
// TODO(nickray): FIXME FIXME
use core::convert::TryFrom;
simple_tlv::Tag::try_from(#tag).unwrap()
}
}
}
gen impl simple_tlv::Container for @Self {
fn fields<F, T>(&self, field_encoder: F) -> simple_tlv::Result<T>
where
F: FnOnce(&[&dyn simple_tlv::Encodable]) -> simple_tlv::Result<T>,
{
use core::convert::TryFrom;
field_encoder(&[#encode_fields])
gen impl simple_tlv::Container for @Self {
fn fields<F, T>(&self, field_encoder: F) -> simple_tlv::Result<T>
where
F: FnOnce(&[&dyn simple_tlv::Encodable]) -> simple_tlv::Result<T>,
{
use core::convert::TryFrom;
field_encoder(&[#encode_fields])
}
}
}
})
})
} else {
s.gen_impl(quote! {
gen impl simple_tlv::Container for @Self {
fn fields<F, T>(&self, field_encoder: F) -> simple_tlv::Result<T>
where
F: FnOnce(&[&dyn simple_tlv::Encodable]) -> simple_tlv::Result<T>,
{
use core::convert::TryFrom;
field_encoder(&[#encode_fields])
}
}
gen impl simple_tlv::Encodable for @Self {
fn encoded_length(&self) -> simple_tlv::Result<simple_tlv::Length> {
use core::convert::TryFrom;
use simple_tlv::Container;
self.fields(|encodables| simple_tlv::Length::try_from(encodables))
}
fn encode(&self, encoder: &mut simple_tlv::Encoder<'_>) -> simple_tlv::Result<()> {
use simple_tlv::Container;
self.fields(|fields| encoder.encode_untagged_collection(fields))
}
}
})
}
}
}
+7 -1
View File
@@ -95,7 +95,7 @@ impl FieldAttrs {
}
}
fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (u8, bool) {
fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<u8>, bool) {
let mut tag = None;
let mut slice = false;
@@ -150,6 +150,12 @@ fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (u8, bool) {
}
}
(tag, slice)
}
fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (u8, bool) {
let (tag, slice) = extract_attrs_optional_tag(name, attrs);
if let Some(tag) = tag {
(tag, slice)
} else {
+11
View File
@@ -75,6 +75,17 @@ impl<'a> Encoder<'a> {
}
}
/// Encode a collection of values which impl the [`Encodable`] trait under a given tag.
pub fn encode_untagged_collection(&mut self, encodables: &[&dyn Encodable]) -> Result<()> {
let expected_len = Length::try_from(encodables)?;
let mut nested_encoder = Encoder::new(self.reserve(expected_len)?);
for encodable in encodables {
encodable.encode(&mut nested_encoder)?;
}
Ok(())
}
/// Encode a single byte into the backing buffer.
pub(crate) fn byte(&mut self, byte: u8) -> Result<()> {
match self.reserve(1u8)?.first_mut() {
+34 -1
View File
@@ -22,6 +22,14 @@ struct T {
x: [u8; 1234],
}
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
struct T2 {
#[tlv(tag = "0x44", slice)]
x: [u8; 1234],
#[tlv(tag = "0x55", slice)]
a: [u8; 5],
}
#[test]
fn derived_reconstruct() {
let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] };
@@ -38,7 +46,6 @@ fn derived_reconstruct() {
);
let s2 = S::from_bytes(encoded).unwrap();
assert_eq!(s, s2);
}
@@ -63,4 +70,30 @@ fn pretty_big() {
// 1234
0x44, 0xFF, 0x04, 0xD2]);
assert_eq!(&encoded[8..], x);
let t2 = T::from_bytes(encoded).unwrap();
assert_eq!(t, t2);
}
#[test]
fn derive_untagged() {
let mut x = [0u8; 1234];
for (i, x) in x.iter_mut().enumerate() {
*x = i as _;
};
let t = T2 { x, a: [17u8; 5] };
let mut buf = [0u8; 1500];
let encoded = t.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..4], [
// 1234
0x44, 0xFF, 0x04, 0xD2]);
assert_eq!(&encoded[4..(encoded.len() - 7)], x);
assert_eq!(&encoded[(encoded.len() - 7)..], [0x55, 5, 17, 17, 17, 17, 17]);
let t2 = T2::from_bytes(encoded).unwrap();
assert_eq!(t, t2);
}