Add slice attribute to derive macros

This commit is contained in:
Nicolas Stalder
2021-02-18 02:13:32 +01:00
parent 70607f73f3
commit b38c0dcd9f
6 changed files with 137 additions and 66 deletions
+11 -4
View File
@@ -3,7 +3,7 @@ use quote::{quote, ToTokens};
use syn::{Attribute, DataStruct, Field, Ident};
use synstructure::Structure;
use crate::{extract_tag, FieldAttrs};
use crate::{extract_attrs, 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_tag(name, attrs);
let (tag, _) = extract_attrs(name, attrs);
let mut state = Self {
decode_fields: TokenStream::new(),
@@ -41,7 +41,14 @@ impl DeriveDecodableStruct {
fn derive_field_decoder(&mut self, field: &FieldAttrs) {
let field_name = &field.name;
let field_tag = field.tag;
let field_decoder = quote! { let #field_name = decoder.decode_tagged_value(::simple_tlv::Tag::try_from(#field_tag).unwrap())?; };
let field_decoder = if field.slice {
quote! { let #field_name =
decoder.decode_tagged_slice(::simple_tlv::Tag::try_from(#field_tag).unwrap())?.try_into()
.map_err(|_| simple_tlv::ErrorKind::Length { tag: simple_tlv::Tag::try_from(#field_tag).unwrap() })?;
}
} else {
quote! { let #field_name = decoder.decode_tagged_value(::simple_tlv::Tag::try_from(#field_tag).unwrap())?; }
};
field_decoder.to_tokens(&mut self.decode_fields);
let field_result = quote!(#field_name,);
@@ -58,7 +65,7 @@ impl DeriveDecodableStruct {
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<S> {
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| {
+7 -3
View File
@@ -3,7 +3,7 @@ use quote::{quote, ToTokens};
use syn::{Attribute, DataStruct, Field, Ident};
use synstructure::Structure;
use crate::{extract_tag, FieldAttrs};
use crate::{extract_attrs, 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_tag(name, attrs);
let (tag, _) = extract_attrs(name, attrs);
let mut state = Self {
encode_fields: TokenStream::new(),
@@ -37,7 +37,11 @@ impl DeriveEncodableStruct {
fn derive_field_encoder(&mut self, field: &FieldAttrs) {
let field_name = &field.name;
let field_tag = field.tag;
let field_encoder = quote! { &(::simple_tlv::Tag::try_from(#field_tag).unwrap().with_value(&self.#field_name)), };
let field_encoder = if field.slice {
quote! { &(::simple_tlv::TaggedSlice::from(simple_tlv::Tag::try_from(#field_tag).unwrap(), &self.#field_name)?), }
} else {
quote! { &(::simple_tlv::Tag::try_from(#field_tag).unwrap().with_value(&self.#field_name)), }
};
field_encoder.to_tokens(&mut self.encode_fields);
}
+45 -29
View File
@@ -1,4 +1,8 @@
//! Custom derive support for the `simple-tlv` crate
//!
//! With `#[tlv(slice)]` set, `Encodable` should work for fields implementing `AsRef<[u8]>`,
//! and `Decodable` should work for fields implementing `TryFrom<[u8]>`, even if the field
//! is not `Decodable` or `Encodable`.
#![crate_type = "proc-macro"]
#![warn(rust_2018_idioms, trivial_casts, unused_qualifications)]
@@ -69,8 +73,11 @@ struct FieldAttrs {
/// Name of the field
pub name: Ident,
/// Value of the `#[asn1(type = "...")]` attribute if provided
/// Value of the `#[tlv(tag = "...")]` attribute if provided
pub tag: u8,
/// Whether the `#[tlv(slice)]` attribute was set
pub slice: bool
}
impl FieldAttrs {
@@ -82,14 +89,15 @@ impl FieldAttrs {
.cloned()
.expect("no name on struct field i.e. tuple structs unsupported");
let tag = extract_tag(&name, &field.attrs);
let (tag, slice) = extract_attrs(&name, &field.attrs);
Self { name, tag }
Self { name, tag, slice }
}
}
fn extract_tag(name: &Ident, attrs: &[Attribute]) -> u8 {
fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (u8, bool) {
let mut tag = None;
let mut slice = false;
for attr in attrs {
if !attr.path.is_ident("tlv") {
@@ -97,45 +105,53 @@ fn extract_tag(name: &Ident, attrs: &[Attribute]) -> u8 {
}
match attr.parse_meta().expect("error parsing `tlv` attribute") {
Meta::List(MetaList { nested, .. }) if nested.len() == 1 => {
match nested.first() {
Some(NestedMeta::Meta(Meta::NameValue(MetaNameValue {
path,
lit: Lit::Str(lit_str),
..
}))) => {
// Parse the `type = "..."` attribute
if !path.is_ident("tag") {
panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
Meta::List(MetaList { nested, .. }) if !nested.is_empty() => {
for entry in nested {
match entry {
NestedMeta::Meta(Meta::Path(path)) => {
if !path.is_ident("slice") {
panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
}
slice = true;
}
NestedMeta::Meta(Meta::NameValue(MetaNameValue {
path,
lit: Lit::Str(lit_str),
..
})) => {
// Parse the `type = "..."` attribute
if !path.is_ident("tag") {
panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
}
if tag.is_some() {
panic!("duplicate SIMPLE-TLV `tag` attribute for field: {}", name);
}
if tag.is_some() {
panic!("duplicate SIMPLE-TLV `tag` attribute for field: {}", name);
}
let possibly_with_prefix = lit_str.value();
let without_prefix = possibly_with_prefix.trim_start_matches("0x");
let tag_value = u8::from_str_radix(without_prefix, 16).expect("tag values must be between one and 254");
if tag_value == 0 || tag_value == 255 {
panic!("SIMPLE-TLV tags must not be zero or 255");
let possibly_with_prefix = lit_str.value();
let without_prefix = possibly_with_prefix.trim_start_matches("0x");
let tag_value = u8::from_str_radix(without_prefix, 16).expect("tag values must be between one and 254");
if tag_value == 0 || tag_value == 255 {
panic!("SIMPLE-TLV tags must not be zero or 255");
}
tag = Some(tag_value);
}
tag = Some(tag_value);
other => panic!(
"a malformed `tlv` attribute for field `{}`: {:?}",
name, other
),
}
other => panic!(
"malformed `tlv` attribute for field `{}`: {:?}",
name, other
),
}
}
other => panic!(
"malformed `tlv` attribute for field `{}`: {:?}",
"malformed `tlv` attribute for field `{}`: {:#?}",
name, other
),
}
}
if let Some(tag) = tag {
tag
(tag, slice)
} else {
panic!("SIMPLE-TLV tag missing for `{}`", name);
}
+7
View File
@@ -42,6 +42,13 @@ impl<'a> Decoder<'a> {
Self::new(tagged.as_bytes()).decode()
}
/// Decode a TaggedSlice with tag checked to be as expected, returning the value
pub fn decode_tagged_slice(&mut self, tag: crate::Tag) -> Result<&'a [u8]> {
let tagged: crate::TaggedSlice = self.decode()?;
tagged.tag().assert_eq(tag)?;
Ok(tagged.as_bytes())
}
/// Return an error with the given [`ErrorKind`], annotating it with
/// context about where the error occurred.
pub fn error<T>(&mut self, kind: ErrorKind) -> Result<T> {
+34 -26
View File
@@ -1,21 +1,18 @@
// pub use der::{Decodable, Encodable};
//! Trait definitions
use core::convert::TryFrom;
use core::convert::{TryFrom, TryInto};
use crate::{Decoder, Encoder, Error, header::Header, Length, Result, Tag, TaggedSlice, TaggedValue};
#[cfg(feature = "alloc")]
use {
alloc::vec::Vec,
core::{convert::TryInto, iter},
core::iter,
crate::ErrorKind,
};
#[cfg(feature = "heapless")]
use {
core::convert::TryInto,
crate::ErrorKind,
};
use crate::ErrorKind;
/// Decoding trait.
///
@@ -244,27 +241,38 @@ where
// }
// }
impl<'a> Encodable for &'a [u8] {
fn encoded_length(&self) -> Result<Length> {
self.len().try_into()
}
/// Encode this value as SIMPLE-TLV using the provided [`Encoder`].
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.bytes(self)
}
}
macro_rules! impl_array {
($($N:literal),*) => {
$(
impl Encodable for [u8; $N] {
fn encoded_length(&self) -> Result<Length> {
Ok(($N as u8).into())
impl Encodable for [u8; $N] {
fn encoded_length(&self) -> Result<Length> {
Ok(($N as u8).into())
}
/// Encode this value as SIMPLE-TLV using the provided [`Encoder`].
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.bytes(self.as_ref())
}
}
/// Encode this value as SIMPLE-TLV using the provided [`Encoder`].
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.bytes(self.as_ref())
impl Decodable<'_> for [u8; $N] {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
use core::convert::TryInto;
let bytes: &[u8] = decoder.bytes($N as u8)?;
Ok(bytes.try_into().unwrap())
}
}
}
impl Decodable<'_> for [u8; $N] {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
use core::convert::TryInto;
let bytes: &[u8] = decoder.bytes($N as u8)?;
Ok(bytes.try_into().unwrap())
}
}
)*
}
}
@@ -331,10 +339,10 @@ mod tests {
{
// both approaches equivalent
field_encoder(&[
&(Tag::try_from(0x11).unwrap().with_value(&self.x)),
&(Tag::try_from(0x11).unwrap().with_value(&self.x.as_ref())),
// &self.x.tagged(Tag::try_from(0x11).unwrap()),
&self.y.tagged(Tag::try_from(0x22).unwrap()),
&self.z.tagged(Tag::try_from(0x33).unwrap()),
&self.y.as_ref().tagged(Tag::try_from(0x22).unwrap()),
&self.z.as_ref().tagged(Tag::try_from(0x33).unwrap()),
])
}
@@ -396,7 +404,7 @@ mod tests {
{
field_encoder(&[
&self.s.tagged(Tag::try_from(0x1).unwrap()),
&self.t.tagged(Tag::try_from(0x2).unwrap()),
&self.t.as_ref().tagged(Tag::try_from(0x2).unwrap()),
])
}
}
@@ -464,7 +472,7 @@ mod tests {
{
field_encoder(&[
&self.s,
&self.t.tagged(Tag::try_from(0x2).unwrap()),
&self.t.as_ref().tagged(Tag::try_from(0x2).unwrap()),
])
}
}
+33 -4
View File
@@ -5,16 +5,23 @@
use simple_tlv::{Decodable, Encodable};
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
#[tlv(tag = "AA")]
#[tlv(tag = "0xAA")]
struct S {
#[tlv(tag = "11")]
#[tlv(slice, tag = "0x11")]
x: [u8; 2],
#[tlv(tag = "22")]
#[tlv(slice, tag = "0x22")]
y: [u8; 3],
#[tlv(tag = "33")]
#[tlv(slice, tag = "0x33")]
z: [u8; 4],
}
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
#[tlv(tag = "0xBB")]
struct T {
#[tlv(tag = "0x44", slice)]
x: [u8; 1234],
}
#[test]
fn derived_reconstruct() {
let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] };
@@ -35,3 +42,25 @@ fn derived_reconstruct() {
assert_eq!(s, s2);
}
#[test]
fn pretty_big() {
let mut x = [0u8; 1234];
for (i, x) in x.iter_mut().enumerate() {
*x = i as _;
};
let t = T { x };
let mut buf = [0u8; 1024];
assert!(t.encode_to_slice(&mut buf).is_err());
let mut buf = [0u8; 1500];
let encoded = t.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..8], [
// 1234 + 4
0xBB, 0xFF, 0x04, 0xD6,
// 1234
0x44, 0xFF, 0x04, 0xD2]);
assert_eq!(&encoded[8..], x);
}