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//! For "plain old data" (see [`Pod`](containers::Pod)) fields (POD newtypes, arrays of POD newtypes, etc),
33//! use [`containers`] to leverage optimized read/write implementations.
34//!
35//! ```
36//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
37//! # use wincode::{containers::{self, Pod}};
38//! # use wincode_derive::{SchemaWrite, SchemaRead};
39//! # use serde::{Serialize, Deserialize};
40//! # #[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug)]
41//! #[repr(transparent)]
42//! #[derive(Clone, Copy)]
43//! struct Address([u8; 32]);
44//!
45//! # #[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug)]
46//! #[repr(transparent)]
47//! #[derive(Clone, Copy)]
48//! struct Hash([u8; 32]);
49//!
50//! # #[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug)]
51//! #[derive(SchemaWrite, SchemaRead)]
52//! struct MyStruct {
53//! #[wincode(with = "Pod<_>")]
54//! hash: Hash,
55//! #[wincode(with = "containers::Vec<Pod<_>>")]
56//! addresses: Vec<Address>,
57//! }
58//!
59//! let val = MyStruct {
60//! hash: Hash([0; 32]),
61//! addresses: vec![Address([0; 32]), Address([1; 32])]
62//! };
63//! assert_eq!(wincode::serialize(&val).unwrap(), bincode::serialize(&val).unwrap());
64//! # }
65//! ```
66//!
67//! # Motivation
68//!
69//! Typical Rust API design employs a *construct-then-move* style of programming.
70//! Common APIs like `Vec::push`, iterator adaptors, `Box::new` (and its `Rc`/`Arc`
71//! variants), and even returning a fully-initialized struct from a function all
72//! follow this pattern. While this style feels intuitive and ergonomic, it
73//! inherently entails copying unless the compiler can perform elision -- which,
74//! today, it generally cannot. To see why this is a consequence of the design,
75//! consider the following code:
76//! ```
77//! # struct MyStruct;
78//! # impl MyStruct {
79//! # fn new() -> Self {
80//! # MyStruct
81//! # }
82//! # }
83//! Box::new(MyStruct::new());
84//! ```
85//! `MyStruct` must be constructed *before* it can be moved into `Box`'s allocation.
86//! This is a classic code ordering problem: to avoid the copy, `Box::new` needs
87//! to execute code before `MyStruct::new()` runs. `Vec::push`, iterator collection,
88//! and similar APIs have this same problem.
89//! (See these [design meeting notes](https://hackmd.io/XXuVXH46T8StJB_y0urnYg) or
90//! or the
91//! [`placement-by-return` RFC](https://github.com/PoignardAzur/rust-rfcs/blob/placement-by-return/text/0000-placement-by-return.md)
92//! for a more in-depth discussion on this topic.) The result of this is that even
93//! performance conscious developers routinely introduce avoidable copying without
94//! realizing it. `serde` inherits these issues since it neither attempts to
95//! initialize in‑place nor exposes APIs to do so.
96//!
97//! These patterns are not inherent limitations of Rust, but are consequences of
98//! conventions and APIs that do not consider in-place initialization as part of
99//! their design. The tools for in-place construction *do* exist (see
100//! [`MaybeUninit`](core::mem::MaybeUninit) and raw pointer APIs), but they are
101//! rarely surfaced in libraries and can be cumbersome to use (see [`addr_of_mut!`](core::ptr::addr_of_mut)),
102//! so programmers are often not even aware of them or avoid them.
103//!
104//! `wincode` makes in-place initialization a first class design goal, and fundamentally
105//! operates on [traits](#traits) that facilitate direct writes of memory.
106//!
107//! # Adapting foreign types
108//!
109//! `wincode` can also be used to implement serialization/deserialization
110//! on foreign types, where serialization/deserialization schemes on those types are unoptimized (and
111//! out of your control as a foreign type). For example, consider the following struct,
112//! defined outside of your crate:
113//! ```
114//! use serde::{Serialize, Deserialize};
115//!
116//! # #[derive(PartialEq, Eq, Debug)]
117//! #[repr(transparent)]
118//! #[derive(Clone, Copy, Serialize, Deserialize)]
119//! struct Address([u8; 32]);
120//!
121//! # #[derive(PartialEq, Eq, Debug)]
122//! #[repr(transparent)]
123//! #[derive(Clone, Copy, Serialize, Deserialize)]
124//! struct Hash([u8; 32]);
125//!
126//! #[derive(Serialize, Deserialize)]
127//! pub struct A {
128//! pub addresses: Vec<Address>,
129//! pub hash: Hash,
130//! }
131//! ```
132//!
133//! `serde`'s default, naive, implementation will perform per-element visitation of all bytes
134//! in `Vec<Address>` and `Hash`. Because these fields are "plain old data", ideally we would
135//! avoid per-element visitation entirely and read / write these fields in a single pass.
136//! The situation worsens if this struct needs to be written into a heap allocated data structure,
137//! like a `Vec<A>` or `Box<[A]>`. As discussed in [motivation](#motivation), all
138//! those bytes will be initialized on the stack before being copied into the heap allocation.
139//!
140//! `wincode` can solve this with the following:
141//! ```
142//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
143//! # use wincode::{Serialize as _, Deserialize as _, containers::{self, Pod}};
144//! # use wincode_derive::{SchemaWrite, SchemaRead};
145//! mod foreign_crate {
146//! // Defined in some foreign crate...
147//! use serde::{Serialize, Deserialize};
148//!
149//! # #[derive(PartialEq, Eq, Debug)]
150//! #[repr(transparent)]
151//! #[derive(Clone, Copy, Serialize, Deserialize)]
152//! pub struct Address(pub [u8; 32]);
153//!
154//! # #[derive(PartialEq, Eq, Debug)]
155//! #[repr(transparent)]
156//! #[derive(Clone, Copy, Serialize, Deserialize)]
157//! pub struct Hash(pub [u8; 32]);
158//!
159//! # #[derive(PartialEq, Eq, Debug)]
160//! #[derive(Serialize, Deserialize)]
161//! pub struct A {
162//! pub addresses: Vec<Address>,
163//! pub hash: Hash,
164//! }
165//! }
166//!
167//! #[derive(SchemaWrite, SchemaRead)]
168//! #[wincode(from = "foreign_crate::A")]
169//! pub struct MyA {
170//! addresses: Vec<Pod<foreign_crate::Address>>,
171//! hash: Pod<foreign_crate::Hash>,
172//! }
173//!
174//! let val = foreign_crate::A {
175//! addresses: vec![foreign_crate::Address([0; 32]), foreign_crate::Address([1; 32])],
176//! hash: foreign_crate::Hash([0; 32]),
177//! };
178//! let bincode_serialize = bincode::serialize(&val).unwrap();
179//! let wincode_serialize = MyA::serialize(&val).unwrap();
180//! assert_eq!(bincode_serialize, wincode_serialize);
181//!
182//! let bincode_deserialize: foreign_crate::A = bincode::deserialize(&bincode_serialize).unwrap();
183//! let wincode_deserialize = MyA::deserialize(&bincode_serialize).unwrap();
184//! assert_eq!(val, bincode_deserialize);
185//! assert_eq!(val, wincode_deserialize);
186//! # }
187//! ```
188//!
189//! Now, when deserializing `A`:
190//! - All initialization is done in-place, including heap-allocated memory
191//! (true of all supported contiguous heap-allocated structures in `wincode`).
192//! - Byte fields are read and written in a single pass.
193//!
194//! # Compatibility
195//!
196//! - Produces the same bytes as `bincode` for the covered shapes when using bincode's
197//! default configuration, provided your [`SchemaWrite`] and [`SchemaRead`] schemas and
198//! [`containers`] match the layout implied by your `serde` types.
199//! - Length encodings are pluggable via [`SeqLen`](len::SeqLen).
200//!
201//! # Zero copy deserialization
202//!
203//! `wincode` supports zero copy deserialization of contiguous byte slices
204//! (serialized with `Vec<u8>`, `Box<[u8]>`, `[u8; N]`, etc.).
205//!
206//! ```
207//! # #[cfg(feature = "derive")] {
208//! use wincode::{SchemaWrite, SchemaRead};
209//!
210//! # #[derive(Debug, PartialEq, Eq)]
211//! #[derive(SchemaWrite, SchemaRead)]
212//! struct ByteRef<'a> {
213//! bytes: &'a [u8],
214//! }
215//!
216//! let bytes: Vec<u8> = vec![1, 2, 3, 4, 5];
217//! let byte_ref = ByteRef { bytes: &bytes };
218//! let serialized = wincode::serialize(&byte_ref).unwrap();
219//! let deserialized = wincode::deserialize(&serialized).unwrap();
220//! assert_eq!(byte_ref, deserialized);
221//! # }
222//! ```
223//! # Derive attributes
224//!
225//! ## Top level
226//! |Attribute|Type|Default|Description
227//! |---|---|---|---|
228//! |`from`|`Type`|`None`|Indicates that type is a mapping from another type (example in previous section)|
229//! |`no_suppress_unused`|`bool`|`false`|Disable unused field lints suppression. Only usable on structs with `from`.|
230//! |`struct_extensions`|`bool`|`false`|Generates placement initialization helpers on `SchemaRead` struct implementations|
231//!
232//! ### `no_suppress_unused`
233//!
234//! When creating a mapping type with `#[wincode(from = "AnotherType")]`, fields are typically
235//! comprised of [`containers`] (of course not strictly always true). As a result, these structs
236//! purely exist for the compiler to generate optimized implementations, and are never actually
237//! constructed. As a result, unused field lints will be triggered, which can be annoying.
238//! By default, when `from` is used, the derive macro will generate dummy function that references all
239//! the struct fields, which suppresses those lints. This function will ultimately be compiled out of your
240//! build, but you can disable this by setting `no_suppress_unused` to `true`. You can also avoid
241//! these lint errors with visibility modifiers (e.g., `pub`).
242//!
243//! Note that this only works on structs, as it is not possible to construct an arbitrary enum variant.
244//!
245//!
246//! ### `struct_extensions`
247//!
248//! You may have some exotic serialization logic that requires you to implement `SchemaRead` manually
249//! for a type. In these scenarios, you'll likely want to leverage some additional helper methods
250//! to reduce the amount of boilerplate that is typically required when dealing with uninitialized
251//! fields.
252//!
253//! For example:
254//!
255//! ```
256//! # #[cfg(all(feature = "alloc", feature = "derive"))] {
257//! # use wincode::{SchemaRead, SchemaWrite, io::Reader, error::ReadResult};
258//! # use serde::{Serialize, Deserialize};
259//! # use core::mem::MaybeUninit;
260//! # #[derive(Debug, PartialEq, Eq)]
261//! #[derive(SchemaRead, SchemaWrite)]
262//! #[wincode(struct_extensions)]
263//! struct Header {
264//! num_required_signatures: u8,
265//! num_signed_accounts: u8,
266//! num_unsigned_accounts: u8,
267//! }
268//!
269//! # #[derive(Debug, PartialEq, Eq)]
270//! #[derive(SchemaRead, SchemaWrite)]
271//! #[wincode(struct_extensions)]
272//! struct Payload {
273//! header: Header,
274//! data: Vec<u8>,
275//! }
276//!
277//! # #[derive(Debug, PartialEq, Eq)]
278//! #[derive(SchemaWrite)]
279//! struct Message {
280//! payload: Payload,
281//! }
282//!
283//! // Assume for some reason we have to manually implement `SchemaRead` for `Message`.
284//! impl SchemaRead<'_> for Message {
285//! type Dst = Message;
286//!
287//! fn read(reader: &mut Reader, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
288//! // We have to do a big ugly cast like this to get a mutable MaybeUninit<Payload>.
289//! let mut payload = unsafe {
290//! &mut *(&raw mut (*dst.as_mut_ptr()).payload).cast::<MaybeUninit<Payload>>()
291//! };
292//! // `Payload::uninit_header_mut` is generated by `#[wincode(struct_extensions)]`.
293//! // This avoids having to do a big ugly cast like we had to do for payload above.
294//! //
295//! // Project a mutable `MaybeUninit<Header>` from the `MaybeUninit<Payload>`.
296//! let header = Payload::uninit_header_mut(payload);
297//! // Similarly, `Header::read_num_required_signatures` is generated
298//! // by `#[wincode(struct_extensions)]`.
299//! //
300//! // Read directly into the projected MaybeUninit<Header> slot.
301//! Header::read_num_required_signatures(reader, header)?;
302//! // ...
303//! Header::read_num_signed_accounts(reader, header)?;
304//! Header::read_num_unsigned_accounts(reader, header)?;
305//! // Alternatively, we could have done `Payload::read_header(reader, payload)?;`
306//! // rather than reading all the fields individually.
307//! Payload::read_data(reader, payload)?;
308//! Ok(())
309//! }
310//! }
311//!
312//! let msg = Message {
313//! payload: Payload {
314//! header: Header {
315//! num_required_signatures: 1,
316//! num_signed_accounts: 2,
317//! num_unsigned_accounts: 3
318//! },
319//! data: vec![4, 5, 6, 7, 8, 9]
320//! }
321//! };
322//! let serialized = wincode::serialize(&msg).unwrap();
323//! let deserialized = wincode::deserialize(&serialized).unwrap();
324//! assert_eq!(msg, deserialized);
325//! # }
326//! ```
327//!
328//! `#[wincode(struct_extensions)]` generates three methods per field:
329//! - `uninit_<field_name>_mut`
330//! - Gets a mutable `MaybeUninit` projection to the `<field_name>` slot.
331//! - `read_<field_name>`
332//! - Reads into a `MaybeUninit`'s `<field_name>` slot from the given [`Reader`](io::Reader).
333//! - `write_uninit_<field_name>`
334//! - Writes a `MaybeUninit`'s `<field_name>` slot with the given value.
335//!
336//!
337//! ## Field level
338//! |Attribute|Type|Default|Description
339//! |---|---|---|---|
340//! |`with`|`Type`|`None`|Overrides the default `SchemaRead` or `SchemaWrite` implementation for the field.|
341//!
342#![cfg_attr(docsrs, feature(doc_cfg))]
343#![cfg_attr(not(feature = "std"), no_std)]
344#[cfg(feature = "alloc")]
345extern crate alloc;
346
347pub mod error;
348pub use error::{Error, ReadError, ReadResult, Result, WriteError, WriteResult};
349pub mod io;
350pub mod len;
351mod schema;
352pub use schema::*;
353mod serde;
354pub use serde::*;
355#[cfg(test)]
356mod proptest_config;
357mod util;
358#[cfg(feature = "derive")]
359pub use wincode_derive::*;
360// Include tuple impls.
361include!(concat!(env!("OUT_DIR"), "/tuples.rs"));