Skip to content
This repository has been archived by the owner on Jul 9, 2023. It is now read-only.

Attribute macro to generate de/serialization functions for fields of big array type #17

Open
dtolnay opened this issue Jan 22, 2019 · 8 comments

Comments

@dtolnay
Copy link
Owner

dtolnay commented Jan 22, 2019

Like all of the standard library's traits, Serde's traits are limited to fixed size arrays up to an arbitrary maximum size. Serde defines Serialize and Deserialize impls for arrays up to size 32.

The current workaround for larger arrays in serde_big_array is workable but not ideal:

big_array! {
    BigArray;
    42, 300,
}

#[derive(Serialize, Deserialize)]
struct S {
    #[serde(with = "BigArray")]
    arr_a: [u8; 300],
    #[serde(with = "BigArray")]
    arr_b: [u8; 42],
    arr_small: [u8; 8],
}

It would be nicer to have an attribute macro that makes big arrays work by finding all fields of array type and inserting the appropriate serde(serialize_with = "...", deserialize_with = "...") functions (also generated by the attribute macro).

#[make_big_arrays_work]
#[derive(Serialize, Deserialize)]
struct S {
    arr_a: [u8; 300],
    arr_b: [u8; 42],
    arr_small: [u8; 8],
}
// generated code

#[derive(Serialize, Deserialize)]
struct S {
    #[serde(
        serialize_with = "big_array_serialize_S_arr_a",
        deserialize_with = "big_array_deserialize_S_arr_a",
    )]
    arr_a: [u8; 300],
    #[serde(
        serialize_with = "big_array_serialize_S_arr_b",
        deserialize_with = "big_array_deserialize_S_arr_b",
    )]
    arr_b: [u8; 42],
    arr_small: [u8; 8],
}

fn big_array_serialize_S_arr_a<S>(
    array: &[u8; 300],
    serializer: S,
) -> core::result::Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    /* ... */
}

fn big_array_deserialize_S_arr_a<'de, D>(
    deserializer: D,
) -> core::result::Result<[u8; 300], D::Error>
where
    D: serde::Deserializer<'de>,
{
    /* ... */
}

/* ... */

The serialize_with attribute should only be emitted if there is a Serialize derive on the data structure, and deserialize_with should only be emitted if there is a Deserialize derive.

Neither attribute should be emitted for a field with array type with literal size that we can see is 32 or smaller.

Attributes do need to be emitted for all arrays of const size not visible to the macro, for example arr_unknown: [u8; BUFFER_SIZE].

Optionally, also support type aliased arrays by specifying the array size in an attribute.

pub const BUFSIZE: usize = 1024;
pub type Buffer = [u8; BUFSIZE];

#[make_big_arrays_work]
#[derive(Serialize, Deserialize)]
struct S {
    #[big_array(BUFSIZE)]
    buffer: Buffer,
}
@est31
Copy link

est31 commented Jan 22, 2019

@dtolnay that would indeed be a bit more convenient. Overall I think that I personally don't want to spend too much time on the issue, given that hopefully this will get fixed by the language.

@est31

This comment has been minimized.

@dtolnay

This comment has been minimized.

@est31

This comment has been minimized.

@uint
Copy link

uint commented Mar 9, 2021

I figured this would be a great way to dig into proc macros, so I played around with it a bit. Who'd guess I'd have a PoC so soon: https://github.com/uint/serbia

For now, it only works on structs (tuple or regular). I guess I'm on it!

@est31
Copy link

est31 commented Mar 10, 2021

serde_with has also recently gained big array support: jonasbb/serde_with#272

Their MSRV is 1.51.

Support is a bit more comprehensive than serde-big-array. I need to investigate if there is a reason to keep serde-big-array around or whether I should deprecate it in favour of serde_with.

@uint
Copy link

uint commented Mar 11, 2021

So I've been looking at implementing this thing:

pub const BUFSIZE: usize = 1024;
pub type Buffer = [u8; BUFSIZE];

#[make_big_arrays_work]
#[derive(Serialize, Deserialize)]
struct S {
    #[big_array(BUFSIZE)]
    buffer: Buffer,
}

I can parse BUFSIZE as a syn::ExprPath or just a syn::Path, but I'm not sure there's a way to get the value of the underlying constant at macro expansion time. I've read somewhere that constants are evaluated after macro expansion. Is there some trick to this?

@uint
Copy link

uint commented Mar 25, 2021

@dtolnay I've been hacking at this. At this point, I think Serbia is pretty usable, but would love feedback.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants