Двоичная (де) сериализация [u8; 128]

У меня есть struct содержащий байтовый массив, который я хотел бы сериализовать и десериализовать в и из двоичного файла. Но это работает только для массивов до 32 элементов.

Вот мой минимальный пример кода

main.rs:

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate bincode;

use bincode::{serialize, deserialize, Infinite};

const BYTECOUNT: usize = 32; // 33 and more does not work, I need 128
type DataArr = [u8; BYTECOUNT];

#[derive(Serialize, Deserialize, Debug)]
struct Entry {
    number: i64,
    data: DataArr
}

fn main() {
    let mut my_entry = Entry { number: 12345, data: [0; BYTECOUNT] };
    my_entry.data[4] = 42;

    // Convert the Entry to binary.
    let serialized: Vec<u8> = serialize(&my_entry, Infinite).unwrap();
    println!("serialized = {:?}", serialized);

    // Convert the binary representation back to an Entry.
    let deserialized: Entry = deserialize(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
}

Cargo.toml:

[package]
name = "array_serialization_test"
version = "0.1.0"

[dependencies]
serde = "*"
serde_derive = "*"
bincode = "*"

выход:

serialized = [57, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
deserialized = Entry { number: 12345, data: [0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 

Как я могу заставить его работать на 128 элементов в массиве? Могу ли я продлить array_impls! как-то вручную в моем коде пользователя с этим?

128 => (0 a 1 b 2 c 3 d 4 e 5 f 6 g 7 h 8 i 9 j 10 k 11 l 12 m 13 n 14 o 15 p 16 q 17 e 18 s 19 t 20 u 21 v 22 w 23 x 24 y 25 z 26 aa 27 ab 28 ac 29 ad 30 ae 31 af 32 ag 33 ah 34 ai 35 aj 36 ak 37 al 38 am 39 an 40 ao 41 ap 42 aq 43 ar 44 as 45 at 46 au 47 av 48 aw 49 ax 50 ay 51 az 52 ba 53 bb 54 bc 55 bd 56 be 57 bf 58 bg 59 bh 60 bi 61 bj 62 bk 63 bl 64 bm 65 bn 66 bo 67 bp 68 bq 69 br 70 bs 71 bt 72 bu 73 bv 74 bw 75 bx 76 by 77 bz 78 ca 79 cb 80 cc 81 cd 82 ce 83 cf 84 cg 85 ch 86 ci 87 cj 88 ck 89 cl 90 cm 91 cn 92 co 93 cp 94 cq 95 cr 96 cs 97 ct 98 cu 99 cv 100 cw 101 cx 102 cy 103 cz 104 da 105 db 106 dc 107 dd 108 de 109 df 110 dg 111 dh 112 di 113 dj 114 dk 115 dl 116 dm 117 dn 118 do 119 dp 120 dq 121 dr 122 ds 123 dt 124 du 125 dv 126 dw 127 dx)

Или есть альтернативный подход?

ПРИМЕЧАНИЕ: я думаю, что этот вопрос отличается от того, как мне сопоставить структуру C с заполнением более 32 байтов, используя serde и bincode? потому что мне действительно нужно содержимое массива, так как он используется не только для заполнения. Также я хотел бы знать, могу ли я продлить array_impls! в моем коде.

3 ответа

Решение

В настоящее время Serde не может предоставить Serialize а также Deserialize подразумевает, что работает для каждого размера массива. Он заблокирован на const generics, над которым сейчас ведутся работы, и, надеюсь, приземлится ночью позже, в 2018 году.

На данный момент вы можете определить свой собственный помощник "большого массива", который может сериализовать и десериализовать массивы любых конкретных размеров, используемых в вашем ящике. Поля, для которых вы хотите использовать помощник большого массива, должны быть помечены #[serde(with = "BigArray")] или же Серде будет искать несуществующее Serialize а также Deserialize impls.

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate bincode;

mod big_array;
use big_array::BigArray;

const BYTECOUNT: usize = 128;
type DataArr = [u8; BYTECOUNT];

#[derive(Serialize, Deserialize)]
struct Entry {
    number: i64,
    #[serde(with = "BigArray")]
    data: DataArr
}

fn main() {
    let mut my_entry = Entry { number: 12345, data: [0; BYTECOUNT] };
    my_entry.data[4] = 42;

    // Convert the Entry to binary.
    let serialized: Vec<u8> = bincode::serialize(&my_entry).unwrap();
    println!("serialized = {:?}", serialized);

    // Convert the binary representation back to an Entry.
    let deserialized: Entry = bincode::deserialize(&serialized).unwrap();
    println!("deserialized = {} {:?}", deserialized.number, &deserialized.data[..]);
}

Помощник большого массива может быть определен в src/big_array.rs следующее. Может быть, это сделало бы хороший ящик сам по себе, если бы вы хотели иметь его!

use std::fmt;
use std::marker::PhantomData;
use serde::ser::{Serialize, Serializer, SerializeTuple};
use serde::de::{Deserialize, Deserializer, Visitor, SeqAccess, Error};

pub trait BigArray<'de>: Sized {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer;
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>;
}

macro_rules! big_array {
    ($($len:expr,)+) => {
        $(
            impl<'de, T> BigArray<'de> for [T; $len]
                where T: Default + Copy + Serialize + Deserialize<'de>
            {
                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                    where S: Serializer
                {
                    let mut seq = serializer.serialize_tuple(self.len())?;
                    for elem in &self[..] {
                        seq.serialize_element(elem)?;
                    }
                    seq.end()
                }

                fn deserialize<D>(deserializer: D) -> Result<[T; $len], D::Error>
                    where D: Deserializer<'de>
                {
                    struct ArrayVisitor<T> {
                        element: PhantomData<T>,
                    }

                    impl<'de, T> Visitor<'de> for ArrayVisitor<T>
                        where T: Default + Copy + Deserialize<'de>
                    {
                        type Value = [T; $len];

                        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                            formatter.write_str(concat!("an array of length ", $len))
                        }

                        fn visit_seq<A>(self, mut seq: A) -> Result<[T; $len], A::Error>
                            where A: SeqAccess<'de>
                        {
                            let mut arr = [T::default(); $len];
                            for i in 0..$len {
                                arr[i] = seq.next_element()?
                                    .ok_or_else(|| Error::invalid_length(i, &self))?;
                            }
                            Ok(arr)
                        }
                    }

                    let visitor = ArrayVisitor { element: PhantomData };
                    deserializer.deserialize_tuple($len, visitor)
                }
            }
        )+
    }
}

big_array! {
    40, 48, 50, 56, 64, 72, 96, 100, 128, 160, 192, 200, 224, 256, 384, 512,
    768, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
}

По состоянию на 2023 год serde по-прежнему не поддерживает массивы общей длины, поскольку это нарушит реализацию [T; 0]. Тем не менее, вы все равно можете воспользоваться константными дженериками, чтобы самостоятельно создать полностью универсальное решение.

Вот функция сериализатора.

      use serde::ser::SerializeTuple;
use serde::{Serialize, Serializer};

fn serialize<const N: usize, S, T>(t: &[T; N], serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
    T: Serialize,
{
    let mut ser_tuple = serializer.serialize_tuple(N)?;
    for elem in t {
        ser_tuple.serialize_element(elem)?;
    }
    ser_tuple.end()
}

Затем вы можете использовать#[serde(serialize_with = "serialize")]

      const BYTECOUNT: usize = 128;
type DataArr = [u8; BYTECOUNT];

#[derive(Serialize, Debug)]
pub struct Entry {
    number: i64,
    #[serde(serialize_with = "serialize")]
    data: DataArr,
}

Также возможно сделать десериализатор, но это гораздо сложнее из-за частично инициализированного массива , поэтому вместо этого я перейду к реальному решению: использовать serde_with, который может обрабатывать массивы произвольного размера и вложенные массивы . В общем#[serde_as(as = "[_; BYTECOUNT]")]должен работать для массивов, содержащих любой тип, хотя в этом случае конкретно можно использоватьBytes.

      use serde_with::{serde_as, Bytes};
use serde::{Serialize, Deserialize};

const BYTECOUNT: usize = 128;
type DataArr = [u8; BYTECOUNT];

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct Entry {
    number: i64,
    #[serde_as(as = "Bytes")]
    data: DataArr,
}

Вот десериализатор serde_with на случай, если вам интересно или вы захотите написать его самостоятельно.

Я думаю, это просто ожидание константных поддержкидженериков в Serde.

Другие вопросы по тегам