Skip to main content

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::*;