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(all(feature = "typescript", not(target_family = "wasm")))]
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(all(feature = "typescript", not(target_family = "wasm")))]
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!(i8, i16, u16, i32, u32, i64, u64);
42 impl super::PackedElement for [u8; 16] {}
43 impl Word for [u8; 16] {
44 #[cfg(all(feature = "typescript", not(target_family = "wasm")))]
45 const SCHEMA: &'static str = "hash16";
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 impl super::PackedElement for [u8; 32] {}
57 impl Word for [u8; 32] {
58 #[cfg(all(feature = "typescript", not(target_family = "wasm")))]
59 const SCHEMA: &'static str = "hash32";
60 #[inline]
61 fn read_le(bytes: &[u8]) -> Self {
62 bytes.try_into().unwrap()
63 }
64 #[cfg(any(target_endian = "big", test))]
65 #[inline]
66 fn append_le(self, bytes: &mut Vec<u8>) {
67 bytes.extend_from_slice(&self);
68 }
69 }
70}
71
72#[derive(Clone, Debug, Eq, PartialEq)]
87pub struct Packed<T>(Vec<T>);
88impl<T> Packed<T> {
89 pub fn new(values: Vec<T>) -> Self {
91 Self(values)
92 }
93 pub fn as_slice(&self) -> &[T] {
95 &self.0
96 }
97 pub fn into_vec(self) -> Vec<T> {
99 self.0
100 }
101}
102impl<T: PackedElement> Serialize for Packed<T> {
103 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
104 serialize_words(&self.0, serializer)
119 }
120}
121pub(crate) fn serialize_words<T: PackedElement, S: serde::Serializer>(
122 words: &[T],
123 serializer: S,
124) -> Result<S::Ok, S::Error> {
125 #[cfg(target_endian = "little")]
126 {
127 serializer.serialize_bytes(bytemuck::must_cast_slice(words))
128 }
129 #[cfg(target_endian = "big")]
130 {
131 let mut bytes = Vec::new();
132 let len = words
133 .len()
134 .checked_mul(size_of::<T>())
135 .ok_or_else(|| serde::ser::Error::custom("length overflow"))?;
136 bytes
137 .try_reserve_exact(len)
138 .map_err(|_| serde::ser::Error::custom("allocation"))?;
139 for value in words {
140 value.append_le(&mut bytes);
141 }
142 serializer.serialize_bytes(&bytes)
143 }
144}
145impl<'de, T: PackedElement> serde::Deserialize<'de> for Packed<T> {
146 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
147 deserializer
148 .deserialize_bytes(PackedVisitor {
149 marker: PhantomData,
150 })
151 .map(Self)
152 }
153}
154macro_rules! decode_words {
159 ($error:ty, $bytes:ident, $width:expr, $excess:expr, $words:expr) => {{
160 if !$bytes.len().is_multiple_of($width) || $excess {
161 return Err(<$error as serde::de::Error>::custom(
162 "invalid packed length",
163 ));
164 }
165 let mut values = alloc::vec::Vec::new();
166 values
167 .try_reserve_exact($bytes.len() / $width)
168 .map_err(|_| <$error as serde::de::Error>::custom("allocation"))?;
169 values.extend($words);
170 Ok(values)
171 }};
172}
173pub(crate) use decode_words;
174
175struct PackedVisitor<T> {
179 marker: PhantomData<T>,
180}
181impl<T: PackedElement> serde::de::Visitor<'_> for PackedVisitor<T> {
182 type Value = Vec<T>;
183 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 f.write_str("packed little-endian words")
185 }
186 #[inline(never)]
187 fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
188 let width = size_of::<T>();
189 decode_words!(
190 E,
191 bytes,
192 width,
193 false,
194 bytes.chunks_exact(width).map(T::read_le)
195 )
196 }
197}
198
199#[cfg(test)]
200mod tests;