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<
125    const ZERO_COPY_ALIGN_CHECK: bool,
126    const PREALLOCATION_SIZE_LIMIT: usize,
127    LengthEncoding,
128    ByteOrder,
129    IntEncoding,
130    TagEncoding,
131>
132    Configuration<
133        ZERO_COPY_ALIGN_CHECK,
134        PREALLOCATION_SIZE_LIMIT,
135        LengthEncoding,
136        ByteOrder,
137        IntEncoding,
138        TagEncoding,
139    >
140{
141    #[expect(clippy::new_without_default)]
142    pub const fn new() -> Self {
143        generate()
144    }
145
146    /// Use the given [`SeqLen`] implementation for sequence length encoding.
147    ///
148    /// Default is [`BincodeLen`].
149    ///
150    /// Note that this default can be overridden for individual cases by using
151    /// [`containers`](crate::containers).
152    pub const fn with_length_encoding<L>(
153        self,
154    ) -> Configuration<ZERO_COPY_ALIGN_CHECK, PREALLOCATION_SIZE_LIMIT, L, ByteOrder, IntEncoding>
155    where
156        Configuration<ZERO_COPY_ALIGN_CHECK, PREALLOCATION_SIZE_LIMIT, L>: Config,
157    {
158        generate()
159    }
160
161    /// Use big-endian byte order.
162    ///
163    /// Note that changing the byte order will have a direct impact on zero-copy eligibility.
164    /// Integers are only eligible for zero-copy when configured byte order matches the native byte order.
165    ///
166    /// Default is [`LittleEndian`].
167    pub const fn with_big_endian(
168        self,
169    ) -> Configuration<
170        ZERO_COPY_ALIGN_CHECK,
171        PREALLOCATION_SIZE_LIMIT,
172        LengthEncoding,
173        BigEndian,
174        IntEncoding,
175    > {
176        generate()
177    }
178
179    /// Use little-endian byte order.
180    ///
181    /// Default is [`LittleEndian`].
182    pub const fn with_little_endian(
183        self,
184    ) -> Configuration<
185        ZERO_COPY_ALIGN_CHECK,
186        PREALLOCATION_SIZE_LIMIT,
187        LengthEncoding,
188        LittleEndian,
189        IntEncoding,
190    > {
191        generate()
192    }
193
194    /// Use target platform byte order.
195    ///
196    /// Will use the native byte order of the target platform.
197    #[cfg(target_endian = "little")]
198    pub const fn with_platform_endian(
199        self,
200    ) -> Configuration<
201        ZERO_COPY_ALIGN_CHECK,
202        PREALLOCATION_SIZE_LIMIT,
203        LengthEncoding,
204        LittleEndian,
205        IntEncoding,
206    > {
207        generate()
208    }
209
210    /// Use target platform byte order.
211    ///
212    /// Will use the native byte order of the target platform.
213    #[cfg(target_endian = "big")]
214    pub const fn with_platform_endian(
215        self,
216    ) -> Configuration<
217        ZERO_COPY_ALIGN_CHECK,
218        PREALLOCATION_SIZE_LIMIT,
219        LengthEncoding,
220        BigEndian,
221        IntEncoding,
222    > {
223        generate()
224    }
225
226    /// Use [`FixInt`] for integer encoding.
227    ///
228    /// Default is [`FixInt`].
229    pub const fn with_fixint_encoding(
230        self,
231    ) -> Configuration<
232        ZERO_COPY_ALIGN_CHECK,
233        PREALLOCATION_SIZE_LIMIT,
234        LengthEncoding,
235        ByteOrder,
236        FixInt,
237    > {
238        generate()
239    }
240
241    /// Use [`VarInt`] for integer encoding.
242    ///
243    /// Default is [`FixInt`].
244    ///
245    /// Performance note: variable length integer encoding will hurt serialization and deserialization
246    /// performance significantly relative to fixed width integer encoding. Additionally, all zero-copy
247    /// capabilities on integers will be lost. Variable length integer encoding may be beneficial if
248    /// reducing the resulting size of serialized data is important, but if serialization / deserialization
249    /// performance is important, fixed width integer encoding is highly recommended.
250    pub const fn with_varint_encoding(
251        self,
252    ) -> Configuration<
253        ZERO_COPY_ALIGN_CHECK,
254        PREALLOCATION_SIZE_LIMIT,
255        LengthEncoding,
256        ByteOrder,
257        VarInt,
258    > {
259        generate()
260    }
261
262    /// Use the given [`IntEncoding`] implementation for integer encoding.
263    ///
264    /// Can be used for custom, unofficial integer encodings.
265    ///
266    /// Default is [`FixInt`].
267    pub const fn with_int_encoding<I>(
268        self,
269    ) -> Configuration<ZERO_COPY_ALIGN_CHECK, PREALLOCATION_SIZE_LIMIT, LengthEncoding, ByteOrder, I>
270    where
271        Configuration<
272            ZERO_COPY_ALIGN_CHECK,
273            PREALLOCATION_SIZE_LIMIT,
274            LengthEncoding,
275            ByteOrder,
276            I,
277        >: Config,
278    {
279        generate()
280    }
281
282    /// Enable the zero-copy alignment check.
283    ///
284    /// If enabled, zero-copy deserialization will ensure that pointers are correctly aligned for the target type
285    /// before creating references.
286    /// You should keep this enabled unless you have a very specific use case for disabling it.
287    ///
288    /// This is enabled by default.
289    pub const fn enable_zero_copy_align_check(
290        self,
291    ) -> Configuration<true, PREALLOCATION_SIZE_LIMIT, LengthEncoding, ByteOrder, IntEncoding> {
292        generate()
293    }
294
295    /// Disable the zero-copy alignment check.
296    ///
297    /// When disabled, zero-copy deserialization (`&'de T` and `&'de [T]` for `T: ZeroCopy`)
298    /// will not verify that pointers into the buffer are correctly aligned before forming
299    /// references. Creating a misaligned reference is **undefined behavior**.
300    ///
301    /// # Safety
302    ///
303    /// You must guarantee every zero-copy reference is correctly aligned for its type.
304    ///
305    /// This holds when:
306    /// - The buffer is aligned to at least `align_of::<T>()` for each zero-copy type `T`,
307    ///   and each zero-copy read occurs at an offset that preserves that alignment.
308    /// - Or you only deserialize types with alignment 1 (e.g., `&[u8]`, `&[u8; N]`, `&str`, etc).
309    ///
310    /// Only disable this when you control the serialized layout and can enforce
311    /// alignment; owned deserialization paths are unaffected.
312    pub const unsafe fn disable_zero_copy_align_check(
313        self,
314    ) -> Configuration<false, PREALLOCATION_SIZE_LIMIT, LengthEncoding, ByteOrder, IntEncoding>
315    {
316        generate()
317    }
318
319    /// Set the preallocation size limit in bytes.
320    ///
321    /// wincode will preallocate all sequences up to this limit, or error
322    /// if the size of the allocation would exceed this limit.
323    /// This is used to prevent malicious data from causing
324    /// excessive memory usage or OOM.
325    ///
326    /// The default limit is 4 MiB.
327    pub const fn with_preallocation_size_limit<const LIMIT: usize>(
328        self,
329    ) -> Configuration<ZERO_COPY_ALIGN_CHECK, LIMIT, LengthEncoding, ByteOrder, IntEncoding> {
330        generate()
331    }
332
333    /// Disable the preallocation size limit.
334    ///
335    /// <div class="warning">Warning: only do this if you absolutely trust your input.</div>
336    pub const fn disable_preallocation_size_limit(
337        self,
338    ) -> Configuration<
339        ZERO_COPY_ALIGN_CHECK,
340        PREALLOCATION_SIZE_LIMIT_DISABLED,
341        LengthEncoding,
342        ByteOrder,
343        IntEncoding,
344    > {
345        generate()
346    }
347
348    /// Use the given [`TagEncoding`] implementation for enum discriminant encoding.
349    ///
350    /// Default is [`u32`].
351    ///
352    /// This can be overriden for individual cases with the `#[wincode(tag_encoding = ...)]`
353    /// attribute.
354    pub const fn with_tag_encoding<T>(
355        self,
356    ) -> Configuration<
357        ZERO_COPY_ALIGN_CHECK,
358        PREALLOCATION_SIZE_LIMIT,
359        LengthEncoding,
360        ByteOrder,
361        IntEncoding,
362        T,
363    >
364    where
365        Configuration<
366            ZERO_COPY_ALIGN_CHECK,
367            PREALLOCATION_SIZE_LIMIT,
368            LengthEncoding,
369            ByteOrder,
370            IntEncoding,
371            T,
372        >: Config,
373    {
374        generate()
375    }
376}
377
378/// Trait for accessing configuration values when only the constant knobs are needed
379/// (e.g., `PREALLOCATION_SIZE_LIMIT`, `ZERO_COPY_ALIGN_CHECK`).
380///
381/// Split from [`Config`] to avoid dependency cycles that can overflow the compiler stack,
382/// such as [`SeqLen`] -> [`Config`] -> [`SeqLen`].
383///
384/// Prefer this trait over [`Config`] when you don't need configuration type parameters
385/// that themselves depend on [`Config`] (e.g., [`SeqLen`], which depends on [`ConfigCore`]).
386pub trait ConfigCore: 'static + Sized {
387    const PREALLOCATION_SIZE_LIMIT: Option<usize>;
388    const ZERO_COPY_ALIGN_CHECK: bool;
389    type ByteOrder: ByteOrder;
390    type IntEncoding: IntEncoding<Self::ByteOrder>;
391}
392
393impl<
394    const ZERO_COPY_ALIGN_CHECK: bool,
395    const PREALLOCATION_SIZE_LIMIT: usize,
396    LengthEncoding: 'static,
397    B,
398    I,
399    TagEncoding: 'static,
400> ConfigCore
401    for Configuration<
402        ZERO_COPY_ALIGN_CHECK,
403        PREALLOCATION_SIZE_LIMIT,
404        LengthEncoding,
405        B,
406        I,
407        TagEncoding,
408    >
409where
410    B: ByteOrder,
411    I: IntEncoding<B>,
412{
413    const PREALLOCATION_SIZE_LIMIT: Option<usize> =
414        if PREALLOCATION_SIZE_LIMIT == PREALLOCATION_SIZE_LIMIT_DISABLED {
415            None
416        } else {
417            Some(PREALLOCATION_SIZE_LIMIT)
418        };
419    const ZERO_COPY_ALIGN_CHECK: bool = ZERO_COPY_ALIGN_CHECK;
420    type ByteOrder = B;
421    type IntEncoding = I;
422}
423
424/// Trait for configuration access when you need access to type parameters that depend on [`Config`]
425/// (e.g., [`Config::LengthEncoding`]).
426///
427/// Prefer [`ConfigCore`] when you don't need those configuration type parameters that depend
428/// on [`Config`] (e.g., primitive types).
429pub trait Config: ConfigCore {
430    type LengthEncoding: SeqLen<Self> + 'static;
431    type TagEncoding: TagEncoding<Self> + 'static;
432}
433
434impl<
435    const ZERO_COPY_ALIGN_CHECK: bool,
436    const PREALLOCATION_SIZE_LIMIT: usize,
437    LengthEncoding: 'static,
438    B,
439    I,
440    T,
441> Config for Configuration<ZERO_COPY_ALIGN_CHECK, PREALLOCATION_SIZE_LIMIT, LengthEncoding, B, I, T>
442where
443    LengthEncoding: SeqLen<Self>,
444    T: TagEncoding<Self>,
445    B: ByteOrder,
446    I: IntEncoding<B>,
447{
448    type LengthEncoding = LengthEncoding;
449    type TagEncoding = T;
450}
451
452mod serde;
453pub use serde::*;