wincode/config/mod.rs
1//! Global configuration for wincode.
2//!
3//! This module provides configuration types and structs for configuring wincode's behavior.
4//! See [`Configuration`] for more details on how to configure wincode.
5//!
6//! Additionally, this module provides traits and functions that mirror the serialization,
7//! deserialization, and zero-copy traits and functions from the crate root, but with an
8//! additional configuration parameter.
9use {
10 crate::{
11 int_encoding::{BigEndian, ByteOrder, FixInt, IntEncoding, LittleEndian, VarInt},
12 len::{BincodeLen, SeqLen},
13 tag_encoding::TagEncoding,
14 },
15 core::marker::PhantomData,
16};
17
18pub const DEFAULT_PREALLOCATION_SIZE_LIMIT: usize = 4 << 20; // 4 MiB
19pub const PREALLOCATION_SIZE_LIMIT_DISABLED: usize = usize::MAX;
20
21/// Compile-time configuration for runtime behavior.
22///
23/// Defaults:
24/// - Zero-copy alignment check is enabled.
25/// - Preallocation size limit is 4 MiB.
26/// - Length encoding is [`BincodeLen`].
27/// - Byte order is [`LittleEndian`].
28/// - Integer encoding is [`FixInt`].
29/// - Tag encoding is [`u32`].
30pub struct Configuration<
31 const ZERO_COPY_ALIGN_CHECK: bool = true,
32 const PREALLOCATION_SIZE_LIMIT: usize = DEFAULT_PREALLOCATION_SIZE_LIMIT,
33 LengthEncoding = BincodeLen,
34 ByteOrder = LittleEndian,
35 IntEncoding = FixInt,
36 TagEncoding = u32,
37> {
38 _l: PhantomData<LengthEncoding>,
39 _b: PhantomData<ByteOrder>,
40 _i: PhantomData<IntEncoding>,
41 _t: PhantomData<TagEncoding>,
42}
43
44impl<
45 const ZERO_COPY_ALIGN_CHECK: bool,
46 const PREALLOCATION_SIZE_LIMIT: usize,
47 LengthEncoding,
48 ByteOrder,
49 IntEncoding,
50 TagEncoding,
51> Clone
52 for Configuration<
53 ZERO_COPY_ALIGN_CHECK,
54 PREALLOCATION_SIZE_LIMIT,
55 LengthEncoding,
56 ByteOrder,
57 IntEncoding,
58 TagEncoding,
59 >
60{
61 fn clone(&self) -> Self {
62 *self
63 }
64}
65
66impl<
67 const ZERO_COPY_ALIGN_CHECK: bool,
68 const PREALLOCATION_SIZE_LIMIT: usize,
69 LengthEncoding,
70 ByteOrder,
71 IntEncoding,
72 TagEncoding,
73> Copy
74 for Configuration<
75 ZERO_COPY_ALIGN_CHECK,
76 PREALLOCATION_SIZE_LIMIT,
77 LengthEncoding,
78 ByteOrder,
79 IntEncoding,
80 TagEncoding,
81 >
82{
83}
84
85const fn generate<
86 const ZERO_COPY_ALIGN_CHECK: bool,
87 const PREALLOCATION_SIZE_LIMIT: usize,
88 LengthEncoding,
89 ByteOrder,
90 IntEncoding,
91 TagEncoding,
92>() -> Configuration<
93 ZERO_COPY_ALIGN_CHECK,
94 PREALLOCATION_SIZE_LIMIT,
95 LengthEncoding,
96 ByteOrder,
97 IntEncoding,
98 TagEncoding,
99> {
100 Configuration {
101 _l: PhantomData,
102 _b: PhantomData,
103 _i: PhantomData,
104 _t: PhantomData,
105 }
106}
107
108impl Configuration {
109 /// Create a new configuration with the default settings.
110 ///
111 /// Defaults:
112 /// - Zero-copy alignment check is enabled.
113 /// - Preallocation size limit is 4 MiB.
114 /// - Length encoding is [`BincodeLen`].
115 /// - Byte order is [`LittleEndian`].
116 /// - Integer encoding is [`FixInt`].
117 pub const fn default() -> DefaultConfig {
118 generate()
119 }
120}
121
122pub type DefaultConfig = Configuration;
123
124impl<const PREALLOCATION_SIZE_LIMIT: usize, LengthEncoding, ByteOrder, IntEncoding, TagEncoding>
125 Configuration<
126 true,
127 PREALLOCATION_SIZE_LIMIT,
128 LengthEncoding,
129 ByteOrder,
130 IntEncoding,
131 TagEncoding,
132 >
133{
134 // This impl is deliberately bounded to `ZERO_COPY_ALIGN_CHECK == true` rather than
135 // being generic over it.
136 //
137 // If `new` were available for `false`, safe code could write
138 // `Configuration::<false>::new()` to obtain an alignment-check-disabled config,
139 // bypassing the `unsafe disable_zero_copy_align_check` gate.
140 #[expect(clippy::new_without_default)]
141 pub const fn new() -> Self {
142 generate()
143 }
144}
145
146impl<
147 const ZERO_COPY_ALIGN_CHECK: bool,
148 const PREALLOCATION_SIZE_LIMIT: usize,
149 LengthEncoding,
150 ByteOrder,
151 IntEncoding,
152 TagEncoding,
153>
154 Configuration<
155 ZERO_COPY_ALIGN_CHECK,
156 PREALLOCATION_SIZE_LIMIT,
157 LengthEncoding,
158 ByteOrder,
159 IntEncoding,
160 TagEncoding,
161 >
162{
163 /// Use the given [`SeqLen`] implementation for sequence length encoding.
164 ///
165 /// Default is [`BincodeLen`].
166 ///
167 /// Note that this default can be overridden for individual cases by using
168 /// [`containers`](crate::containers).
169 pub const fn with_length_encoding<L>(
170 self,
171 ) -> Configuration<
172 ZERO_COPY_ALIGN_CHECK,
173 PREALLOCATION_SIZE_LIMIT,
174 L,
175 ByteOrder,
176 IntEncoding,
177 TagEncoding,
178 >
179 where
180 Configuration<
181 ZERO_COPY_ALIGN_CHECK,
182 PREALLOCATION_SIZE_LIMIT,
183 L,
184 ByteOrder,
185 IntEncoding,
186 TagEncoding,
187 >: Config,
188 {
189 generate()
190 }
191
192 /// Use big-endian byte order.
193 ///
194 /// Note that changing the byte order will have a direct impact on zero-copy eligibility.
195 /// Integers are only eligible for zero-copy when configured byte order matches the native byte order.
196 ///
197 /// Default is [`LittleEndian`].
198 pub const fn with_big_endian(
199 self,
200 ) -> Configuration<
201 ZERO_COPY_ALIGN_CHECK,
202 PREALLOCATION_SIZE_LIMIT,
203 LengthEncoding,
204 BigEndian,
205 IntEncoding,
206 TagEncoding,
207 > {
208 generate()
209 }
210
211 /// Use little-endian byte order.
212 ///
213 /// Default is [`LittleEndian`].
214 pub const fn with_little_endian(
215 self,
216 ) -> Configuration<
217 ZERO_COPY_ALIGN_CHECK,
218 PREALLOCATION_SIZE_LIMIT,
219 LengthEncoding,
220 LittleEndian,
221 IntEncoding,
222 TagEncoding,
223 > {
224 generate()
225 }
226
227 /// Use target platform byte order.
228 ///
229 /// Will use the native byte order of the target platform.
230 #[cfg(target_endian = "little")]
231 pub const fn with_platform_endian(
232 self,
233 ) -> Configuration<
234 ZERO_COPY_ALIGN_CHECK,
235 PREALLOCATION_SIZE_LIMIT,
236 LengthEncoding,
237 LittleEndian,
238 IntEncoding,
239 TagEncoding,
240 > {
241 generate()
242 }
243
244 /// Use target platform byte order.
245 ///
246 /// Will use the native byte order of the target platform.
247 #[cfg(target_endian = "big")]
248 pub const fn with_platform_endian(
249 self,
250 ) -> Configuration<
251 ZERO_COPY_ALIGN_CHECK,
252 PREALLOCATION_SIZE_LIMIT,
253 LengthEncoding,
254 BigEndian,
255 IntEncoding,
256 TagEncoding,
257 > {
258 generate()
259 }
260
261 /// Use [`FixInt`] for integer encoding.
262 ///
263 /// Default is [`FixInt`].
264 pub const fn with_fixint_encoding(
265 self,
266 ) -> Configuration<
267 ZERO_COPY_ALIGN_CHECK,
268 PREALLOCATION_SIZE_LIMIT,
269 LengthEncoding,
270 ByteOrder,
271 FixInt,
272 TagEncoding,
273 > {
274 generate()
275 }
276
277 /// Use [`VarInt`] for integer encoding.
278 ///
279 /// Default is [`FixInt`].
280 ///
281 /// Performance note: variable length integer encoding will hurt serialization and deserialization
282 /// performance significantly relative to fixed width integer encoding. Additionally, all zero-copy
283 /// capabilities on integers will be lost. Variable length integer encoding may be beneficial if
284 /// reducing the resulting size of serialized data is important, but if serialization / deserialization
285 /// performance is important, fixed width integer encoding is highly recommended.
286 pub const fn with_varint_encoding(
287 self,
288 ) -> Configuration<
289 ZERO_COPY_ALIGN_CHECK,
290 PREALLOCATION_SIZE_LIMIT,
291 LengthEncoding,
292 ByteOrder,
293 VarInt,
294 TagEncoding,
295 > {
296 generate()
297 }
298
299 /// Use the given [`IntEncoding`] implementation for integer encoding.
300 ///
301 /// Can be used for custom, unofficial integer encodings.
302 ///
303 /// Default is [`FixInt`].
304 pub const fn with_int_encoding<I>(
305 self,
306 ) -> Configuration<
307 ZERO_COPY_ALIGN_CHECK,
308 PREALLOCATION_SIZE_LIMIT,
309 LengthEncoding,
310 ByteOrder,
311 I,
312 TagEncoding,
313 >
314 where
315 Configuration<
316 ZERO_COPY_ALIGN_CHECK,
317 PREALLOCATION_SIZE_LIMIT,
318 LengthEncoding,
319 ByteOrder,
320 I,
321 TagEncoding,
322 >: Config,
323 {
324 generate()
325 }
326
327 /// Enable the zero-copy alignment check.
328 ///
329 /// If enabled, zero-copy deserialization will ensure that pointers are correctly aligned for the target type
330 /// before creating references.
331 /// You should keep this enabled unless you have a very specific use case for disabling it.
332 ///
333 /// This is enabled by default.
334 pub const fn enable_zero_copy_align_check(
335 self,
336 ) -> Configuration<
337 true,
338 PREALLOCATION_SIZE_LIMIT,
339 LengthEncoding,
340 ByteOrder,
341 IntEncoding,
342 TagEncoding,
343 > {
344 generate()
345 }
346
347 /// Disable the zero-copy alignment check.
348 ///
349 /// When disabled, zero-copy deserialization (`&'de T` and `&'de [T]` for `T: ZeroCopy`)
350 /// will not verify that pointers into the buffer are correctly aligned before forming
351 /// references. Creating a misaligned reference is **undefined behavior**.
352 ///
353 /// # Safety
354 ///
355 /// You must guarantee every zero-copy reference is correctly aligned for its type.
356 ///
357 /// This holds when:
358 /// - The buffer is aligned to at least `align_of::<T>()` for each zero-copy type `T`,
359 /// and each zero-copy read occurs at an offset that preserves that alignment.
360 /// - Or you only deserialize types with alignment 1 (e.g., `&[u8]`, `&[u8; N]`, `&str`, etc).
361 ///
362 /// Only disable this when you control the serialized layout and can enforce
363 /// alignment; owned deserialization paths are unaffected.
364 pub const unsafe fn disable_zero_copy_align_check(
365 self,
366 ) -> Configuration<
367 false,
368 PREALLOCATION_SIZE_LIMIT,
369 LengthEncoding,
370 ByteOrder,
371 IntEncoding,
372 TagEncoding,
373 > {
374 generate()
375 }
376
377 /// Set the preallocation size limit in bytes.
378 ///
379 /// wincode will preallocate all sequences up to this limit, or error
380 /// if the size of the allocation would exceed this limit.
381 /// This is used to prevent malicious data from causing
382 /// excessive memory usage or OOM.
383 ///
384 /// The default limit is 4 MiB.
385 pub const fn with_preallocation_size_limit<const LIMIT: usize>(
386 self,
387 ) -> Configuration<
388 ZERO_COPY_ALIGN_CHECK,
389 LIMIT,
390 LengthEncoding,
391 ByteOrder,
392 IntEncoding,
393 TagEncoding,
394 > {
395 generate()
396 }
397
398 /// Disable the preallocation size limit.
399 ///
400 /// <div class="warning">Warning: only do this if you absolutely trust your input.</div>
401 pub const fn disable_preallocation_size_limit(
402 self,
403 ) -> Configuration<
404 ZERO_COPY_ALIGN_CHECK,
405 PREALLOCATION_SIZE_LIMIT_DISABLED,
406 LengthEncoding,
407 ByteOrder,
408 IntEncoding,
409 TagEncoding,
410 > {
411 generate()
412 }
413
414 /// Use the given [`TagEncoding`] implementation for enum discriminant encoding.
415 ///
416 /// Default is [`u32`].
417 ///
418 /// This can be overriden for individual cases with the `#[wincode(tag_encoding = ...)]`
419 /// attribute.
420 pub const fn with_tag_encoding<T>(
421 self,
422 ) -> Configuration<
423 ZERO_COPY_ALIGN_CHECK,
424 PREALLOCATION_SIZE_LIMIT,
425 LengthEncoding,
426 ByteOrder,
427 IntEncoding,
428 T,
429 >
430 where
431 Configuration<
432 ZERO_COPY_ALIGN_CHECK,
433 PREALLOCATION_SIZE_LIMIT,
434 LengthEncoding,
435 ByteOrder,
436 IntEncoding,
437 T,
438 >: Config,
439 {
440 generate()
441 }
442}
443
444/// Trait for accessing configuration values when only the constant knobs are needed
445/// (e.g., `PREALLOCATION_SIZE_LIMIT`, `ZERO_COPY_ALIGN_CHECK`).
446///
447/// Split from [`Config`] to avoid dependency cycles that can overflow the compiler stack,
448/// such as [`SeqLen`] -> [`Config`] -> [`SeqLen`].
449///
450/// Prefer this trait over [`Config`] when you don't need configuration type parameters
451/// that themselves depend on [`Config`] (e.g., [`SeqLen`], which depends on [`ConfigCore`]).
452pub trait ConfigCore: 'static + Sized {
453 const PREALLOCATION_SIZE_LIMIT: Option<usize>;
454 const ZERO_COPY_ALIGN_CHECK: bool;
455 type ByteOrder: ByteOrder;
456 type IntEncoding: IntEncoding<Self::ByteOrder>;
457}
458
459impl<
460 const ZERO_COPY_ALIGN_CHECK: bool,
461 const PREALLOCATION_SIZE_LIMIT: usize,
462 LengthEncoding: 'static,
463 B,
464 I,
465 TagEncoding: 'static,
466> ConfigCore
467 for Configuration<
468 ZERO_COPY_ALIGN_CHECK,
469 PREALLOCATION_SIZE_LIMIT,
470 LengthEncoding,
471 B,
472 I,
473 TagEncoding,
474 >
475where
476 B: ByteOrder,
477 I: IntEncoding<B>,
478{
479 const PREALLOCATION_SIZE_LIMIT: Option<usize> =
480 if PREALLOCATION_SIZE_LIMIT == PREALLOCATION_SIZE_LIMIT_DISABLED {
481 None
482 } else {
483 Some(PREALLOCATION_SIZE_LIMIT)
484 };
485 const ZERO_COPY_ALIGN_CHECK: bool = ZERO_COPY_ALIGN_CHECK;
486 type ByteOrder = B;
487 type IntEncoding = I;
488}
489
490/// Trait for configuration access when you need access to type parameters that depend on [`Config`]
491/// (e.g., [`Config::LengthEncoding`]).
492///
493/// Prefer [`ConfigCore`] when you don't need those configuration type parameters that depend
494/// on [`Config`] (e.g., primitive types).
495pub trait Config: ConfigCore {
496 type LengthEncoding: SeqLen<Self> + 'static;
497 type TagEncoding: TagEncoding<Self> + 'static;
498}
499
500impl<
501 const ZERO_COPY_ALIGN_CHECK: bool,
502 const PREALLOCATION_SIZE_LIMIT: usize,
503 LengthEncoding: 'static,
504 B,
505 I,
506 T,
507> Config for Configuration<ZERO_COPY_ALIGN_CHECK, PREALLOCATION_SIZE_LIMIT, LengthEncoding, B, I, T>
508where
509 LengthEncoding: SeqLen<Self>,
510 T: TagEncoding<Self>,
511 B: ByteOrder,
512 I: IntEncoding<B>,
513{
514 type LengthEncoding = LengthEncoding;
515 type TagEncoding = T;
516}
517
518mod serde;
519pub use serde::*;