Skip to main content

bincode_next/
config.rs

1//! The config module is used to change the behavior of bincode's encoding and decoding logic.
2//!
3//! *Important* make sure you use the same config for encoding and decoding, or else bincode will not work properly.
4//!
5//! To use a config, first create a type of [Configuration]. This type will implement trait [Config] for use with bincode.
6//!
7//! ```
8//! let config = bincode_next::config::standard()
9//!     // pick one of:
10//!     .with_big_endian()
11//!     .with_little_endian()
12//!     // pick one of:
13//!     .with_variable_int_encoding()
14//!     .with_fixed_int_encoding()
15//!     // pick one of:
16//!     .with_bit_packing()
17//!     .with_no_bit_packing();
18//! ```
19//!
20//! See [Configuration] for more information on the configuration options.
21
22#[doc(hidden)]
23pub use self::internal::*;
24use core::marker::PhantomData;
25
26/// The Configuration struct is used to build bincode configurations. The [Config] trait is implemented
27/// by this struct when a valid configuration has been constructed.
28///
29/// The following methods are mutually exclusive and will overwrite each other. The last call to one of these methods determines the behavior of the configuration:
30///
31/// - [`with_little_endian`\] and [`with_big_endian`\]
32/// - [`with_fixed_int_encoding`\] and [`with_variable_int_encoding`\]
33/// - [`with_bit_packing`\] and [`with_no_bit_packing`\]
34///
35///
36/// [with_little_endian]: #method.with_little_endian
37/// [with_big_endian]: #method.with_big_endian
38/// [with_fixed_int_encoding]: #method.with_fixed_int_encoding
39/// [with_variable_int_encoding]: #method.with_variable_int_encoding
40/// [with_bit_packing]: #method.with_bit_packing
41/// [with_no_bit_packing]: #method.with_no_bit_packing
42#[derive(Copy, Clone, Debug)]
43pub struct Configuration<
44    E = LittleEndian,
45    I = Varint,
46    L = NoLimit,
47    B = SkipBitPacking,
48    O = LsbFirst,
49    F = FingerprintDisabled,
50    FO = BincodeFormat,
51> {
52    _e: PhantomData<E>,
53    _i: PhantomData<I>,
54    _l: PhantomData<L>,
55    _b: PhantomData<B>,
56    _o: PhantomData<O>,
57    _f: PhantomData<F>,
58    _fo: PhantomData<FO>,
59}
60
61/// The modes for CBOR deterministic encoding.
62#[derive(Copy, Clone, Debug, PartialEq, Eq)]
63#[repr(u8)]
64pub enum CborDeterministicMode {
65    /// No deterministic encoding requirements.
66    None = 0,
67    /// Core deterministic encoding (RFC 8949 §4.2.1).
68    Core = 1,
69    /// Length-first deterministic encoding (RFC 8949 §4.2.3).
70    LengthFirst = 2,
71}
72
73/// Detailed options for CBOR encoding.
74#[allow(clippy::struct_excessive_bools)]
75#[derive(Copy, Clone, Debug, PartialEq, Eq)]
76pub struct CborOptions {
77    /// Deterministic encoding mode.
78    pub deterministic_mode: CborDeterministicMode,
79    /// Preferred serialization for floating-point values (§4.1).
80    pub preferred_float: bool,
81    /// Canonical NaN encoding (0xf97e00).
82    pub canonical_nan: bool,
83    /// Normalize negative zero (-0.0) to positive zero (0.0).
84    pub normalize_neg_zero: bool,
85    /// Allow indefinite-length items.
86    pub allow_indefinite: bool,
87    /// Strict tag validity checking.
88    pub strict_tags: bool,
89}
90
91impl CborOptions {
92    /// Default CBOR options.
93    pub const DEFAULT: Self = Self {
94        deterministic_mode: CborDeterministicMode::None,
95        preferred_float: true,
96        canonical_nan: true,
97        normalize_neg_zero: false,
98        allow_indefinite: true,
99        strict_tags: false,
100    };
101    /// Default options for deterministic CBOR.
102    pub const DETERMINISTIC: Self = Self {
103        deterministic_mode: CborDeterministicMode::Core,
104        preferred_float: true,
105        canonical_nan: true,
106        normalize_neg_zero: false, // RFC 8949 §4.2.1: MUST NOT normalize -0.0
107        allow_indefinite: false,
108        strict_tags: true,
109    };
110}
111
112// When adding more features to configuration, follow these steps:
113// - Create 2 or more structs that can be used as a type (e.g. Limit and NoLimit)
114// - Add an `Internal...Config` to the `internal` module
115// - Make sure `Config` and `impl<T> Config for T` extend from this new trait
116// - Add a generic to `Configuration`
117// - Add this generic to `impl<...> Default for Configuration<...>`
118// - Add this generic to `const fn generate<...>()`
119// - Add this generic to _every_ function in `Configuration`
120// - Add your new methods
121
122/// The default config for bincode 2.0. By default this will be:
123/// - Little endian
124/// - Variable int encoding
125#[must_use]
126pub const fn standard() -> Configuration {
127    generate()
128}
129
130/// Creates the "legacy" default config. This is the default config that was present in bincode 1.0
131/// - Little endian
132/// - Fixed int length encoding
133#[must_use]
134pub const fn legacy() -> Configuration<
135    LittleEndian,
136    Fixint,
137    NoLimit,
138    SkipBitPacking,
139    LsbFirst,
140    FingerprintDisabled,
141    BincodeLegacyFormat,
142> {
143    generate()
144}
145
146impl<E, I, L, B, O, F, FO> Default for Configuration<E, I, L, B, O, F, FO> {
147    fn default() -> Self {
148        generate()
149    }
150}
151
152const fn generate<E, I, L, B, O, F, FO>() -> Configuration<E, I, L, B, O, F, FO> {
153    Configuration {
154        _e: PhantomData,
155        _i: PhantomData,
156        _l: PhantomData,
157        _b: PhantomData,
158        _o: PhantomData,
159        _f: PhantomData,
160        _fo: PhantomData,
161    }
162}
163
164impl<E, I, L, B, O, F, FO> Configuration<E, I, L, B, O, F, FO> {
165    /// Makes bincode encode all integer types in big endian.
166    #[must_use]
167    pub const fn with_big_endian(self) -> Configuration<BigEndian, I, L, B, MsbFirst, F, FO> {
168        generate()
169    }
170
171    /// Makes bincode encode all integer types in little endian.
172    #[must_use]
173    pub const fn with_little_endian(self) -> Configuration<LittleEndian, I, L, B, LsbFirst, F, FO> {
174        generate()
175    }
176
177    /// Makes bincode encode all integer types with a variable integer encoding.
178    ///
179    /// Encoding an unsigned integer `v` (of any type excepting `u8`) works as follows:
180    ///
181    /// 1. If `u < 251`, encode it as a single byte with that value.
182    /// 2. If `251 <= u < 2**16`, encode it as a literal byte `251`, followed by a `u16` with value `u`.
183    /// 3. If `2**16 <= u < 2**32`, encode it as a literal byte `252`, followed by a `u32` with value `u`.
184    /// 4. If `2**32 <= u < 2**64`, encode it as a literal byte `253`, followed by a `u64` with value `u`.
185    /// 5. If `2**64 <= u < 2**128`, encode it as a literal byte `254`, followed by a `u128` with value `u`.
186    ///
187    /// Then, for signed integers, we first convert to unsigned using the zigzag algorithm,
188    /// and then encode them as we do for unsigned integers generally. The reason we use this
189    /// algorithm is that it encodes those values which are close to zero in less bytes; the
190    /// obvious algorithm, where we encode the cast values, gives a very large encoding for all
191    /// negative values.
192    ///
193    /// The zigzag algorithm is defined as follows:
194    ///
195    /// ```rust
196    /// # type Signed = i32;
197    /// # type Unsigned = u32;
198    /// fn zigzag(v: Signed) -> Unsigned {
199    ///     match v {
200    ///         | 0 => 0,
201    ///         // To avoid the edge case of Signed::min_value()
202    ///         // !n is equal to `-n - 1`, so this is:
203    ///         // !n * 2 + 1 = 2(-n - 1) + 1 = -2n - 2 + 1 = -2n - 1
204    ///         | v if v < 0 => !(v as Unsigned) * 2 - 1,
205    ///         | v if v > 0 => (v as Unsigned) * 2,
206    /// #       _ => unreachable!()
207    ///     }
208    /// }
209    /// ```
210    ///
211    /// And works such that:
212    ///
213    /// ```rust
214    /// # let zigzag = |n: i64| -> u64 {
215    /// #     match n {
216    /// #         0 => 0,
217    /// #         v if v < 0 => !(v as u64) * 2 + 1,
218    /// #         v if v > 0 => (v as u64) * 2,
219    /// #         _ => unreachable!(),
220    /// #     }
221    /// # };
222    /// assert_eq!(zigzag(0), 0);
223    /// assert_eq!(zigzag(-1), 1);
224    /// assert_eq!(zigzag(1), 2);
225    /// assert_eq!(zigzag(-2), 3);
226    /// assert_eq!(zigzag(2), 4);
227    /// // etc
228    /// assert_eq!(zigzag(i64::min_value()), u64::max_value());
229    /// ```
230    ///
231    /// Note that u256 and the like are unsupported by this format; if and when they are added to the
232    /// language, they may be supported via the extension point given by the 255 byte.
233    #[must_use]
234    pub const fn with_variable_int_encoding(self) -> Configuration<E, Varint, L, B, O, F, FO> {
235        generate()
236    }
237
238    /// Fixed-size integer encoding.
239    ///
240    /// * Fixed size integers are encoded directly
241    /// * Enum discriminants are encoded as u32
242    /// * Lengths and usize are encoded as u64
243    #[must_use]
244    pub const fn with_fixed_int_encoding(self) -> Configuration<E, Fixint, L, B, O, F, FO> {
245        generate()
246    }
247
248    /// Sets the byte limit to `limit`.
249    #[must_use]
250    pub const fn with_limit<const N: usize>(self) -> Configuration<E, I, Limit<N>, B, O, F, FO> {
251        generate()
252    }
253
254    /// Clear the byte limit.
255    #[must_use]
256    pub const fn with_no_limit(self) -> Configuration<E, I, NoLimit, B, O, F, FO> {
257        generate()
258    }
259
260    /// Enables bit-packing for types that support it.
261    #[must_use]
262    pub const fn with_bit_packing(self) -> Configuration<E, I, L, AllowBitPacking, O, F, FO> {
263        generate()
264    }
265
266    /// Disables bit-packing.
267    #[must_use]
268    pub const fn with_no_bit_packing(self) -> Configuration<E, I, L, SkipBitPacking, O, F, FO> {
269        generate()
270    }
271
272    /// Enables fingerprinting with the default deterministic seed (0).
273    ///
274    /// The fingerprint is a 64-bit hash that covers:
275    /// - The type name
276    /// - For structs: field names, types, and their order
277    /// - For enums: variant names, field names, types, and their order
278    /// - Configuration settings such as endianness and integer encoding
279    #[must_use]
280    pub const fn with_fingerprint(self) -> Configuration<E, I, L, B, O, FingerprintEnabled<0>, FO> {
281        generate()
282    }
283
284    /// Enables fingerprinting with a custom seed.
285    ///
286    /// This is similar to [`with_fingerprint`](Self::with_fingerprint), but allows specifying a custom seed
287    /// to further differentiate fingerprints.
288    #[must_use]
289    pub const fn with_fingerprint_and_seed<const SEED: u64>(
290        self
291    ) -> Configuration<E, I, L, B, O, FingerprintEnabled<SEED>, FO> {
292        generate()
293    }
294
295    /// Enables legacy fingerprinting with an expected hash.
296    ///
297    /// In this mode, bincode will not calculate the fingerprint automatically.
298    /// Instead, it will use the provided `EXPECTED` hash for verification.
299    #[must_use]
300    pub const fn with_legacy_fingerprint<const EXPECTED: u64>(
301        self
302    ) -> Configuration<E, I, L, B, O, FingerprintLegacy<EXPECTED>, FO> {
303        generate()
304    }
305
306    /// Sets the format to CBOR with default options.
307    #[must_use]
308    pub const fn with_cbor_format(self) -> Configuration<E, I, L, B, O, F, CborFormat> {
309        generate()
310    }
311
312    /// Sets the format to CBOR with deterministic encoding (Core mode).
313    #[must_use]
314    pub const fn with_deterministic_cbor(
315        self
316    ) -> Configuration<E, I, L, B, O, F, CborDeterministicFormat> {
317        generate()
318    }
319
320    /// Sets the format to Bincode with deterministic encoding.
321    #[must_use]
322    pub const fn with_deterministic_bincode(
323        self
324    ) -> Configuration<E, I, L, B, O, F, BincodeDeterministicFormat> {
325        generate()
326    }
327
328    /// Sets the format to CBOR with custom options.
329    #[must_use]
330    pub const fn with_cbor_options<
331        const DET: u8,
332        const PREF: bool,
333        const NAN: bool,
334        const NZ: bool,
335        const INDEF: bool,
336        const TAGS: bool,
337    >(
338        self
339    ) -> Configuration<E, I, L, B, O, F, CborConfig<DET, PREF, NAN, NZ, INDEF, TAGS>> {
340        generate()
341    }
342}
343
344/// Indicates a type is valid for controlling the bincode configuration
345pub trait Config:
346    InternalEndianConfig
347    + InternalIntEncodingConfig
348    + InternalLimitConfig
349    + InternalBitPackingConfig
350    + InternalBitOrderingConfig
351    + InternalFingerprintConfig
352    + InternalFormatConfig
353    + InternalConfigFingerprint
354    + InternalFingerprintConfigExt
355    + Copy
356    + Clone
357{
358    /// This configuration's Endianness
359    fn endianness(&self) -> Endianness;
360
361    /// This configuration's Integer Encoding
362    fn int_encoding(&self) -> IntEncoding;
363
364    /// This configuration's byte limit, or `None` if no limit is configured
365    fn limit(&self) -> Option<usize>;
366
367    /// Whether bit-packing is enabled for this configuration
368    fn bit_packing_enabled(&self) -> bool;
369
370    /// Returns the bit ordering of this configuration
371    fn bit_ordering(&self) -> BitOrdering;
372
373    /// Returns the fingerprint mode of this configuration
374    fn fingerprint_mode(&self) -> FingerprintMode;
375
376    /// Returns the format of this configuration
377    fn format(&self) -> Format;
378
379    /// Returns the CBOR options of this configuration.
380    /// Returns default options if the format is not CBOR.
381    fn cbor_options(&self) -> CborOptions;
382
383    /// Returns true if this configuration uses compact network encoding (omitting `flowinfo/scope_id`).
384    fn is_compact_net(&self) -> bool;
385}
386
387impl<T> Config for T
388where
389    T: InternalEndianConfig
390        + InternalIntEncodingConfig
391        + InternalLimitConfig
392        + InternalBitPackingConfig
393        + InternalBitOrderingConfig
394        + InternalFingerprintConfig
395        + InternalFormatConfig
396        + InternalConfigFingerprint
397        + InternalFingerprintConfigExt
398        + Copy
399        + Clone,
400{
401    fn endianness(&self) -> Endianness {
402        <T as InternalEndianConfig>::ENDIAN
403    }
404
405    fn int_encoding(&self) -> IntEncoding {
406        <T as InternalIntEncodingConfig>::INT_ENCODING
407    }
408
409    fn limit(&self) -> Option<usize> {
410        <T as InternalLimitConfig>::LIMIT
411    }
412
413    fn bit_packing_enabled(&self) -> bool {
414        matches!(
415            <T as InternalBitPackingConfig>::BIT_PACKING,
416            BitPacking::Enabled
417        )
418    }
419
420    fn bit_ordering(&self) -> BitOrdering {
421        <T as InternalBitOrderingConfig>::BIT_ORDERING
422    }
423
424    fn fingerprint_mode(&self) -> FingerprintMode {
425        <T as InternalFingerprintConfig>::FINGERPRINT_MODE
426    }
427
428    fn format(&self) -> Format {
429        <T as InternalFormatConfig>::FORMAT
430    }
431
432    fn cbor_options(&self) -> CborOptions {
433        <T as InternalFormatConfig>::CBOR_OPTIONS
434    }
435
436    fn is_compact_net(&self) -> bool {
437        <T as InternalFormatConfig>::IS_COMPACT_NET
438    }
439}
440
441/// Encodes all integer types in big endian.
442#[derive(Copy, Clone, Debug)]
443#[doc(hidden)]
444pub struct BigEndian;
445
446impl InternalEndianConfig for BigEndian {
447    const ENDIAN: Endianness = Endianness::Big;
448}
449
450/// Encodes all integer types in little endian.
451#[derive(Copy, Clone, Debug)]
452#[doc(hidden)]
453pub struct LittleEndian;
454
455impl InternalEndianConfig for LittleEndian {
456    const ENDIAN: Endianness = Endianness::Little;
457}
458
459/// Use fixed-size integer encoding.
460#[derive(Copy, Clone, Debug)]
461#[doc(hidden)]
462pub struct Fixint;
463
464impl InternalIntEncodingConfig for Fixint {
465    const INT_ENCODING: IntEncoding = IntEncoding::Fixed;
466}
467
468/// Use variable integer encoding.
469#[derive(Copy, Clone, Debug)]
470#[doc(hidden)]
471pub struct Varint;
472
473impl InternalIntEncodingConfig for Varint {
474    const INT_ENCODING: IntEncoding = IntEncoding::Variable;
475}
476
477/// Sets an unlimited byte limit.
478#[derive(Copy, Clone, Debug)]
479#[doc(hidden)]
480pub struct NoLimit;
481impl InternalLimitConfig for NoLimit {
482    const LIMIT: Option<usize> = None;
483}
484
485/// Sets the byte limit to N.
486#[derive(Copy, Clone, Debug)]
487#[doc(hidden)]
488pub struct Limit<const N: usize>;
489impl<const N: usize> InternalLimitConfig for Limit<N> {
490    const LIMIT: Option<usize> = Some(N);
491}
492
493/// Use bit-packing for types that support it.
494#[derive(Copy, Clone, Debug)]
495#[doc(hidden)]
496pub struct AllowBitPacking;
497
498impl InternalBitPackingConfig for AllowBitPacking {
499    const BIT_PACKING: BitPacking = BitPacking::Enabled;
500}
501
502/// Skip bit-packing.
503#[derive(Copy, Clone, Debug)]
504#[doc(hidden)]
505pub struct SkipBitPacking;
506
507impl InternalBitPackingConfig for SkipBitPacking {
508    const BIT_PACKING: BitPacking = BitPacking::Disabled;
509}
510
511/// Use MSB-first bit ordering.
512#[derive(Copy, Clone, Debug)]
513#[doc(hidden)]
514pub struct MsbFirst;
515
516impl InternalBitOrderingConfig for MsbFirst {
517    const BIT_ORDERING: BitOrdering = BitOrdering::Msb;
518}
519
520/// Use LSB-first bit ordering.
521#[derive(Copy, Clone, Debug)]
522#[doc(hidden)]
523pub struct LsbFirst;
524
525impl InternalBitOrderingConfig for LsbFirst {
526    const BIT_ORDERING: BitOrdering = BitOrdering::Lsb;
527}
528
529/// Fingerprinting is disabled.
530#[derive(Copy, Clone, Debug)]
531#[doc(hidden)]
532pub struct FingerprintDisabled;
533
534impl InternalFingerprintConfig for FingerprintDisabled {
535    const FINGERPRINT_MODE: FingerprintMode = FingerprintMode::Disabled;
536}
537
538/// Fingerprinting is enabled with a seed.
539#[derive(Copy, Clone, Debug)]
540#[doc(hidden)]
541pub struct FingerprintEnabled<const SEED: u64>;
542
543impl<const SEED: u64> InternalFingerprintConfig for FingerprintEnabled<SEED> {
544    const FINGERPRINT_MODE: FingerprintMode = FingerprintMode::Enabled { seed: SEED };
545}
546
547/// Legacy fingerprinting with an expected hash.
548#[derive(Copy, Clone, Debug)]
549#[doc(hidden)]
550pub struct FingerprintLegacy<const EXPECTED: u64>;
551
552impl<const EXPECTED: u64> InternalFingerprintConfig for FingerprintLegacy<EXPECTED> {
553    const FINGERPRINT_MODE: FingerprintMode = FingerprintMode::Legacy { expected: EXPECTED };
554}
555
556/// The default bincode format.
557#[derive(Copy, Clone, Debug)]
558#[doc(hidden)]
559pub struct BincodeFormat;
560
561impl InternalFormatConfig for BincodeFormat {
562    const CBOR_OPTIONS: CborOptions = CborOptions::DEFAULT;
563    const FORMAT: Format = Format::Bincode;
564    const IS_COMPACT_NET: bool = false;
565}
566
567/// Bincode format with deterministic encoding.
568#[derive(Copy, Clone, Debug)]
569#[doc(hidden)]
570pub struct BincodeDeterministicFormat;
571
572impl InternalFormatConfig for BincodeDeterministicFormat {
573    const CBOR_OPTIONS: CborOptions = CborOptions::DEFAULT;
574    const FORMAT: Format = Format::BincodeDeterministic;
575    const IS_COMPACT_NET: bool = false;
576}
577
578/// CBOR configuration with const generic options.
579#[derive(Copy, Clone, Debug)]
580#[doc(hidden)]
581pub struct CborConfig<
582    const DET: u8,
583    const PREF: bool,
584    const NAN: bool,
585    const NZ: bool,
586    const INDEF: bool,
587    const TAGS: bool,
588>;
589
590impl<
591    const DET: u8,
592    const PREF: bool,
593    const NAN: bool,
594    const NZ: bool,
595    const INDEF: bool,
596    const TAGS: bool,
597> InternalFormatConfig for CborConfig<DET, PREF, NAN, NZ, INDEF, TAGS>
598{
599    const CBOR_OPTIONS: CborOptions = CborOptions {
600        deterministic_mode: match DET {
601            | 1 => CborDeterministicMode::Core,
602            | 2 => CborDeterministicMode::LengthFirst,
603            | _ => CborDeterministicMode::None,
604        },
605        preferred_float: PREF,
606        canonical_nan: NAN,
607        normalize_neg_zero: NZ,
608        allow_indefinite: INDEF,
609        strict_tags: TAGS,
610    };
611    const FORMAT: Format = if DET == 0 {
612        Format::Cbor
613    } else {
614        Format::CborDeterministic
615    };
616    const IS_COMPACT_NET: bool = false;
617}
618
619/// Bincode format for legacy compatibility (omitting flowinfo/scope_id).
620#[derive(Copy, Clone, Debug)]
621#[doc(hidden)]
622pub struct BincodeLegacyFormat;
623
624impl InternalFormatConfig for BincodeLegacyFormat {
625    const CBOR_OPTIONS: CborOptions = CborOptions::DEFAULT;
626    const FORMAT: Format = Format::Bincode;
627    const IS_COMPACT_NET: bool = true;
628}
629
630/// CBOR format with default options.
631#[doc(hidden)]
632pub type CborFormat = CborConfig<0, true, true, false, true, false>;
633
634/// CBOR format with deterministic encoding (Canonical CBOR).
635#[doc(hidden)]
636pub type CborDeterministicFormat = CborConfig<1, true, true, false, false, true>;
637
638/// Endianness of a `Configuration`.
639#[derive(Copy, Clone, Debug, PartialEq, Eq)]
640#[non_exhaustive]
641pub enum Endianness {
642    /// Little Endian encoding, see `LittleEndian`.
643    Little,
644    /// Big Endian encoding, see `BigEndian`.
645    Big,
646}
647
648/// Integer Encoding of a `Configuration`.
649#[derive(Copy, Clone, Debug, PartialEq, Eq)]
650#[non_exhaustive]
651pub enum IntEncoding {
652    /// Fixed Integer Encoding, see `Fixint`.
653    Fixed,
654    /// Variable Integer Encoding, see `Varint`.
655    Variable,
656}
657
658/// Bit packing of a `Configuration`.
659#[derive(Copy, Clone, Debug, PartialEq, Eq)]
660#[non_exhaustive]
661pub enum BitPacking {
662    /// Enable bit-packing.
663    Enabled,
664    /// Disable bit-packing.
665    Disabled,
666}
667
668/// Bit ordering of a `Configuration`.
669#[derive(Copy, Clone, Debug, PartialEq, Eq)]
670#[non_exhaustive]
671pub enum BitOrdering {
672    /// Least Significant Bit first.
673    Lsb,
674    /// Most Significant Bit first.
675    Msb,
676}
677
678/// Fingerprint mode of a `Configuration`.
679#[derive(Copy, Clone, Debug, PartialEq, Eq)]
680#[non_exhaustive]
681pub enum FingerprintMode {
682    /// Fingerprinting is disabled.
683    Disabled,
684    /// Fingerprinting is enabled with a seed.
685    Enabled {
686        /// The seed to use for hashing.
687        seed: u64,
688    },
689    /// Legacy fingerprinting with an expected hash.
690    Legacy {
691        /// The hash that is expected to be present.
692        expected: u64,
693    },
694}
695
696/// Format of a `Configuration`.
697#[derive(Copy, Clone, Debug, PartialEq, Eq)]
698#[non_exhaustive]
699pub enum Format {
700    /// Bincode (default).
701    Bincode,
702    /// Bincode with deterministic encoding.
703    BincodeDeterministic,
704    /// CBOR.
705    Cbor,
706    /// CBOR with deterministic encoding.
707    CborDeterministic,
708}
709
710#[doc(hidden)]
711pub mod internal {
712    use super::BitOrdering;
713    use super::BitPacking;
714    use super::Configuration;
715    use super::Endianness;
716    use super::FingerprintMode;
717    use super::IntEncoding;
718
719    pub trait InternalEndianConfig {
720        const ENDIAN: Endianness;
721    }
722
723    impl<E: InternalEndianConfig, I, L, B, O, F, FO> InternalEndianConfig
724        for Configuration<E, I, L, B, O, F, FO>
725    {
726        const ENDIAN: Endianness = E::ENDIAN;
727    }
728
729    pub trait InternalIntEncodingConfig {
730        const INT_ENCODING: IntEncoding;
731    }
732
733    impl<E, I: InternalIntEncodingConfig, L, B, O, F, FO> InternalIntEncodingConfig
734        for Configuration<E, I, L, B, O, F, FO>
735    {
736        const INT_ENCODING: IntEncoding = I::INT_ENCODING;
737    }
738
739    pub trait InternalLimitConfig {
740        const LIMIT: Option<usize>;
741    }
742
743    impl<E, I, L: InternalLimitConfig, B, O, F, FO> InternalLimitConfig
744        for Configuration<E, I, L, B, O, F, FO>
745    {
746        const LIMIT: Option<usize> = L::LIMIT;
747    }
748
749    pub trait InternalBitPackingConfig {
750        const BIT_PACKING: BitPacking;
751    }
752
753    impl<E, I, L, B: InternalBitPackingConfig, O, F, FO> InternalBitPackingConfig
754        for Configuration<E, I, L, B, O, F, FO>
755    {
756        const BIT_PACKING: BitPacking = B::BIT_PACKING;
757    }
758
759    pub trait InternalBitOrderingConfig {
760        const BIT_ORDERING: BitOrdering;
761    }
762
763    impl<E, I, L, B, O: InternalBitOrderingConfig, F, FO> InternalBitOrderingConfig
764        for Configuration<E, I, L, B, O, F, FO>
765    {
766        const BIT_ORDERING: BitOrdering = O::BIT_ORDERING;
767    }
768
769    pub trait InternalFingerprintConfig {
770        const FINGERPRINT_MODE: FingerprintMode;
771    }
772
773    impl<E, I, L, B, O, F: InternalFingerprintConfig, FO> InternalFingerprintConfig
774        for Configuration<E, I, L, B, O, F, FO>
775    {
776        const FINGERPRINT_MODE: FingerprintMode = F::FINGERPRINT_MODE;
777    }
778
779    pub trait InternalFingerprintConfigExt {
780        type Mode;
781    }
782
783    impl<E, I, L, B, O, F, FO> InternalFingerprintConfigExt for Configuration<E, I, L, B, O, F, FO> {
784        type Mode = F;
785    }
786
787    pub trait InternalFingerprintGuard<D, C: super::Config> {
788        fn decode_check<R: crate::de::read::Reader>(
789            _config: &C,
790            reader: &mut R,
791        ) -> core::result::Result<(), crate::error::DecodeError>;
792        fn encode_check<W: crate::enc::write::Writer>(
793            _config: &C,
794            writer: &mut W,
795        ) -> core::result::Result<(), crate::error::EncodeError>;
796    }
797
798    impl<D, C: super::Config> InternalFingerprintGuard<D, C> for super::FingerprintDisabled {
799        #[inline(always)]
800        fn decode_check<R: crate::de::read::Reader>(
801            _config: &C,
802            _reader: &mut R,
803        ) -> core::result::Result<(), crate::error::DecodeError> {
804            core::result::Result::Ok(())
805        }
806
807        #[inline(always)]
808        fn encode_check<W: crate::enc::write::Writer>(
809            _config: &C,
810            _writer: &mut W,
811        ) -> core::result::Result<(), crate::error::EncodeError> {
812            core::result::Result::Ok(())
813        }
814    }
815
816    impl<D: crate::fingerprint::Fingerprint<C>, C: super::Config, const SEED: u64>
817        InternalFingerprintGuard<D, C> for super::FingerprintEnabled<SEED>
818    {
819        #[inline]
820        fn decode_check<R: crate::de::read::Reader>(
821            _config: &C,
822            reader: &mut R,
823        ) -> core::result::Result<(), crate::error::DecodeError> {
824            let mut bytes = [0u8; 8];
825            reader.read(&mut bytes)?;
826            let actual = u64::from_le_bytes(bytes);
827            if actual != D::SCHEMA_HASH {
828                return crate::error::cold_decode_error_schema_mismatch(D::SCHEMA_HASH, actual);
829            }
830            core::result::Result::Ok(())
831        }
832
833        #[inline]
834        fn encode_check<W: crate::enc::write::Writer>(
835            _config: &C,
836            writer: &mut W,
837        ) -> core::result::Result<(), crate::error::EncodeError> {
838            writer.write(&D::SCHEMA_HASH.to_le_bytes())
839        }
840    }
841
842    impl<D, C: super::Config, const EXPECTED: u64> InternalFingerprintGuard<D, C>
843        for super::FingerprintLegacy<EXPECTED>
844    {
845        #[inline]
846        fn decode_check<R: crate::de::read::Reader>(
847            _config: &C,
848            reader: &mut R,
849        ) -> core::result::Result<(), crate::error::DecodeError> {
850            let mut bytes = [0u8; 8];
851            reader.read(&mut bytes)?;
852            let actual = u64::from_le_bytes(bytes);
853            if actual != EXPECTED {
854                return crate::error::cold_decode_error_schema_mismatch(EXPECTED, actual);
855            }
856            core::result::Result::Ok(())
857        }
858
859        #[inline]
860        fn encode_check<W: crate::enc::write::Writer>(
861            _config: &C,
862            _writer: &mut W,
863        ) -> core::result::Result<(), crate::error::EncodeError> {
864            core::result::Result::Ok(())
865        }
866    }
867
868    pub trait InternalConfigFingerprint {
869        const CONFIG_HASH: u64;
870    }
871
872    impl<E, I, L, B, O, F, FO> InternalConfigFingerprint for Configuration<E, I, L, B, O, F, FO>
873    where
874        E: InternalEndianConfig,
875        I: InternalIntEncodingConfig,
876        L: InternalLimitConfig,
877        B: InternalBitPackingConfig,
878        O: InternalBitOrderingConfig,
879        FO: InternalFormatConfig,
880    {
881        const CONFIG_HASH: u64 = {
882            let format_byte: u8 = match FO::FORMAT {
883                | super::Format::Bincode => 0,
884                | super::Format::BincodeDeterministic => 1,
885                | super::Format::Cbor => 2,
886                | super::Format::CborDeterministic => 3,
887            };
888            let opts = FO::CBOR_OPTIONS;
889            rapidhash::v3::rapidhash_v3_seeded(
890                &[
891                    crate::BINCODE_MAJOR_VERSION as u8,
892                    E::ENDIAN as u8,
893                    I::INT_ENCODING as u8,
894                    match L::LIMIT {
895                        | None => 0,
896                        | Some(_) => 1,
897                    },
898                    B::BIT_PACKING as u8,
899                    O::BIT_ORDERING as u8,
900                    format_byte,
901                    opts.deterministic_mode as u8,
902                    opts.preferred_float as u8,
903                    opts.canonical_nan as u8,
904                    opts.normalize_neg_zero as u8,
905                    opts.allow_indefinite as u8,
906                    opts.strict_tags as u8,
907                ],
908                &rapidhash::v3::RapidSecrets::seed_cpp(0),
909            )
910        };
911    }
912
913    pub trait InternalFormatConfig {
914        const FORMAT: super::Format;
915        const CBOR_OPTIONS: super::CborOptions;
916        const IS_COMPACT_NET: bool;
917    }
918
919    impl<E, I, L, B, O, F, FO: InternalFormatConfig> InternalFormatConfig
920        for Configuration<E, I, L, B, O, F, FO>
921    {
922        const CBOR_OPTIONS: super::CborOptions = FO::CBOR_OPTIONS;
923        const FORMAT: super::Format = FO::FORMAT;
924        const IS_COMPACT_NET: bool = FO::IS_COMPACT_NET;
925    }
926}