musli_zerocopy/lib.rs
1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/musli-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/musli)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/musli-zerocopy.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/musli-zerocopy)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-musli--zerocopy-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/musli-zerocopy)
4//!
5//! Refreshingly simple, blazingly fast zero copy primitives by Müsli.
6//!
7//! This provides a basic set of tools to deal with types which do not require
8//! copying during deserialization. You define the `T`, and we provide the safe
9//! `&[u8]` <-> `&T` conversions.
10//!
11//! Reading a zero-copy structure has full `#[no_std]` support. Constructing
12//! ones currently requires the `alloc` feature to be enabled.
13//!
14//! ```
15//! # #[cfg(target_endian = "little")]
16//! # macro_rules! include_bytes { ("author.bin") => { &[35, 0, 0, 0, 12, 0, 0, 0, 9, 0, 0, 0, 74, 111, 104, 110, 45, 74, 111, 104, 110] } }
17//! # #[cfg(target_endian = "big")]
18//! # macro_rules! include_bytes { ("author.bin") => { &[0, 0, 0, 35, 0, 0, 0, 12, 0, 0, 0, 9, 74, 111, 104, 110, 45, 74, 111, 104, 110] } }
19//! use musli_zerocopy::{buf, Ref, ZeroCopy};
20//!
21//! #[derive(ZeroCopy)]
22//! #[repr(C)]
23//! struct Person {
24//! age: u8,
25//! name: Ref<str>,
26//! }
27//!
28//! let buf = buf::aligned_buf::<Person>(include_bytes!("author.bin"));
29//! let person = Person::from_bytes(&buf[..])?;
30//!
31//! assert_eq!(person.age, 35);
32//! // References are incrementally validated.
33//! assert_eq!(buf.load(person.name)?, "John-John");
34//! # Ok::<_, musli_zerocopy::Error>(())
35//! ```
36//!
37//! For a detailed overview of how this works, see the [`ZeroCopy`] and its
38//! corresponding [`ZeroCopy`][derive] derive. There's also a high level guide
39//! just below.
40//!
41//! This crate also includes a couple of neat high level data structures you
42//! might be interested in:
43//! * [`phf`] provides maps and sets based on [`phf` crate], or perfect hash
44//! functions.
45//! * [`swiss`] is a port of the [`hashbrown` crate] which is a Google
46//! SwissTable implementation.
47//! * [`trie`] is an implementation of a prefix-trie, which supports efficient
48//! multi-value byte-prefixed lookups.
49//!
50//! Finally if you're interested in the performance of `musli-zerocopy` you
51//! should go to [`benchmarks`]. I will be extending this suite with more
52//! zero-copy types, but for now we have a clear lead in the use cases I've
53//! tested it for.
54//!
55//! This is because:
56//! * Zero-copy doesn't incur a deserialization overhead if done correctly. You
57//! take bytes in one place, validate them, and treat them as the destination
58//! type. There are only so many ways this can be done;
59//! * Padding has been implemented and optimized in such a way that it mostly
60//! generates the equivalent code you'd write by hand, and;
61//! * Incremental validation means that you only need to pay for what you're
62//! accessing. So for random access we only need to validate the parts that
63//! are being accessed.
64//!
65//! Overview:
66//! * [Why should I consider `musli-zerocopy` over X?](#why-should-i-consider-musli-zerocopy-over-x)
67//! * [Guide](#guide)
68//! * [Reading data](#reading-data)
69//! * [Writing data at offset zero](#writing-data-at-offset-zero)
70//! * [Portability](#portability)
71//! * [Limits](#limits)
72//!
73//! <br>
74//!
75//! ## Why should I consider `musli-zerocopy` over X?
76//!
77//! Since this is the first question anyone will ask, here is how we differ from
78//! other popular libraries:
79//! * [`zerocopy`](https://docs.rs/zerocopy) doesn't support padded
80//! types[^padded], bytes to reference conversions, or conversions which does
81//! not permit decoding types unless all bit patterns can be inhabited by
82//! zeroes[^zeroes]. We still want to provide more of a complete toolkit that
83//! you'd need to build and interact with complex data structures like we get
84//! through the [`phf`] and [`swiss`] modules. This crate might indeed at some
85//! point make use of `zerocopy`'s traits.
86//! * [`rkyv`](https://docs.rs/rkyv) operates on `#[repr(Rust)]` types and from
87//! this derives an optimized `Archived` variation for you. This library lets
88//! you build the equivalent of the `Archived` variant directly and the way
89//! you interact with the data model doesn't incur the cost of validation up
90//! front. With `rkyv` it took my computer 100% of a CPU core and about half a
91//! second to load 12 million dictionary entries[^dictionary], which is a cost
92//! that is simply not incurred by incrementally validating. Not validating is
93//! not an option since that would be wildly unsound - your application would
94//! be vulnerable to malicious dictionary files.
95//!
96//! [^padded]: This is on zerocopy's roadmap, but it fundamentally doesn't play
97//! well with the central `FromBytes` / `ToBytes` pair of traits
98//!
99//! [^zeroes]: [FromBytes extends FromZeros](https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html)
100//!
101//! [^dictionary]: <https://github.com/udoprog/jpv/blob/main/crates/lib/src/database.rs>
102//!
103//! <br>
104//!
105//! ## Guide
106//!
107//! Zero-copy in this library refers to the act of interacting with data
108//! structures that reside directly in `&[u8]` memory without the need to first
109//! decode them.
110//!
111//! Conceptually it works a bit like this.
112//!
113//! Say you want to store the string `"Hello World!"`.
114//!
115//! ```
116//! use musli_zerocopy::OwnedBuf;
117//!
118//! let mut buf = OwnedBuf::new();
119//! let string = buf.store_unsized("Hello World!");
120//! let reference = buf.store(&string);
121//!
122//! assert_eq!(reference.offset(), 12);
123//! # Ok::<_, musli_zerocopy::Error>(())
124//! ```
125//!
126//! This would result in the following buffer:
127//!
128//! ```text
129//! 0000: "Hello World!"
130//! // Might get padded to ensure that the size is aligned by 4 bytes.
131//! 0012: offset -> 0000
132//! 0016: size -> 12
133//! ```
134//!
135//! What we see at offset `0016` is an 8 byte [`Ref<str>`]. The first field
136//! stores the offset where to fetch the string, and the second field the length
137//! of the string.
138//!
139//! Let's have a look at a [`Ref<[u32]>`][ref-u32] next:
140//!
141//! ```
142//! use musli_zerocopy::{Ref, OwnedBuf};
143//!
144//! let mut buf = OwnedBuf::new();
145//! let slice: Ref<[u32]> = buf.store_slice(&[1, 2, 3, 4]);
146//! let reference = buf.store(&slice);
147//!
148//! assert_eq!(reference.offset(), 16);
149//! # Ok::<_, musli_zerocopy::Error>(())
150//! ```
151//!
152//! This would result in the following buffer:
153//!
154//! ```text
155//! 0000: u32 -> 1
156//! 0004: u32 -> 2
157//! 0008: u32 -> 3
158//! 0012: u32 -> 4
159//! 0016: offset -> 0000
160//! 0020: length -> 4
161//! ```
162//!
163//! At address `0016` we store two fields which corresponds to a
164//! [`Ref<[u32]>`][ref-u32].
165//!
166//! Next lets investigate an example using a `Custom` struct:
167//!
168//! ```
169//! # use anyhow::Context;
170//! use core::mem::size_of;
171//! use musli_zerocopy::{OwnedBuf, Ref, ZeroCopy};
172//!
173//! #[derive(ZeroCopy)]
174//! #[repr(C)]
175//! struct Custom {
176//! field: u32,
177//! string: Ref<str>,
178//! }
179//!
180//! let mut buf = OwnedBuf::new();
181//!
182//! let string = buf.store_unsized("Hello World!");
183//! let custom = buf.store(&Custom { field: 42, string });
184//!
185//! // The buffer stores both the unsized string and the Custom element.
186//! assert!(buf.len() >= 24);
187//! // We assert that the produced alignment is smaller or equal to 8
188//! // since we'll be relying on this below.
189//! assert!(buf.requested() <= 8);
190//! # Ok::<_, musli_zerocopy::Error>(())
191//! ```
192//!
193//! This would result in the following buffer:
194//!
195//! ```text
196//! 0000: "Hello World!"
197//! 0012: u32 -> 42
198//! 0016: offset -> 0000
199//! 0020: size -> 12
200//! ```
201//!
202//! Our struct starts at address `0012`, first we have the `u32` field, and
203//! immediately after that we have the string.
204//!
205//! <br>
206//!
207//! ## Reading data
208//!
209//! Later when we want to use the type, we take the buffer we've generated and
210//! include it somewhere else.
211//!
212//! There's a few pieces of data (lets call it DNA) we need to have to read a
213//! type back from a raw buffer:
214//! * The *alignment* of the buffer. Which you can read through the
215//! [`requested()`]. On the receiving end we need to ensure that the buffer
216//! follow this alignment. Dynamically this can be achieved by loading the
217//! buffer using [`aligned_buf(bytes, align)`]. Other tricks include embedding
218//! a static buffer inside of an aligned newtype which we'll showcase below.
219//! Networked applications might simply agree to use a particular alignment up
220//! front. This alignment has to be compatible with the types being coerced.
221//! * The *endianness* of the machine which produced the buffer. Any numerical
222//! elements will in native endian ordering, so they would have to be adjusted
223//! on the read side if it differ.
224//! * The type definition which is being read which implements [`ZeroCopy`].
225//! This is `Custom` above. The [`ZeroCopy`] derive ensures that we can safely
226//! coerce a buffer into a reference of the type. The data might at worst be
227//! garbled, but we can never do anything unsound while using safe APIs.
228//! * The offset at where the [`ZeroCopy`] structure is read. To read a
229//! structure we combine a pointer and a type into a [`Ref`] instance.
230//!
231//! If the goal is to both produce and read the buffer on the same system
232//! certain assumptions can be made. And if those assumptions turn out to be
233//! wrong the worst outcome will only ever be an error as long as you're using
234//! the safe APIs or abide by the safety documentation of the unsafe APIs.
235//!
236//! > **Info** A note on sending data over the network. This is perfectly doable
237//! > as long as you include the alignment of the buffer and the endianness of
238//! > the data structure. Both of these can be retrieved:
239//! >
240//! > ```
241//! > # use musli_zerocopy::OwnedBuf;
242//! > let buf = OwnedBuf::new();
243//! >
244//! > /* write something */
245//! >
246//! > let is_little_endian = cfg!(target_endian = "little");
247//! > let alignment = buf.requested();
248//! > ```
249//!
250//! The following is an example of reading the type directly out of a newtype
251//! aligned `&'static [u8]` buffer:
252//!
253//! ```
254//! # use musli_zerocopy::{Ref, ZeroCopy};
255//! # macro_rules! include_bytes {
256//! # ($path:literal) => { &[
257//! # b'H', b'e', b'l', b'l', b'o', b' ', b'W', b'o', b'r', b'l', b'd', b'!',
258//! # 42, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0,
259//! # ] };
260//! # }
261//! # #[derive(ZeroCopy)]
262//! # #[repr(C)]
263//! # struct Custom { field: u32, string: Ref<str> }
264//! use core::mem::size_of;
265//! use musli_zerocopy::Buf;
266//!
267//! // Helper to force the static buffer to be aligned like `A`.
268//! #[repr(C)]
269//! struct Align<A, T: ?Sized>([A; 0], T);
270//!
271//! static BYTES: &Align<u64, [u8]> = &Align([], *include_bytes!("custom.bin"));
272//!
273//! let buf = Buf::new(&BYTES.1);
274//!
275//! // Construct a pointer into the buffer.
276//! let custom = Ref::<Custom>::new(BYTES.1.len() - size_of::<Custom>());
277//!
278//! let custom: &Custom = buf.load(custom)?;
279//! assert_eq!(custom.field, 42);
280//! assert_eq!(buf.load(custom.string)?, "Hello World!");
281//! # Ok::<_, musli_zerocopy::Error>(())
282//! ```
283//!
284//! <br>
285//!
286//! ## Writing data at offset zero
287//!
288//! Most of the time you want to write data where the first element in the
289//! buffer is the element currently being written.
290//!
291//! This is useful because it satisfies the last requirement above, *the offset*
292//! at where the struct can be read will then simply be zero, and all the data
293//! it depends on are stored at subsequent offsets.
294//!
295//! ```
296//! # use musli_zerocopy::{Ref, ZeroCopy};
297//! # #[derive(ZeroCopy)]
298//! # #[repr(C)]
299//! # struct Custom { field: u32, string: Ref<str> }
300//! use musli_zerocopy::OwnedBuf;
301//! use musli_zerocopy::mem::MaybeUninit;
302//!
303//! let mut buf = OwnedBuf::new();
304//! let reference: Ref<MaybeUninit<Custom>> = buf.store_uninit::<Custom>();
305//!
306//! let string = buf.store_unsized("Hello World!");
307//!
308//! buf.load_uninit_mut(reference).write(&Custom { field: 42, string });
309//!
310//! let reference = reference.assume_init();
311//! assert_eq!(reference.offset(), 0);
312//! # Ok::<_, musli_zerocopy::Error>(())
313//! ```
314//!
315//! <br>
316//!
317//! ## Portability
318//!
319//! By default archives will use the native [`ByteOrder`]. In order to construct
320//! and load a portable archive, the byte order in use has to be explicitly
321//! specified.
322//!
323//! This is done by specifying the byte order in use during buffer construction
324//! and expliclty setting the `E` parameter in types which received it such as
325//! [`Ref<T, E, O>`].
326//!
327//! We can start of by defining a fully `Portable` archive structure, which
328//! received both size and [`ByteOrder`]. Note that it could also just
329//! explicitly specify a desired byte order but doing it like this makes it
330//! maximally flexible as an example:
331//!
332//! ```
333//! use musli_zerocopy::{Size, ByteOrder, Ref, Endian, ZeroCopy};
334//!
335//! #[derive(ZeroCopy)]
336//! #[repr(C)]
337//! struct Archive<E, O>
338//! where
339//! E: ByteOrder,
340//! O: Size
341//! {
342//! string: Ref<str, E, O>,
343//! number: Endian<u32, E>,
344//! }
345//! ```
346//!
347//! Building a buffer out of the structure is fairly straight forward,
348//! [`OwnedBuf`] has the [`with_byte_order::<E>()`] method which allows us to
349//! specify a "sticky" [`ByteOrder`] to use in types which interact with it
350//! during construction:
351//!
352//! ```
353//! use musli_zerocopy::{endian, Endian, OwnedBuf};
354//! # use musli_zerocopy::{Size, ByteOrder, Ref, ZeroCopy};
355//! # #[derive(ZeroCopy)]
356//! # #[repr(C)]
357//! # struct Archive<E, O> where E: ByteOrder, O: Size {
358//! # string: Ref<str, E, O>,
359//! # number: Endian<u32, E>
360//! # }
361//! let mut buf = OwnedBuf::new()
362//! .with_byte_order::<endian::Little>();
363//!
364//! let first = buf.store(&Endian::le(42u16));
365//! let portable = Archive {
366//! string: buf.store_unsized("Hello World!"),
367//! number: Endian::new(10),
368//! };
369//! let portable = buf.store(&portable);
370//!
371//! assert_eq!(&buf[..], &[
372//! 42, 0, // 42u16
373//! 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, // "Hello World!"
374//! 0, 0, // padding
375//! 2, 0, 0, 0, 12, 0, 0, 0, // Ref<str>
376//! 10, 0, 0, 0 // 10u32
377//! ]);
378//!
379//! let portable = buf.load(portable)?;
380//! # assert_eq!(buf.load(first)?.to_ne(), 42);
381//! # assert_eq!(buf.load(portable.string)?, "Hello World!");
382//! # assert_eq!(portable.number.to_ne(), 10);
383//!
384//! let mut buf = OwnedBuf::new()
385//! .with_byte_order::<endian::Big>();
386//!
387//! let first = buf.store(&Endian::be(42u16));
388//! let portable = Archive {
389//! string: buf.store_unsized("Hello World!"),
390//! number: Endian::new(10),
391//! };
392//! let portable = buf.store(&portable);
393//!
394//! assert_eq!(&buf[..], &[
395//! 0, 42, // 42u16
396//! 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, // "Hello World!"
397//! 0, 0, // padding
398//! 0, 0, 0, 2, 0, 0, 0, 12, // Ref<str>
399//! 0, 0, 0, 10 // 10u32
400//! ]);
401//!
402//! let portable = buf.load(portable)?;
403//! # assert_eq!(buf.load(first)?.to_ne(), 42);
404//! # assert_eq!(buf.load(portable.string)?, "Hello World!");
405//! # assert_eq!(portable.number.to_ne(), 10);
406//! # Ok::<_, musli_zerocopy::Error>(())
407//! ```
408//!
409//! <br>
410//!
411//! ## Limits
412//!
413//! Offset, the size of unsized values, and slice lengths are all limited to
414//! 32-bit. The system you're using must have a `usize` type which is at least
415//! 32-bits wide. This is done to save space by default.
416//!
417//! The pointer width on the system is checked at compile time, while trying to
418//! use an offset or a size larger than `2^32` will result in a panic.
419//!
420//! Example of using an address larger than `2^32` causing a panic:
421//!
422//! ```should_panic
423//! # use musli_zerocopy::{ZeroCopy, Ref};
424//! # #[derive(ZeroCopy)]
425//! # #[repr(C)]
426//! # struct Custom;
427//! Ref::<Custom>::new(1usize << 32);
428//! ```
429//!
430//! Example panic using a [`Ref<\[T\]>`] with a length larger than `2^32`:
431//!
432//! ```should_panic
433//! # use musli_zerocopy::{Ref, ZeroCopy};
434//! # #[derive(ZeroCopy)]
435//! # #[repr(C)]
436//! # struct Custom;
437//! Ref::<[Custom]>::with_metadata(0, 1usize << 32);
438//! ```
439//!
440//! Example panic using an [`Ref<str>`] value with a size larger than `2^32`:
441//!
442//! ```should_panic
443//! # use musli_zerocopy::Ref;
444//! Ref::<str>::with_metadata(0, 1usize << 32);
445//! ```
446//!
447//! If you want to address data larger than this limit, it is recommended that
448//! you partition your dataset into 32-bit addressable chunks.
449//!
450//! If you really want to change this limit, you can modify it by setting the
451//! default `O` parameter on the various [`Size`]-dependent types:
452//!
453//! The available [`Size`] implementations are:
454//! * `u32` for 32-bit sized pointers (the default).
455//! * `usize` for target-dependently sized pointers.
456//!
457//! ```
458//! # use musli_zerocopy::{Ref, ZeroCopy};
459//! # use musli_zerocopy::endian::Native;
460//! # #[derive(ZeroCopy)]
461//! # #[repr(C)]
462//! # struct Custom;
463//! // These no longer panic:
464//! let reference = Ref::<Custom, Native, usize>::new(1usize << 32);
465//! let slice = Ref::<[Custom], Native, usize>::with_metadata(0, 1usize << 32);
466//! let unsize = Ref::<str, Native, usize>::with_metadata(0, 1usize << 32);
467//! ```
468//!
469//! To initialize an [`OwnedBuf`] with a custom [`Size`], you can use
470//! [`OwnedBuf::with_size`]:
471//!
472//! ```
473//! use musli_zerocopy::OwnedBuf;
474//! use musli_zerocopy::buf::DefaultAlignment;
475//!
476//! let mut buf = OwnedBuf::with_capacity_and_alignment::<DefaultAlignment>(0)
477//! .with_size::<usize>();
478//! ```
479//!
480//! The [`Size`] you've specified during construction of an [`OwnedBuf`] will
481//! then carry into any pointers it return:
482//!
483//! ```
484//! use musli_zerocopy::{DefaultAlignment, OwnedBuf, Ref, ZeroCopy};
485//! use musli_zerocopy::endian::Native;
486//!
487//! #[derive(ZeroCopy)]
488//! #[repr(C)]
489//! struct Custom {
490//! reference: Ref<u32, Native, usize>,
491//! slice: Ref::<[u32], Native, usize>,
492//! unsize: Ref::<str, Native, usize>,
493//! }
494//!
495//! let mut buf = OwnedBuf::with_capacity(0)
496//! .with_size::<usize>();
497//!
498//! let reference = buf.store(&42u32);
499//! let slice = buf.store_slice(&[1, 2, 3, 4]);
500//! let unsize = buf.store_unsized("Hello World");
501//!
502//! buf.store(&Custom { reference, slice, unsize });
503//! # Ok::<_, musli_zerocopy::Error>(())
504//! ```
505//!
506//! <br>
507//!
508//! [`aligned_buf(bytes, align)`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/pointer/trait.Size.html
509//! [`benchmarks`]: https://udoprog.github.io/musli/benchmarks/
510//! [`ByteOrder`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/trait.ByteOrder.html
511//! [`hashbrown` crate]: https://docs.rs/phf
512//! [`OwnedBuf::with_size`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/buf/struct.OwnedBuf.html#method.with_size
513//! [`OwnedBuf`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/buf/struct.OwnedBuf.html
514//! [`phf` crate]: https://docs.rs/phf
515//! [`phf`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/phf/index.html
516//! [`Ref`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/pointer/struct.Ref.html
517//! [`Ref<str>`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/pointer/struct.Ref.html
518//! [`Ref<T, E, O>`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/pointer/struct.Ref.html
519//! [`requested()`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/struct.OwnedBuf.html#method.requested
520//! [`Size`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/pointer/trait.Size.html
521//! [`swiss`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/swiss/index.html
522//! [`trie`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/trie/index.html
523//! [`with_byte_order::<E>()`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/buf/struct.OwnedBuf.html#method.with_byte_order
524//! [`ZeroCopy`]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/trait.ZeroCopy.html
525//! [derive]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/derive.ZeroCopy.html
526//! [ref-u32]: https://docs.rs/musli-zerocopy/latest/musli_zerocopy/pointer/struct.Ref.html
527
528#![no_std]
529#![allow(clippy::module_inception)]
530#![allow(clippy::enum_variant_names)]
531#![deny(missing_docs)]
532#![cfg_attr(all(musli_nightly), feature(repr128))]
533#![cfg_attr(all(musli_nightly), allow(incomplete_features))]
534#![cfg_attr(doc_cfg, feature(doc_cfg))]
535
536#[cfg(feature = "alloc")]
537extern crate alloc;
538
539#[cfg(feature = "std")]
540extern crate std;
541
542#[cfg(feature = "alloc")]
543#[doc(inline)]
544pub use self::buf::OwnedBuf;
545#[doc(inline)]
546pub use self::buf::{Buf, DefaultAlignment, SliceMut, Visit};
547pub mod buf;
548
549pub mod mem;
550
551pub mod slice;
552
553pub mod trie;
554
555#[doc(inline)]
556pub use self::error::Error;
557mod error;
558
559/// `Result` alias provided for convenience.
560pub type Result<T, E = Error> = core::result::Result<T, E>;
561
562#[doc(inline)]
563pub use self::traits::{UnsizedZeroCopy, ZeroCopy, ZeroSized};
564mod traits;
565
566pub(crate) mod sip;
567
568pub mod phf;
569pub mod swiss;
570
571#[doc(inline)]
572pub use self::pointer::{DefaultSize, Ref, Size};
573pub mod pointer;
574
575#[doc(inline)]
576pub use self::endian::{ByteOrder, Endian};
577pub mod endian;
578
579mod lossy_str;
580mod stack;
581
582/// Macro to derive a simple [`Visit`] implementation.
583pub use musli_zerocopy_macros::Visit;
584
585/// Derive macro to implement [`ZeroCopy`].
586///
587/// Implementing this trait ensures that the type can safely be coerced to and
588/// from initialized bytes.
589///
590/// <br>
591///
592/// # Using with structs
593///
594/// The following are the requirements for deriving structs:
595/// * The struct must either be `#[repr(C)]` or `[repr(transparent)]`.
596/// * All fields in the struct must either implement [`ZeroCopy`] or be
597/// [`ZeroSized`] and marked as `#[zero_copy(ignore)]`.
598///
599/// If the struct is zero-sized, it will implement [`ZeroSized`] along with the
600/// [`ZeroCopy`] trait.
601///
602/// [`ZeroSized`]: crate::traits::ZeroSized
603///
604/// ```
605/// use musli_zerocopy::{OwnedBuf, ZeroCopy};
606///
607/// #[derive(Debug, PartialEq, ZeroCopy)]
608/// #[repr(C, align(128))]
609/// struct Custom { field: u32 }
610///
611/// let mut buf = OwnedBuf::new();
612/// let ptr = buf.store(&Custom { field: 10 });
613/// buf.align_in_place();
614/// assert_eq!(buf.load(ptr)?, &Custom { field: 10 });
615/// # Ok::<_, musli_zerocopy::Error>(())
616/// ```
617///
618/// [`ZeroCopy`]: crate::traits::ZeroCopy
619///
620/// <br>
621///
622/// # Using with enums
623///
624/// The following are the requirements for deriving for enums:
625///
626/// The enum must be marked with a valid, fixed representation. Such as
627/// `#[repr(u8)]`, or `#[repr(usize)]`.
628///
629/// ```
630/// use musli_zerocopy::ZeroCopy;
631///
632/// #[derive(ZeroCopy)]
633/// #[repr(u8)]
634/// enum Flags {
635/// First ,
636/// Second(u32),
637/// Third {
638/// first: u32,
639/// second: u64,
640/// },
641/// }
642/// ```
643///
644/// If a custom discriminant is used, only constant expressions are supported.
645///
646/// For example:
647/// * A literal like `42`,
648/// * A simple constant expression like `u32::MIN + 10`.
649/// * A more complex constant expression like `my_constant_fn(u32::MIN >> 2)`.
650///
651/// ```
652/// const fn my_constant_fn(base: u8) -> u8 {
653/// base + 3
654/// }
655///
656/// # use musli_zerocopy::ZeroCopy;
657/// #[derive(ZeroCopy)]
658/// #[repr(u8)]
659/// enum Flags {
660/// First = 1,
661/// Second(u32), // will automatically assigned 2
662/// Third {
663/// first: u32,
664/// second: u64,
665/// } = u8::MAX,
666/// Fourth = my_constant_fn(u8::MIN >> 2),
667/// }
668/// ```
669///
670/// Complete example:
671///
672/// ```
673/// use musli_zerocopy::{OwnedBuf, ZeroCopy};
674///
675/// #[derive(Debug, PartialEq, ZeroCopy)]
676/// #[repr(u8)]
677/// enum Flags {
678/// First,
679/// Second(u32),
680/// Third { first: u32, second: u64 },
681/// }
682///
683/// let mut buf = OwnedBuf::with_alignment::<Flags>();
684///
685/// buf.clear();
686/// let ptr = buf.store(&Flags::First);
687/// assert_eq!(buf.load(ptr)?, &Flags::First);
688///
689/// buf.clear();
690/// let ptr = buf.store(&Flags::Second(42));
691/// assert_eq!(buf.load(ptr)?, &Flags::Second(42));
692///
693/// buf.clear();
694/// let ptr = buf.store(&Flags::Third { first: 42, second: 84 });
695/// assert_eq!(buf.load(ptr)?, &Flags::Third { first: 42, second: 84 });
696/// # Ok::<_, musli_zerocopy::Error>(())
697/// ```
698///
699/// <br>
700///
701/// # Padding
702///
703/// The constant [`ZeroCopy::PADDED`] determines whether the derives struct uses
704/// padding or not. This derive currently uses a fairly conservative algorithm:
705///
706/// The constant [`ZeroCopy::PADDED`] will be set to `true` if:
707/// * The size of the type is 0, and the alignment is larger than 1. This
708/// indicates a zero-sized type with an explicit `#[repr(align(N))]` that is
709/// not set to 1.
710/// * The sum of the size of all the fields is not the same as the size of the
711/// type.
712/// * Any of the fields has its [`ZeroCopy::PADDED`] set to `true`.
713/// * For enums, we test every variant with the same rules, except each variant
714/// is treated as a struct where the discriminant (`u32` in `#[repr(u32)]`) is
715/// treated like [a leading hidden field].
716///
717/// [`ZeroCopy::PADDED`]: ZeroCopy::PADDED
718/// [a first hidden field]: https://doc.rust-lang.org/beta/reference/type-layout.html#primitive-representation-of-enums-with-fields
719///
720/// ```
721/// use musli_zerocopy::ZeroCopy;
722///
723/// #[derive(ZeroCopy)]
724/// #[repr(C)]
725/// struct Zst;
726/// const _: () = assert!(!Zst::PADDED);
727///
728/// #[derive(ZeroCopy)]
729/// #[repr(C, align(1))]
730/// struct ZstAlign1;
731/// const _: () = assert!(!ZstAlign1::PADDED);
732///
733/// #[derive(ZeroCopy)]
734/// #[repr(C, align(128))]
735/// struct ZstPadded;
736/// const _: () = assert!(!ZstPadded::PADDED);
737///
738/// #[derive(ZeroCopy)]
739/// #[repr(u8)]
740/// enum ZstEnum { EmptyField }
741/// const _: () = assert!(!ZstEnum::PADDED);
742///
743/// #[derive(ZeroCopy)]
744/// #[repr(u8)]
745/// enum SameEnum { Variant1(u8), Variant2(u8) }
746/// const _: () = assert!(!SameEnum::PADDED);
747///
748/// #[derive(ZeroCopy)]
749/// #[repr(u16)]
750/// enum PaddedU16 { Variant1(u8), Variant2(u8) }
751/// const _: () = assert!(PaddedU16::PADDED);
752///
753/// #[derive(ZeroCopy)]
754/// #[repr(u16)]
755/// enum NotPaddedU16 { Variant1(u8, u8), Variant2([u8; 2]), Variant3(u16) }
756/// const _: () = assert!(!NotPaddedU16::PADDED);
757///
758/// #[derive(ZeroCopy)]
759/// #[repr(C, packed)]
760/// struct Packed { inner: u8, inner2: u32 }
761/// const _: () = assert!(!Packed::PADDED);
762///
763/// #[derive(ZeroCopy)]
764/// #[repr(C, packed(2))]
765/// struct Packed1 { inner: u8, inner2: u32 }
766/// const _: () = assert!(Packed1::PADDED);
767///
768/// #[derive(ZeroCopy)]
769/// #[repr(C)]
770/// struct Inner { inner: u8, inner2: u32 }
771/// const _: () = assert!(Inner::PADDED);
772///
773/// #[derive(ZeroCopy)]
774/// #[repr(C)]
775/// struct Outer { first: u8, inner: Inner }
776/// const _: () = assert!(Outer::PADDED);
777/// ```
778///
779/// <br>
780///
781/// # Supported attributes
782///
783/// <br>
784///
785/// ## Type attributes
786///
787/// The following `repr` attributes are supported:
788/// * `#[repr(C)]` - Ensures that the type has the mandatory represention.
789/// * `#[repr(transparent)]` - If there is a single field inside of the marked
790/// struct which implements `ZeroCopy`.
791/// * `#[repr(align(N))]` - Allows for control over the type's alignment.
792/// * `#[repr(packed)]` or `#[repr(packed(N))]` - Allows for control over the
793/// struct alignment. Namely to lower it. This is not supported for enums.
794///
795/// The following `zero_copy(..)` attribute are supported:
796///
797/// <br>
798///
799/// ### `#[zero_copy(bounds = {<bound>,*})]`
800///
801/// Allows for adding additional bounds to implement `ZeroCopy` for generic
802/// types:
803///
804/// ```
805/// use musli_zerocopy::ZeroCopy;
806///
807/// #[derive(ZeroCopy)]
808/// #[repr(C)]
809/// #[zero_copy(bounds = {A: ZeroCopy, B: ZeroCopy})]
810/// struct Pair<A, B> {
811/// left: A,
812/// right: B,
813/// }
814/// ```
815///
816/// <br>
817///
818/// ### `#[zero_copy(crate = <path>)]`
819///
820/// Allows for specifying a custom path to the `musli_zerocopy` crate
821/// (default).
822///
823/// ```
824/// # use musli_zerocopy as zerocopy;
825/// use zerocopy::ZeroCopy;
826///
827/// #[derive(ZeroCopy)]
828/// #[repr(C)]
829/// #[zero_copy(crate = zerocopy)]
830/// struct Custom { field: u32 }
831/// ```
832///
833/// <br>
834///
835/// ### `#[zero_copy(swap_bytes)]`
836///
837/// Allows enums to be byte-ordered swap with some extra work.
838///
839/// This requires that an even number of fields is specified, and that each
840/// field has a discriminant which is the byte-swapped variant of the other. If
841/// a variant contains a `_` suffix, it will automatically be considered as the
842/// byte-swapped variant for the variant without the suffix. The
843/// `#[zero_copy(swap = <variant>)]` field attribute can be used to explicitly
844/// specify which variant is the byte-swapped equivalent of this one.
845///
846/// ```
847/// # use musli_zerocopy as zerocopy;
848/// use zerocopy::ZeroCopy;
849/// use zerocopy::endian::{Other, Native};
850///
851/// #[derive(ZeroCopy, PartialEq, Debug, Clone, Copy)]
852/// #[zero_copy(swap_bytes)]
853/// #[repr(u32)]
854/// enum Enum {
855/// A { field: u32 } = 1,
856/// A_ { field: u32 } = u32::swap_bytes(1),
857/// B = 2,
858/// B_ = u32::swap_bytes(2),
859/// }
860///
861/// const _: () = assert!(Enum::CAN_SWAP_BYTES);
862///
863/// let a = Enum::A { field: 42 };
864/// let a2 = a.swap_bytes::<Native>();
865/// let a_ = a.swap_bytes::<Other>();
866///
867/// assert_eq!(a2, Enum::A { field: 42 });
868/// assert_eq!(a_, Enum::A_ { field: u32::swap_bytes(42) });
869/// ```
870///
871/// If the discriminant value does not involved a method called `swap_bytes`,
872/// the field being swapped with can be specified with the `#[zero_copy(swap =
873/// <field>)]` attribute.
874///
875/// ```
876/// # use musli_zerocopy as zerocopy;
877/// use zerocopy::ZeroCopy;
878/// use zerocopy::endian::{Other, Native};
879///
880/// #[derive(ZeroCopy, PartialEq, Debug, Clone, Copy)]
881/// #[zero_copy(swap_bytes)]
882/// #[repr(u32)]
883/// enum Enum {
884/// A = 0x00000001,
885/// #[zero_copy(swap = A)]
886/// B = 0x01000000,
887/// C = 0x00000002,
888/// #[zero_copy(swap = C)]
889/// D = 0x02000000,
890/// }
891///
892/// const _: () = assert!(Enum::CAN_SWAP_BYTES);
893///
894/// let v = Enum::A;
895/// let v2 = v.swap_bytes::<Native>();
896/// let v3 = v.swap_bytes::<Other>();
897///
898/// assert_eq!(v2, Enum::A);
899/// assert_eq!(v3, Enum::B);
900///
901/// let v = Enum::C;
902/// let v2 = v.swap_bytes::<Native>();
903/// let v3 = v.swap_bytes::<Other>();
904///
905/// assert_eq!(v2, Enum::C);
906/// assert_eq!(v3, Enum::D);
907/// ```
908#[doc(inline)]
909pub use musli_zerocopy_macros::ZeroCopy;
910
911#[cfg(test)]
912mod tests;
913
914#[doc(hidden)]
915pub mod __private {
916 use core::fmt;
917
918 pub mod result {
919 pub use ::core::result::Result;
920 }
921
922 pub mod mem {
923 pub use ::core::mem::{align_of, size_of};
924 }
925
926 pub use crate::buf::{Buf, Visit};
927 pub use crate::endian::ByteOrder;
928 pub use crate::traits::{ZeroCopy, ZeroSized};
929
930 #[inline(always)]
931 pub fn unknown_discriminant<D>(discriminant: D)
932 where
933 D: fmt::Display,
934 {
935 core::unreachable!("Unknown discriminant `{discriminant}`, this is a bug since it should be present in the type being padded.")
936 }
937}