wincode/len.rs
1//! Support for heterogenous sequence length encoding.
2use {
3 crate::{
4 SchemaRead, SchemaWrite, TypeMeta,
5 config::{ConfigCore, PREALLOCATION_SIZE_LIMIT_DISABLED},
6 error::{
7 PreallocationError, ReadResult, WriteResult, pointer_sized_decode_error,
8 preallocation_size_limit, write_length_encoding_overflow,
9 },
10 int_encoding::{ByteOrder, Endian},
11 io::{Reader, Writer},
12 },
13 core::{any::type_name, marker::PhantomData},
14};
15
16pub const PREALLOCATION_SIZE_LIMIT_USE_CONFIG: usize = 0;
17
18/// Cap a requested capacity at the `2^(8 * size_of::<K>())` distinct values a
19/// `K` can represent.
20///
21/// A collection that keys on a unique `K` cannot hold more entries than there
22/// are `K` values, so a malicious length keyed on a tiny type (ZST, `u8`, ...)
23/// can drive an oversized `with_capacity` the data can never fill, even within
24/// the preallocation limit. The cap is an upper bound on distinct keys, so it
25/// never under-allocates. `None` (representable count `>= usize::MAX`) means no
26/// useful cap.
27#[cfg(any(feature = "std", feature = "indexmap"))]
28#[inline]
29pub(crate) fn unique_key_capacity<K>(len: usize) -> usize {
30 match u32::try_from(size_of::<K>())
31 .ok()
32 .and_then(|bytes| bytes.checked_mul(u8::BITS))
33 .and_then(|bits| 1usize.checked_shl(bits))
34 {
35 Some(max_keys) => len.min(max_keys),
36 None => len,
37 }
38}
39
40/// [`SeqLen`] level override of configured preallocation size limit.
41#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
42pub enum PreallocationLimitOverride {
43 /// Use the configuration's preallocation size limit.
44 #[default]
45 UseConfig,
46 /// Override with no limit.
47 NoLimit,
48 /// Override with a specific limit, in bytes.
49 Override(usize),
50}
51
52impl PreallocationLimitOverride {
53 /// Convert the given [`PreallocationLimitOverride`] to an `Option<usize>`,
54 /// reconciling with the given configuration.
55 ///
56 /// If the override is [`PreallocationLimitOverride::UseConfig`], then the
57 /// configuration's preallocation size limit is returned.
58 /// If the override is [`PreallocationLimitOverride::NoLimit`], then `None` is returned.
59 /// Otherwise, the override is returned.
60 #[inline]
61 pub const fn to_opt_limit_with_config<C: ConfigCore>(self) -> Option<usize> {
62 match self {
63 PreallocationLimitOverride::UseConfig => C::PREALLOCATION_SIZE_LIMIT,
64 PreallocationLimitOverride::NoLimit => None,
65 PreallocationLimitOverride::Override(limit) => Some(limit),
66 }
67 }
68
69 /// Convert a raw preallocation usize value to a [`PreallocationLimitOverride`].
70 ///
71 /// Handles special case values [`PREALLOCATION_SIZE_LIMIT_USE_CONFIG`] and
72 /// [`PREALLOCATION_SIZE_LIMIT_DISABLED`].
73 #[inline]
74 pub const fn from_usize(limit: usize) -> Self {
75 match limit {
76 PREALLOCATION_SIZE_LIMIT_USE_CONFIG => PreallocationLimitOverride::UseConfig,
77 PREALLOCATION_SIZE_LIMIT_DISABLED => PreallocationLimitOverride::NoLimit,
78 _ => PreallocationLimitOverride::Override(limit),
79 }
80 }
81}
82
83/// Behavior to support heterogenous sequence length encoding.
84///
85/// It is possible for sequences to have different length encoding schemes.
86/// This trait abstracts over that possibility, allowing users to specify
87/// the length encoding scheme for a sequence.
88///
89/// # Safety
90///
91/// Implementors must adhere to the Safety section of the method `write_bytes_needed`.
92pub unsafe trait SeqLen<C: ConfigCore> {
93 /// [`SeqLen`] level override of configured preallocation size limit, in bytes.
94 ///
95 /// Allows specializing specific uses of a given [`SeqLen`] implementation
96 /// to override any configured preallocation size limit.
97 const PREALLOCATION_SIZE_LIMIT_OVERRIDE: PreallocationLimitOverride =
98 PreallocationLimitOverride::UseConfig;
99
100 #[inline]
101 fn prealloc_check<T>(len: usize) -> Result<(), PreallocationError> {
102 fn check(len: usize, type_size: usize, limit: usize) -> Result<(), PreallocationError> {
103 let needed = len
104 .checked_mul(type_size)
105 .ok_or_else(|| preallocation_size_limit(usize::MAX, limit))?;
106 if needed > limit {
107 return Err(preallocation_size_limit(needed, limit));
108 }
109 Ok(())
110 }
111 // Everything here can be const-folded by the compiler.
112 if let Some(prealloc_limit) =
113 Self::PREALLOCATION_SIZE_LIMIT_OVERRIDE.to_opt_limit_with_config::<C>()
114 {
115 // ZSTs do not require element storage in Vec-like containers, but
116 // user-controlled lengths still drive iteration and may allocate
117 // collection metadata. Charge one byte per ZST element so the
118 // preallocation limit also bounds sequence length.
119 check(len, size_of::<T>().max(1), prealloc_limit)?;
120 }
121 Ok(())
122 }
123
124 /// Read the length of a sequence from the reader, where
125 /// `T` is the type of the sequence elements. This can be used to
126 /// enforce size constraints for preallocations.
127 ///
128 /// May return an error if some length condition is not met
129 /// (e.g., size constraints, overflow, etc.).
130 #[inline]
131 fn read_prealloc_check<'de, T>(reader: impl Reader<'de>) -> ReadResult<usize> {
132 let len = Self::read(reader)?;
133 Self::prealloc_check::<T>(len)?;
134 Ok(len)
135 }
136 /// Read the length of a sequence, without doing any preallocation size checks.
137 ///
138 /// Note this may still return typical read errors and there is no unsafety implied.
139 fn read<'de>(reader: impl Reader<'de>) -> ReadResult<usize>;
140 /// Write the length of a sequence to the writer.
141 fn write(writer: impl Writer, len: usize) -> WriteResult<()>;
142 /// Calculate the number of bytes needed to write the given length.
143 ///
144 /// Return an error if the written size would be larger than the
145 /// corresponding allocation limit while reading.
146 ///
147 /// # Safety
148 ///
149 /// If `Ok(…)` is returned, it must contain the exact number of bytes
150 /// written by the `write` function for this particular object instance.
151 fn write_bytes_needed_prealloc_check<T>(len: usize) -> WriteResult<usize> {
152 Self::prealloc_check::<T>(len)?;
153 Self::write_bytes_needed(len)
154 }
155 /// Calculate the number of bytes needed to write the given length.
156 ///
157 /// Useful for variable length encoding schemes.
158 ///
159 /// # Safety
160 ///
161 /// If `Ok(…)` is returned, it must contain the exact number of bytes
162 /// written by the `write` function for this particular object instance.
163 fn write_bytes_needed(len: usize) -> WriteResult<usize>;
164}
165
166/// Use the configuration's integer encoding for sequence length encoding.
167///
168/// For example, if the configuration's integer encoding is `FixInt`, then `UseIntLen<u64>`
169/// will use the fixed-width u64 encoding.
170/// If the configuration's integer encoding is `VarInt`, then `UseIntLen<u64>` will use
171/// the variable-width u64 encoding.
172///
173/// This is bincode's default behavior.
174///
175/// Allows overriding the preallocation size limit per individual use.
176///
177/// # Examples
178///
179/// Override the preallocation size limit to 8 bytes.
180///
181/// ```
182/// # #[cfg(feature = "alloc")] {
183/// # use wincode::{containers, len::UseIntLen, SchemaRead, SchemaWrite};
184/// type Max8Bytes = UseIntLen<u32, 8>;
185///
186/// #[derive(SchemaWrite, SchemaRead)]
187/// struct OverrideLen {
188/// #[wincode(with = "containers::Vec<u8, Max8Bytes>")]
189/// bytes: Vec<u8>,
190/// }
191///
192/// let data_ok = OverrideLen { bytes: vec![0; 8] };
193/// let serialized = wincode::serialize(&data_ok).unwrap();
194/// assert!(wincode::deserialize::<OverrideLen>(&serialized).is_ok());
195///
196/// let data_err = OverrideLen { bytes: vec![0; 9] };
197/// assert!(wincode::serialize(&data_err).is_err());
198/// let serialized = wincode::serialize(&vec![0; 9]).unwrap();
199/// assert!(wincode::deserialize::<OverrideLen>(&serialized).is_err());
200/// # }
201/// ```
202pub struct UseIntLen<T, const PREALLOCATION_SIZE_LIMIT: usize = PREALLOCATION_SIZE_LIMIT_USE_CONFIG>(
203 PhantomData<T>,
204);
205
206unsafe impl<const PREALLOCATION_SIZE_LIMIT: usize, T, C: ConfigCore> SeqLen<C>
207 for UseIntLen<T, PREALLOCATION_SIZE_LIMIT>
208where
209 T: SchemaWrite<C> + for<'de> SchemaRead<'de, C>,
210 T::Src: TryFrom<usize>,
211 usize: for<'de> TryFrom<<T as SchemaRead<'de, C>>::Dst>,
212{
213 const PREALLOCATION_SIZE_LIMIT_OVERRIDE: PreallocationLimitOverride =
214 PreallocationLimitOverride::from_usize(PREALLOCATION_SIZE_LIMIT);
215
216 #[inline(always)]
217 fn read<'de>(reader: impl Reader<'de>) -> ReadResult<usize> {
218 let len = T::get(reader)?;
219 let Ok(len) = usize::try_from(len) else {
220 return Err(pointer_sized_decode_error());
221 };
222 Ok(len)
223 }
224
225 #[inline(always)]
226 fn write(writer: impl Writer, len: usize) -> WriteResult<()> {
227 let Ok(len) = T::Src::try_from(len) else {
228 return Err(write_length_encoding_overflow(type_name::<T::Src>()));
229 };
230 T::write(writer, &len)
231 }
232
233 #[inline(always)]
234 fn write_bytes_needed(len: usize) -> WriteResult<usize> {
235 if let TypeMeta::Static { size, .. } = <T as SchemaWrite<C>>::TYPE_META {
236 return Ok(size);
237 }
238 let Ok(len) = T::Src::try_from(len) else {
239 return Err(write_length_encoding_overflow(type_name::<T::Src>()));
240 };
241 T::size_of(&len)
242 }
243}
244
245/// Allow using integer primitives directly as [`SeqLen`].
246///
247/// Will use the configuration's integer encoding.
248macro_rules! impl_use_int_primitive {
249 ($($type:ty),+) => {
250 $(
251 unsafe impl<C: ConfigCore> SeqLen<C> for $type {
252 #[inline(always)]
253 #[allow(irrefutable_let_patterns)]
254 fn read<'de>(reader: impl Reader<'de>) -> ReadResult<usize> {
255 let len = <$type as SchemaRead<C>>::get(reader)?;
256 let Ok(len) = usize::try_from(len) else {
257 return Err(pointer_sized_decode_error());
258 };
259 Ok(len)
260 }
261
262 #[inline(always)]
263 fn write(writer: impl Writer, len: usize) -> WriteResult<()> {
264 let Ok(len) = <$type>::try_from(len) else {
265 return Err(write_length_encoding_overflow(type_name::<$type>()));
266 };
267 <$type as SchemaWrite<C>>::write(writer, &len)
268 }
269
270 #[inline(always)]
271 fn write_bytes_needed(len: usize) -> WriteResult<usize> {
272 if let TypeMeta::Static { size, .. } = <$type as SchemaWrite<C>>::TYPE_META {
273 return Ok(size);
274 }
275 let Ok(len) = <$type>::try_from(len) else {
276 return Err(write_length_encoding_overflow(type_name::<$type>()));
277 };
278 <$type as SchemaWrite<C>>::size_of(&len)
279 }
280 }
281 )+
282 };
283}
284
285impl_use_int_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
286
287/// Fixed-width integer length encoding.
288///
289/// Integers respect the configured byte order.
290///
291/// Allows overriding the preallocation size limit per individual use.
292///
293/// # Examples
294///
295/// Override the preallocation size limit to 8 bytes.
296///
297/// ```
298/// # #[cfg(feature = "alloc")] {
299/// # use wincode::{containers, len::FixIntLen, SchemaRead, SchemaWrite};
300/// type Max8Bytes = FixIntLen<u32, 8>;
301///
302/// #[derive(SchemaWrite, SchemaRead)]
303/// struct OverrideLen {
304/// #[wincode(with = "containers::Vec<u8, Max8Bytes>")]
305/// bytes: Vec<u8>,
306/// }
307///
308/// let data_ok = OverrideLen { bytes: vec![0; 8] };
309/// let serialized = wincode::serialize(&data_ok).unwrap();
310/// assert!(wincode::deserialize::<OverrideLen>(&serialized).is_ok());
311///
312/// let data_err = OverrideLen { bytes: vec![0; 9] };
313/// assert!(wincode::serialize(&data_err).is_err());
314/// let serialized = wincode::serialize(&vec![0; 9]).unwrap();
315/// assert!(wincode::deserialize::<OverrideLen>(&serialized).is_err());
316/// # }
317/// ```
318pub struct FixIntLen<T, const PREALLOCATION_SIZE_LIMIT: usize = PREALLOCATION_SIZE_LIMIT_USE_CONFIG>(
319 PhantomData<T>,
320);
321
322macro_rules! impl_fix_int {
323 ($type:ty) => {
324 unsafe impl<const PREALLOCATION_SIZE_LIMIT: usize, C: ConfigCore> SeqLen<C>
325 for FixIntLen<$type, PREALLOCATION_SIZE_LIMIT>
326 {
327 const PREALLOCATION_SIZE_LIMIT_OVERRIDE: PreallocationLimitOverride =
328 PreallocationLimitOverride::from_usize(PREALLOCATION_SIZE_LIMIT);
329
330 #[inline(always)]
331 #[allow(irrefutable_let_patterns)]
332 fn read<'de>(mut reader: impl Reader<'de>) -> ReadResult<usize> {
333 let bytes = reader.take_array::<{ size_of::<$type>() }>()?;
334 let len = match C::ByteOrder::ENDIAN {
335 Endian::Big => <$type>::from_be_bytes(bytes),
336 Endian::Little => <$type>::from_le_bytes(bytes),
337 };
338 let Ok(len) = usize::try_from(len) else {
339 return Err(pointer_sized_decode_error());
340 };
341 Ok(len)
342 }
343
344 #[inline(always)]
345 fn write(mut writer: impl Writer, len: usize) -> WriteResult<()> {
346 let Ok(len) = <$type>::try_from(len) else {
347 return Err(write_length_encoding_overflow(type_name::<$type>()));
348 };
349 let bytes = match C::ByteOrder::ENDIAN {
350 Endian::Big => len.to_be_bytes(),
351 Endian::Little => len.to_le_bytes(),
352 };
353 writer.write(&bytes)?;
354 Ok(())
355 }
356
357 #[inline(always)]
358 fn write_bytes_needed(_: usize) -> WriteResult<usize> {
359 Ok(size_of::<$type>())
360 }
361 }
362 };
363}
364
365impl_fix_int!(u8);
366impl_fix_int!(u16);
367impl_fix_int!(u32);
368impl_fix_int!(u64);
369impl_fix_int!(u128);
370
371impl_fix_int!(i8);
372impl_fix_int!(i16);
373impl_fix_int!(i32);
374impl_fix_int!(i64);
375impl_fix_int!(i128);
376
377/// Bincode always uses a `u64` encoded with the configuration's integer encoding.
378///
379/// Allows overriding the preallocation size limit per individual use.
380///
381/// # Examples
382///
383/// Override the preallocation size limit to 8 bytes.
384///
385/// ```
386/// # #[cfg(feature = "alloc")] {
387/// # use wincode::{containers, len::BincodeLen, SchemaRead, SchemaWrite};
388/// type Max8Bytes = BincodeLen<8>;
389///
390/// #[derive(SchemaWrite, SchemaRead)]
391/// struct OverrideLen {
392/// #[wincode(with = "containers::Vec<u8, Max8Bytes>")]
393/// bytes: Vec<u8>,
394/// }
395///
396/// let data_ok = OverrideLen { bytes: vec![0; 8] };
397/// let serialized = wincode::serialize(&data_ok).unwrap();
398/// assert!(wincode::deserialize::<OverrideLen>(&serialized).is_ok());
399///
400/// let data_err = OverrideLen { bytes: vec![0; 9] };
401/// assert!(wincode::serialize(&data_err).is_err());
402/// let serialized = wincode::serialize(&vec![0; 9]).unwrap();
403/// assert!(wincode::deserialize::<OverrideLen>(&serialized).is_err());
404/// # }
405/// ```
406pub type BincodeLen<const PREALLOCATION_SIZE_LIMIT: usize = PREALLOCATION_SIZE_LIMIT_USE_CONFIG> =
407 UseIntLen<u64, PREALLOCATION_SIZE_LIMIT>;
408
409#[cfg(all(test, any(feature = "std", feature = "indexmap")))]
410mod tests {
411 use super::unique_key_capacity;
412
413 #[test]
414 fn caps_at_representable_key_count() {
415 // ZST: 1 value; u8: 256; u16: 65536. `len` below the cap is untouched;
416 // 8+ byte keys represent >= usize::MAX values, so no cap applies.
417 assert_eq!(unique_key_capacity::<()>(usize::MAX), 1);
418 assert_eq!(unique_key_capacity::<()>(0), 0);
419 assert_eq!(unique_key_capacity::<u8>(usize::MAX), 256);
420 assert_eq!(unique_key_capacity::<u8>(10), 10);
421 assert_eq!(unique_key_capacity::<u16>(usize::MAX), 1 << 16);
422 assert_eq!(unique_key_capacity::<u64>(usize::MAX), usize::MAX);
423 assert_eq!(unique_key_capacity::<[u8; 32]>(12_345), 12_345);
424 }
425}