1use alloc::vec::Vec;
3use core::{
4 fmt::{self, Debug},
5 marker::PhantomData,
6};
7use serde::Serialize;
8
9pub trait PackedElement: sealed::Word {}
12mod sealed {
13 use super::*;
14 pub trait Word: bytemuck::Pod + Debug + Eq {
15 #[cfg(feature = "typescript")]
16 const SCHEMA: &'static str;
17 fn read_le(bytes: &[u8]) -> Self;
18 #[cfg(any(target_endian = "big", test))]
19 fn append_le(self, bytes: &mut Vec<u8>);
20 }
21 macro_rules! words {
24 ($($ty:ty),*) => {$(
25 impl super::PackedElement for $ty {}
26 impl Word for $ty {
27 #[cfg(feature = "typescript")]
28 const SCHEMA: &'static str = stringify!($ty);
29 #[inline]
30 fn read_le(bytes: &[u8]) -> Self {
31 Self::from_le_bytes(bytes.try_into().unwrap())
32 }
33 #[cfg(any(target_endian = "big", test))]
34 #[inline]
35 fn append_le(self, bytes: &mut Vec<u8>) {
36 bytes.extend_from_slice(&self.to_le_bytes());
37 }
38 }
39 )*};
40}
41 words!(i16, u16, i32, u32, i64, u64);
42 impl super::PackedElement for [u8; 32] {}
43 impl Word for [u8; 32] {
44 #[cfg(feature = "typescript")]
45 const SCHEMA: &'static str = "hash32";
46 #[inline]
47 fn read_le(bytes: &[u8]) -> Self {
48 bytes.try_into().unwrap()
49 }
50 #[cfg(any(target_endian = "big", test))]
51 #[inline]
52 fn append_le(self, bytes: &mut Vec<u8>) {
53 bytes.extend_from_slice(&self);
54 }
55 }
56}
57
58#[derive(Clone, Debug, Eq, PartialEq)]
71pub struct Packed<T>(Vec<T>);
72impl<T> Packed<T> {
73 pub fn new(values: Vec<T>) -> Self {
75 Self(values)
76 }
77 pub fn as_slice(&self) -> &[T] {
79 &self.0
80 }
81 pub fn into_vec(self) -> Vec<T> {
83 self.0
84 }
85}
86impl<T: PackedElement> Serialize for Packed<T> {
87 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
88 #[cfg(target_endian = "little")]
96 {
97 serializer.serialize_bytes(bytemuck::must_cast_slice(&self.0))
98 }
99 #[cfg(target_endian = "big")]
100 {
101 let mut bytes = Vec::new();
102 let len = self
103 .0
104 .len()
105 .checked_mul(size_of::<T>())
106 .ok_or_else(|| serde::ser::Error::custom("length overflow"))?;
107 bytes
108 .try_reserve_exact(len)
109 .map_err(|_| serde::ser::Error::custom("allocation"))?;
110 for value in &self.0 {
111 value.append_le(&mut bytes);
112 }
113 serializer.serialize_bytes(&bytes)
114 }
115 }
116}
117impl<'de, T: PackedElement> serde::Deserialize<'de> for Packed<T> {
118 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
119 deserializer
120 .deserialize_bytes(PackedVisitor {
121 marker: PhantomData,
122 })
123 .map(Self)
124 }
125}
126struct PackedVisitor<T> {
130 marker: PhantomData<T>,
131}
132impl<T: PackedElement> serde::de::Visitor<'_> for PackedVisitor<T> {
133 type Value = Vec<T>;
134 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.write_str("packed little-endian words")
136 }
137 #[inline(never)]
138 fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
139 let width = size_of::<T>();
140 if !bytes.len().is_multiple_of(width) {
141 return Err(E::custom("invalid packed length"));
142 }
143 let mut values = Vec::new();
144 values
145 .try_reserve_exact(bytes.len() / width)
146 .map_err(|_| E::custom("allocation"))?;
147 values.extend(bytes.chunks_exact(width).map(T::read_le));
148 Ok(values)
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 #[test]
155 fn endian_append_matches_wire_bytes() {
156 use super::sealed::Word;
157 use alloc::vec::Vec;
158 macro_rules! check {
159 ($($ty:ty),*) => {$(
160 let words = [<$ty>::MIN, 1, <$ty>::MAX];
161 let mut bytes = Vec::new();
162 for word in words { word.append_le(&mut bytes); }
163 let expected: Vec<_> = words.iter().flat_map(|word| word.to_le_bytes()).collect();
164 assert_eq!(bytes, expected);
165 for (chunk, word) in bytes.chunks_exact(size_of::<$ty>()).zip(words) {
166 assert_eq!(<$ty>::read_le(chunk), word);
167 }
168 )*};
169 }
170 check!(i16, u16, i32, u32, i64, u64);
171 let hash = core::array::from_fn::<_, 32, _>(|i| i as u8);
172 let mut bytes = Vec::new();
173 hash.append_le(&mut bytes);
174 assert_eq!(bytes, hash);
175 assert_eq!(<[u8; 32]>::read_le(&bytes), hash);
176 }
177}