wincode/lib.rs
1//! wincode is a fast, bincode‑compatible serializer/deserializer focused on in‑place
2//! initialization and direct memory writes.
3//!
4//! In short, `wincode` operates over traits that facilitate direct writes of memory
5//! into final destinations (including heap-allocated buffers) without intermediate
6//! staging buffers.
7//!
8//! # Quickstart
9//!
10//! `wincode` traits are implemented for many built-in types (like `Vec`, integers, etc.).
11//!
12//! You'll most likely want to start by using `wincode` on your own struct types, which can be
13//! done easily with the derive macros.
14//!
15//! ```
16//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
17//! # use serde::{Serialize, Deserialize};
18//! # use wincode_derive::{SchemaWrite, SchemaRead};
19//! # #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
20//! #
21//! #[derive(SchemaWrite, SchemaRead)]
22//! struct MyStruct {
23//! data: Vec<u8>,
24//! win: bool,
25//! }
26//!
27//! let val = MyStruct { data: vec![1,2,3], win: true };
28//! assert_eq!(wincode::serialize(&val).unwrap(), bincode::serialize(&val).unwrap());
29//! # }
30//! ```
31//!
32//! # Motivation
33//!
34//! Typical Rust API design employs a *construct-then-move* style of programming.
35//! Common APIs like `Vec::push`, iterator adaptors, `Box::new` (and its `Rc`/`Arc`
36//! variants), and even returning a fully-initialized struct from a function all
37//! follow this pattern. While this style feels intuitive and ergonomic, it
38//! inherently entails copying unless the compiler can perform elision -- which,
39//! today, it generally cannot. To see why this is a consequence of the design,
40//! consider the following code:
41//! ```
42//! # struct MyStruct;
43//! # impl MyStruct {
44//! # fn new() -> Self {
45//! # MyStruct
46//! # }
47//! # }
48//! Box::new(MyStruct::new());
49//! ```
50//! `MyStruct` must be constructed *before* it can be moved into `Box`'s allocation.
51//! This is a classic code ordering problem: to avoid the copy, `Box::new` needs
52//! to execute code before `MyStruct::new()` runs. `Vec::push`, iterator collection,
53//! and similar APIs have this same problem.
54//! (See these [design meeting notes](https://hackmd.io/XXuVXH46T8StJB_y0urnYg) or
55//! or the
56//! [`placement-by-return` RFC](https://github.com/PoignardAzur/rust-rfcs/blob/placement-by-return/text/0000-placement-by-return.md)
57//! for a more in-depth discussion on this topic.) The result of this is that even
58//! performance conscious developers routinely introduce avoidable copying without
59//! realizing it. `serde` inherits these issues since it neither attempts to
60//! initialize in‑place nor exposes APIs to do so.
61//!
62//! These patterns are not inherent limitations of Rust, but are consequences of
63//! conventions and APIs that do not consider in-place initialization as part of
64//! their design. The tools for in-place construction *do* exist (see
65//! [`MaybeUninit`](core::mem::MaybeUninit) and raw pointer APIs), but they are
66//! rarely surfaced in libraries and can be cumbersome to use (see [`addr_of_mut!`](core::ptr::addr_of_mut)),
67//! so programmers are often not even aware of them or avoid them.
68//!
69//! `wincode` makes in-place initialization a first class design goal, and fundamentally
70//! operates on [traits](#traits) that facilitate direct writes of memory.
71//!
72//! # Adapting foreign types
73//!
74//! `wincode` can also be used to implement serialization/deserialization
75//! on foreign types, where serialization/deserialization schemes on those types are unoptimized (and
76//! out of your control as a foreign type). For example, consider the following struct,
77//! defined outside of your crate:
78//! ```
79//! use serde::{Serialize, Deserialize};
80//!
81//! # #[derive(PartialEq, Eq, Debug)]
82//! #[repr(transparent)]
83//! #[derive(Clone, Copy, Serialize, Deserialize)]
84//! struct Address([u8; 32]);
85//!
86//! # #[derive(PartialEq, Eq, Debug)]
87//! #[repr(transparent)]
88//! #[derive(Clone, Copy, Serialize, Deserialize)]
89//! struct Hash([u8; 32]);
90//!
91//! #[derive(Serialize, Deserialize)]
92//! pub struct A {
93//! pub addresses: Vec<Address>,
94//! pub hash: Hash,
95//! }
96//! ```
97//!
98//! `serde`'s default, naive, implementation will perform per-element visitation of all bytes
99//! in `Vec<Address>` and `Hash`. Because these fields are "plain old data", ideally we would
100//! avoid per-element visitation entirely and read / write these fields in a single pass.
101//! The situation worsens if this struct needs to be written into a heap allocated data structure,
102//! like a `Vec<A>` or `Box<[A]>`. As discussed in [motivation](#motivation), all
103//! those bytes will be initialized on the stack before being copied into the heap allocation.
104//!
105//! `wincode` can solve this with the following:
106//! ```
107//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
108//! # use wincode::{Serialize as _, Deserialize as _, containers};
109//! # use wincode_derive::{SchemaWrite, SchemaRead};
110//! mod foreign_crate {
111//! // Defined in some foreign crate...
112//! use serde::{Serialize, Deserialize};
113//!
114//! # #[derive(PartialEq, Eq, Debug)]
115//! #[repr(transparent)]
116//! #[derive(Clone, Copy, Serialize, Deserialize)]
117//! pub struct Address(pub [u8; 32]);
118//!
119//! # #[derive(PartialEq, Eq, Debug)]
120//! #[repr(transparent)]
121//! #[derive(Clone, Copy, Serialize, Deserialize)]
122//! pub struct Hash(pub [u8; 32]);
123//!
124//! # #[derive(PartialEq, Eq, Debug)]
125//! #[derive(Serialize, Deserialize)]
126//! pub struct A {
127//! pub addresses: Vec<Address>,
128//! pub hash: Hash,
129//! }
130//! }
131//!
132//! wincode::pod_wrapper! {
133//! unsafe struct PodAddress(foreign_crate::Address);
134//! unsafe struct PodHash(foreign_crate::Hash);
135//! }
136//!
137//! #[derive(SchemaWrite, SchemaRead)]
138//! #[wincode(from = "foreign_crate::A")]
139//! pub struct MyA {
140//! addresses: Vec<PodAddress>,
141//! hash: PodHash,
142//! }
143//!
144//! let val = foreign_crate::A {
145//! addresses: vec![foreign_crate::Address([0; 32]), foreign_crate::Address([1; 32])],
146//! hash: foreign_crate::Hash([0; 32]),
147//! };
148//! let bincode_serialize = bincode::serialize(&val).unwrap();
149//! let wincode_serialize = MyA::serialize(&val).unwrap();
150//! assert_eq!(bincode_serialize, wincode_serialize);
151//!
152//! let bincode_deserialize: foreign_crate::A = bincode::deserialize(&bincode_serialize).unwrap();
153//! let wincode_deserialize = MyA::deserialize(&bincode_serialize).unwrap();
154//! assert_eq!(val, bincode_deserialize);
155//! assert_eq!(val, wincode_deserialize);
156//! # }
157//! ```
158//!
159//! Now, when deserializing `A`:
160//! - All initialization is done in-place, including heap-allocated memory
161//! (true of all supported contiguous heap-allocated structures in `wincode`).
162//! - Byte fields are read and written in a single pass.
163//!
164//! # Compatibility
165//!
166//! - Produces the same bytes as `bincode` for the covered shapes when using bincode's
167//! default configuration, provided your [`SchemaWrite`] and [`SchemaRead`] schemas and
168//! [`containers`] match the layout implied by your `serde` types.
169//! - Length encodings are pluggable via [`SeqLen`](len::SeqLen).
170//! - Unlike `bincode`, this crate will fail to serialize or deserialize large
171//! dynamic data structures by default, but this can be configured. This is
172//! done for security and performance, as it allows to preallocate these data
173//! structures safely.
174//!
175//! # Zero-copy deserialization
176//!
177//! `wincode`'s zero-copy deserialization is built on the following primitives:
178//! - [`u8`]
179//! - [`i8`]
180//!
181//! In addition to the following on little endian targets:
182//! - [`u16`], [`i16`], [`u32`], [`i32`], [`u64`], [`i64`], [`u128`], [`i128`], [`f32`], [`f64`]
183//!
184//! Types with alignment greater than 1 can force the compiler to insert padding into your structs.
185//! Zero-copy requires padding-free layouts; if the layout has implicit padding, `wincode` will not
186//! qualify the type as zero-copy.
187//!
188//! ---
189//!
190//! Within `wincode`, any type that is composed entirely of the above primitives is
191//! eligible for zero-copy deserialization. This includes arrays, slices, and structs.
192//!
193//! Structs deriving [`SchemaRead`] are eligible for zero-copy deserialization
194//! as long as they are composed entirely of the above zero-copy types, are annotated with
195//! `#[repr(transparent)]` or `#[repr(C)]`, and have no implicit padding. Use appropriate
196//! field ordering or add explicit padding fields if needed to eliminate implicit padding.
197//!
198//! Note that tuples are **not** eligible for zero-copy deserialization, as Rust does not
199//! currently guarantee tuple layout.
200//!
201//! ## Field reordering
202//! If your struct has implicit padding, you may be able to reorder fields to avoid it.
203//!
204//! ```
205//! #[repr(C)]
206//! struct HasPadding {
207//! a: u8,
208//! b: u32,
209//! c: u16,
210//! d: u8,
211//! }
212//!
213//! #[repr(C)]
214//! struct ZeroPadding {
215//! b: u32,
216//! c: u16,
217//! a: u8,
218//! d: u8,
219//! }
220//! ```
221//!
222//! ## Explicit padding
223//! You may need to add an explicit padding field if reordering fields cannot yield
224//! a padding-free layout.
225//!
226//! ```
227//! #[repr(C)]
228//! struct HasPadding {
229//! a: u32,
230//! b: u16,
231//! _pad: [u8; 2],
232//! }
233//! ```
234//!
235//! ## Examples
236//!
237//! ### `&[u8]`
238//! ```
239//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
240//! use wincode::{SchemaWrite, SchemaRead};
241//!
242//! # #[derive(Debug, PartialEq, Eq)]
243//! #[derive(SchemaWrite, SchemaRead)]
244//! struct ByteRef<'a> {
245//! bytes: &'a [u8],
246//! }
247//!
248//! let bytes: Vec<u8> = vec![1, 2, 3, 4, 5];
249//! let byte_ref = ByteRef { bytes: &bytes };
250//! let serialized = wincode::serialize(&byte_ref).unwrap();
251//! let deserialized: ByteRef<'_> = wincode::deserialize(&serialized).unwrap();
252//! assert_eq!(byte_ref, deserialized);
253//! # }
254//! ```
255//!
256//! ### struct newtype
257//! ```
258//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
259//! # use rand::random;
260//! # use std::array;
261//! use wincode::{SchemaWrite, SchemaRead};
262//!
263//! # #[derive(Debug, PartialEq, Eq)]
264//! #[derive(SchemaWrite, SchemaRead)]
265//! #[repr(transparent)]
266//! struct Signature([u8; 64]);
267//!
268//! # #[derive(Debug, PartialEq, Eq)]
269//! #[derive(SchemaWrite, SchemaRead)]
270//! struct Data<'a> {
271//! signature: &'a Signature,
272//! data: &'a [u8],
273//! }
274//!
275//! let signature = Signature(array::from_fn(|_| random()));
276//! let data = Data {
277//! signature: &signature,
278//! data: &[1, 2, 3, 4, 5],
279//! };
280//! let serialized = wincode::serialize(&data).unwrap();
281//! let deserialized: Data<'_> = wincode::deserialize(&serialized).unwrap();
282//! assert_eq!(data, deserialized);
283//! # }
284//! ```
285//!
286//! ### `&[u8; N]`
287//! ```
288//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
289//! use wincode::{SchemaWrite, SchemaRead};
290//!
291//! # #[derive(Debug, PartialEq, Eq)]
292//! #[derive(SchemaWrite, SchemaRead)]
293//! struct HeaderRef<'a> {
294//! magic: &'a [u8; 7],
295//! }
296//!
297//! let header = HeaderRef { magic: b"W1NC0D3" };
298//! let serialized = wincode::serialize(&header).unwrap();
299//! let deserialized: HeaderRef<'_> = wincode::deserialize(&serialized).unwrap();
300//! assert_eq!(header, deserialized);
301//! # }
302//! ```
303//!
304//! ## In-place mutation
305//!
306//! wincode supports in-place mutation of zero-copy types.
307//! See [`deserialize_mut`] or [`ZeroCopy::from_bytes_mut`] for more details.
308//!
309//! ## `ZeroCopy` and `config::ZeroCopy` methods
310//!
311//! The [`ZeroCopy`] and [`config::ZeroCopy`] traits provide some convenience methods for
312//! working with zero-copy types.
313//!
314//! See those trait definitions for more details.
315//!
316//! # Crate Features
317//!
318//! |Feature|Default|Description
319//! |---|---|---|
320//! |`std`|enabled|Enables `std` support.|
321//! |`alloc`|enabled automatically when `std` is enabled|Enables `alloc` support.|
322//! |`solana-short-vec`|disabled|Enables `solana-short-vec` support.|
323//! |`derive`|disabled|Enables the derive macros for [`SchemaRead`] and [`SchemaWrite`].|
324//! |`uuid`|disabled|Enables support for the `uuid` crate.|
325//! |`uuid-serde-compat`|disabled|Encodes and decodes `uuid::Uuid` with an additional length prefix, making it compatible with `serde`'s serialization scheme. Note that enabling this will result in strictly worse performance.|
326//!
327//! # Derive attributes
328//!
329//! ## Top level
330//! |Attribute|Type|Default|Description
331//! |---|---|---|---|
332//! |`from`|`Type`|`None`|Indicates that type is a mapping from another type (example in previous section)|
333//! |`no_suppress_unused`|`bool`|`false`|Disable unused field lints suppression. Only usable on structs with `from`.|
334//! |`struct_extensions` (DEPRECATED)|`bool`|`false`|Generates placement initialization helpers on `SchemaRead` struct implementations. DEPRECATED; Use `#[derive(UninitBuilder)]` instead.|
335//! |`tag_encoding`|`Type`|`None`|Specifies the encoding/decoding schema to use for the variant discriminant. Only usable on enums.|
336//! |`assert_zero_copy`|`bool`\|`Path`|`false`|Generates compile-time asserts to ensure the type meets zero-copy requirements. Can specify a custom config path, will use the [`DefaultConfig`](config::DefaultConfig) if `bool` form is used.|
337//!
338//! ### `no_suppress_unused`
339//!
340//! When creating a mapping type with `#[wincode(from = "AnotherType")]`, fields are typically
341//! comprised of [`containers`] (of course not strictly always true). As a result, these structs
342//! purely exist for the compiler to generate optimized implementations, and are never actually
343//! constructed. As a result, unused field lints will be triggered, which can be annoying.
344//! By default, when `from` is used, the derive macro will generate dummy function that references all
345//! the struct fields, which suppresses those lints. This function will ultimately be compiled out of your
346//! build, but you can disable this by setting `no_suppress_unused` to `true`. You can also avoid
347//! these lint errors with visibility modifiers (e.g., `pub`).
348//!
349//! Note that this only works on structs, as it is not possible to construct an arbitrary enum variant.
350//!
351//! ### `tag_encoding`
352//!
353//! Allows specifying the encoding/decoding schema to use for the variant discriminant. Only usable on enums.
354//!
355//! <div class="warning">
356//! There is no bincode analog to this attribute.
357//! Specifying this attribute will make your enum incompatible with bincode's default enum encoding.
358//! If you need strict bincode compatibility, you should implement a custom <code>Deserialize</code> and
359//! <code>Serialize</code> impl for your enum on the serde / bincode side.
360//! </div>
361//!
362//! Example:
363//! ```
364//! # #[cfg(all(feature = "derive", feature = "alloc"))] {
365//! use wincode::{SchemaWrite, SchemaRead};
366//!
367//! # #[derive(Debug, PartialEq, Eq)]
368//! #[derive(SchemaWrite, SchemaRead)]
369//! #[wincode(tag_encoding = "u8")]
370//! enum Enum {
371//! A,
372//! B,
373//! C,
374//! }
375//!
376//! assert_eq!(&wincode::serialize(&Enum::B).unwrap(), &1u8.to_le_bytes());
377//! # }
378//! ```
379//!
380//! ## Field level
381//! |Attribute|Type|Default|Description
382//! |---|---|---|---|
383//! |`with`|`Type`|`None`|Overrides the default `SchemaRead` or `SchemaWrite` implementation for the field.|
384//! |`skip`|`bool`\|`Expr`|`false`|Skips the field during serialization and deserialization (initializing with default value).|
385//!
386//! ### `skip`
387//!
388//! Allows omitting the field during serialization and deserialization. When type is initialized
389//! during deserialization, the field will be set to the default value. This is typically
390//! `Default::default()` (when using `#[wincode(skip)]` or `#[wincode(skip(default))]`), but can
391//! be overridden by specifying `#[wincode(skip(default_val = <value>))]`.
392//!
393//! ## Variant level (enum variants)
394//! |Attribute|Type|Default|Description
395//! |---|---|---|---|
396//! |`tag`|`Expr`|`None`|Specifies the discriminant expression for the variant. Only usable on enums.|
397//!
398//! ### `tag`
399//!
400//! Specifies the discriminant expression for the variant. Only usable on enums.
401//!
402//! <div class="warning">
403//! There is no bincode analog to this attribute.
404//! Specifying this attribute will make your enum incompatible with bincode's default enum encoding.
405//! If you need strict bincode compatibility, you should implement a custom <code>Deserialize</code> and
406//! <code>Serialize</code> impl for your enum on the serde / bincode side.
407//! </div>
408//!
409//! Example:
410//! ```
411//! # #[cfg(all(feature = "derive", feature = "alloc"))] {
412//! use wincode::{SchemaWrite, SchemaRead};
413//!
414//! #[derive(SchemaWrite, SchemaRead)]
415//! enum Enum {
416//! #[wincode(tag = 5)]
417//! A,
418//! #[wincode(tag = 8)]
419//! B,
420//! #[wincode(tag = 13)]
421//! C,
422//! }
423//!
424//! assert_eq!(&wincode::serialize(&Enum::A).unwrap(), &5u32.to_le_bytes());
425//! # }
426//! ```
427//!
428//! # UninitBuilder
429//!
430//! You may have some exotic serialization logic that requires you to implement `SchemaRead` manually
431//! for a type. In these scenarios, you'll likely want to leverage some additional helper methods
432//! to reduce the amount of boilerplate that is typically required when dealing with uninitialized
433//! fields.
434//!
435//! `#[derive(UninitBuilder)]` generates a corresponding uninit builder struct for the type.
436//! The name of the builder struct is the name of the type with `UninitBuilder` appended.
437//! E.g., `Header` -> `HeaderUninitBuilder`.
438//!
439//! The builder has automatic initialization tracking that does bookkeeping of which fields have been initialized.
440//! Calling `write_<field_name>` or `read_<field_name>`, for example, will mark the field as
441//! initialized so that it's properly dropped if the builder is dropped on error or panic.
442//!
443//! The builder struct has the following methods:
444//! - `from_maybe_uninit_mut`
445//! - Creates a new builder from a mutable `MaybeUninit` reference to the type.
446//! - `into_assume_init_mut`
447//! - Assumes the builder is fully initialized, drops it, and returns a mutable reference to the inner type.
448//! - `finish`
449//! - Forgets the builder, disabling the drop logic.
450//! - `is_init`
451//! - Checks if the builder is fully initialized by checking if all field initialization bits are set.
452//!
453//! For each field, the builder struct provides the following methods:
454//! - `uninit_<field_name>_mut`
455//! - Gets a mutable `MaybeUninit` projection to the `<field_name>` slot.
456//! - `read_<field_name>`
457//! - Reads into a `MaybeUninit`'s `<field_name>` slot from the given [`Reader`](io::Reader).
458//! - `write_<field_name>`
459//! - Writes a `MaybeUninit`'s `<field_name>` slot with the given value.
460//! - `init_<field_name>_with`
461//! - Initializes the `<field_name>` slot with a given initializer function.
462//! - `assume_init_<field_name>`
463//! - Marks the `<field_name>` slot as initialized.
464//!
465//! #### Safety
466//!
467//! Correct code will call `finish` or `into_assume_init_mut` once all fields have been initialized.
468//! Failing to do so will result in the initialized fields being dropped when the builder is dropped, which
469//! is undefined behavior if the `MaybeUninit` is later assumed to be initialized (e.g., on successful deserialization).
470//!
471//! #### Example
472//!
473//! ```
474//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
475//! # use wincode::{SchemaRead, SchemaWrite, io::Reader, error::ReadResult, config::Config, UninitBuilder};
476//! # use serde::{Serialize, Deserialize};
477//! # use core::mem::MaybeUninit;
478//! # #[derive(Debug, PartialEq, Eq)]
479//! #[derive(SchemaRead, SchemaWrite, UninitBuilder)]
480//! struct Header {
481//! num_required_signatures: u8,
482//! num_signed_accounts: u8,
483//! num_unsigned_accounts: u8,
484//! }
485//!
486//! # #[derive(Debug, PartialEq, Eq)]
487//! #[derive(SchemaRead, SchemaWrite, UninitBuilder)]
488//! struct Payload {
489//! header: Header,
490//! data: Vec<u8>,
491//! }
492//!
493//! # #[derive(Debug, PartialEq, Eq)]
494//! #[derive(SchemaWrite, UninitBuilder)]
495//! struct Message {
496//! payload: Payload,
497//! }
498//!
499//! // Assume for some reason we have to manually implement `SchemaRead` for `Message`.
500//! unsafe impl<'de, C: Config> SchemaRead<'de, C> for Message {
501//! type Dst = Message;
502//!
503//! fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
504//! let mut msg_builder = MessageUninitBuilder::<C>::from_maybe_uninit_mut(dst);
505//! unsafe {
506//! msg_builder.init_payload_with(|payload| {
507//! // Note that the order matters here. Values are dropped in reverse
508//! // declaration order, and we need to ensure `header_builder` is dropped
509//! // before `payload_builder` in the event of an error or panic.
510//! let mut payload_builder = PayloadUninitBuilder::<C>::from_maybe_uninit_mut(payload);
511//! // payload.header will be marked as initialized if the function succeeds.
512//! payload_builder.init_header_with(|header| {
513//! // Read directly into the projected MaybeUninit<Header> slot.
514//! let mut header_builder = HeaderUninitBuilder::<C>::from_maybe_uninit_mut(header);
515//! header_builder.read_num_required_signatures(reader.by_ref())?;
516//! header_builder.read_num_signed_accounts(reader.by_ref())?;
517//! header_builder.read_num_unsigned_accounts(reader.by_ref())?;
518//! header_builder.finish();
519//! Ok(())
520//! })?;
521//! // Alternatively, we could have done `payload_builder.read_header(reader.by_ref())?;`
522//! // rather than reading all the fields individually.
523//! payload_builder.read_data(reader.by_ref())?;
524//! // Payload is fully initialized, so we forget the builder
525//! // to avoid dropping the initialized fields.
526//! payload_builder.finish();
527//! Ok(())
528//! })?;
529//! }
530//! // Message is fully initialized.
531//! msg_builder.finish();
532//! Ok(())
533//! }
534//! }
535//!
536//! let msg = Message {
537//! payload: Payload {
538//! header: Header {
539//! num_required_signatures: 1,
540//! num_signed_accounts: 2,
541//! num_unsigned_accounts: 3
542//! },
543//! data: vec![4, 5, 6, 7, 8, 9]
544//! }
545//! };
546//! let serialized = wincode::serialize(&msg).unwrap();
547//! let deserialized = wincode::deserialize(&serialized).unwrap();
548//! assert_eq!(msg, deserialized);
549//! # }
550//! ```
551#![cfg_attr(docsrs, feature(doc_cfg))]
552#![cfg_attr(not(feature = "std"), no_std)]
553#[cfg(feature = "alloc")]
554extern crate alloc;
555
556pub mod error;
557pub use error::{Error, ReadError, ReadResult, Result, WriteError, WriteResult};
558pub mod io;
559pub mod len;
560mod schema;
561pub use schema::*;
562mod serde;
563pub use serde::*;
564pub mod config;
565#[cfg(test)]
566mod proptest_config;
567#[cfg(feature = "derive")]
568pub use wincode_derive::*;
569// Include tuple impls.
570include!(concat!(env!("OUT_DIR"), "/tuples.rs"));