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::{self, Pod}};
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//! #[derive(SchemaWrite, SchemaRead)]
133//! #[wincode(from = "foreign_crate::A")]
134//! pub struct MyA {
135//! addresses: Vec<Pod<foreign_crate::Address>>,
136//! hash: Pod<foreign_crate::Hash>,
137//! }
138//!
139//! let val = foreign_crate::A {
140//! addresses: vec![foreign_crate::Address([0; 32]), foreign_crate::Address([1; 32])],
141//! hash: foreign_crate::Hash([0; 32]),
142//! };
143//! let bincode_serialize = bincode::serialize(&val).unwrap();
144//! let wincode_serialize = MyA::serialize(&val).unwrap();
145//! assert_eq!(bincode_serialize, wincode_serialize);
146//!
147//! let bincode_deserialize: foreign_crate::A = bincode::deserialize(&bincode_serialize).unwrap();
148//! let wincode_deserialize = MyA::deserialize(&bincode_serialize).unwrap();
149//! assert_eq!(val, bincode_deserialize);
150//! assert_eq!(val, wincode_deserialize);
151//! # }
152//! ```
153//!
154//! Now, when deserializing `A`:
155//! - All initialization is done in-place, including heap-allocated memory
156//! (true of all supported contiguous heap-allocated structures in `wincode`).
157//! - Byte fields are read and written in a single pass.
158//!
159//! # Compatibility
160//!
161//! - Produces the same bytes as `bincode` for the covered shapes when using bincode's
162//! default configuration, provided your [`SchemaWrite`] and [`SchemaRead`] schemas and
163//! [`containers`] match the layout implied by your `serde` types.
164//! - Length encodings are pluggable via [`SeqLen`](len::SeqLen).
165//!
166//! # Zero-copy deserialization
167//!
168//! `wincode`'s zero-copy deserialization is built on the following primitives:
169//! - [`u8`]
170//! - [`i8`]
171//!
172//! In addition to the following on little endian targets:
173//! - [`u16`], [`i16`], [`u32`], [`i32`], [`u64`], [`i64`], [`u128`], [`i128`], [`f32`], [`f64`]
174//!
175//! Types with alignment greater than 1 can force the compiler to insert padding into your structs.
176//! Zero-copy requires padding-free layouts; if the layout has implicit padding, `wincode` will not
177//! qualify the type as zero-copy.
178//!
179//! ---
180//!
181//! Within `wincode`, any type that is composed entirely of the above primitives is
182//! eligible for zero-copy deserialization. This includes arrays, slices, and structs.
183//!
184//! Structs deriving [`SchemaRead`] are eligible for zero-copy deserialization
185//! as long as they are composed entirely of the above zero-copy types, are annotated with
186//! `#[repr(transparent)]` or `#[repr(C)]`, and have no implicit padding. Use appropriate
187//! field ordering or add explicit padding fields if needed to eliminate implicit padding.
188//!
189//! Note that tuples are **not** eligible for zero-copy deserialization, as Rust does not
190//! currently guarantee tuple layout.
191//!
192//! ## Field reordering
193//! If your struct has implicit padding, you may be able to reorder fields to avoid it.
194//!
195//! ```
196//! #[repr(C)]
197//! struct HasPadding {
198//! a: u8,
199//! b: u32,
200//! c: u16,
201//! d: u8,
202//! }
203//!
204//! #[repr(C)]
205//! struct ZeroPadding {
206//! b: u32,
207//! c: u16,
208//! a: u8,
209//! d: u8,
210//! }
211//! ```
212//!
213//! ## Explicit padding
214//! You may need to add an explicit padding field if reordering fields cannot yield
215//! a padding-free layout.
216//!
217//! ```
218//! #[repr(C)]
219//! struct HasPadding {
220//! a: u32,
221//! b: u16,
222//! _pad: [u8; 2],
223//! }
224//! ```
225//!
226//! ## Examples
227//!
228//! ### `&[u8]`
229//! ```
230//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
231//! use wincode::{SchemaWrite, SchemaRead};
232//!
233//! # #[derive(Debug, PartialEq, Eq)]
234//! #[derive(SchemaWrite, SchemaRead)]
235//! struct ByteRef<'a> {
236//! bytes: &'a [u8],
237//! }
238//!
239//! let bytes: Vec<u8> = vec![1, 2, 3, 4, 5];
240//! let byte_ref = ByteRef { bytes: &bytes };
241//! let serialized = wincode::serialize(&byte_ref).unwrap();
242//! let deserialized: ByteRef<'_> = wincode::deserialize(&serialized).unwrap();
243//! assert_eq!(byte_ref, deserialized);
244//! # }
245//! ```
246//!
247//! ### struct newtype
248//! ```
249//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
250//! # use rand::random;
251//! # use std::array;
252//! use wincode::{SchemaWrite, SchemaRead};
253//!
254//! # #[derive(Debug, PartialEq, Eq)]
255//! #[derive(SchemaWrite, SchemaRead)]
256//! #[repr(transparent)]
257//! struct Signature([u8; 64]);
258//!
259//! # #[derive(Debug, PartialEq, Eq)]
260//! #[derive(SchemaWrite, SchemaRead)]
261//! struct Data<'a> {
262//! signature: &'a Signature,
263//! data: &'a [u8],
264//! }
265//!
266//! let signature = Signature(array::from_fn(|_| random()));
267//! let data = Data {
268//! signature: &signature,
269//! data: &[1, 2, 3, 4, 5],
270//! };
271//! let serialized = wincode::serialize(&data).unwrap();
272//! let deserialized: Data<'_> = wincode::deserialize(&serialized).unwrap();
273//! assert_eq!(data, deserialized);
274//! # }
275//! ```
276//!
277//! ### `&[u8; N]`
278//! ```
279//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
280//! use wincode::{SchemaWrite, SchemaRead};
281//!
282//! # #[derive(Debug, PartialEq, Eq)]
283//! #[derive(SchemaWrite, SchemaRead)]
284//! struct HeaderRef<'a> {
285//! magic: &'a [u8; 7],
286//! }
287//!
288//! let header = HeaderRef { magic: b"W1NC0D3" };
289//! let serialized = wincode::serialize(&header).unwrap();
290//! let deserialized: HeaderRef<'_> = wincode::deserialize(&serialized).unwrap();
291//! assert_eq!(header, deserialized);
292//! # }
293//! ```
294//!
295//! ## In-place mutation
296//!
297//! wincode supports in-place mutation of zero-copy types.
298//! See [`deserialize_mut`] or [`ZeroCopy::from_bytes_mut`] for more details.
299//!
300//! ## `ZeroCopy` methods
301//!
302//! The [`ZeroCopy`] trait provides some convenience methods for
303//! working with zero-copy types.
304//!
305//! See [`ZeroCopy::from_bytes`] and [`ZeroCopy::from_bytes_mut`] for more details.
306//!
307//! # Derive attributes
308//!
309//! ## Top level
310//! |Attribute|Type|Default|Description
311//! |---|---|---|---|
312//! |`from`|`Type`|`None`|Indicates that type is a mapping from another type (example in previous section)|
313//! |`no_suppress_unused`|`bool`|`false`|Disable unused field lints suppression. Only usable on structs with `from`.|
314//! |`struct_extensions`|`bool`|`false`|Generates placement initialization helpers on `SchemaRead` struct implementations|
315//! |`tag_encoding`|`Type`|`None`|Specifies the encoding/decoding schema to use for the variant discriminant. Only usable on enums.|
316//! |`assert_zero_copy`|`bool`|`false`|Generates compile-time asserts to ensure the type meets zero-copy requirements.|
317//!
318//! ### `no_suppress_unused`
319//!
320//! When creating a mapping type with `#[wincode(from = "AnotherType")]`, fields are typically
321//! comprised of [`containers`] (of course not strictly always true). As a result, these structs
322//! purely exist for the compiler to generate optimized implementations, and are never actually
323//! constructed. As a result, unused field lints will be triggered, which can be annoying.
324//! By default, when `from` is used, the derive macro will generate dummy function that references all
325//! the struct fields, which suppresses those lints. This function will ultimately be compiled out of your
326//! build, but you can disable this by setting `no_suppress_unused` to `true`. You can also avoid
327//! these lint errors with visibility modifiers (e.g., `pub`).
328//!
329//! Note that this only works on structs, as it is not possible to construct an arbitrary enum variant.
330//!
331//! ### `tag_encoding`
332//!
333//! Allows specifying the encoding/decoding schema to use for the variant discriminant. Only usable on enums.
334//!
335//! <div class="warning">
336//! There is no bincode analog to this attribute.
337//! Specifying this attribute will make your enum incompatible with bincode's default enum encoding.
338//! If you need strict bincode compatibility, you should implement a custom <code>Deserialize</code> and
339//! <code>Serialize</code> impl for your enum on the serde / bincode side.
340//! </div>
341//!
342//! Example:
343//! ```
344//! # #[cfg(all(feature = "derive", feature = "alloc"))] {
345//! use wincode::{SchemaWrite, SchemaRead};
346//!
347//! # #[derive(Debug, PartialEq, Eq)]
348//! #[derive(SchemaWrite, SchemaRead)]
349//! #[wincode(tag_encoding = "u8")]
350//! enum Enum {
351//! A,
352//! B,
353//! C,
354//! }
355//!
356//! assert_eq!(&wincode::serialize(&Enum::B).unwrap(), &1u8.to_le_bytes());
357//! # }
358//! ```
359//!
360//! ### `struct_extensions`
361//!
362//! You may have some exotic serialization logic that requires you to implement `SchemaRead` manually
363//! for a type. In these scenarios, you'll likely want to leverage some additional helper methods
364//! to reduce the amount of boilerplate that is typically required when dealing with uninitialized
365//! fields.
366//!
367//! `#[wincode(struct_extensions)]` generates a corresponding uninit builder struct for the type.
368//! The name of the builder struct is the name of the type with `UninitBuilder` appended.
369//! E.g., `Header` -> `HeaderUninitBuilder`.
370//!
371//! The builder has automatic initialization tracking that does bookkeeping of which fields have been initialized.
372//! Calling `write_<field_name>` or `read_<field_name>`, for example, will mark the field as
373//! initialized so that it's properly dropped if the builder is dropped on error or panic.
374//!
375//! The builder struct has the following methods:
376//! - `from_maybe_uninit_mut`
377//! - Creates a new builder from a mutable `MaybeUninit` reference to the type.
378//! - `into_assume_init_mut`
379//! - Assumes the builder is fully initialized, drops it, and returns a mutable reference to the inner type.
380//! - `finish`
381//! - Forgets the builder, disabling the drop logic.
382//! - `is_init`
383//! - Checks if the builder is fully initialized by checking if all field initialization bits are set.
384//!
385//! For each field, the builder struct provides the following methods:
386//! - `uninit_<field_name>_mut`
387//! - Gets a mutable `MaybeUninit` projection to the `<field_name>` slot.
388//! - `read_<field_name>`
389//! - Reads into a `MaybeUninit`'s `<field_name>` slot from the given [`Reader`](io::Reader).
390//! - `write_<field_name>`
391//! - Writes a `MaybeUninit`'s `<field_name>` slot with the given value.
392//! - `init_<field_name>_with`
393//! - Initializes the `<field_name>` slot with a given initializer function.
394//! - `assume_init_<field_name>`
395//! - Marks the `<field_name>` slot as initialized.
396//!
397//! #### Safety
398//!
399//! Correct code will call `finish` or `into_assume_init_mut` once all fields have been initialized.
400//! Failing to do so will result in the initialized fields being dropped when the builder is dropped, which
401//! is undefined behavior if the `MaybeUninit` is later assumed to be initialized (e.g., on successful deserialization).
402//!
403//! #### Example
404//!
405//! ```
406//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
407//! # use wincode::{SchemaRead, SchemaWrite, io::Reader, error::ReadResult};
408//! # use serde::{Serialize, Deserialize};
409//! # use core::mem::MaybeUninit;
410//! # #[derive(Debug, PartialEq, Eq)]
411//! #[derive(SchemaRead, SchemaWrite)]
412//! #[wincode(struct_extensions)]
413//! struct Header {
414//! num_required_signatures: u8,
415//! num_signed_accounts: u8,
416//! num_unsigned_accounts: u8,
417//! }
418//!
419//! # #[derive(Debug, PartialEq, Eq)]
420//! #[derive(SchemaRead, SchemaWrite)]
421//! #[wincode(struct_extensions)]
422//! struct Payload {
423//! header: Header,
424//! data: Vec<u8>,
425//! }
426//!
427//! # #[derive(Debug, PartialEq, Eq)]
428//! #[derive(SchemaWrite)]
429//! struct Message {
430//! payload: Payload,
431//! }
432//!
433//! // Assume for some reason we have to manually implement `SchemaRead` for `Message`.
434//! impl<'de> SchemaRead<'de> for Message {
435//! type Dst = Message;
436//!
437//! fn read(reader: &mut impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
438//! // Normally we have to do a big ugly cast like this
439//! // to get a mutable `MaybeUninit<Payload>`.
440//! let payload = unsafe {
441//! &mut *(&raw mut (*dst.as_mut_ptr()).payload).cast::<MaybeUninit<Payload>>()
442//! };
443//! // Note that the order matters here. Values are dropped in reverse
444//! // declaration order, and we need to ensure `header_builder` is dropped
445//! // before `payload_builder` in the event of an error or panic.
446//! let mut payload_builder = PayloadUninitBuilder::from_maybe_uninit_mut(payload);
447//! unsafe {
448//! // payload.header will be marked as initialized if the function succeeds.
449//! payload_builder.init_header_with(|header| {
450//! // Read directly into the projected MaybeUninit<Header> slot.
451//! let mut header_builder = HeaderUninitBuilder::from_maybe_uninit_mut(header);
452//! header_builder.read_num_required_signatures(reader)?;
453//! header_builder.read_num_signed_accounts(reader)?;
454//! header_builder.read_num_unsigned_accounts(reader)?;
455//! header_builder.finish();
456//! Ok(())
457//! })?;
458//! }
459//! // Alternatively, we could have done `payload_builder.read_header(reader)?;`
460//! // rather than reading all the fields individually.
461//! payload_builder.read_data(reader)?;
462//! // Message is fully initialized, so we forget the builders
463//! // to avoid dropping the initialized fields.
464//! payload_builder.finish();
465//! Ok(())
466//! }
467//! }
468//!
469//! let msg = Message {
470//! payload: Payload {
471//! header: Header {
472//! num_required_signatures: 1,
473//! num_signed_accounts: 2,
474//! num_unsigned_accounts: 3
475//! },
476//! data: vec![4, 5, 6, 7, 8, 9]
477//! }
478//! };
479//! let serialized = wincode::serialize(&msg).unwrap();
480//! let deserialized = wincode::deserialize(&serialized).unwrap();
481//! assert_eq!(msg, deserialized);
482//! # }
483//! ```
484//!
485//! ## Field level
486//! |Attribute|Type|Default|Description
487//! |---|---|---|---|
488//! |`with`|`Type`|`None`|Overrides the default `SchemaRead` or `SchemaWrite` implementation for the field.|
489//!
490//! ## Variant level (enum variants)
491//! |Attribute|Type|Default|Description
492//! |---|---|---|---|
493//! |`tag`|`Expr`|`None`|Specifies the discriminant expression for the variant. Only usable on enums.|
494//!
495//! ### `tag`
496//!
497//! Specifies the discriminant expression for the variant. Only usable on enums.
498//!
499//! <div class="warning">
500//! There is no bincode analog to this attribute.
501//! Specifying this attribute will make your enum incompatible with bincode's default enum encoding.
502//! If you need strict bincode compatibility, you should implement a custom <code>Deserialize</code> and
503//! <code>Serialize</code> impl for your enum on the serde / bincode side.
504//! </div>
505//!
506//! Example:
507//! ```
508//! # #[cfg(all(feature = "derive", feature = "alloc"))] {
509//! use wincode::{SchemaWrite, SchemaRead};
510//!
511//! #[derive(SchemaWrite, SchemaRead)]
512//! enum Enum {
513//! #[wincode(tag = 5)]
514//! A,
515//! #[wincode(tag = 8)]
516//! B,
517//! #[wincode(tag = 13)]
518//! C,
519//! }
520//!
521//! assert_eq!(&wincode::serialize(&Enum::A).unwrap(), &5u32.to_le_bytes());
522//! # }
523//! ```
524#![cfg_attr(docsrs, feature(doc_cfg))]
525#![cfg_attr(not(feature = "std"), no_std)]
526#[cfg(feature = "alloc")]
527extern crate alloc;
528
529pub mod error;
530pub use error::{Error, ReadError, ReadResult, Result, WriteError, WriteResult};
531pub mod io;
532pub mod len;
533mod schema;
534pub use schema::*;
535mod serde;
536pub use serde::*;
537#[cfg(test)]
538mod proptest_config;
539#[cfg(feature = "derive")]
540pub use wincode_derive::*;
541// Include tuple impls.
542include!(concat!(env!("OUT_DIR"), "/tuples.rs"));