Skip to main content

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//! # Compatibility
73//!
74//! - Produces the same bytes as `bincode` for the covered shapes when using bincode's
75//!   default configuration, provided your [`SchemaWrite`] and [`SchemaRead`] schemas and
76//!   [`containers`] match the layout implied by your `serde` types.
77//! - Length encodings are pluggable via [`SeqLen`](len::SeqLen).
78//! - Unlike `bincode`, this crate will fail to serialize or deserialize large
79//!   dynamic data structures by default, but this can be configured. This is
80//!   done for security and performance, as it allows to preallocate these data
81//!   structures safely.
82//!
83//! # Zero-copy deserialization
84//!
85//! `wincode`'s zero-copy deserialization is built on the following primitives:
86//! - [`u8`]
87//! - [`i8`]
88//!
89//! In addition to the following on little endian targets:
90//! - [`u16`], [`i16`], [`u32`], [`i32`], [`u64`], [`i64`], [`u128`], [`i128`], [`f32`], [`f64`]
91//!
92//! Types with alignment greater than 1 can force the compiler to insert padding into your structs.
93//! Zero-copy requires padding-free layouts; if the layout has implicit padding, `wincode` will not
94//! qualify the type as zero-copy.
95//!
96//! ---
97//!
98//! Within `wincode`, any type that is composed entirely of the above primitives is
99//! eligible for zero-copy deserialization. This includes arrays, slices, and structs.
100//!
101//! Structs deriving [`SchemaRead`] are eligible for zero-copy deserialization
102//! as long as they are composed entirely of the above zero-copy types, are annotated with
103//! `#[repr(transparent)]` or `#[repr(C)]`, and have no implicit padding. Use appropriate
104//! field ordering or add explicit padding fields if needed to eliminate implicit padding.
105//!
106//! Note that tuples are **not** eligible for zero-copy deserialization, as Rust does not
107//! currently guarantee tuple layout.
108//!
109//! ## Field reordering
110//! If your struct has implicit padding, you may be able to reorder fields to avoid it.
111//!
112//! ```
113//! #[repr(C)]
114//! struct HasPadding {
115//!    a: u8,
116//!    b: u32,
117//!    c: u16,
118//!    d: u8,
119//! }
120//!
121//! #[repr(C)]
122//! struct ZeroPadding {
123//!    b: u32,
124//!    c: u16,
125//!    a: u8,
126//!    d: u8,
127//! }
128//! ```
129//!
130//! ## Explicit padding
131//! You may need to add an explicit padding field if reordering fields cannot yield
132//! a padding-free layout.
133//!
134//! ```
135//! #[repr(C)]
136//! struct HasPadding {
137//!    a: u32,
138//!    b: u16,
139//!    _pad: [u8; 2],
140//! }
141//! ```
142//!
143//! ## Examples
144//!
145//! ### `&[u8]`
146//! ```
147//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
148//! use wincode::{SchemaWrite, SchemaRead};
149//!
150//! # #[derive(Debug, PartialEq, Eq)]
151//! #[derive(SchemaWrite, SchemaRead)]
152//! struct ByteRef<'a> {
153//!     bytes: &'a [u8],
154//! }
155//!
156//! let bytes: Vec<u8> = vec![1, 2, 3, 4, 5];
157//! let byte_ref = ByteRef { bytes: &bytes };
158//! let serialized = wincode::serialize(&byte_ref).unwrap();
159//! let deserialized: ByteRef<'_> = wincode::deserialize(&serialized).unwrap();
160//! assert_eq!(byte_ref, deserialized);
161//! # }
162//! ```
163//!
164//! ### struct newtype
165//! ```
166//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
167//! # use rand::random;
168//! # use std::array;
169//! use wincode::{SchemaWrite, SchemaRead};
170//!
171//! # #[derive(Debug, PartialEq, Eq)]
172//! #[derive(SchemaWrite, SchemaRead)]
173//! #[repr(transparent)]
174//! struct Signature([u8; 64]);
175//!
176//! # #[derive(Debug, PartialEq, Eq)]
177//! #[derive(SchemaWrite, SchemaRead)]
178//! struct Data<'a> {
179//!     signature: &'a Signature,
180//!     data: &'a [u8],
181//! }
182//!
183//! let signature = Signature(array::from_fn(|_| random()));
184//! let data = Data {
185//!     signature: &signature,
186//!     data: &[1, 2, 3, 4, 5],
187//! };
188//! let serialized = wincode::serialize(&data).unwrap();
189//! let deserialized: Data<'_> = wincode::deserialize(&serialized).unwrap();
190//! assert_eq!(data, deserialized);
191//! # }
192//! ```
193//!
194//! ### `&[u8; N]`
195//! ```
196//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
197//! use wincode::{SchemaWrite, SchemaRead};
198//!
199//! # #[derive(Debug, PartialEq, Eq)]
200//! #[derive(SchemaWrite, SchemaRead)]
201//! struct HeaderRef<'a> {
202//!     magic: &'a [u8; 7],
203//! }
204//!
205//! let header = HeaderRef { magic: b"W1NC0D3" };
206//! let serialized = wincode::serialize(&header).unwrap();
207//! let deserialized: HeaderRef<'_> = wincode::deserialize(&serialized).unwrap();
208//! assert_eq!(header, deserialized);
209//! # }
210//! ```
211//!
212//! ## In-place mutation
213//!
214//! wincode supports in-place mutation of zero-copy types.
215//! See [`deserialize_mut`] or [`ZeroCopy::from_bytes_mut`] for more details.
216//!
217//! ## `ZeroCopy` and `config::ZeroCopy` methods
218//!
219//! The [`ZeroCopy`] and [`config::ZeroCopy`] traits provide some convenience methods for
220//! working with zero-copy types.
221//!
222//! See those trait definitions for more details.
223//!
224//! # Crate Features
225//!
226//! |Feature|Default|Description
227//! |---|---|---|
228//! |`std`|enabled|Enables `std` support.|
229//! |`alloc`|enabled automatically when `std` is enabled|Enables `alloc` support.|
230//! |`derive`|disabled|Enables the derive macros for [`SchemaRead`] and [`SchemaWrite`].|
231//! |`bv`|disabled|Enables support for the `bv` crate. Encoded values can be decoded by its `serde` implementation.|
232//! |`bv-strict`|disabled|Enables `bv` support with canonical encoding and strict decoding. This masks unused padding bits when encoding and rejects non-canonical block counts or padding when decoding.|
233//! |`uuid`|disabled|Enables support for the `uuid` crate.|
234//! |`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.|
235//! |`bumpalo`|disabled|Enables support for the `bumpalo` crate.|
236//!
237//! # Derive attributes
238//!
239//! ## Top level
240//! |Attribute|Type|Default|Description
241//! |---|---|---|---|
242//! |`tag_encoding`|`Type`|`None`|Specifies the encoding/decoding schema to use for the variant discriminant. Only usable on enums.|
243//! |`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.|
244//! |`crate`|`Path`|`::wincode`|Specifies the path to the `wincode` crate. Useful when `wincode` is renamed in `Cargo.toml` or re-exported from another module. The path is emitted as written and resolved from the derive expansion site.|
245//! |`context`|`Type`|`None`|Makes `SchemaRead` derive [`SchemaReadContext`] for the given context type instead of [`SchemaRead`].|
246//!
247//! ### `context`
248//!
249//! A top-level `context` changes the trait generated by `SchemaRead` from [`SchemaRead`] to
250//! [`SchemaReadContext`]. Mark each field that should receive the context with
251//! `#[wincode(context)]`; unmarked fields continue to use their ordinary [`SchemaRead`]
252//! implementation. The field marker can also be combined with `with`, for example
253//! `#[wincode(with = "MyAdapter", context)]`.
254//!
255//! Lifetimes appearing in the context type remain tied to the context instead of the serialized
256//! input. This allows data derived from the context to outlive the input bytes while ordinary
257//! borrowed fields remain tied to them.
258//!
259//! `SchemaWrite` is unchanged: field-level contexts only select how fields are read.
260//! `UninitBuilder` does not currently support types with a top-level context.
261//!
262//! Example:
263//! ```
264//! # #[cfg(all(feature = "derive", feature = "bumpalo"))] {
265//! use bumpalo::{Bump, collections::String};
266//! use wincode::{SchemaRead, SchemaWrite, deserialize_with_context, serialize};
267//!
268//! #[derive(SchemaRead, SchemaWrite, Debug, PartialEq)]
269//! #[wincode(context = "&'bump Bump")]
270//! struct Message<'bump> {
271//!     id: u32,
272//!     #[wincode(context)]
273//!     text: String<'bump>,
274//! }
275//!
276//! let bump = Bump::new();
277//! let message = Message {
278//!     id: 42,
279//!     text: String::from_str_in("hello", &bump),
280//! };
281//!
282//! let decoded: Message = {
283//!     let bytes = serialize(&message).unwrap();
284//!     deserialize_with_context(&bump, &bytes).unwrap()
285//! };
286//!
287//! assert_eq!(decoded, message);
288//! # }
289//! ```
290//!
291//! ### `tag_encoding`
292//!
293//! Allows specifying the encoding/decoding schema to use for the variant discriminant. Only usable on enums.
294//!
295//! <div class="warning">
296//! There is no bincode analog to this attribute.
297//! Specifying this attribute will make your enum incompatible with bincode's default enum encoding.
298//! If you need strict bincode compatibility, you should implement a custom <code>Deserialize</code> and
299//! <code>Serialize</code> impl for your enum on the serde / bincode side.
300//! </div>
301//!
302//! Example:
303//! ```
304//! # #[cfg(all(feature = "derive", feature = "alloc"))] {
305//! use wincode::{SchemaWrite, SchemaRead};
306//!
307//! # #[derive(Debug, PartialEq, Eq)]
308//! #[derive(SchemaWrite, SchemaRead)]
309//! #[wincode(tag_encoding = "u8")]
310//! enum Enum {
311//!     A,
312//!     B,
313//!     C,
314//! }
315//!
316//! assert_eq!(&wincode::serialize(&Enum::B).unwrap(), &1u8.to_le_bytes());
317//! # }
318//! ```
319//!
320//! ## Field level
321//! |Attribute|Type|Default|Description
322//! |---|---|---|---|
323//! |`with`|`Type`|`None`|Overrides the default `SchemaRead` or `SchemaWrite` implementation for the field.|
324//! |`skip`|`bool`\|`Expr`|`false`|Skips the field during serialization and deserialization (initializing with default value).|
325//! |`context`|`bool`|`false`|Reads the field using [`SchemaReadContext`] and the top-level `context`. Requires a top-level `context` type.|
326//!
327//! ### `skip`
328//!
329//! Allows omitting the field during serialization and deserialization. When type is initialized
330//! during deserialization, the field will be set to the default value. This is typically
331//! `Default::default()` (when using `#[wincode(skip)]` or `#[wincode(skip(default))]`), but can
332//! be overridden by specifying `#[wincode(skip(default_val = <value>))]`.
333//!
334//! ## Variant level (enum variants)
335//! |Attribute|Type|Default|Description
336//! |---|---|---|---|
337//! |`tag`|`Expr`|`None`|Specifies the discriminant expression for the variant. Only usable on enums.|
338//!
339//! ### `tag`
340//!
341//! Specifies the discriminant expression for the variant. Only usable on enums.
342//!
343//! <div class="warning">
344//! There is no bincode analog to this attribute.
345//! Specifying this attribute will make your enum incompatible with bincode's default enum encoding.
346//! If you need strict bincode compatibility, you should implement a custom <code>Deserialize</code> and
347//! <code>Serialize</code> impl for your enum on the serde / bincode side.
348//! </div>
349//!
350//! Example:
351//! ```
352//! # #[cfg(all(feature = "derive", feature = "alloc"))] {
353//! use wincode::{SchemaWrite, SchemaRead};
354//!
355//! #[derive(SchemaWrite, SchemaRead)]
356//! enum Enum {
357//!     #[wincode(tag = 5)]
358//!     A,
359//!     #[wincode(tag = 8)]
360//!     B,
361//!     #[wincode(tag = 13)]
362//!     C,
363//! }
364//!
365//! assert_eq!(&wincode::serialize(&Enum::A).unwrap(), &5u32.to_le_bytes());
366//! # }
367//! ```
368//!
369//! # UninitBuilder
370//!
371//! You may have some exotic serialization logic that requires you to implement `SchemaRead` manually
372//! for a type. In these scenarios, you'll likely want to leverage some additional helper methods
373//! to reduce the amount of boilerplate that is typically required when dealing with uninitialized
374//! fields.
375//!
376//! `#[derive(UninitBuilder)]` generates a corresponding uninit builder struct for the type.
377//! The name of the builder struct is the name of the type with `UninitBuilder` appended.
378//! E.g., `Header` -> `HeaderUninitBuilder`.
379//!
380//! The builder has automatic initialization tracking that does bookkeeping of which fields have been initialized.
381//! Calling `write_<field_name>` or `read_<field_name>`, for example, will mark the field as
382//! initialized so that it's properly dropped if the builder is dropped on error or panic.
383//! Successfully initializing the same field more than once overwrites the previous value without dropping it.
384//! Repeated initialization may therefore leak resources or skip other destructor side effects.
385//!
386//! The builder struct has the following methods:
387//! - `from_maybe_uninit_mut`
388//!   - Creates a new builder from a mutable `MaybeUninit` reference to the type.
389//! - `into_assume_init_mut`
390//!   - Assumes the builder is fully initialized, drops it, and returns a mutable reference to the inner type.
391//! - `finish`
392//!   - Forgets the builder, disabling the drop logic.
393//! - `is_init`
394//!   - Checks if the builder is fully initialized by checking if all field initialization bits are set.
395//!
396//! For each field, the builder struct provides the following methods:
397//! - `uninit_<field_name>_mut`
398//!   - Gets a mutable `MaybeUninit` projection to the `<field_name>` slot.
399//! - `uninit_<field_name>_ref`
400//!   - Gets a shared `MaybeUninit` projection of the `<field_name>` slot.
401//! - `read_<field_name>`
402//!   - Reads into a `MaybeUninit`'s `<field_name>` slot from the given [`Reader`](io::Reader).
403//! - `write_<field_name>`
404//!   - Writes a `MaybeUninit`'s `<field_name>` slot with the given value.
405//! - `init_<field_name>_with`
406//!   - Initializes the `<field_name>` slot with a given initializer function.
407//! - `assume_init_<field_name>`
408//!   - Marks the `<field_name>` slot as initialized.
409//!
410//! #### Safety
411//!
412//! Correct code will call `finish` or `into_assume_init_mut` once all fields have been initialized.
413//! Failing to do so will result in the initialized fields being dropped when the builder is dropped, which
414//! is undefined behavior if the `MaybeUninit` is later assumed to be initialized (e.g., on successful deserialization).
415//!
416//! #### Example
417//!
418//! ```
419//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
420//! # use wincode::{SchemaRead, SchemaWrite, io::Reader, error::ReadResult, config::Config, UninitBuilder};
421//! # use serde::{Serialize, Deserialize};
422//! # use core::mem::MaybeUninit;
423//! # #[derive(Debug, PartialEq, Eq)]
424//! #[derive(SchemaRead, SchemaWrite, UninitBuilder)]
425//! struct Header {
426//!     num_required_signatures: u8,
427//!     num_signed_accounts: u8,
428//!     num_unsigned_accounts: u8,
429//! }
430//!
431//! # #[derive(Debug, PartialEq, Eq)]
432//! #[derive(SchemaRead, SchemaWrite, UninitBuilder)]
433//! struct Payload {
434//!     header: Header,
435//!     data: Vec<u8>,
436//! }
437//!
438//! # #[derive(Debug, PartialEq, Eq)]
439//! #[derive(SchemaWrite, UninitBuilder)]
440//! struct Message {
441//!     payload: Payload,
442//! }
443//!
444//! // Assume for some reason we have to manually implement `SchemaRead` for `Message`.
445//! unsafe impl<'de, C: Config> SchemaRead<'de, C> for Message {
446//!     type Dst = Message;
447//!
448//!     fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
449//!         let mut msg_builder = MessageUninitBuilder::<C>::from_maybe_uninit_mut(dst);
450//!         unsafe {
451//!             msg_builder.init_payload_with(|payload| {
452//!                 // Note that the order matters here. Values are dropped in reverse
453//!                 // declaration order, and we need to ensure `header_builder` is dropped
454//!                 // before `payload_builder` in the event of an error or panic.
455//!                 let mut payload_builder = PayloadUninitBuilder::<C>::from_maybe_uninit_mut(payload);
456//!                 // payload.header will be marked as initialized if the function succeeds.
457//!                 payload_builder.init_header_with(|header| {
458//!                     // Read directly into the projected MaybeUninit<Header> slot.
459//!                     let mut header_builder = HeaderUninitBuilder::<C>::from_maybe_uninit_mut(header);
460//!                     header_builder.read_num_required_signatures(reader.by_ref())?;
461//!                     header_builder.read_num_signed_accounts(reader.by_ref())?;
462//!                     header_builder.read_num_unsigned_accounts(reader.by_ref())?;
463//!                     header_builder.finish();
464//!                     Ok(())
465//!                 })?;
466//!                 // Alternatively, we could have done `payload_builder.read_header(reader.by_ref())?;`
467//!                 // rather than reading all the fields individually.
468//!                 payload_builder.read_data(reader.by_ref())?;
469//!                 // Payload is fully initialized, so we forget the builder
470//!                 // to avoid dropping the initialized fields.
471//!                 payload_builder.finish();
472//!                 Ok(())
473//!             })?;
474//!         }
475//!         // Message is fully initialized.
476//!         msg_builder.finish();
477//!         Ok(())
478//!     }
479//! }
480//!
481//! let msg = Message {
482//!     payload: Payload {
483//!         header: Header {
484//!             num_required_signatures: 1,
485//!             num_signed_accounts: 2,
486//!             num_unsigned_accounts: 3
487//!         },
488//!         data: vec![4, 5, 6, 7, 8, 9]
489//!     }
490//! };
491//! let serialized = wincode::serialize(&msg).unwrap();
492//! let deserialized = wincode::deserialize(&serialized).unwrap();
493//! assert_eq!(msg, deserialized);
494//! # }
495//! ```
496#![cfg_attr(docsrs, feature(doc_cfg))]
497#![cfg_attr(not(feature = "std"), no_std)]
498#[cfg(feature = "alloc")]
499extern crate alloc;
500
501pub mod error;
502pub use error::{Error, ReadError, ReadResult, Result, WriteError, WriteResult};
503pub mod io;
504pub mod len;
505mod schema;
506pub use schema::*;
507mod serde;
508pub use serde::*;
509pub mod config;
510#[cfg(test)]
511mod proptest_config;
512#[cfg(feature = "derive")]
513pub use wincode_derive::*;
514// Include tuple impls.
515include!(concat!(env!("OUT_DIR"), "/tuples.rs"));