[PATCH v2 08/19] rust: zerocopy: import crate
Miguel Ojeda <[email protected]>
| Newsgroups | org.kernel.vger.linux-kbuild,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
This is a subset of the Rust `zerocopy` crate, version v0.8.50 (released
2026-05-31), licensed under "BSD-2-Clause OR Apache-2.0 OR MIT", from:
https://github.com/google/zerocopy/tree/v0.8.50
The files are copied as-is, with no modifications whatsoever (not even
adding the SPDX identifiers).
The `benches` folder is added (i.e. not just `src` like in other cases)
since the files there are included in the rendered documentation,
as well as the `rustdoc` CSS style file that is needed to make those
visually more understandable.
For copyright details, please see:
https://github.com/google/zerocopy/blob/v0.8.50/README.md?plain=1
https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-BSD
https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-APACHE
https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-MIT
The next two patches modify these files as needed for use within the
kernel. This patch split allows reviewers to double-check the import
and to clearly see the differences introduced.
The following script may be used to verify the contents:
for path in $(cd rust/zerocopy/ && find . -type f); do
curl --silent --show-error --location \
https://github.com/google/zerocopy/raw/v0.8.50/$path \
| diff --unified rust/zerocopy/$path - && echo $path: OK
done
Cc: Joshua Liebow-Feeser <[email protected]>
Cc: Jack Wrenn <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
.../zerocopy/benches/as_bytes_dynamic_size.rs | 9 +
.../benches/as_bytes_dynamic_size.x86-64 | 5 +
.../benches/as_bytes_dynamic_size.x86-64.mca | 47 +
rust/zerocopy/benches/as_bytes_static_size.rs | 9 +
.../benches/as_bytes_static_size.x86-64 | 4 +
.../benches/as_bytes_static_size.x86-64.mca | 45 +
rust/zerocopy/benches/extend_vec_zeroed.rs | 9 +
.../zerocopy/benches/extend_vec_zeroed.x86-64 | 60 +
.../benches/extend_vec_zeroed.x86-64.mca | 147 +
.../benches/formats/coco_dynamic_padding.rs | 24 +
.../benches/formats/coco_dynamic_size.rs | 27 +
.../benches/formats/coco_static_size.rs | 27 +
rust/zerocopy/benches/insert_vec_zeroed.rs | 13 +
.../zerocopy/benches/insert_vec_zeroed.x86-64 | 79 +
.../benches/insert_vec_zeroed.x86-64.mca | 183 +
rust/zerocopy/benches/new_box_zeroed.rs | 9 +
rust/zerocopy/benches/new_box_zeroed.x86-64 | 7 +
.../benches/new_box_zeroed.x86-64.mca | 51 +
...w_box_zeroed_with_elems_dynamic_padding.rs | 11 +
...x_zeroed_with_elems_dynamic_padding.x86-64 | 24 +
...roed_with_elems_dynamic_padding.x86-64.mca | 81 +
.../new_box_zeroed_with_elems_dynamic_size.rs | 9 +
..._box_zeroed_with_elems_dynamic_size.x86-64 | 22 +
..._zeroed_with_elems_dynamic_size.x86-64.mca | 77 +
rust/zerocopy/benches/new_vec_zeroed.rs | 9 +
rust/zerocopy/benches/new_vec_zeroed.x86-64 | 40 +
.../benches/new_vec_zeroed.x86-64.mca | 113 +
rust/zerocopy/benches/new_zeroed.rs | 9 +
rust/zerocopy/benches/new_zeroed.x86-64 | 3 +
rust/zerocopy/benches/new_zeroed.x86-64.mca | 43 +
rust/zerocopy/benches/read_from_bytes.rs | 7 +
rust/zerocopy/benches/read_from_bytes.x86-64 | 15 +
.../benches/read_from_bytes.x86-64.mca | 65 +
rust/zerocopy/benches/read_from_prefix.rs | 10 +
rust/zerocopy/benches/read_from_prefix.x86-64 | 14 +
.../benches/read_from_prefix.x86-64.mca | 63 +
rust/zerocopy/benches/read_from_suffix.rs | 10 +
rust/zerocopy/benches/read_from_suffix.x86-64 | 15 +
.../benches/read_from_suffix.x86-64.mca | 65 +
.../benches/ref_from_bytes_dynamic_padding.rs | 7 +
.../ref_from_bytes_dynamic_padding.x86-64 | 22 +
.../ref_from_bytes_dynamic_padding.x86-64.mca | 77 +
.../benches/ref_from_bytes_dynamic_size.rs | 7 +
.../ref_from_bytes_dynamic_size.x86-64 | 20 +
.../ref_from_bytes_dynamic_size.x86-64.mca | 75 +
.../benches/ref_from_bytes_static_size.rs | 7 +
.../benches/ref_from_bytes_static_size.x86-64 | 8 +
.../ref_from_bytes_static_size.x86-64.mca | 53 +
...f_from_bytes_with_elems_dynamic_padding.rs | 10 +
...om_bytes_with_elems_dynamic_padding.x86-64 | 19 +
...ytes_with_elems_dynamic_padding.x86-64.mca | 71 +
.../ref_from_bytes_with_elems_dynamic_size.rs | 10 +
..._from_bytes_with_elems_dynamic_size.x86-64 | 16 +
...m_bytes_with_elems_dynamic_size.x86-64.mca | 65 +
.../ref_from_prefix_dynamic_padding.rs | 10 +
.../ref_from_prefix_dynamic_padding.x86-64 | 22 +
...ref_from_prefix_dynamic_padding.x86-64.mca | 77 +
.../benches/ref_from_prefix_dynamic_size.rs | 10 +
.../ref_from_prefix_dynamic_size.x86-64 | 17 +
.../ref_from_prefix_dynamic_size.x86-64.mca | 67 +
.../benches/ref_from_prefix_static_size.rs | 10 +
.../ref_from_prefix_static_size.x86-64 | 8 +
.../ref_from_prefix_static_size.x86-64.mca | 53 +
..._from_prefix_with_elems_dynamic_padding.rs | 13 +
...m_prefix_with_elems_dynamic_padding.x86-64 | 26 +
...efix_with_elems_dynamic_padding.x86-64.mca | 85 +
...ref_from_prefix_with_elems_dynamic_size.rs | 13 +
...from_prefix_with_elems_dynamic_size.x86-64 | 22 +
..._prefix_with_elems_dynamic_size.x86-64.mca | 77 +
.../ref_from_suffix_dynamic_padding.rs | 10 +
.../ref_from_suffix_dynamic_padding.x86-64 | 23 +
...ref_from_suffix_dynamic_padding.x86-64.mca | 79 +
.../benches/ref_from_suffix_dynamic_size.rs | 10 +
.../ref_from_suffix_dynamic_size.x86-64 | 13 +
.../ref_from_suffix_dynamic_size.x86-64.mca | 63 +
.../benches/ref_from_suffix_static_size.rs | 10 +
.../ref_from_suffix_static_size.x86-64 | 13 +
.../ref_from_suffix_static_size.x86-64.mca | 61 +
..._from_suffix_with_elems_dynamic_padding.rs | 13 +
...m_suffix_with_elems_dynamic_padding.x86-64 | 27 +
...ffix_with_elems_dynamic_padding.x86-64.mca | 85 +
...ref_from_suffix_with_elems_dynamic_size.rs | 13 +
...from_suffix_with_elems_dynamic_size.x86-64 | 23 +
..._suffix_with_elems_dynamic_size.x86-64.mca | 77 +
.../benches/split_at_dynamic_padding.rs | 12 +
.../benches/split_at_dynamic_padding.x86-64 | 12 +
.../split_at_dynamic_padding.x86-64.mca | 59 +
.../zerocopy/benches/split_at_dynamic_size.rs | 12 +
.../benches/split_at_dynamic_size.x86-64 | 12 +
.../benches/split_at_dynamic_size.x86-64.mca | 59 +
.../split_at_unchecked_dynamic_padding.rs | 12 +
.../split_at_unchecked_dynamic_padding.x86-64 | 6 +
...it_at_unchecked_dynamic_padding.x86-64.mca | 49 +
.../split_at_unchecked_dynamic_size.rs | 12 +
.../split_at_unchecked_dynamic_size.x86-64 | 6 +
...split_at_unchecked_dynamic_size.x86-64.mca | 49 +
.../split_via_immutable_dynamic_padding.rs | 11 +
...split_via_immutable_dynamic_padding.x86-64 | 14 +
...t_via_immutable_dynamic_padding.x86-64.mca | 65 +
.../split_via_immutable_dynamic_size.rs | 11 +
.../split_via_immutable_dynamic_size.x86-64 | 13 +
...plit_via_immutable_dynamic_size.x86-64.mca | 63 +
...split_via_runtime_check_dynamic_padding.rs | 11 +
...t_via_runtime_check_dynamic_padding.x86-64 | 22 +
...a_runtime_check_dynamic_padding.x86-64.mca | 79 +
.../split_via_runtime_check_dynamic_size.rs | 11 +
...plit_via_runtime_check_dynamic_size.x86-64 | 13 +
..._via_runtime_check_dynamic_size.x86-64.mca | 63 +
.../split_via_unchecked_dynamic_padding.rs | 11 +
...split_via_unchecked_dynamic_padding.x86-64 | 14 +
...t_via_unchecked_dynamic_padding.x86-64.mca | 65 +
.../split_via_unchecked_dynamic_size.rs | 11 +
.../split_via_unchecked_dynamic_size.x86-64 | 13 +
...plit_via_unchecked_dynamic_size.x86-64.mca | 63 +
rust/zerocopy/benches/transmute.rs | 16 +
rust/zerocopy/benches/transmute.x86-64 | 3 +
rust/zerocopy/benches/transmute.x86-64.mca | 43 +
.../benches/transmute_ref_dynamic_size.rs | 16 +
.../benches/transmute_ref_dynamic_size.x86-64 | 4 +
.../transmute_ref_dynamic_size.x86-64.mca | 45 +
.../benches/transmute_ref_static_size.rs | 15 +
.../benches/transmute_ref_static_size.x86-64 | 3 +
.../transmute_ref_static_size.x86-64.mca | 43 +
rust/zerocopy/benches/try_read_from_bytes.rs | 7 +
.../benches/try_read_from_bytes.x86-64 | 23 +
.../benches/try_read_from_bytes.x86-64.mca | 79 +
rust/zerocopy/benches/try_read_from_prefix.rs | 10 +
.../benches/try_read_from_prefix.x86-64 | 16 +
.../benches/try_read_from_prefix.x86-64.mca | 67 +
rust/zerocopy/benches/try_read_from_suffix.rs | 10 +
.../benches/try_read_from_suffix.x86-64 | 18 +
.../benches/try_read_from_suffix.x86-64.mca | 71 +
.../try_ref_from_bytes_dynamic_padding.rs | 7 +
.../try_ref_from_bytes_dynamic_padding.x86-64 | 24 +
..._ref_from_bytes_dynamic_padding.x86-64.mca | 81 +
.../try_ref_from_bytes_dynamic_size.rs | 7 +
.../try_ref_from_bytes_dynamic_size.x86-64 | 22 +
...try_ref_from_bytes_dynamic_size.x86-64.mca | 79 +
.../benches/try_ref_from_bytes_static_size.rs | 7 +
.../try_ref_from_bytes_static_size.x86-64 | 13 +
.../try_ref_from_bytes_static_size.x86-64.mca | 59 +
...f_from_bytes_with_elems_dynamic_padding.rs | 10 +
...om_bytes_with_elems_dynamic_padding.x86-64 | 21 +
...ytes_with_elems_dynamic_padding.x86-64.mca | 75 +
..._ref_from_bytes_with_elems_dynamic_size.rs | 10 +
..._from_bytes_with_elems_dynamic_size.x86-64 | 18 +
...m_bytes_with_elems_dynamic_size.x86-64.mca | 69 +
.../try_ref_from_prefix_dynamic_padding.rs | 10 +
...try_ref_from_prefix_dynamic_padding.x86-64 | 29 +
...ref_from_prefix_dynamic_padding.x86-64.mca | 91 +
.../try_ref_from_prefix_dynamic_size.rs | 10 +
.../try_ref_from_prefix_dynamic_size.x86-64 | 22 +
...ry_ref_from_prefix_dynamic_size.x86-64.mca | 77 +
.../try_ref_from_prefix_static_size.rs | 10 +
.../try_ref_from_prefix_static_size.x86-64 | 15 +
...try_ref_from_prefix_static_size.x86-64.mca | 63 +
..._from_prefix_with_elems_dynamic_padding.rs | 13 +
...m_prefix_with_elems_dynamic_padding.x86-64 | 30 +
...efix_with_elems_dynamic_padding.x86-64.mca | 91 +
...ref_from_prefix_with_elems_dynamic_size.rs | 13 +
...from_prefix_with_elems_dynamic_size.x86-64 | 26 +
..._prefix_with_elems_dynamic_size.x86-64.mca | 83 +
.../try_ref_from_suffix_dynamic_padding.rs | 10 +
...try_ref_from_suffix_dynamic_padding.x86-64 | 26 +
...ref_from_suffix_dynamic_padding.x86-64.mca | 85 +
.../try_ref_from_suffix_dynamic_size.rs | 10 +
.../try_ref_from_suffix_dynamic_size.x86-64 | 18 +
...ry_ref_from_suffix_dynamic_size.x86-64.mca | 71 +
.../try_ref_from_suffix_static_size.rs | 10 +
.../try_ref_from_suffix_static_size.x86-64 | 16 +
...try_ref_from_suffix_static_size.x86-64.mca | 67 +
..._from_suffix_with_elems_dynamic_padding.rs | 13 +
...m_suffix_with_elems_dynamic_padding.x86-64 | 32 +
...ffix_with_elems_dynamic_padding.x86-64.mca | 95 +
...ref_from_suffix_with_elems_dynamic_size.rs | 13 +
...from_suffix_with_elems_dynamic_size.x86-64 | 28 +
..._suffix_with_elems_dynamic_size.x86-64.mca | 87 +
rust/zerocopy/benches/try_transmute.rs | 16 +
rust/zerocopy/benches/try_transmute.x86-64 | 9 +
.../zerocopy/benches/try_transmute.x86-64.mca | 55 +
.../benches/try_transmute_ref_dynamic_size.rs | 18 +
.../try_transmute_ref_dynamic_size.x86-64 | 6 +
.../try_transmute_ref_dynamic_size.x86-64.mca | 49 +
.../benches/try_transmute_ref_static_size.rs | 17 +
.../try_transmute_ref_static_size.x86-64 | 5 +
.../try_transmute_ref_static_size.x86-64.mca | 47 +
.../zerocopy/benches/write_to_dynamic_size.rs | 9 +
.../benches/write_to_dynamic_size.x86-64 | 21 +
.../benches/write_to_dynamic_size.x86-64.mca | 77 +
.../benches/write_to_prefix_dynamic_size.rs | 12 +
.../write_to_prefix_dynamic_size.x86-64 | 21 +
.../write_to_prefix_dynamic_size.x86-64.mca | 77 +
.../benches/write_to_prefix_static_size.rs | 12 +
.../write_to_prefix_static_size.x86-64 | 11 +
.../write_to_prefix_static_size.x86-64.mca | 57 +
rust/zerocopy/benches/write_to_static_size.rs | 9 +
.../benches/write_to_static_size.x86-64 | 11 +
.../benches/write_to_static_size.x86-64.mca | 57 +
.../benches/write_to_suffix_dynamic_size.rs | 12 +
.../write_to_suffix_dynamic_size.x86-64 | 22 +
.../write_to_suffix_dynamic_size.x86-64.mca | 79 +
.../benches/write_to_suffix_static_size.rs | 12 +
.../write_to_suffix_static_size.x86-64 | 11 +
.../write_to_suffix_static_size.x86-64.mca | 57 +
rust/zerocopy/benches/zero_dynamic_padding.rs | 9 +
.../benches/zero_dynamic_padding.x86-64 | 7 +
.../benches/zero_dynamic_padding.x86-64.mca | 51 +
rust/zerocopy/benches/zero_dynamic_size.rs | 9 +
.../zerocopy/benches/zero_dynamic_size.x86-64 | 5 +
.../benches/zero_dynamic_size.x86-64.mca | 47 +
rust/zerocopy/benches/zero_static_size.rs | 9 +
rust/zerocopy/benches/zero_static_size.x86-64 | 4 +
.../benches/zero_static_size.x86-64.mca | 45 +
rust/zerocopy/rustdoc/style.css | 55 +
rust/zerocopy/src/byte_slice.rs | 432 +
rust/zerocopy/src/byteorder.rs | 1563 ++++
rust/zerocopy/src/deprecated.rs | 279 +
rust/zerocopy/src/error.rs | 1348 +++
rust/zerocopy/src/impls.rs | 2387 ++++++
rust/zerocopy/src/layout.rs | 2223 +++++
rust/zerocopy/src/lib.rs | 7610 +++++++++++++++++
rust/zerocopy/src/macros.rs | 1823 ++++
rust/zerocopy/src/pointer/inner.rs | 752 ++
rust/zerocopy/src/pointer/invariant.rs | 296 +
rust/zerocopy/src/pointer/mod.rs | 408 +
rust/zerocopy/src/pointer/ptr.rs | 1584 ++++
rust/zerocopy/src/pointer/transmute.rs | 520 ++
rust/zerocopy/src/ref.rs | 1356 +++
rust/zerocopy/src/split_at.rs | 1088 +++
rust/zerocopy/src/util/macro_util.rs | 1308 +++
rust/zerocopy/src/util/macros.rs | 1065 +++
rust/zerocopy/src/util/mod.rs | 936 ++
rust/zerocopy/src/wrappers.rs | 1032 +++
233 files changed, 35005 insertions(+)
create mode 100644 rust/zerocopy/benches/as_bytes_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/as_bytes_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/as_bytes_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/as_bytes_static_size.rs
create mode 100644 rust/zerocopy/benches/as_bytes_static_size.x86-64
create mode 100644 rust/zerocopy/benches/as_bytes_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/extend_vec_zeroed.rs
create mode 100644 rust/zerocopy/benches/extend_vec_zeroed.x86-64
create mode 100644 rust/zerocopy/benches/extend_vec_zeroed.x86-64.mca
create mode 100644 rust/zerocopy/benches/formats/coco_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/formats/coco_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/formats/coco_static_size.rs
create mode 100644 rust/zerocopy/benches/insert_vec_zeroed.rs
create mode 100644 rust/zerocopy/benches/insert_vec_zeroed.x86-64
create mode 100644 rust/zerocopy/benches/insert_vec_zeroed.x86-64.mca
create mode 100644 rust/zerocopy/benches/new_box_zeroed.rs
create mode 100644 rust/zerocopy/benches/new_box_zeroed.x86-64
create mode 100644 rust/zerocopy/benches/new_box_zeroed.x86-64.mca
create mode 100644 rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/new_vec_zeroed.rs
create mode 100644 rust/zerocopy/benches/new_vec_zeroed.x86-64
create mode 100644 rust/zerocopy/benches/new_vec_zeroed.x86-64.mca
create mode 100644 rust/zerocopy/benches/new_zeroed.rs
create mode 100644 rust/zerocopy/benches/new_zeroed.x86-64
create mode 100644 rust/zerocopy/benches/new_zeroed.x86-64.mca
create mode 100644 rust/zerocopy/benches/read_from_bytes.rs
create mode 100644 rust/zerocopy/benches/read_from_bytes.x86-64
create mode 100644 rust/zerocopy/benches/read_from_bytes.x86-64.mca
create mode 100644 rust/zerocopy/benches/read_from_prefix.rs
create mode 100644 rust/zerocopy/benches/read_from_prefix.x86-64
create mode 100644 rust/zerocopy/benches/read_from_prefix.x86-64.mca
create mode 100644 rust/zerocopy/benches/read_from_suffix.rs
create mode 100644 rust/zerocopy/benches/read_from_suffix.x86-64
create mode 100644 rust/zerocopy/benches/read_from_suffix.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_bytes_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_bytes_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_bytes_static_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_bytes_static_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_bytes_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_prefix_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_prefix_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_prefix_static_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_prefix_static_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_prefix_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_suffix_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_suffix_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_suffix_static_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_suffix_static_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_suffix_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_at_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/split_at_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/split_at_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_at_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/split_at_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/split_at_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_at_unchecked_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_at_unchecked_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_via_immutable_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_via_immutable_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_via_runtime_check_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_via_unchecked_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/split_via_unchecked_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/transmute.rs
create mode 100644 rust/zerocopy/benches/transmute.x86-64
create mode 100644 rust/zerocopy/benches/transmute.x86-64.mca
create mode 100644 rust/zerocopy/benches/transmute_ref_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/transmute_ref_static_size.rs
create mode 100644 rust/zerocopy/benches/transmute_ref_static_size.x86-64
create mode 100644 rust/zerocopy/benches/transmute_ref_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_read_from_bytes.rs
create mode 100644 rust/zerocopy/benches/try_read_from_bytes.x86-64
create mode 100644 rust/zerocopy/benches/try_read_from_bytes.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_read_from_prefix.rs
create mode 100644 rust/zerocopy/benches/try_read_from_prefix.x86-64
create mode 100644 rust/zerocopy/benches/try_read_from_prefix.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_read_from_suffix.rs
create mode 100644 rust/zerocopy/benches/try_read_from_suffix.x86-64
create mode 100644 rust/zerocopy/benches/try_read_from_suffix.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_static_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_static_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_static_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_transmute.rs
create mode 100644 rust/zerocopy/benches/try_transmute.x86-64
create mode 100644 rust/zerocopy/benches/try_transmute.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_transmute_ref_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/try_transmute_ref_static_size.rs
create mode 100644 rust/zerocopy/benches/try_transmute_ref_static_size.x86-64
create mode 100644 rust/zerocopy/benches/try_transmute_ref_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/write_to_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/write_to_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/write_to_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/write_to_prefix_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/write_to_prefix_static_size.rs
create mode 100644 rust/zerocopy/benches/write_to_prefix_static_size.x86-64
create mode 100644 rust/zerocopy/benches/write_to_prefix_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/write_to_static_size.rs
create mode 100644 rust/zerocopy/benches/write_to_static_size.x86-64
create mode 100644 rust/zerocopy/benches/write_to_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/write_to_suffix_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/write_to_suffix_static_size.rs
create mode 100644 rust/zerocopy/benches/write_to_suffix_static_size.x86-64
create mode 100644 rust/zerocopy/benches/write_to_suffix_static_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/zero_dynamic_padding.rs
create mode 100644 rust/zerocopy/benches/zero_dynamic_padding.x86-64
create mode 100644 rust/zerocopy/benches/zero_dynamic_padding.x86-64.mca
create mode 100644 rust/zerocopy/benches/zero_dynamic_size.rs
create mode 100644 rust/zerocopy/benches/zero_dynamic_size.x86-64
create mode 100644 rust/zerocopy/benches/zero_dynamic_size.x86-64.mca
create mode 100644 rust/zerocopy/benches/zero_static_size.rs
create mode 100644 rust/zerocopy/benches/zero_static_size.x86-64
create mode 100644 rust/zerocopy/benches/zero_static_size.x86-64.mca
create mode 100644 rust/zerocopy/rustdoc/style.css
create mode 100644 rust/zerocopy/src/byte_slice.rs
create mode 100644 rust/zerocopy/src/byteorder.rs
create mode 100644 rust/zerocopy/src/deprecated.rs
create mode 100644 rust/zerocopy/src/error.rs
create mode 100644 rust/zerocopy/src/impls.rs
create mode 100644 rust/zerocopy/src/layout.rs
create mode 100644 rust/zerocopy/src/lib.rs
create mode 100644 rust/zerocopy/src/macros.rs
create mode 100644 rust/zerocopy/src/pointer/inner.rs
create mode 100644 rust/zerocopy/src/pointer/invariant.rs
create mode 100644 rust/zerocopy/src/pointer/mod.rs
create mode 100644 rust/zerocopy/src/pointer/ptr.rs
create mode 100644 rust/zerocopy/src/pointer/transmute.rs
create mode 100644 rust/zerocopy/src/ref.rs
create mode 100644 rust/zerocopy/src/split_at.rs
create mode 100644 rust/zerocopy/src/util/macro_util.rs
create mode 100644 rust/zerocopy/src/util/macros.rs
create mode 100644 rust/zerocopy/src/util/mod.rs
create mode 100644 rust/zerocopy/src/wrappers.rs
diff --git a/rust/zerocopy/benches/as_bytes_dynamic_size.rs b/rust/zerocopy/benches/as_bytes_dynamic_size.rs
new file mode 100644
index 000000000000..68cd1d6f4111
--- /dev/null
+++ b/rust/zerocopy/benches/as_bytes_dynamic_size.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_as_bytes_dynamic_size(source: &format::CocoPacket) -> &[u8] {
+ source.as_bytes()
+}
diff --git a/rust/zerocopy/benches/as_bytes_dynamic_size.x86-64 b/rust/zerocopy/benches/as_bytes_dynamic_size.x86-64
new file mode 100644
index 000000000000..f68bad612695
--- /dev/null
+++ b/rust/zerocopy/benches/as_bytes_dynamic_size.x86-64
@@ -0,0 +1,5 @@
+bench_as_bytes_dynamic_size:
+ mov rax, rdi
+ lea rdx, [2*rsi + 5]
+ and rdx, -2
+ ret
diff --git a/rust/zerocopy/benches/as_bytes_dynamic_size.x86-64.mca b/rust/zerocopy/benches/as_bytes_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..c3b92a9a95fe
--- /dev/null
+++ b/rust/zerocopy/benches/as_bytes_dynamic_size.x86-64.mca
@@ -0,0 +1,47 @@
+Iterations: 100
+Instructions: 400
+Total Cycles: 137
+Total uOps: 400
+
+Dispatch Width: 4
+uOps Per Cycle: 2.92
+IPC: 2.92
+Block RThroughput: 1.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.50 lea rdx, [2*rsi + 5]
+ 1 1 0.33 and rdx, -2
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.33 1.33 - 1.34 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.66 - 0.34 - - mov rax, rdi
+ - - 0.33 0.67 - - - - lea rdx, [2*rsi + 5]
+ - - 1.00 - - - - - and rdx, -2
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/as_bytes_static_size.rs b/rust/zerocopy/benches/as_bytes_static_size.rs
new file mode 100644
index 000000000000..2ad738e95480
--- /dev/null
+++ b/rust/zerocopy/benches/as_bytes_static_size.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_as_bytes_static_size(source: &format::CocoPacket) -> &[u8] {
+ source.as_bytes()
+}
diff --git a/rust/zerocopy/benches/as_bytes_static_size.x86-64 b/rust/zerocopy/benches/as_bytes_static_size.x86-64
new file mode 100644
index 000000000000..213e74ab54ff
--- /dev/null
+++ b/rust/zerocopy/benches/as_bytes_static_size.x86-64
@@ -0,0 +1,4 @@
+bench_as_bytes_static_size:
+ mov rax, rdi
+ mov edx, 6
+ ret
diff --git a/rust/zerocopy/benches/as_bytes_static_size.x86-64.mca b/rust/zerocopy/benches/as_bytes_static_size.x86-64.mca
new file mode 100644
index 000000000000..ae04a6ba9061
--- /dev/null
+++ b/rust/zerocopy/benches/as_bytes_static_size.x86-64.mca
@@ -0,0 +1,45 @@
+Iterations: 100
+Instructions: 300
+Total Cycles: 104
+Total uOps: 300
+
+Dispatch Width: 4
+uOps Per Cycle: 2.88
+IPC: 2.88
+Block RThroughput: 1.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 mov edx, 6
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.99 1.00 - 1.01 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.99 - - 0.01 - - mov rax, rdi
+ - - - 1.00 - - - - mov edx, 6
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/extend_vec_zeroed.rs b/rust/zerocopy/benches/extend_vec_zeroed.rs
new file mode 100644
index 000000000000..1fbf772d1ea7
--- /dev/null
+++ b/rust/zerocopy/benches/extend_vec_zeroed.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_extend_vec_zeroed(v: &mut Vec<format::LocoPacket>, additional: usize) -> Option<()> {
+ FromZeros::extend_vec_zeroed(v, additional).ok()
+}
diff --git a/rust/zerocopy/benches/extend_vec_zeroed.x86-64 b/rust/zerocopy/benches/extend_vec_zeroed.x86-64
new file mode 100644
index 000000000000..831b2a075fec
--- /dev/null
+++ b/rust/zerocopy/benches/extend_vec_zeroed.x86-64
@@ -0,0 +1,60 @@
+bench_extend_vec_zeroed:
+ push r15
+ push r14
+ push r13
+ push r12
+ push rbx
+ sub rsp, 32
+ mov rbx, rdi
+ mov rax, qword ptr [rdi]
+ mov r12, qword ptr [rdi + 16]
+ mov rcx, rax
+ sub rcx, r12
+ cmp rsi, rcx
+ jbe .LBB6_3
+ mov r15, r12
+ add r15, rsi
+ jae .LBB6_6
+.LBB6_2:
+ xor eax, eax
+ jmp .LBB6_5
+.LBB6_3:
+ mov rax, qword ptr [rbx + 8]
+ lea r15, [r12 + rsi]
+.LBB6_4:
+ lea rcx, [r12 + 2*r12]
+ lea rdi, [rax + 2*rcx]
+ add rsi, rsi
+ lea rdx, [rsi + 2*rsi]
+ xor esi, esi
+ call qword ptr [rip + memset@GOTPCREL]
+ mov qword ptr [rbx + 16], r15
+ mov al, 1
+.LBB6_5:
+ add rsp, 32
+ pop rbx
+ pop r12
+ pop r13
+ pop r14
+ pop r15
+ ret
+.LBB6_6:
+ mov r13, rsi
+ lea rcx, [rax + rax]
+ cmp r15, rcx
+ cmova rcx, r15
+ cmp rcx, 5
+ mov r14d, 4
+ cmovae r14, rcx
+ mov rdx, qword ptr [rbx + 8]
+ lea rdi, [rsp + 8]
+ mov rsi, rax
+ mov rcx, r14
+ call <alloc::raw_vec::RawVecInner>::finish_grow
+ cmp dword ptr [rsp + 8], 1
+ je .LBB6_2
+ mov rax, qword ptr [rsp + 16]
+ mov qword ptr [rbx + 8], rax
+ mov qword ptr [rbx], r14
+ mov rsi, r13
+ jmp .LBB6_4
diff --git a/rust/zerocopy/benches/extend_vec_zeroed.x86-64.mca b/rust/zerocopy/benches/extend_vec_zeroed.x86-64.mca
new file mode 100644
index 000000000000..cfab1eea8f56
--- /dev/null
+++ b/rust/zerocopy/benches/extend_vec_zeroed.x86-64.mca
@@ -0,0 +1,147 @@
+Iterations: 100
+Instructions: 5400
+Total Cycles: 6595
+Total uOps: 6800
+
+Dispatch Width: 4
+uOps Per Cycle: 1.03
+IPC: 0.82
+Block RThroughput: 17.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push r15
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push r13
+ 2 5 1.00 * push r12
+ 2 5 1.00 * push rbx
+ 1 1 0.33 sub rsp, 32
+ 1 1 0.33 mov rbx, rdi
+ 1 5 0.50 * mov rax, qword ptr [rdi]
+ 1 5 0.50 * mov r12, qword ptr [rdi + 16]
+ 1 1 0.33 mov rcx, rax
+ 1 1 0.33 sub rcx, r12
+ 1 1 0.33 cmp rsi, rcx
+ 1 1 1.00 jbe .LBB6_3
+ 1 1 0.33 mov r15, r12
+ 1 1 0.33 add r15, rsi
+ 1 1 1.00 jae .LBB6_6
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 jmp .LBB6_5
+ 1 5 0.50 * mov rax, qword ptr [rbx + 8]
+ 1 1 0.50 lea r15, [r12 + rsi]
+ 1 1 0.50 lea rcx, [r12 + 2*r12]
+ 1 1 0.50 lea rdi, [rax + 2*rcx]
+ 1 1 0.33 add rsi, rsi
+ 1 1 0.50 lea rdx, [rsi + 2*rsi]
+ 1 0 0.25 xor esi, esi
+ 4 7 1.00 * call qword ptr [rip + memset@GOTPCREL]
+ 1 1 1.00 * mov qword ptr [rbx + 16], r15
+ 1 1 0.33 mov al, 1
+ 1 1 0.33 add rsp, 32
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r12
+ 1 6 0.50 * pop r13
+ 1 6 0.50 * pop r14
+ 1 6 0.50 * pop r15
+ 1 1 1.00 U ret
+ 1 1 0.33 mov r13, rsi
+ 1 1 0.50 lea rcx, [rax + rax]
+ 1 1 0.33 cmp r15, rcx
+ 3 3 1.00 cmova rcx, r15
+ 1 1 0.33 cmp rcx, 5
+ 1 1 0.33 mov r14d, 4
+ 2 2 0.67 cmovae r14, rcx
+ 1 5 0.50 * mov rdx, qword ptr [rbx + 8]
+ 1 1 0.50 lea rdi, [rsp + 8]
+ 1 1 0.33 mov rsi, rax
+ 1 1 0.33 mov rcx, r14
+ 3 5 1.00 call <alloc::raw_vec::RawVecInner>::finish_grow
+ 2 6 0.50 * cmp dword ptr [rsp + 8], 1
+ 1 1 1.00 je .LBB6_2
+ 1 5 0.50 * mov rax, qword ptr [rsp + 16]
+ 1 1 1.00 * mov qword ptr [rbx + 8], rax
+ 1 1 1.00 * mov qword ptr [rbx], r14
+ 1 1 0.33 mov rsi, r13
+ 1 1 1.00 jmp .LBB6_4
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 12.00 12.00 10.00 13.00 11.00 11.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - 0.49 0.51 push r15
+ - - - - 1.00 - 0.51 0.49 push r14
+ - - - - 1.00 - 0.50 0.50 push r13
+ - - - - 1.00 - 0.50 0.50 push r12
+ - - - - 1.00 - 0.50 0.50 push rbx
+ - - 0.01 0.99 - - - - sub rsp, 32
+ - - - - - 1.00 - - mov rbx, rdi
+ - - - - - - 0.50 0.50 mov rax, qword ptr [rdi]
+ - - - - - - 0.50 0.50 mov r12, qword ptr [rdi + 16]
+ - - - 1.00 - - - - mov rcx, rax
+ - - - 0.99 - 0.01 - - sub rcx, r12
+ - - - - - 1.00 - - cmp rsi, rcx
+ - - - - - 1.00 - - jbe .LBB6_3
+ - - 0.01 0.98 - 0.01 - - mov r15, r12
+ - - 0.99 0.01 - - - - add r15, rsi
+ - - - - - 1.00 - - jae .LBB6_6
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - jmp .LBB6_5
+ - - - - - - 0.50 0.50 mov rax, qword ptr [rbx + 8]
+ - - 1.00 - - - - - lea r15, [r12 + rsi]
+ - - 0.98 0.02 - - - - lea rcx, [r12 + 2*r12]
+ - - 0.99 0.01 - - - - lea rdi, [rax + 2*rcx]
+ - - - 1.00 - - - - add rsi, rsi
+ - - 0.99 0.01 - - - - lea rdx, [rsi + 2*rsi]
+ - - - - - - - - xor esi, esi
+ - - - - 1.00 1.00 1.00 1.00 call qword ptr [rip + memset@GOTPCREL]
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rbx + 16], r15
+ - - 0.01 0.99 - - - - mov al, 1
+ - - 1.00 - - - - - add rsp, 32
+ - - - - - - 0.50 0.50 pop rbx
+ - - - - - - 0.50 0.50 pop r12
+ - - - - - - 0.50 0.50 pop r13
+ - - - - - - 0.50 0.50 pop r14
+ - - - - - - 0.50 0.50 pop r15
+ - - - - - 1.00 - - ret
+ - - 1.00 - - - - - mov r13, rsi
+ - - 0.01 0.99 - - - - lea rcx, [rax + rax]
+ - - 0.99 0.01 - - - - cmp r15, rcx
+ - - 2.00 0.01 - 0.99 - - cmova rcx, r15
+ - - 0.01 0.99 - - - - cmp rcx, 5
+ - - 0.01 0.99 - - - - mov r14d, 4
+ - - 1.00 0.01 - 0.99 - - cmovae r14, rcx
+ - - - - - - 0.50 0.50 mov rdx, qword ptr [rbx + 8]
+ - - 0.01 0.99 - - - - lea rdi, [rsp + 8]
+ - - - 1.00 - - - - mov rsi, rax
+ - - - 0.01 - 0.99 - - mov rcx, r14
+ - - - - 1.00 1.00 0.50 0.50 call <alloc::raw_vec::RawVecInner>::finish_grow
+ - - - 0.99 - 0.01 0.50 0.50 cmp dword ptr [rsp + 8], 1
+ - - - - - 1.00 - - je .LBB6_2
+ - - - - - - 0.50 0.50 mov rax, qword ptr [rsp + 16]
+ - - - - 1.00 - 0.49 0.51 mov qword ptr [rbx + 8], rax
+ - - - - 1.00 - 0.51 0.49 mov qword ptr [rbx], r14
+ - - 0.99 0.01 - - - - mov rsi, r13
+ - - - - - 1.00 - - jmp .LBB6_4
diff --git a/rust/zerocopy/benches/formats/coco_dynamic_padding.rs b/rust/zerocopy/benches/formats/coco_dynamic_padding.rs
new file mode 100644
index 000000000000..e494bce67312
--- /dev/null
+++ b/rust/zerocopy/benches/formats/coco_dynamic_padding.rs
@@ -0,0 +1,24 @@
+use zerocopy_derive::*;
+
+// The only valid value of this type are the bytes `0xC0C0`.
+#[derive(TryFromBytes, KnownLayout, Immutable)]
+#[repr(u16)]
+pub enum C0C0 {
+ _XC0C0 = 0xC0C0,
+}
+
+#[derive(FromBytes, KnownLayout, Immutable, SplitAt)]
+#[repr(C, align(4))]
+pub struct Packet<Magic> {
+ magic_number: Magic,
+ milk: u8,
+ mug_size: u8,
+ temperature: [u8; 5],
+ marshmallows: [[u8; 3]],
+}
+
+/// A packet begining with the magic number `0xC0C0`.
+pub type CocoPacket = Packet<C0C0>;
+
+/// A packet beginning with any two initialized bytes.
+pub type LocoPacket = Packet<[u8; 2]>;
diff --git a/rust/zerocopy/benches/formats/coco_dynamic_size.rs b/rust/zerocopy/benches/formats/coco_dynamic_size.rs
new file mode 100644
index 000000000000..59364638e66c
--- /dev/null
+++ b/rust/zerocopy/benches/formats/coco_dynamic_size.rs
@@ -0,0 +1,27 @@
+use zerocopy_derive::*;
+
+// The only valid value of this type are the bytes `0xC0C0`.
+#[derive(TryFromBytes, KnownLayout, Immutable, IntoBytes)]
+#[repr(u16)]
+pub enum C0C0 {
+ _XC0C0 = 0xC0C0,
+}
+
+macro_rules! define_packet {
+ ($name: ident, $trait: ident, $leading_field: ty) => {
+ #[derive($trait, KnownLayout, Immutable, IntoBytes, SplitAt)]
+ #[repr(C, align(2))]
+ pub struct $name {
+ magic_number: $leading_field,
+ mug_size: u8,
+ temperature: u8,
+ marshmallows: [[u8; 2]],
+ }
+ };
+}
+
+/// Packet begins with bytes 0xC0C0.
+define_packet!(CocoPacket, TryFromBytes, C0C0);
+
+/// Packet begins with any two bytes.
+define_packet!(LocoPacket, FromBytes, [u8; 2]);
diff --git a/rust/zerocopy/benches/formats/coco_static_size.rs b/rust/zerocopy/benches/formats/coco_static_size.rs
new file mode 100644
index 000000000000..0839497e1748
--- /dev/null
+++ b/rust/zerocopy/benches/formats/coco_static_size.rs
@@ -0,0 +1,27 @@
+use zerocopy_derive::*;
+
+// The only valid value of this type are the bytes `0xC0C0`.
+#[derive(TryFromBytes, KnownLayout, Immutable, IntoBytes)]
+#[repr(u16)]
+pub enum C0C0 {
+ _XC0C0 = 0xC0C0,
+}
+
+macro_rules! define_packet {
+ ($name: ident, $trait: ident, $leading_field: ty) => {
+ #[derive($trait, KnownLayout, Immutable, IntoBytes)]
+ #[repr(C, align(2))]
+ pub struct $name {
+ magic_number: $leading_field,
+ mug_size: u8,
+ temperature: u8,
+ marshmallows: [u8; 2],
+ }
+ };
+}
+
+/// Packet begins with bytes 0xC0C0.
+define_packet!(CocoPacket, TryFromBytes, C0C0);
+
+/// Packet begins with any two bytes.
+define_packet!(LocoPacket, FromBytes, [u8; 2]);
diff --git a/rust/zerocopy/benches/insert_vec_zeroed.rs b/rust/zerocopy/benches/insert_vec_zeroed.rs
new file mode 100644
index 000000000000..a5d685c2b027
--- /dev/null
+++ b/rust/zerocopy/benches/insert_vec_zeroed.rs
@@ -0,0 +1,13 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_insert_vec_zeroed(
+ v: &mut Vec<format::LocoPacket>,
+ position: usize,
+ additional: usize,
+) -> Option<()> {
+ FromZeros::insert_vec_zeroed(v, position, additional).ok()
+}
diff --git a/rust/zerocopy/benches/insert_vec_zeroed.x86-64 b/rust/zerocopy/benches/insert_vec_zeroed.x86-64
new file mode 100644
index 000000000000..9db87403cb55
--- /dev/null
+++ b/rust/zerocopy/benches/insert_vec_zeroed.x86-64
@@ -0,0 +1,79 @@
+bench_insert_vec_zeroed:
+ push rbp
+ push r15
+ push r14
+ push r13
+ push r12
+ push rbx
+ sub rsp, 24
+ mov r12, qword ptr [rdi + 16]
+ mov r13, r12
+ sub r13, rsi
+ jb .LBB6_10
+ mov rbx, rdi
+ mov rax, qword ptr [rdi]
+ mov rcx, rax
+ sub rcx, r12
+ cmp rdx, rcx
+ jbe .LBB6_4
+ add r12, rdx
+ jae .LBB6_7
+.LBB6_3:
+ xor eax, eax
+ jmp .LBB6_6
+.LBB6_4:
+ mov rax, qword ptr [rbx + 8]
+ add r12, rdx
+.LBB6_5:
+ lea rcx, [rsi + 2*rsi]
+ lea r14, [rax + 2*rcx]
+ add rdx, rdx
+ lea r15, [rdx + 2*rdx]
+ lea rdi, [r14 + r15]
+ add r13, r13
+ lea rdx, [2*r13]
+ add rdx, r13
+ mov rsi, r14
+ call qword ptr [rip + memmove@GOTPCREL]
+ mov rdi, r14
+ xor esi, esi
+ mov rdx, r15
+ call qword ptr [rip + memset@GOTPCREL]
+ mov qword ptr [rbx + 16], r12
+ mov al, 1
+.LBB6_6:
+ add rsp, 24
+ pop rbx
+ pop r12
+ pop r13
+ pop r14
+ pop r15
+ pop rbp
+ ret
+.LBB6_7:
+ mov r15, rsi
+ mov rbp, rdx
+ lea rcx, [rax + rax]
+ cmp r12, rcx
+ cmova rcx, r12
+ cmp rcx, 5
+ mov r14d, 4
+ cmovae r14, rcx
+ mov rdx, qword ptr [rbx + 8]
+ mov rdi, rsp
+ mov rsi, rax
+ mov rcx, r14
+ call <alloc::raw_vec::RawVecInner>::finish_grow
+ cmp dword ptr [rsp], 1
+ je .LBB6_3
+ mov rax, qword ptr [rsp + 8]
+ mov qword ptr [rbx + 8], rax
+ mov qword ptr [rbx], r14
+ mov rdx, rbp
+ mov rsi, r15
+ jmp .LBB6_5
+.LBB6_10:
+ lea rdi, [rip + .Lanon.HASH.1]
+ lea rdx, [rip + .Lanon.HASH.3]
+ mov esi, 37
+ call qword ptr [rip + core::panicking::panic@GOTPCREL]
diff --git a/rust/zerocopy/benches/insert_vec_zeroed.x86-64.mca b/rust/zerocopy/benches/insert_vec_zeroed.x86-64.mca
new file mode 100644
index 000000000000..665240667844
--- /dev/null
+++ b/rust/zerocopy/benches/insert_vec_zeroed.x86-64.mca
@@ -0,0 +1,183 @@
+Iterations: 100
+Instructions: 7200
+Total Cycles: 7648
+Total uOps: 9300
+
+Dispatch Width: 4
+uOps Per Cycle: 1.22
+IPC: 0.94
+Block RThroughput: 23.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push rbp
+ 2 5 1.00 * push r15
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push r13
+ 2 5 1.00 * push r12
+ 2 5 1.00 * push rbx
+ 1 1 0.33 sub rsp, 24
+ 1 5 0.50 * mov r12, qword ptr [rdi + 16]
+ 1 1 0.33 mov r13, r12
+ 1 1 0.33 sub r13, rsi
+ 1 1 1.00 jb .LBB6_10
+ 1 1 0.33 mov rbx, rdi
+ 1 5 0.50 * mov rax, qword ptr [rdi]
+ 1 1 0.33 mov rcx, rax
+ 1 1 0.33 sub rcx, r12
+ 1 1 0.33 cmp rdx, rcx
+ 1 1 1.00 jbe .LBB6_4
+ 1 1 0.33 add r12, rdx
+ 1 1 1.00 jae .LBB6_7
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 jmp .LBB6_6
+ 1 5 0.50 * mov rax, qword ptr [rbx + 8]
+ 1 1 0.33 add r12, rdx
+ 1 1 0.50 lea rcx, [rsi + 2*rsi]
+ 1 1 0.50 lea r14, [rax + 2*rcx]
+ 1 1 0.33 add rdx, rdx
+ 1 1 0.50 lea r15, [rdx + 2*rdx]
+ 1 1 0.50 lea rdi, [r14 + r15]
+ 1 1 0.33 add r13, r13
+ 1 1 0.50 lea rdx, [2*r13]
+ 1 1 0.33 add rdx, r13
+ 1 1 0.33 mov rsi, r14
+ 4 7 1.00 * call qword ptr [rip + memmove@GOTPCREL]
+ 1 1 0.33 mov rdi, r14
+ 1 0 0.25 xor esi, esi
+ 1 1 0.33 mov rdx, r15
+ 4 7 1.00 * call qword ptr [rip + memset@GOTPCREL]
+ 1 1 1.00 * mov qword ptr [rbx + 16], r12
+ 1 1 0.33 mov al, 1
+ 1 1 0.33 add rsp, 24
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r12
+ 1 6 0.50 * pop r13
+ 1 6 0.50 * pop r14
+ 1 6 0.50 * pop r15
+ 1 6 0.50 * pop rbp
+ 1 1 1.00 U ret
+ 1 1 0.33 mov r15, rsi
+ 1 1 0.33 mov rbp, rdx
+ 1 1 0.50 lea rcx, [rax + rax]
+ 1 1 0.33 cmp r12, rcx
+ 3 3 1.00 cmova rcx, r12
+ 1 1 0.33 cmp rcx, 5
+ 1 1 0.33 mov r14d, 4
+ 2 2 0.67 cmovae r14, rcx
+ 1 5 0.50 * mov rdx, qword ptr [rbx + 8]
+ 1 1 0.33 mov rdi, rsp
+ 1 1 0.33 mov rsi, rax
+ 1 1 0.33 mov rcx, r14
+ 3 5 1.00 call <alloc::raw_vec::RawVecInner>::finish_grow
+ 2 6 0.50 * cmp dword ptr [rsp], 1
+ 1 1 1.00 je .LBB6_3
+ 1 5 0.50 * mov rax, qword ptr [rsp + 8]
+ 1 1 1.00 * mov qword ptr [rbx + 8], rax
+ 1 1 1.00 * mov qword ptr [rbx], r14
+ 1 1 0.33 mov rdx, rbp
+ 1 1 0.33 mov rsi, r15
+ 1 1 1.00 jmp .LBB6_5
+ 1 1 0.50 lea rdi, [rip + .Lanon.HASH.1]
+ 1 1 0.50 lea rdx, [rip + .Lanon.HASH.3]
+ 1 1 0.33 mov esi, 37
+ 4 7 1.00 * call qword ptr [rip + core::panicking::panic@GOTPCREL]
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 17.02 16.50 13.00 19.48 14.00 14.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - 0.98 0.02 push rbp
+ - - - - 1.00 - 0.02 0.98 push r15
+ - - - - 1.00 - 0.99 0.01 push r14
+ - - - - 1.00 - 0.01 0.99 push r13
+ - - - - 1.00 - 0.99 0.01 push r12
+ - - - - 1.00 - 0.01 0.99 push rbx
+ - - 0.49 0.51 - - - - sub rsp, 24
+ - - - - - - 0.04 0.96 mov r12, qword ptr [rdi + 16]
+ - - 0.49 0.50 - 0.01 - - mov r13, r12
+ - - 0.48 0.51 - 0.01 - - sub r13, rsi
+ - - - - - 1.00 - - jb .LBB6_10
+ - - 0.49 0.49 - 0.02 - - mov rbx, rdi
+ - - - - - - 0.97 0.03 mov rax, qword ptr [rdi]
+ - - 0.51 0.49 - - - - mov rcx, rax
+ - - 0.49 0.02 - 0.49 - - sub rcx, r12
+ - - 0.49 0.50 - 0.01 - - cmp rdx, rcx
+ - - - - - 1.00 - - jbe .LBB6_4
+ - - 0.02 0.49 - 0.49 - - add r12, rdx
+ - - - - - 1.00 - - jae .LBB6_7
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - jmp .LBB6_6
+ - - - - - - 0.97 0.03 mov rax, qword ptr [rbx + 8]
+ - - 0.51 0.49 - - - - add r12, rdx
+ - - 0.49 0.51 - - - - lea rcx, [rsi + 2*rsi]
+ - - 0.50 0.50 - - - - lea r14, [rax + 2*rcx]
+ - - 0.51 0.49 - - - - add rdx, rdx
+ - - 0.50 0.50 - - - - lea r15, [rdx + 2*rdx]
+ - - 0.49 0.51 - - - - lea rdi, [r14 + r15]
+ - - 0.50 0.49 - 0.01 - - add r13, r13
+ - - 0.51 0.49 - - - - lea rdx, [2*r13]
+ - - 0.01 0.01 - 0.98 - - add rdx, r13
+ - - 0.01 - - 0.99 - - mov rsi, r14
+ - - - - 1.00 1.00 1.98 0.02 call qword ptr [rip + memmove@GOTPCREL]
+ - - 0.49 0.50 - 0.01 - - mov rdi, r14
+ - - - - - - - - xor esi, esi
+ - - 0.50 0.50 - - - - mov rdx, r15
+ - - - - 1.00 1.00 1.96 0.04 call qword ptr [rip + memset@GOTPCREL]
+ - - - - 1.00 - 0.01 0.99 mov qword ptr [rbx + 16], r12
+ - - 0.50 - - 0.50 - - mov al, 1
+ - - 0.51 0.49 - - - - add rsp, 24
+ - - - - - - 0.02 0.98 pop rbx
+ - - - - - - 0.03 0.97 pop r12
+ - - - - - - 0.03 0.97 pop r13
+ - - - - - - 0.97 0.03 pop r14
+ - - - - - - 0.03 0.97 pop r15
+ - - - - - - 0.01 0.99 pop rbp
+ - - - - - 1.00 - - ret
+ - - 0.49 0.51 - - - - mov r15, rsi
+ - - 0.51 0.49 - - - - mov rbp, rdx
+ - - 0.49 0.51 - - - - lea rcx, [rax + rax]
+ - - 0.49 0.50 - 0.01 - - cmp r12, rcx
+ - - 1.04 0.50 - 1.46 - - cmova rcx, r12
+ - - 0.49 0.49 - 0.02 - - cmp rcx, 5
+ - - 0.50 - - 0.50 - - mov r14d, 4
+ - - 0.50 0.51 - 0.99 - - cmovae r14, rcx
+ - - - - - - 0.97 0.03 mov rdx, qword ptr [rbx + 8]
+ - - - 0.51 - 0.49 - - mov rdi, rsp
+ - - 0.01 0.50 - 0.49 - - mov rsi, rax
+ - - 0.49 0.50 - 0.01 - - mov rcx, r14
+ - - - - 1.00 1.00 0.99 0.01 call <alloc::raw_vec::RawVecInner>::finish_grow
+ - - 0.51 0.49 - - 0.50 0.50 cmp dword ptr [rsp], 1
+ - - - - - 1.00 - - je .LBB6_3
+ - - - - - - 0.50 0.50 mov rax, qword ptr [rsp + 8]
+ - - - - 1.00 - 0.99 0.01 mov qword ptr [rbx + 8], rax
+ - - - - 1.00 - 0.01 0.99 mov qword ptr [rbx], r14
+ - - 0.49 0.50 - 0.01 - - mov rdx, rbp
+ - - 0.50 0.01 - 0.49 - - mov rsi, r15
+ - - - - - 1.00 - - jmp .LBB6_5
+ - - 0.01 0.99 - - - - lea rdi, [rip + .Lanon.HASH.1]
+ - - 0.99 0.01 - - - - lea rdx, [rip + .Lanon.HASH.3]
+ - - 0.02 0.49 - 0.49 - - mov esi, 37
+ - - - - 1.00 1.00 0.02 1.98 call qword ptr [rip + core::panicking::panic@GOTPCREL]
diff --git a/rust/zerocopy/benches/new_box_zeroed.rs b/rust/zerocopy/benches/new_box_zeroed.rs
new file mode 100644
index 000000000000..aa9a66cce353
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_new_box_zeroed() -> Option<Box<format::LocoPacket>> {
+ FromZeros::new_box_zeroed().ok()
+}
diff --git a/rust/zerocopy/benches/new_box_zeroed.x86-64 b/rust/zerocopy/benches/new_box_zeroed.x86-64
new file mode 100644
index 000000000000..ef74ea5388ac
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed.x86-64
@@ -0,0 +1,7 @@
+bench_new_box_zeroed:
+ push rax
+ call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ mov edi, 6
+ mov esi, 2
+ pop rax
+ jmp qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
diff --git a/rust/zerocopy/benches/new_box_zeroed.x86-64.mca b/rust/zerocopy/benches/new_box_zeroed.x86-64.mca
new file mode 100644
index 000000000000..05afa7feb0b8
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed.x86-64.mca
@@ -0,0 +1,51 @@
+Iterations: 100
+Instructions: 600
+Total Cycles: 1197
+Total uOps: 1100
+
+Dispatch Width: 4
+uOps Per Cycle: 0.92
+IPC: 0.50
+Block RThroughput: 2.8
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push rax
+ 4 7 1.00 * call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ 1 1 0.33 mov edi, 6
+ 1 1 0.33 mov esi, 2
+ 1 6 0.50 * pop rax
+ 2 6 1.00 * jmp qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.99 1.00 2.00 2.01 2.07 2.93
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - 0.93 0.07 push rax
+ - - - - 1.00 1.00 0.12 1.88 call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ - - 0.99 - - 0.01 - - mov edi, 6
+ - - - 1.00 - - - - mov esi, 2
+ - - - - - - 0.94 0.06 pop rax
+ - - - - - 1.00 0.08 0.92 jmp qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
diff --git a/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.rs b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.rs
new file mode 100644
index 000000000000..0afde999bff8
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.rs
@@ -0,0 +1,11 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_new_box_zeroed_with_elems_dynamic_padding(
+ count: usize,
+) -> Option<Box<format::LocoPacket>> {
+ FromZeros::new_box_zeroed_with_elems(count).ok()
+}
diff --git a/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64 b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64
new file mode 100644
index 000000000000..22a8d048ce0f
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64
@@ -0,0 +1,24 @@
+bench_new_box_zeroed_with_elems_dynamic_padding:
+ push r14
+ push rbx
+ push rax
+ mov rbx, rdi
+ movabs rax, 3074457345618258598
+ cmp rdi, rax
+ ja .LBB5_1
+ lea r14, [rbx + 2*rbx]
+ or r14, 3
+ add r14, 9
+ call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ mov esi, 4
+ mov rdi, r14
+ call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ jmp .LBB5_3
+.LBB5_1:
+ xor eax, eax
+.LBB5_3:
+ mov rdx, rbx
+ add rsp, 8
+ pop rbx
+ pop r14
+ ret
diff --git a/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..e6efaeded476
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_padding.x86-64.mca
@@ -0,0 +1,81 @@
+Iterations: 100
+Instructions: 2100
+Total Cycles: 2990
+Total uOps: 3000
+
+Dispatch Width: 4
+uOps Per Cycle: 1.00
+IPC: 0.70
+Block RThroughput: 7.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push rbx
+ 2 5 1.00 * push rax
+ 1 1 0.33 mov rbx, rdi
+ 1 1 0.33 movabs rax, 3074457345618258598
+ 1 1 0.33 cmp rdi, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.50 lea r14, [rbx + 2*rbx]
+ 1 1 0.33 or r14, 3
+ 1 1 0.33 add r14, 9
+ 4 7 1.00 * call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ 1 1 0.33 mov esi, 4
+ 1 1 0.33 mov rdi, r14
+ 4 7 1.00 * call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ 1 1 1.00 jmp .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov rdx, rbx
+ 1 1 0.33 add rsp, 8
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r14
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.49 4.50 5.00 6.01 4.50 4.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - 0.50 0.50 push r14
+ - - - - 1.00 - 0.50 0.50 push rbx
+ - - - - 1.00 - 0.50 0.50 push rax
+ - - 0.49 0.50 - 0.01 - - mov rbx, rdi
+ - - 0.50 0.50 - - - - movabs rax, 3074457345618258598
+ - - 0.50 0.50 - - - - cmp rdi, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - 0.50 0.50 - - - - lea r14, [rbx + 2*rbx]
+ - - 0.50 0.50 - - - - or r14, 3
+ - - 0.50 - - 0.50 - - add r14, 9
+ - - - - 1.00 1.00 1.00 1.00 call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ - - - 0.50 - 0.50 - - mov esi, 4
+ - - 0.50 0.50 - - - - mov rdi, r14
+ - - - - 1.00 1.00 1.00 1.00 call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ - - - - - 1.00 - - jmp .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - 0.51 0.49 - - - - mov rdx, rbx
+ - - 0.49 0.51 - - - - add rsp, 8
+ - - - - - - 0.50 0.50 pop rbx
+ - - - - - - 0.50 0.50 pop r14
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.rs b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.rs
new file mode 100644
index 000000000000..1b12ca220692
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_new_box_zeroed_with_elems_dynamic_size(count: usize) -> Option<Box<format::LocoPacket>> {
+ FromZeros::new_box_zeroed_with_elems(count).ok()
+}
diff --git a/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64 b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64
new file mode 100644
index 000000000000..bff15e55ad9f
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64
@@ -0,0 +1,22 @@
+bench_new_box_zeroed_with_elems_dynamic_size:
+ push r14
+ push rbx
+ push rax
+ mov rbx, rdi
+ movabs rax, 4611686018427387901
+ cmp rdi, rax
+ ja .LBB5_1
+ lea r14, [2*rbx + 4]
+ call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ mov esi, 2
+ mov rdi, r14
+ call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ jmp .LBB5_3
+.LBB5_1:
+ xor eax, eax
+.LBB5_3:
+ mov rdx, rbx
+ add rsp, 8
+ pop rbx
+ pop r14
+ ret
diff --git a/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64.mca b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..153d36c01ce0
--- /dev/null
+++ b/rust/zerocopy/benches/new_box_zeroed_with_elems_dynamic_size.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 2990
+Total uOps: 2800
+
+Dispatch Width: 4
+uOps Per Cycle: 0.94
+IPC: 0.64
+Block RThroughput: 7.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push rbx
+ 2 5 1.00 * push rax
+ 1 1 0.33 mov rbx, rdi
+ 1 1 0.33 movabs rax, 4611686018427387901
+ 1 1 0.33 cmp rdi, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.50 lea r14, [2*rbx + 4]
+ 4 7 1.00 * call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ 1 1 0.33 mov esi, 2
+ 1 1 0.33 mov rdi, r14
+ 4 7 1.00 * call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ 1 1 1.00 jmp .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov rdx, rbx
+ 1 1 0.33 add rsp, 8
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r14
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.97 3.98 5.00 5.05 4.50 4.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - 0.50 0.50 push r14
+ - - - - 1.00 - 0.50 0.50 push rbx
+ - - - - 1.00 - 0.50 0.50 push rax
+ - - 0.05 0.94 - 0.01 - - mov rbx, rdi
+ - - 0.94 0.06 - - - - movabs rax, 4611686018427387901
+ - - 0.06 0.94 - - - - cmp rdi, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - 0.94 0.06 - - - - lea r14, [2*rbx + 4]
+ - - - - 1.00 1.00 1.00 1.00 call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ - - 0.98 0.02 - - - - mov esi, 2
+ - - 0.02 0.94 - 0.04 - - mov rdi, r14
+ - - - - 1.00 1.00 1.00 1.00 call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ - - - - - 1.00 - - jmp .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - 0.94 0.06 - - - - mov rdx, rbx
+ - - 0.04 0.96 - - - - add rsp, 8
+ - - - - - - 0.50 0.50 pop rbx
+ - - - - - - 0.50 0.50 pop r14
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/new_vec_zeroed.rs b/rust/zerocopy/benches/new_vec_zeroed.rs
new file mode 100644
index 000000000000..3d95b2b24d8d
--- /dev/null
+++ b/rust/zerocopy/benches/new_vec_zeroed.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_new_vec_zeroed(len: usize) -> Option<Vec<format::LocoPacket>> {
+ FromZeros::new_vec_zeroed(len).ok()
+}
diff --git a/rust/zerocopy/benches/new_vec_zeroed.x86-64 b/rust/zerocopy/benches/new_vec_zeroed.x86-64
new file mode 100644
index 000000000000..b5c083aa0d36
--- /dev/null
+++ b/rust/zerocopy/benches/new_vec_zeroed.x86-64
@@ -0,0 +1,40 @@
+bench_new_vec_zeroed:
+ mov rax, rdi
+ movabs rcx, 1537228672809129301
+ cmp rsi, rcx
+ ja .LBB5_5
+ test rsi, rsi
+ je .LBB5_2
+ push r15
+ push r14
+ push rbx
+ lea rcx, [rsi + rsi]
+ lea rbx, [rcx + 2*rcx]
+ mov r14, rax
+ mov r15, rsi
+ call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ mov esi, 2
+ mov rdi, rbx
+ call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ mov rsi, r15
+ mov rcx, rax
+ mov rax, r14
+ test rcx, rcx
+ pop rbx
+ pop r14
+ pop r15
+ je .LBB5_5
+ mov qword ptr [rax], rsi
+ mov qword ptr [rax + 8], rcx
+ mov qword ptr [rax + 16], rsi
+ ret
+.LBB5_5:
+ movabs rcx, -9223372036854775808
+ mov qword ptr [rax], rcx
+ ret
+.LBB5_2:
+ mov ecx, 2
+ mov qword ptr [rax], rsi
+ mov qword ptr [rax + 8], rcx
+ mov qword ptr [rax + 16], rsi
+ ret
diff --git a/rust/zerocopy/benches/new_vec_zeroed.x86-64.mca b/rust/zerocopy/benches/new_vec_zeroed.x86-64.mca
new file mode 100644
index 000000000000..b4fb4544ec39
--- /dev/null
+++ b/rust/zerocopy/benches/new_vec_zeroed.x86-64.mca
@@ -0,0 +1,113 @@
+Iterations: 100
+Instructions: 3700
+Total Cycles: 3486
+Total uOps: 4600
+
+Dispatch Width: 4
+uOps Per Cycle: 1.32
+IPC: 1.06
+Block RThroughput: 12.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 movabs rcx, 1537228672809129301
+ 1 1 0.33 cmp rsi, rcx
+ 1 1 1.00 ja .LBB5_5
+ 1 1 0.33 test rsi, rsi
+ 1 1 1.00 je .LBB5_2
+ 2 5 1.00 * push r15
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push rbx
+ 1 1 0.50 lea rcx, [rsi + rsi]
+ 1 1 0.50 lea rbx, [rcx + 2*rcx]
+ 1 1 0.33 mov r14, rax
+ 1 1 0.33 mov r15, rsi
+ 4 7 1.00 * call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ 1 1 0.33 mov esi, 2
+ 1 1 0.33 mov rdi, rbx
+ 4 7 1.00 * call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ 1 1 0.33 mov rsi, r15
+ 1 1 0.33 mov rcx, rax
+ 1 1 0.33 mov rax, r14
+ 1 1 0.33 test rcx, rcx
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r14
+ 1 6 0.50 * pop r15
+ 1 1 1.00 je .LBB5_5
+ 1 1 1.00 * mov qword ptr [rax], rsi
+ 1 1 1.00 * mov qword ptr [rax + 8], rcx
+ 1 1 1.00 * mov qword ptr [rax + 16], rsi
+ 1 1 1.00 U ret
+ 1 1 0.33 movabs rcx, -9223372036854775808
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 U ret
+ 1 1 0.33 mov ecx, 2
+ 1 1 1.00 * mov qword ptr [rax], rsi
+ 1 1 1.00 * mov qword ptr [rax + 8], rcx
+ 1 1 1.00 * mov qword ptr [rax + 16], rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.99 6.99 12.00 10.02 8.00 9.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.01 0.98 - 0.01 - - mov rax, rdi
+ - - 0.98 0.02 - - - - movabs rcx, 1537228672809129301
+ - - 0.02 0.98 - - - - cmp rsi, rcx
+ - - - - - 1.00 - - ja .LBB5_5
+ - - 0.98 - - 0.02 - - test rsi, rsi
+ - - - - - 1.00 - - je .LBB5_2
+ - - - - 1.00 - - 1.00 push r15
+ - - - - 1.00 - 1.00 - push r14
+ - - - - 1.00 - - 1.00 push rbx
+ - - - 1.00 - - - - lea rcx, [rsi + rsi]
+ - - - 1.00 - - - - lea rbx, [rcx + 2*rcx]
+ - - 1.00 - - - - - mov r14, rax
+ - - 1.00 - - - - - mov r15, rsi
+ - - - - 1.00 1.00 2.00 - call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
+ - - - 0.01 - 0.99 - - mov esi, 2
+ - - 0.01 0.99 - - - - mov rdi, rbx
+ - - - - 1.00 1.00 - 2.00 call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
+ - - 0.01 - - 0.99 - - mov rsi, r15
+ - - 0.99 0.01 - - - - mov rcx, rax
+ - - - 0.99 - 0.01 - - mov rax, r14
+ - - 0.99 0.01 - - - - test rcx, rcx
+ - - - - - - - 1.00 pop rbx
+ - - - - - - 1.00 - pop r14
+ - - - - - - - 1.00 pop r15
+ - - - - - 1.00 - - je .LBB5_5
+ - - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
+ - - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rcx
+ - - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rsi
+ - - - - - 1.00 - - ret
+ - - 0.01 0.99 - - - - movabs rcx, -9223372036854775808
+ - - - - 1.00 - - 1.00 mov qword ptr [rax], rcx
+ - - - - - 1.00 - - ret
+ - - 0.99 0.01 - - - - mov ecx, 2
+ - - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
+ - - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rcx
+ - - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/new_zeroed.rs b/rust/zerocopy/benches/new_zeroed.rs
new file mode 100644
index 000000000000..b49f62edb146
--- /dev/null
+++ b/rust/zerocopy/benches/new_zeroed.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_new_zeroed() -> format::LocoPacket {
+ FromZeros::new_zeroed()
+}
diff --git a/rust/zerocopy/benches/new_zeroed.x86-64 b/rust/zerocopy/benches/new_zeroed.x86-64
new file mode 100644
index 000000000000..b4d305e41fff
--- /dev/null
+++ b/rust/zerocopy/benches/new_zeroed.x86-64
@@ -0,0 +1,3 @@
+bench_new_zeroed:
+ xor eax, eax
+ ret
diff --git a/rust/zerocopy/benches/new_zeroed.x86-64.mca b/rust/zerocopy/benches/new_zeroed.x86-64.mca
new file mode 100644
index 000000000000..44583ca3089f
--- /dev/null
+++ b/rust/zerocopy/benches/new_zeroed.x86-64.mca
@@ -0,0 +1,43 @@
+Iterations: 100
+Instructions: 200
+Total Cycles: 103
+Total uOps: 200
+
+Dispatch Width: 4
+uOps Per Cycle: 1.94
+IPC: 1.94
+Block RThroughput: 1.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - - - - 1.00 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/read_from_bytes.rs b/rust/zerocopy/benches/read_from_bytes.rs
new file mode 100644
index 000000000000..8a3baddad9cb
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_bytes.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_read_from_bytes_static_size(source: &[u8]) -> Option<format::LocoPacket> {
+ zerocopy::FromBytes::read_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/read_from_bytes.x86-64 b/rust/zerocopy/benches/read_from_bytes.x86-64
new file mode 100644
index 000000000000..9082d79f1fd5
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_bytes.x86-64
@@ -0,0 +1,15 @@
+bench_read_from_bytes_static_size:
+ mov rcx, rsi
+ cmp rsi, 6
+ jne .LBB5_2
+ mov eax, dword ptr [rdi]
+ movzx ecx, word ptr [rdi + 4]
+ shl rcx, 32
+ or rcx, rax
+.LBB5_2:
+ shl rcx, 16
+ inc rcx
+ xor eax, eax
+ cmp rsi, 6
+ cmove rax, rcx
+ ret
diff --git a/rust/zerocopy/benches/read_from_bytes.x86-64.mca b/rust/zerocopy/benches/read_from_bytes.x86-64.mca
new file mode 100644
index 000000000000..77e787c19032
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_bytes.x86-64.mca
@@ -0,0 +1,65 @@
+Iterations: 100
+Instructions: 1300
+Total Cycles: 377
+Total uOps: 1400
+
+Dispatch Width: 4
+uOps Per Cycle: 3.71
+IPC: 3.45
+Block RThroughput: 3.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rcx, rsi
+ 1 1 0.33 cmp rsi, 6
+ 1 1 1.00 jne .LBB5_2
+ 1 5 0.50 * mov eax, dword ptr [rdi]
+ 1 5 0.50 * movzx ecx, word ptr [rdi + 4]
+ 1 1 0.50 shl rcx, 32
+ 1 1 0.33 or rcx, rax
+ 1 1 0.50 shl rcx, 16
+ 1 1 0.33 inc rcx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp rsi, 6
+ 2 2 0.67 cmove rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.66 3.67 - 3.67 1.00 1.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.63 0.36 - 0.01 - - mov rcx, rsi
+ - - 0.05 0.05 - 0.90 - - cmp rsi, 6
+ - - - - - 1.00 - - jne .LBB5_2
+ - - - - - - - 1.00 mov eax, dword ptr [rdi]
+ - - - - - - 1.00 - movzx ecx, word ptr [rdi + 4]
+ - - 0.97 - - 0.03 - - shl rcx, 32
+ - - 0.02 0.35 - 0.63 - - or rcx, rax
+ - - 0.98 - - 0.02 - - shl rcx, 16
+ - - - 0.98 - 0.02 - - inc rcx
+ - - - - - - - - xor eax, eax
+ - - 0.03 0.93 - 0.04 - - cmp rsi, 6
+ - - 0.98 1.00 - 0.02 - - cmove rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/read_from_prefix.rs b/rust/zerocopy/benches/read_from_prefix.rs
new file mode 100644
index 000000000000..d49bf80ab785
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_prefix.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_read_from_prefix_static_size(source: &[u8]) -> Option<format::LocoPacket> {
+ match zerocopy::FromBytes::read_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/read_from_prefix.x86-64 b/rust/zerocopy/benches/read_from_prefix.x86-64
new file mode 100644
index 000000000000..c75b06c0c22a
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_prefix.x86-64
@@ -0,0 +1,14 @@
+bench_read_from_prefix_static_size:
+ cmp rsi, 5
+ jbe .LBB5_2
+ mov eax, dword ptr [rdi]
+ movzx edi, word ptr [rdi + 4]
+ shl rdi, 32
+ or rdi, rax
+.LBB5_2:
+ shl rdi, 16
+ inc rdi
+ xor eax, eax
+ cmp rsi, 6
+ cmovae rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/read_from_prefix.x86-64.mca b/rust/zerocopy/benches/read_from_prefix.x86-64.mca
new file mode 100644
index 000000000000..04e76cdd07ee
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_prefix.x86-64.mca
@@ -0,0 +1,63 @@
+Iterations: 100
+Instructions: 1200
+Total Cycles: 905
+Total uOps: 1300
+
+Dispatch Width: 4
+uOps Per Cycle: 1.44
+IPC: 1.33
+Block RThroughput: 3.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 cmp rsi, 5
+ 1 1 1.00 jbe .LBB5_2
+ 1 5 0.50 * mov eax, dword ptr [rdi]
+ 1 5 0.50 * movzx edi, word ptr [rdi + 4]
+ 1 1 0.50 shl rdi, 32
+ 1 1 0.33 or rdi, rax
+ 1 1 0.50 shl rdi, 16
+ 1 1 0.33 inc rdi
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp rsi, 6
+ 2 2 0.67 cmovae rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.32 3.32 - 3.36 1.00 1.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.05 0.94 - 0.01 - - cmp rsi, 5
+ - - - - - 1.00 - - jbe .LBB5_2
+ - - - - - - - 1.00 mov eax, dword ptr [rdi]
+ - - - - - - 1.00 - movzx edi, word ptr [rdi + 4]
+ - - 0.71 - - 0.29 - - shl rdi, 32
+ - - - 0.64 - 0.36 - - or rdi, rax
+ - - 1.00 - - - - - shl rdi, 16
+ - - 0.31 0.40 - 0.29 - - inc rdi
+ - - - - - - - - xor eax, eax
+ - - 0.34 0.35 - 0.31 - - cmp rsi, 6
+ - - 0.91 0.99 - 0.10 - - cmovae rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/read_from_suffix.rs b/rust/zerocopy/benches/read_from_suffix.rs
new file mode 100644
index 000000000000..4eaadb0d4dff
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_suffix.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_read_from_suffix_static_size(source: &[u8]) -> Option<format::LocoPacket> {
+ match zerocopy::FromBytes::read_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/read_from_suffix.x86-64 b/rust/zerocopy/benches/read_from_suffix.x86-64
new file mode 100644
index 000000000000..5cff2a0e2f36
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_suffix.x86-64
@@ -0,0 +1,15 @@
+bench_read_from_suffix_static_size:
+ mov rcx, rsi
+ cmp rsi, 6
+ jb .LBB5_2
+ mov eax, dword ptr [rdi + rsi - 6]
+ movzx ecx, word ptr [rdi + rsi - 2]
+ shl rcx, 32
+ or rcx, rax
+.LBB5_2:
+ shl rcx, 16
+ inc rcx
+ xor eax, eax
+ cmp rsi, 6
+ cmovae rax, rcx
+ ret
diff --git a/rust/zerocopy/benches/read_from_suffix.x86-64.mca b/rust/zerocopy/benches/read_from_suffix.x86-64.mca
new file mode 100644
index 000000000000..0107de89562a
--- /dev/null
+++ b/rust/zerocopy/benches/read_from_suffix.x86-64.mca
@@ -0,0 +1,65 @@
+Iterations: 100
+Instructions: 1300
+Total Cycles: 377
+Total uOps: 1400
+
+Dispatch Width: 4
+uOps Per Cycle: 3.71
+IPC: 3.45
+Block RThroughput: 3.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rcx, rsi
+ 1 1 0.33 cmp rsi, 6
+ 1 1 1.00 jb .LBB5_2
+ 1 5 0.50 * mov eax, dword ptr [rdi + rsi - 6]
+ 1 5 0.50 * movzx ecx, word ptr [rdi + rsi - 2]
+ 1 1 0.50 shl rcx, 32
+ 1 1 0.33 or rcx, rax
+ 1 1 0.50 shl rcx, 16
+ 1 1 0.33 inc rcx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp rsi, 6
+ 2 2 0.67 cmovae rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.66 3.67 - 3.67 1.00 1.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.63 0.36 - 0.01 - - mov rcx, rsi
+ - - 0.05 0.05 - 0.90 - - cmp rsi, 6
+ - - - - - 1.00 - - jb .LBB5_2
+ - - - - - - - 1.00 mov eax, dword ptr [rdi + rsi - 6]
+ - - - - - - 1.00 - movzx ecx, word ptr [rdi + rsi - 2]
+ - - 0.97 - - 0.03 - - shl rcx, 32
+ - - 0.02 0.35 - 0.63 - - or rcx, rax
+ - - 0.98 - - 0.02 - - shl rcx, 16
+ - - - 0.98 - 0.02 - - inc rcx
+ - - - - - - - - xor eax, eax
+ - - 0.03 0.93 - 0.04 - - cmp rsi, 6
+ - - 0.98 1.00 - 0.02 - - cmovae rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.rs b/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.rs
new file mode 100644
index 000000000000..29708df55b45
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_bytes_dynamic_padding(source: &[u8]) -> Option<&format::LocoPacket> {
+ zerocopy::FromBytes::ref_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64 b/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64
new file mode 100644
index 000000000000..e844a4608fac
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64
@@ -0,0 +1,22 @@
+bench_ref_from_bytes_dynamic_padding:
+ test dil, 3
+ jne .LBB5_3
+ movabs rax, 9223372036854775804
+ and rax, rsi
+ cmp rax, 9
+ jb .LBB5_3
+ add rax, -9
+ movabs rcx, -6148914691236517205
+ mul rcx
+ shr rdx
+ lea rax, [rdx + 2*rdx]
+ or rax, 3
+ add rax, 9
+ cmp rsi, rax
+ je .LBB5_4
+.LBB5_3:
+ xor edi, edi
+ mov rdx, rsi
+.LBB5_4:
+ mov rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..423ed38ba28d
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_dynamic_padding.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 645
+Total uOps: 2000
+
+Dispatch Width: 4
+uOps Per Cycle: 3.10
+IPC: 2.95
+Block RThroughput: 5.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 test dil, 3
+ 1 1 1.00 jne .LBB5_3
+ 1 1 0.33 movabs rax, 9223372036854775804
+ 1 1 0.33 and rax, rsi
+ 1 1 0.33 cmp rax, 9
+ 1 1 1.00 jb .LBB5_3
+ 1 1 0.33 add rax, -9
+ 1 1 0.33 movabs rcx, -6148914691236517205
+ 2 4 1.00 mul rcx
+ 1 1 0.50 shr rdx
+ 1 1 0.50 lea rax, [rdx + 2*rdx]
+ 1 1 0.33 or rax, 3
+ 1 1 0.33 add rax, 9
+ 1 1 0.33 cmp rsi, rax
+ 1 1 1.00 je .LBB5_4
+ 1 0 0.25 xor edi, edi
+ 1 1 0.33 mov rdx, rsi
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.32 6.33 - 6.35 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.64 0.35 - 0.01 - - test dil, 3
+ - - - - - 1.00 - - jne .LBB5_3
+ - - 0.34 0.65 - 0.01 - - movabs rax, 9223372036854775804
+ - - 0.35 0.65 - - - - and rax, rsi
+ - - 0.33 0.34 - 0.33 - - cmp rax, 9
+ - - - - - 1.00 - - jb .LBB5_3
+ - - 0.35 - - 0.65 - - add rax, -9
+ - - 0.97 0.01 - 0.02 - - movabs rcx, -6148914691236517205
+ - - 1.00 1.00 - - - - mul rcx
+ - - 0.99 - - 0.01 - - shr rdx
+ - - 0.33 0.67 - - - - lea rax, [rdx + 2*rdx]
+ - - 0.34 0.66 - - - - or rax, 3
+ - - 0.33 0.66 - 0.01 - - add rax, 9
+ - - 0.01 0.99 - - - - cmp rsi, rax
+ - - - - - 1.00 - - je .LBB5_4
+ - - - - - - - - xor edi, edi
+ - - 0.32 0.01 - 0.67 - - mov rdx, rsi
+ - - 0.02 0.34 - 0.64 - - mov rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_dynamic_size.rs b/rust/zerocopy/benches/ref_from_bytes_dynamic_size.rs
new file mode 100644
index 000000000000..4eb4f970e365
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_dynamic_size.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_bytes_dynamic_size(source: &[u8]) -> Option<&format::LocoPacket> {
+ zerocopy::FromBytes::ref_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64 b/rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64
new file mode 100644
index 000000000000..cc905b76c06f
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64
@@ -0,0 +1,20 @@
+bench_ref_from_bytes_dynamic_size:
+ mov rdx, rsi
+ cmp rsi, 4
+ setb al
+ or al, dil
+ test al, 1
+ je .LBB5_2
+ xor eax, eax
+ ret
+.LBB5_2:
+ lea rcx, [rdx - 4]
+ mov rsi, rcx
+ and rsi, -2
+ add rsi, 4
+ shr rcx
+ xor eax, eax
+ cmp rdx, rsi
+ cmove rdx, rcx
+ cmove rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64.mca b/rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..68aea583e401
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_dynamic_size.x86-64.mca
@@ -0,0 +1,75 @@
+Iterations: 100
+Instructions: 1800
+Total Cycles: 704
+Total uOps: 2000
+
+Dispatch Width: 4
+uOps Per Cycle: 2.84
+IPC: 2.56
+Block RThroughput: 5.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rdx, rsi
+ 1 1 0.33 cmp rsi, 4
+ 1 1 0.50 setb al
+ 1 1 0.33 or al, dil
+ 1 1 0.33 test al, 1
+ 1 1 1.00 je .LBB5_2
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rcx, [rdx - 4]
+ 1 1 0.33 mov rsi, rcx
+ 1 1 0.33 and rsi, -2
+ 1 1 0.33 add rsi, 4
+ 1 1 0.50 shr rcx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp rdx, rsi
+ 2 2 0.67 cmove rdx, rcx
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 5.97 5.98 - 6.05 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.97 0.01 - 0.02 - - mov rdx, rsi
+ - - 0.01 0.02 - 0.97 - - cmp rsi, 4
+ - - 0.03 - - 0.97 - - setb al
+ - - 0.01 0.02 - 0.97 - - or al, dil
+ - - - 0.98 - 0.02 - - test al, 1
+ - - - - - 1.00 - - je .LBB5_2
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.98 0.02 - - - - lea rcx, [rdx - 4]
+ - - 0.01 0.99 - - - - mov rsi, rcx
+ - - - 0.98 - 0.02 - - and rsi, -2
+ - - 0.98 0.01 - 0.01 - - add rsi, 4
+ - - 0.99 - - 0.01 - - shr rcx
+ - - - - - - - - xor eax, eax
+ - - 0.02 0.97 - 0.01 - - cmp rdx, rsi
+ - - 0.99 0.99 - 0.02 - - cmove rdx, rcx
+ - - 0.98 0.99 - 0.03 - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_static_size.rs b/rust/zerocopy/benches/ref_from_bytes_static_size.rs
new file mode 100644
index 000000000000..3742bba0780f
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_static_size.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_bytes_static_size(source: &[u8]) -> Option<&format::LocoPacket> {
+ zerocopy::FromBytes::ref_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/ref_from_bytes_static_size.x86-64 b/rust/zerocopy/benches/ref_from_bytes_static_size.x86-64
new file mode 100644
index 000000000000..2c8da68c8b5b
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_static_size.x86-64
@@ -0,0 +1,8 @@
+bench_ref_from_bytes_static_size:
+ mov ecx, edi
+ and ecx, 1
+ xor rsi, 6
+ xor eax, eax
+ or rsi, rcx
+ cmove rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_static_size.x86-64.mca b/rust/zerocopy/benches/ref_from_bytes_static_size.x86-64.mca
new file mode 100644
index 000000000000..832697801ee2
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_static_size.x86-64.mca
@@ -0,0 +1,53 @@
+Iterations: 100
+Instructions: 700
+Total Cycles: 240
+Total uOps: 800
+
+Dispatch Width: 4
+uOps Per Cycle: 3.33
+IPC: 2.92
+Block RThroughput: 2.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov ecx, edi
+ 1 1 0.33 and ecx, 1
+ 1 1 0.33 xor rsi, 6
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 or rsi, rcx
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 2.33 2.33 - 2.34 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.01 0.98 - 0.01 - - mov ecx, edi
+ - - 0.02 0.66 - 0.32 - - and ecx, 1
+ - - 0.33 0.66 - 0.01 - - xor rsi, 6
+ - - - - - - - - xor eax, eax
+ - - 0.98 0.02 - - - - or rsi, rcx
+ - - 0.99 0.01 - 1.00 - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.rs b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.rs
new file mode 100644
index 000000000000..b4fea4aee51d
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_bytes_with_elems_dynamic_padding(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::LocoPacket> {
+ zerocopy::FromBytes::ref_from_bytes_with_elems(source, count).ok()
+}
diff --git a/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64 b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64
new file mode 100644
index 000000000000..d579b3faefe7
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64
@@ -0,0 +1,19 @@
+bench_ref_from_bytes_with_elems_dynamic_padding:
+ movabs rax, 3074457345618258598
+ cmp rdx, rax
+ seta cl
+ mov rax, rdi
+ test al, 3
+ setne dil
+ or dil, cl
+ jne .LBB5_2
+ lea rcx, [rdx + 2*rdx]
+ or rcx, 3
+ add rcx, 9
+ cmp rsi, rcx
+ je .LBB5_3
+.LBB5_2:
+ xor eax, eax
+ mov rdx, rsi
+.LBB5_3:
+ ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..ea2d83dbd17a
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_padding.x86-64.mca
@@ -0,0 +1,71 @@
+Iterations: 100
+Instructions: 1600
+Total Cycles: 539
+Total uOps: 1700
+
+Dispatch Width: 4
+uOps Per Cycle: 3.15
+IPC: 2.97
+Block RThroughput: 4.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 3074457345618258598
+ 1 1 0.33 cmp rdx, rax
+ 2 2 1.00 seta cl
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 test al, 3
+ 1 1 0.50 setne dil
+ 1 1 0.33 or dil, cl
+ 1 1 1.00 jne .LBB5_2
+ 1 1 0.50 lea rcx, [rdx + 2*rdx]
+ 1 1 0.33 or rcx, 3
+ 1 1 0.33 add rcx, 9
+ 1 1 0.33 cmp rsi, rcx
+ 1 1 1.00 je .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov rdx, rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 5.33 5.32 - 5.35 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.01 0.98 - 0.01 - - movabs rax, 3074457345618258598
+ - - - 1.00 - - - - cmp rdx, rax
+ - - 1.98 - - 0.02 - - seta cl
+ - - 0.02 0.98 - - - - mov rax, rdi
+ - - - 0.67 - 0.33 - - test al, 3
+ - - 0.67 - - 0.33 - - setne dil
+ - - 0.99 - - 0.01 - - or dil, cl
+ - - - - - 1.00 - - jne .LBB5_2
+ - - 0.01 0.99 - - - - lea rcx, [rdx + 2*rdx]
+ - - - 0.01 - 0.99 - - or rcx, 3
+ - - 0.65 0.02 - 0.33 - - add rcx, 9
+ - - 0.99 0.01 - - - - cmp rsi, rcx
+ - - - - - 1.00 - - je .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - 0.01 0.66 - 0.33 - - mov rdx, rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.rs b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.rs
new file mode 100644
index 000000000000..9d33a7c31bc3
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_bytes_with_elems_dynamic_size(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::LocoPacket> {
+ zerocopy::FromBytes::ref_from_bytes_with_elems(source, count).ok()
+}
diff --git a/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64 b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64
new file mode 100644
index 000000000000..3d8d15b7f6c1
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64
@@ -0,0 +1,16 @@
+bench_ref_from_bytes_with_elems_dynamic_size:
+ movabs rax, 4611686018427387901
+ cmp rdx, rax
+ seta cl
+ mov rax, rdi
+ or dil, cl
+ test dil, 1
+ jne .LBB5_2
+ lea rcx, [2*rdx + 4]
+ cmp rsi, rcx
+ je .LBB5_3
+.LBB5_2:
+ xor eax, eax
+ mov rdx, rsi
+.LBB5_3:
+ ret
diff --git a/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64.mca b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..602179f3c903
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_bytes_with_elems_dynamic_size.x86-64.mca
@@ -0,0 +1,65 @@
+Iterations: 100
+Instructions: 1300
+Total Cycles: 439
+Total uOps: 1400
+
+Dispatch Width: 4
+uOps Per Cycle: 3.19
+IPC: 2.96
+Block RThroughput: 3.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 4611686018427387901
+ 1 1 0.33 cmp rdx, rax
+ 2 2 1.00 seta cl
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 or dil, cl
+ 1 1 0.33 test dil, 1
+ 1 1 1.00 jne .LBB5_2
+ 1 1 0.50 lea rcx, [2*rdx + 4]
+ 1 1 0.33 cmp rsi, rcx
+ 1 1 1.00 je .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov rdx, rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.32 4.33 - 4.35 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.99 - 0.01 - - movabs rax, 4611686018427387901
+ - - 0.33 0.67 - - - - cmp rdx, rax
+ - - 1.98 - - 0.02 - - seta cl
+ - - 0.01 0.99 - - - - mov rax, rdi
+ - - 1.00 - - - - - or dil, cl
+ - - 0.99 0.01 - - - - test dil, 1
+ - - - - - 1.00 - - jne .LBB5_2
+ - - - 1.00 - - - - lea rcx, [2*rdx + 4]
+ - - 0.01 - - 0.99 - - cmp rsi, rcx
+ - - - - - 1.00 - - je .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - - 0.67 - 0.33 - - mov rdx, rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.rs b/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.rs
new file mode 100644
index 000000000000..53c707b88256
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_prefix_dynamic_padding(source: &[u8]) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64 b/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64
new file mode 100644
index 000000000000..a58592a24503
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64
@@ -0,0 +1,22 @@
+bench_ref_from_prefix_dynamic_padding:
+ xor edx, edx
+ mov eax, 0
+ test dil, 3
+ je .LBB5_1
+ ret
+.LBB5_1:
+ movabs rax, 9223372036854775804
+ and rsi, rax
+ cmp rsi, 9
+ jae .LBB5_3
+ mov edx, 1
+ xor eax, eax
+ ret
+.LBB5_3:
+ add rsi, -9
+ movabs rcx, -6148914691236517205
+ mov rax, rsi
+ mul rcx
+ shr rdx
+ mov rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..62ea4babaf28
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_dynamic_padding.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 608
+Total uOps: 2000
+
+Dispatch Width: 4
+uOps Per Cycle: 3.29
+IPC: 3.13
+Block RThroughput: 5.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 0 0.25 xor edx, edx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 3
+ 1 1 1.00 je .LBB5_1
+ 1 1 1.00 U ret
+ 1 1 0.33 movabs rax, 9223372036854775804
+ 1 1 0.33 and rsi, rax
+ 1 1 0.33 cmp rsi, 9
+ 1 1 1.00 jae .LBB5_3
+ 1 1 0.33 mov edx, 1
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.33 add rsi, -9
+ 1 1 0.33 movabs rcx, -6148914691236517205
+ 1 1 0.33 mov rax, rsi
+ 2 4 1.00 mul rcx
+ 1 1 0.50 shr rdx
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.00 6.00 - 6.00 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - - - - - xor edx, edx
+ - - 0.01 0.98 - 0.01 - - mov eax, 0
+ - - 0.98 0.01 - 0.01 - - test dil, 3
+ - - - - - 1.00 - - je .LBB5_1
+ - - - - - 1.00 - - ret
+ - - 0.01 0.99 - - - - movabs rax, 9223372036854775804
+ - - - 1.00 - - - - and rsi, rax
+ - - - 1.00 - - - - cmp rsi, 9
+ - - - - - 1.00 - - jae .LBB5_3
+ - - 1.00 - - - - - mov edx, 1
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.02 0.02 - 0.96 - - add rsi, -9
+ - - 0.99 0.01 - - - - movabs rcx, -6148914691236517205
+ - - 0.01 0.99 - - - - mov rax, rsi
+ - - 1.00 1.00 - - - - mul rcx
+ - - 1.00 - - - - - shr rdx
+ - - 0.98 - - 0.02 - - mov rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_dynamic_size.rs b/rust/zerocopy/benches/ref_from_prefix_dynamic_size.rs
new file mode 100644
index 000000000000..a3f26f6b4e6c
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_dynamic_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_prefix_dynamic_size(source: &[u8]) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64 b/rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64
new file mode 100644
index 000000000000..fe6332c9100c
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64
@@ -0,0 +1,17 @@
+bench_ref_from_prefix_dynamic_size:
+ xor edx, edx
+ mov eax, 0
+ test dil, 1
+ jne .LBB5_4
+ cmp rsi, 4
+ jae .LBB5_3
+ mov edx, 1
+ xor eax, eax
+ ret
+.LBB5_3:
+ add rsi, -4
+ shr rsi
+ mov rdx, rsi
+ mov rax, rdi
+.LBB5_4:
+ ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64.mca b/rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..3900a5946138
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_dynamic_size.x86-64.mca
@@ -0,0 +1,67 @@
+Iterations: 100
+Instructions: 1400
+Total Cycles: 405
+Total uOps: 1400
+
+Dispatch Width: 4
+uOps Per Cycle: 3.46
+IPC: 3.46
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 0 0.25 xor edx, edx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 1
+ 1 1 1.00 jne .LBB5_4
+ 1 1 0.33 cmp rsi, 4
+ 1 1 1.00 jae .LBB5_3
+ 1 1 0.33 mov edx, 1
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.33 add rsi, -4
+ 1 1 0.50 shr rsi
+ 1 1 0.33 mov rdx, rsi
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.99 3.99 - 4.02 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - - - - - xor edx, edx
+ - - 0.01 0.98 - 0.01 - - mov eax, 0
+ - - 0.98 0.02 - - - - test dil, 1
+ - - - - - 1.00 - - jne .LBB5_4
+ - - 0.02 0.98 - - - - cmp rsi, 4
+ - - - - - 1.00 - - jae .LBB5_3
+ - - 0.98 0.01 - 0.01 - - mov edx, 1
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.01 0.99 - - - - add rsi, -4
+ - - 1.00 - - - - - shr rsi
+ - - - 1.00 - - - - mov rdx, rsi
+ - - 0.99 0.01 - - - - mov rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_static_size.rs b/rust/zerocopy/benches/ref_from_prefix_static_size.rs
new file mode 100644
index 000000000000..834fa3928611
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_static_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_prefix_static_size(source: &[u8]) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_prefix_static_size.x86-64 b/rust/zerocopy/benches/ref_from_prefix_static_size.x86-64
new file mode 100644
index 000000000000..7c1bf45bb6c2
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_static_size.x86-64
@@ -0,0 +1,8 @@
+bench_ref_from_prefix_static_size:
+ xor eax, eax
+ cmp rsi, 6
+ mov rcx, rdi
+ cmovb rcx, rax
+ test dil, 1
+ cmove rax, rcx
+ ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_static_size.x86-64.mca b/rust/zerocopy/benches/ref_from_prefix_static_size.x86-64.mca
new file mode 100644
index 000000000000..9691b88fe03a
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_static_size.x86-64.mca
@@ -0,0 +1,53 @@
+Iterations: 100
+Instructions: 700
+Total Cycles: 274
+Total uOps: 900
+
+Dispatch Width: 4
+uOps Per Cycle: 3.28
+IPC: 2.55
+Block RThroughput: 2.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp rsi, 6
+ 1 1 0.33 mov rcx, rdi
+ 2 2 0.67 cmovb rcx, rax
+ 1 1 0.33 test dil, 1
+ 2 2 0.67 cmove rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 2.66 2.67 - 2.67 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - - - - - xor eax, eax
+ - - - 0.01 - 0.99 - - cmp rsi, 6
+ - - 0.01 0.67 - 0.32 - - mov rcx, rdi
+ - - 1.00 0.99 - 0.01 - - cmovb rcx, rax
+ - - 0.66 0.01 - 0.33 - - test dil, 1
+ - - 0.99 0.99 - 0.02 - - cmove rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.rs b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.rs
new file mode 100644
index 000000000000..55d495e00c59
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_prefix_with_elems_dynamic_padding(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_prefix_with_elems(source, count) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64 b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64
new file mode 100644
index 000000000000..5b31277bdebe
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64
@@ -0,0 +1,26 @@
+bench_ref_from_prefix_with_elems_dynamic_padding:
+ movabs rax, 3074457345618258598
+ cmp rdx, rax
+ ja .LBB5_1
+ xor ecx, ecx
+ mov eax, 0
+ test dil, 3
+ je .LBB5_3
+ mov rdx, rcx
+ ret
+.LBB5_1:
+ mov edx, 1
+ xor eax, eax
+ ret
+.LBB5_3:
+ lea rax, [rdx + 2*rdx]
+ or rax, 3
+ add rax, 9
+ xor r8d, r8d
+ cmp rax, rsi
+ mov ecx, 1
+ cmovbe rcx, rdx
+ cmova rdi, r8
+ mov rax, rdi
+ mov rdx, rcx
+ ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..2f212ec6d03b
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_padding.x86-64.mca
@@ -0,0 +1,85 @@
+Iterations: 100
+Instructions: 2300
+Total Cycles: 807
+Total uOps: 2700
+
+Dispatch Width: 4
+uOps Per Cycle: 3.35
+IPC: 2.85
+Block RThroughput: 6.8
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 3074457345618258598
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 0 0.25 xor ecx, ecx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 3
+ 1 1 1.00 je .LBB5_3
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+ 1 1 0.33 mov edx, 1
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rax, [rdx + 2*rdx]
+ 1 1 0.33 or rax, 3
+ 1 1 0.33 add rax, 9
+ 1 0 0.25 xor r8d, r8d
+ 1 1 0.33 cmp rax, rsi
+ 1 1 0.33 mov ecx, 1
+ 3 3 1.00 cmovbe rcx, rdx
+ 3 3 1.00 cmova rdi, r8
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 7.99 7.99 - 8.02 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.47 0.52 - 0.01 - - movabs rax, 3074457345618258598
+ - - 0.94 0.01 - 0.05 - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - - - - - - - xor ecx, ecx
+ - - 0.03 0.97 - - - - mov eax, 0
+ - - 0.01 0.52 - 0.47 - - test dil, 3
+ - - - - - 1.00 - - je .LBB5_3
+ - - 0.03 0.51 - 0.46 - - mov rdx, rcx
+ - - - - - 1.00 - - ret
+ - - 0.04 0.96 - - - - mov edx, 1
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.01 0.99 - - - - lea rax, [rdx + 2*rdx]
+ - - 0.52 0.48 - - - - or rax, 3
+ - - 0.51 0.49 - - - - add rax, 9
+ - - - - - - - - xor r8d, r8d
+ - - 0.97 0.03 - - - - cmp rax, rsi
+ - - 0.01 0.99 - - - - mov ecx, 1
+ - - 1.04 0.97 - 0.99 - - cmovbe rcx, rdx
+ - - 1.44 0.54 - 1.02 - - cmova rdi, r8
+ - - 0.97 0.01 - 0.02 - - mov rax, rdi
+ - - 1.00 - - - - - mov rdx, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.rs b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.rs
new file mode 100644
index 000000000000..e9663c721ece
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_prefix_with_elems_dynamic_size(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_prefix_with_elems(source, count) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64 b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64
new file mode 100644
index 000000000000..069fd4859c74
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64
@@ -0,0 +1,22 @@
+bench_ref_from_prefix_with_elems_dynamic_size:
+ movabs rax, 4611686018427387901
+ cmp rdx, rax
+ ja .LBB5_1
+ mov rcx, rdx
+ xor edx, edx
+ mov eax, 0
+ test dil, 1
+ jne .LBB5_4
+ lea rax, [2*rcx + 4]
+ xor r8d, r8d
+ cmp rax, rsi
+ mov edx, 1
+ cmovbe rdx, rcx
+ cmova rdi, r8
+ mov rax, rdi
+.LBB5_4:
+ ret
+.LBB5_1:
+ mov edx, 1
+ xor eax, eax
+ ret
diff --git a/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64.mca b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..6f227264066d
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_prefix_with_elems_dynamic_size.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 672
+Total uOps: 2300
+
+Dispatch Width: 4
+uOps Per Cycle: 3.42
+IPC: 2.83
+Block RThroughput: 5.8
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 4611686018427387901
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.33 mov rcx, rdx
+ 1 0 0.25 xor edx, edx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 1
+ 1 1 1.00 jne .LBB5_4
+ 1 1 0.50 lea rax, [2*rcx + 4]
+ 1 0 0.25 xor r8d, r8d
+ 1 1 0.33 cmp rax, rsi
+ 1 1 0.33 mov edx, 1
+ 3 3 1.00 cmovbe rdx, rcx
+ 3 3 1.00 cmova rdi, r8
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 U ret
+ 1 1 0.33 mov edx, 1
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.66 6.66 - 6.68 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.99 - 0.01 - - movabs rax, 4611686018427387901
+ - - 0.37 0.63 - - - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - 0.63 0.37 - - - - mov rcx, rdx
+ - - - - - - - - xor edx, edx
+ - - 0.01 0.98 - 0.01 - - mov eax, 0
+ - - 0.98 0.02 - - - - test dil, 1
+ - - - - - 1.00 - - jne .LBB5_4
+ - - 0.01 0.99 - - - - lea rax, [2*rcx + 4]
+ - - - - - - - - xor r8d, r8d
+ - - 1.00 - - - - - cmp rax, rsi
+ - - - 0.67 - 0.33 - - mov edx, 1
+ - - 0.73 0.98 - 1.29 - - cmovbe rdx, rcx
+ - - 1.60 0.36 - 1.04 - - cmova rdi, r8
+ - - 0.99 0.01 - - - - mov rax, rdi
+ - - - - - 1.00 - - ret
+ - - 0.34 0.66 - - - - mov edx, 1
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.rs b/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.rs
new file mode 100644
index 000000000000..5a6ea3a33dde
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_suffix_dynamic_padding(source: &[u8]) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64 b/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64
new file mode 100644
index 000000000000..3e05f6023f38
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64
@@ -0,0 +1,23 @@
+bench_ref_from_suffix_dynamic_padding:
+ lea eax, [rsi + rdi]
+ test al, 3
+ jne .LBB5_1
+ movabs rax, 9223372036854775804
+ and rax, rsi
+ cmp rax, 9
+ jae .LBB5_3
+.LBB5_1:
+ xor eax, eax
+ ret
+.LBB5_3:
+ add rax, -9
+ movabs rcx, -6148914691236517205
+ mul rcx
+ shr rdx
+ lea rax, [rdx + 2*rdx]
+ sub rsi, rax
+ or rax, -4
+ add rsi, rdi
+ add rax, rsi
+ add rax, -8
+ ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..73599d5b6aab
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_dynamic_padding.x86-64.mca
@@ -0,0 +1,79 @@
+Iterations: 100
+Instructions: 2000
+Total Cycles: 682
+Total uOps: 2100
+
+Dispatch Width: 4
+uOps Per Cycle: 3.08
+IPC: 2.93
+Block RThroughput: 5.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea eax, [rsi + rdi]
+ 1 1 0.33 test al, 3
+ 1 1 1.00 jne .LBB5_1
+ 1 1 0.33 movabs rax, 9223372036854775804
+ 1 1 0.33 and rax, rsi
+ 1 1 0.33 cmp rax, 9
+ 1 1 1.00 jae .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.33 add rax, -9
+ 1 1 0.33 movabs rcx, -6148914691236517205
+ 2 4 1.00 mul rcx
+ 1 1 0.50 shr rdx
+ 1 1 0.50 lea rax, [rdx + 2*rdx]
+ 1 1 0.33 sub rsi, rax
+ 1 1 0.33 or rax, -4
+ 1 1 0.33 add rsi, rdi
+ 1 1 0.33 add rax, rsi
+ 1 1 0.33 add rax, -8
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.65 6.67 - 6.68 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.90 0.10 - - - - lea eax, [rsi + rdi]
+ - - 0.93 - - 0.07 - - test al, 3
+ - - - - - 1.00 - - jne .LBB5_1
+ - - 0.51 0.47 - 0.02 - - movabs rax, 9223372036854775804
+ - - - - - 1.00 - - and rax, rsi
+ - - - 0.09 - 0.91 - - cmp rax, 9
+ - - - - - 1.00 - - jae .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.43 0.47 - 0.10 - - add rax, -9
+ - - 0.42 0.39 - 0.19 - - movabs rcx, -6148914691236517205
+ - - 1.00 1.00 - - - - mul rcx
+ - - 0.69 - - 0.31 - - shr rdx
+ - - 0.54 0.46 - - - - lea rax, [rdx + 2*rdx]
+ - - 0.07 0.91 - 0.02 - - sub rsi, rax
+ - - 0.91 0.05 - 0.04 - - or rax, -4
+ - - 0.08 0.90 - 0.02 - - add rsi, rdi
+ - - 0.09 0.91 - - - - add rax, rsi
+ - - 0.08 0.92 - - - - add rax, -8
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_dynamic_size.rs b/rust/zerocopy/benches/ref_from_suffix_dynamic_size.rs
new file mode 100644
index 000000000000..3437b14f404a
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_dynamic_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_suffix_dynamic_size(source: &[u8]) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64 b/rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64
new file mode 100644
index 000000000000..bd4ace89836a
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64
@@ -0,0 +1,13 @@
+bench_ref_from_suffix_dynamic_size:
+ mov rdx, rsi
+ lea ecx, [rsi + rdi]
+ mov eax, edx
+ and eax, 1
+ add rax, rdi
+ xor esi, esi
+ sub rdx, 4
+ cmovb rax, rsi
+ shr rdx
+ test cl, 1
+ cmovne rax, rsi
+ ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64.mca b/rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..1398bcfe27ae
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_dynamic_size.x86-64.mca
@@ -0,0 +1,63 @@
+Iterations: 100
+Instructions: 1200
+Total Cycles: 439
+Total uOps: 1400
+
+Dispatch Width: 4
+uOps Per Cycle: 3.19
+IPC: 2.73
+Block RThroughput: 3.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rdx, rsi
+ 1 1 0.50 lea ecx, [rsi + rdi]
+ 1 1 0.33 mov eax, edx
+ 1 1 0.33 and eax, 1
+ 1 1 0.33 add rax, rdi
+ 1 0 0.25 xor esi, esi
+ 1 1 0.33 sub rdx, 4
+ 2 2 0.67 cmovb rax, rsi
+ 1 1 0.50 shr rdx
+ 1 1 0.33 test cl, 1
+ 2 2 0.67 cmovne rax, rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.33 4.33 - 4.34 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.02 0.32 - 0.66 - - mov rdx, rsi
+ - - 0.32 0.68 - - - - lea ecx, [rsi + rdi]
+ - - 0.66 - - 0.34 - - mov eax, edx
+ - - 0.02 0.33 - 0.65 - - and eax, 1
+ - - - 0.99 - 0.01 - - add rax, rdi
+ - - - - - - - - xor esi, esi
+ - - 0.65 - - 0.35 - - sub rdx, 4
+ - - 1.00 1.00 - - - - cmovb rax, rsi
+ - - 0.66 - - 0.34 - - shr rdx
+ - - - 0.01 - 0.99 - - test cl, 1
+ - - 1.00 1.00 - - - - cmovne rax, rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_static_size.rs b/rust/zerocopy/benches/ref_from_suffix_static_size.rs
new file mode 100644
index 000000000000..c8435d10d38c
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_static_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_suffix_static_size(source: &[u8]) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_suffix_static_size.x86-64 b/rust/zerocopy/benches/ref_from_suffix_static_size.x86-64
new file mode 100644
index 000000000000..9e90b9e2543f
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_static_size.x86-64
@@ -0,0 +1,13 @@
+bench_ref_from_suffix_static_size:
+ lea eax, [rsi + rdi]
+ cmp rsi, 6
+ setb cl
+ or cl, al
+ test cl, 1
+ je .LBB5_2
+ xor eax, eax
+ ret
+.LBB5_2:
+ lea rax, [rdi + rsi]
+ add rax, -6
+ ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_static_size.x86-64.mca b/rust/zerocopy/benches/ref_from_suffix_static_size.x86-64.mca
new file mode 100644
index 000000000000..ef5892647b81
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_static_size.x86-64.mca
@@ -0,0 +1,61 @@
+Iterations: 100
+Instructions: 1100
+Total Cycles: 338
+Total uOps: 1100
+
+Dispatch Width: 4
+uOps Per Cycle: 3.25
+IPC: 3.25
+Block RThroughput: 3.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea eax, [rsi + rdi]
+ 1 1 0.33 cmp rsi, 6
+ 1 1 0.50 setb cl
+ 1 1 0.33 or cl, al
+ 1 1 0.33 test cl, 1
+ 1 1 1.00 je .LBB5_2
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rax, [rdi + rsi]
+ 1 1 0.33 add rax, -6
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.32 3.33 - 3.35 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.97 0.03 - - - - lea eax, [rsi + rdi]
+ - - 0.33 0.32 - 0.35 - - cmp rsi, 6
+ - - 1.00 - - - - - setb cl
+ - - - 1.00 - - - - or cl, al
+ - - - 1.00 - - - - test cl, 1
+ - - - - - 1.00 - - je .LBB5_2
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.34 0.66 - - - - lea rax, [rdi + rsi]
+ - - 0.68 0.32 - - - - add rax, -6
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.rs b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.rs
new file mode 100644
index 000000000000..73d91cee5992
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_suffix_with_elems_dynamic_padding(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_suffix_with_elems(source, count) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64 b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64
new file mode 100644
index 000000000000..c3d10b5fc685
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64
@@ -0,0 +1,27 @@
+bench_ref_from_suffix_with_elems_dynamic_padding:
+ movabs rax, 3074457345618258598
+ cmp rdx, rax
+ ja .LBB5_1
+ lea r8d, [rsi + rdi]
+ xor ecx, ecx
+ mov eax, 0
+ test r8b, 3
+ je .LBB5_3
+ mov rdx, rcx
+ ret
+.LBB5_3:
+ lea rax, [rdx + 2*rdx]
+ or rax, 3
+ add rax, 9
+ sub rsi, rax
+ jae .LBB5_4
+.LBB5_1:
+ xor eax, eax
+ mov edx, 1
+ ret
+.LBB5_4:
+ add rdi, rsi
+ mov rcx, rdx
+ mov rax, rdi
+ mov rdx, rcx
+ ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..92e6280bb4cc
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_padding.x86-64.mca
@@ -0,0 +1,85 @@
+Iterations: 100
+Instructions: 2300
+Total Cycles: 706
+Total uOps: 2300
+
+Dispatch Width: 4
+uOps Per Cycle: 3.26
+IPC: 3.26
+Block RThroughput: 6.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 3074457345618258598
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.50 lea r8d, [rsi + rdi]
+ 1 0 0.25 xor ecx, ecx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test r8b, 3
+ 1 1 1.00 je .LBB5_3
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rax, [rdx + 2*rdx]
+ 1 1 0.33 or rax, 3
+ 1 1 0.33 add rax, 9
+ 1 1 0.33 sub rsi, rax
+ 1 1 1.00 jae .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov edx, 1
+ 1 1 1.00 U ret
+ 1 1 0.33 add rdi, rsi
+ 1 1 0.33 mov rcx, rdx
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.99 7.00 - 7.01 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.99 - 0.01 - - movabs rax, 3074457345618258598
+ - - 0.01 0.50 - 0.49 - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - - 1.00 - - - - lea r8d, [rsi + rdi]
+ - - - - - - - - xor ecx, ecx
+ - - 0.50 0.49 - 0.01 - - mov eax, 0
+ - - 0.49 0.51 - - - - test r8b, 3
+ - - - - - 1.00 - - je .LBB5_3
+ - - 0.51 0.49 - - - - mov rdx, rcx
+ - - - - - 1.00 - - ret
+ - - 0.50 0.50 - - - - lea rax, [rdx + 2*rdx]
+ - - 1.00 - - - - - or rax, 3
+ - - 1.00 - - - - - add rax, 9
+ - - 0.99 0.01 - - - - sub rsi, rax
+ - - - - - 1.00 - - jae .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - - 1.00 - - - - mov edx, 1
+ - - - - - 1.00 - - ret
+ - - 1.00 - - - - - add rdi, rsi
+ - - - 1.00 - - - - mov rcx, rdx
+ - - 0.99 0.01 - - - - mov rax, rdi
+ - - - 0.50 - 0.50 - - mov rdx, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.rs b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.rs
new file mode 100644
index 000000000000..68a28baf55e6
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_ref_from_suffix_with_elems_dynamic_size(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::LocoPacket> {
+ match zerocopy::FromBytes::ref_from_suffix_with_elems(source, count) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64 b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64
new file mode 100644
index 000000000000..bdca57192455
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64
@@ -0,0 +1,23 @@
+bench_ref_from_suffix_with_elems_dynamic_size:
+ movabs rax, 4611686018427387901
+ cmp rdx, rax
+ ja .LBB5_1
+ lea r8d, [rsi + rdi]
+ xor ecx, ecx
+ mov eax, 0
+ test r8b, 1
+ jne .LBB5_5
+ lea rax, [2*rdx + 4]
+ sub rsi, rax
+ jae .LBB5_4
+.LBB5_1:
+ xor eax, eax
+ mov edx, 1
+ ret
+.LBB5_4:
+ add rdi, rsi
+ mov rcx, rdx
+ mov rax, rdi
+.LBB5_5:
+ mov rdx, rcx
+ ret
diff --git a/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64.mca b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..6d9de0b3eb5c
--- /dev/null
+++ b/rust/zerocopy/benches/ref_from_suffix_with_elems_dynamic_size.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 571
+Total uOps: 1900
+
+Dispatch Width: 4
+uOps Per Cycle: 3.33
+IPC: 3.33
+Block RThroughput: 5.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 4611686018427387901
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.50 lea r8d, [rsi + rdi]
+ 1 0 0.25 xor ecx, ecx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test r8b, 1
+ 1 1 1.00 jne .LBB5_5
+ 1 1 0.50 lea rax, [2*rdx + 4]
+ 1 1 0.33 sub rsi, rax
+ 1 1 1.00 jae .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov edx, 1
+ 1 1 1.00 U ret
+ 1 1 0.33 add rdi, rsi
+ 1 1 0.33 mov rcx, rdx
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 5.66 5.66 - 5.68 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.66 0.33 - 0.01 - - movabs rax, 4611686018427387901
+ - - 0.01 0.99 - - - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - 0.99 0.01 - - - - lea r8d, [rsi + rdi]
+ - - - - - - - - xor ecx, ecx
+ - - 0.33 0.33 - 0.34 - - mov eax, 0
+ - - 0.33 0.34 - 0.33 - - test r8b, 1
+ - - - - - 1.00 - - jne .LBB5_5
+ - - 0.34 0.66 - - - - lea rax, [2*rdx + 4]
+ - - - 1.00 - - - - sub rsi, rax
+ - - - - - 1.00 - - jae .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - 1.00 - - - - - mov edx, 1
+ - - - - - 1.00 - - ret
+ - - - 1.00 - - - - add rdi, rsi
+ - - 1.00 - - - - - mov rcx, rdx
+ - - 0.32 0.68 - - - - mov rax, rdi
+ - - 0.68 0.32 - - - - mov rdx, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_at_dynamic_padding.rs b/rust/zerocopy/benches/split_at_dynamic_padding.rs
new file mode 100644
index 000000000000..bed90f60165e
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_dynamic_padding.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_split_at_dynamic_padding(
+ source: &format::CocoPacket,
+ len: usize,
+) -> Option<Split<&format::CocoPacket>> {
+ source.split_at(len)
+}
diff --git a/rust/zerocopy/benches/split_at_dynamic_padding.x86-64 b/rust/zerocopy/benches/split_at_dynamic_padding.x86-64
new file mode 100644
index 000000000000..6eaf5a004612
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_dynamic_padding.x86-64
@@ -0,0 +1,12 @@
+bench_split_at_dynamic_padding:
+ mov rax, rdi
+ cmp rcx, rdx
+ jbe .LBB5_2
+ xor esi, esi
+ mov qword ptr [rax], rsi
+ ret
+.LBB5_2:
+ mov qword ptr [rax + 8], rdx
+ mov qword ptr [rax + 16], rcx
+ mov qword ptr [rax], rsi
+ ret
diff --git a/rust/zerocopy/benches/split_at_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/split_at_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..19ab3414d77a
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_dynamic_padding.x86-64.mca
@@ -0,0 +1,59 @@
+Iterations: 100
+Instructions: 1000
+Total Cycles: 404
+Total uOps: 1000
+
+Dispatch Width: 4
+uOps Per Cycle: 2.48
+IPC: 2.48
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 cmp rcx, rdx
+ 1 1 1.00 jbe .LBB5_2
+ 1 0 0.25 xor esi, esi
+ 1 1 1.00 * mov qword ptr [rax], rsi
+ 1 1 1.00 U ret
+ 1 1 1.00 * mov qword ptr [rax + 8], rdx
+ 1 1 1.00 * mov qword ptr [rax + 16], rcx
+ 1 1 1.00 * mov qword ptr [rax], rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.99 1.00 4.00 3.01 2.00 2.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.99 - - 0.01 - - mov rax, rdi
+ - - - 1.00 - - - - cmp rcx, rdx
+ - - - - - 1.00 - - jbe .LBB5_2
+ - - - - - - - - xor esi, esi
+ - - - - 1.00 - - 1.00 mov qword ptr [rax], rsi
+ - - - - - 1.00 - - ret
+ - - - - 1.00 - 1.00 - mov qword ptr [rax + 8], rdx
+ - - - - 1.00 - - 1.00 mov qword ptr [rax + 16], rcx
+ - - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_at_dynamic_size.rs b/rust/zerocopy/benches/split_at_dynamic_size.rs
new file mode 100644
index 000000000000..07a22ba2e5b5
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_dynamic_size.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_split_at_dynamic_size(
+ source: &format::CocoPacket,
+ len: usize,
+) -> Option<Split<&format::CocoPacket>> {
+ source.split_at(len)
+}
diff --git a/rust/zerocopy/benches/split_at_dynamic_size.x86-64 b/rust/zerocopy/benches/split_at_dynamic_size.x86-64
new file mode 100644
index 000000000000..8d81b98bdab1
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_dynamic_size.x86-64
@@ -0,0 +1,12 @@
+bench_split_at_dynamic_size:
+ mov rax, rdi
+ cmp rcx, rdx
+ jbe .LBB5_2
+ xor esi, esi
+ mov qword ptr [rax], rsi
+ ret
+.LBB5_2:
+ mov qword ptr [rax + 8], rdx
+ mov qword ptr [rax + 16], rcx
+ mov qword ptr [rax], rsi
+ ret
diff --git a/rust/zerocopy/benches/split_at_dynamic_size.x86-64.mca b/rust/zerocopy/benches/split_at_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..19ab3414d77a
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_dynamic_size.x86-64.mca
@@ -0,0 +1,59 @@
+Iterations: 100
+Instructions: 1000
+Total Cycles: 404
+Total uOps: 1000
+
+Dispatch Width: 4
+uOps Per Cycle: 2.48
+IPC: 2.48
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 cmp rcx, rdx
+ 1 1 1.00 jbe .LBB5_2
+ 1 0 0.25 xor esi, esi
+ 1 1 1.00 * mov qword ptr [rax], rsi
+ 1 1 1.00 U ret
+ 1 1 1.00 * mov qword ptr [rax + 8], rdx
+ 1 1 1.00 * mov qword ptr [rax + 16], rcx
+ 1 1 1.00 * mov qword ptr [rax], rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.99 1.00 4.00 3.01 2.00 2.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.99 - - 0.01 - - mov rax, rdi
+ - - - 1.00 - - - - cmp rcx, rdx
+ - - - - - 1.00 - - jbe .LBB5_2
+ - - - - - - - - xor esi, esi
+ - - - - 1.00 - - 1.00 mov qword ptr [rax], rsi
+ - - - - - 1.00 - - ret
+ - - - - 1.00 - 1.00 - mov qword ptr [rax + 8], rdx
+ - - - - 1.00 - - 1.00 mov qword ptr [rax + 16], rcx
+ - - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.rs b/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.rs
new file mode 100644
index 000000000000..3c147d3bdd39
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+unsafe fn bench_split_at_unchecked_dynamic_padding(
+ source: &format::CocoPacket,
+ len: usize,
+) -> Split<&format::CocoPacket> {
+ unsafe { source.split_at_unchecked(len) }
+}
diff --git a/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64 b/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64
new file mode 100644
index 000000000000..74c3b52f63fa
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64
@@ -0,0 +1,6 @@
+bench_split_at_unchecked_dynamic_padding:
+ mov rax, rdi
+ mov qword ptr [rdi], rsi
+ mov qword ptr [rdi + 8], rdx
+ mov qword ptr [rdi + 16], rcx
+ ret
diff --git a/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..e8c61591c086
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_unchecked_dynamic_padding.x86-64.mca
@@ -0,0 +1,49 @@
+Iterations: 100
+Instructions: 500
+Total Cycles: 303
+Total uOps: 500
+
+Dispatch Width: 4
+uOps Per Cycle: 1.65
+IPC: 1.65
+Block RThroughput: 3.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 * mov qword ptr [rdi], rsi
+ 1 1 1.00 * mov qword ptr [rdi + 8], rdx
+ 1 1 1.00 * mov qword ptr [rdi + 16], rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.49 0.50 3.00 1.01 1.50 1.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.49 0.50 - 0.01 - - mov rax, rdi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rdi], rsi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rdi + 8], rdx
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rdi + 16], rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_at_unchecked_dynamic_size.rs b/rust/zerocopy/benches/split_at_unchecked_dynamic_size.rs
new file mode 100644
index 000000000000..b1aa1dfb35be
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_unchecked_dynamic_size.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+unsafe fn bench_split_at_unchecked_dynamic_size(
+ source: &format::CocoPacket,
+ len: usize,
+) -> Split<&format::CocoPacket> {
+ unsafe { source.split_at_unchecked(len) }
+}
diff --git a/rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64 b/rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64
new file mode 100644
index 000000000000..56671d1ee8dc
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64
@@ -0,0 +1,6 @@
+bench_split_at_unchecked_dynamic_size:
+ mov rax, rdi
+ mov qword ptr [rdi], rsi
+ mov qword ptr [rdi + 8], rdx
+ mov qword ptr [rdi + 16], rcx
+ ret
diff --git a/rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64.mca b/rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..e8c61591c086
--- /dev/null
+++ b/rust/zerocopy/benches/split_at_unchecked_dynamic_size.x86-64.mca
@@ -0,0 +1,49 @@
+Iterations: 100
+Instructions: 500
+Total Cycles: 303
+Total uOps: 500
+
+Dispatch Width: 4
+uOps Per Cycle: 1.65
+IPC: 1.65
+Block RThroughput: 3.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 * mov qword ptr [rdi], rsi
+ 1 1 1.00 * mov qword ptr [rdi + 8], rdx
+ 1 1 1.00 * mov qword ptr [rdi + 16], rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.49 0.50 3.00 1.01 1.50 1.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.49 0.50 - 0.01 - - mov rax, rdi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rdi], rsi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rdi + 8], rdx
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rdi + 16], rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_via_immutable_dynamic_padding.rs b/rust/zerocopy/benches/split_via_immutable_dynamic_padding.rs
new file mode 100644
index 000000000000..b86ad3614bf7
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_immutable_dynamic_padding.rs
@@ -0,0 +1,11 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_split_via_immutable_dynamic_padding(
+ split: Split<&format::CocoPacket>,
+) -> (&format::CocoPacket, &[[u8; 3]]) {
+ split.via_immutable()
+}
diff --git a/rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64 b/rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64
new file mode 100644
index 000000000000..dac183428b31
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64
@@ -0,0 +1,14 @@
+bench_split_via_immutable_dynamic_padding:
+ mov rax, rdi
+ mov rcx, qword ptr [rsi]
+ mov rdx, qword ptr [rsi + 8]
+ mov rsi, qword ptr [rsi + 16]
+ lea rdi, [rsi + 2*rsi]
+ add rdi, rcx
+ add rdi, 9
+ sub rdx, rsi
+ mov qword ptr [rax], rcx
+ mov qword ptr [rax + 8], rsi
+ mov qword ptr [rax + 16], rdi
+ mov qword ptr [rax + 24], rdx
+ ret
diff --git a/rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..6ab4e838767e
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_immutable_dynamic_padding.x86-64.mca
@@ -0,0 +1,65 @@
+Iterations: 100
+Instructions: 1300
+Total Cycles: 510
+Total uOps: 1300
+
+Dispatch Width: 4
+uOps Per Cycle: 2.55
+IPC: 2.55
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 5 0.50 * mov rcx, qword ptr [rsi]
+ 1 5 0.50 * mov rdx, qword ptr [rsi + 8]
+ 1 5 0.50 * mov rsi, qword ptr [rsi + 16]
+ 1 1 0.50 lea rdi, [rsi + 2*rsi]
+ 1 1 0.33 add rdi, rcx
+ 1 1 0.33 add rdi, 9
+ 1 1 0.33 sub rdx, rsi
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 * mov qword ptr [rax + 8], rsi
+ 1 1 1.00 * mov qword ptr [rax + 16], rdi
+ 1 1 1.00 * mov qword ptr [rax + 24], rdx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 2.00 2.00 4.00 2.00 3.50 3.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.03 0.93 - 0.04 - - mov rax, rdi
+ - - - - - - 0.49 0.51 mov rcx, qword ptr [rsi]
+ - - - - - - 1.00 - mov rdx, qword ptr [rsi + 8]
+ - - - - - - 0.01 0.99 mov rsi, qword ptr [rsi + 16]
+ - - 0.93 0.07 - - - - lea rdi, [rsi + 2*rsi]
+ - - 0.05 0.02 - 0.93 - - add rdi, rcx
+ - - 0.49 0.49 - 0.02 - - add rdi, 9
+ - - 0.50 0.49 - 0.01 - - sub rdx, rsi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax], rcx
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax + 8], rsi
+ - - - - 1.00 - 0.49 0.51 mov qword ptr [rax + 16], rdi
+ - - - - 1.00 - 0.51 0.49 mov qword ptr [rax + 24], rdx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_via_immutable_dynamic_size.rs b/rust/zerocopy/benches/split_via_immutable_dynamic_size.rs
new file mode 100644
index 000000000000..7d115caa3c00
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_immutable_dynamic_size.rs
@@ -0,0 +1,11 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_split_via_immutable_dynamic_size(
+ split: Split<&format::CocoPacket>,
+) -> (&format::CocoPacket, &[[u8; 2]]) {
+ split.via_immutable()
+}
diff --git a/rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64 b/rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64
new file mode 100644
index 000000000000..58f6b09fc9d2
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64
@@ -0,0 +1,13 @@
+bench_split_via_immutable_dynamic_size:
+ mov rax, rdi
+ mov rcx, qword ptr [rsi]
+ mov rdx, qword ptr [rsi + 8]
+ mov rsi, qword ptr [rsi + 16]
+ lea rdi, [rcx + 2*rsi]
+ add rdi, 4
+ sub rdx, rsi
+ mov qword ptr [rax], rcx
+ mov qword ptr [rax + 8], rsi
+ mov qword ptr [rax + 16], rdi
+ mov qword ptr [rax + 24], rdx
+ ret
diff --git a/rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64.mca b/rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..4549f20de53c
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_immutable_dynamic_size.x86-64.mca
@@ -0,0 +1,63 @@
+Iterations: 100
+Instructions: 1200
+Total Cycles: 509
+Total uOps: 1200
+
+Dispatch Width: 4
+uOps Per Cycle: 2.36
+IPC: 2.36
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 5 0.50 * mov rcx, qword ptr [rsi]
+ 1 5 0.50 * mov rdx, qword ptr [rsi + 8]
+ 1 5 0.50 * mov rsi, qword ptr [rsi + 16]
+ 1 1 0.50 lea rdi, [rcx + 2*rsi]
+ 1 1 0.33 add rdi, 4
+ 1 1 0.33 sub rdx, rsi
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 * mov qword ptr [rax + 8], rsi
+ 1 1 1.00 * mov qword ptr [rax + 16], rdi
+ 1 1 1.00 * mov qword ptr [rax + 24], rdx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.66 1.66 4.00 1.68 3.50 3.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.34 0.33 - 0.33 - - mov rax, rdi
+ - - - - - - 0.49 0.51 mov rcx, qword ptr [rsi]
+ - - - - - - 0.51 0.49 mov rdx, qword ptr [rsi + 8]
+ - - - - - - 0.01 0.99 mov rsi, qword ptr [rsi + 16]
+ - - 0.33 0.67 - - - - lea rdi, [rcx + 2*rsi]
+ - - 0.63 0.34 - 0.03 - - add rdi, 4
+ - - 0.36 0.32 - 0.32 - - sub rdx, rsi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax], rcx
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax + 8], rsi
+ - - - - 1.00 - 0.98 0.02 mov qword ptr [rax + 16], rdi
+ - - - - 1.00 - 0.51 0.49 mov qword ptr [rax + 24], rdx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.rs b/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.rs
new file mode 100644
index 000000000000..edba7bf34ba8
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.rs
@@ -0,0 +1,11 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_split_via_runtime_check_dynamic_padding(
+ split: Split<&format::CocoPacket>,
+) -> Option<(&format::CocoPacket, &[[u8; 3]])> {
+ split.via_runtime_check().ok()
+}
diff --git a/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64 b/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64
new file mode 100644
index 000000000000..03684cb09f8f
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64
@@ -0,0 +1,22 @@
+bench_split_via_runtime_check_dynamic_padding:
+ mov rax, rdi
+ mov rdx, qword ptr [rsi + 16]
+ mov ecx, edx
+ and ecx, 3
+ cmp ecx, 1
+ jne .LBB5_1
+ mov rcx, qword ptr [rsi]
+ mov rsi, qword ptr [rsi + 8]
+ lea rdi, [rdx + 2*rdx]
+ add rdi, rcx
+ add rdi, 9
+ sub rsi, rdx
+ mov qword ptr [rax + 8], rdx
+ mov qword ptr [rax + 16], rdi
+ mov qword ptr [rax + 24], rsi
+ mov qword ptr [rax], rcx
+ ret
+.LBB5_1:
+ xor ecx, ecx
+ mov qword ptr [rax], rcx
+ ret
diff --git a/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..5034ab0583bd
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_runtime_check_dynamic_padding.x86-64.mca
@@ -0,0 +1,79 @@
+Iterations: 100
+Instructions: 2000
+Total Cycles: 708
+Total uOps: 2000
+
+Dispatch Width: 4
+uOps Per Cycle: 2.82
+IPC: 2.82
+Block RThroughput: 5.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 5 0.50 * mov rdx, qword ptr [rsi + 16]
+ 1 1 0.33 mov ecx, edx
+ 1 1 0.33 and ecx, 3
+ 1 1 0.33 cmp ecx, 1
+ 1 1 1.00 jne .LBB5_1
+ 1 5 0.50 * mov rcx, qword ptr [rsi]
+ 1 5 0.50 * mov rsi, qword ptr [rsi + 8]
+ 1 1 0.50 lea rdi, [rdx + 2*rdx]
+ 1 1 0.33 add rdi, rcx
+ 1 1 0.33 add rdi, 9
+ 1 1 0.33 sub rsi, rdx
+ 1 1 1.00 * mov qword ptr [rax + 8], rdx
+ 1 1 1.00 * mov qword ptr [rax + 16], rdi
+ 1 1 1.00 * mov qword ptr [rax + 24], rsi
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 U ret
+ 1 0 0.25 xor ecx, ecx
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.00 3.02 5.00 4.98 4.00 4.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.99 - 0.01 - - mov rax, rdi
+ - - - - - - - 1.00 mov rdx, qword ptr [rsi + 16]
+ - - 0.99 0.01 - - - - mov ecx, edx
+ - - 0.99 0.01 - - - - and ecx, 3
+ - - 0.97 0.03 - - - - cmp ecx, 1
+ - - - - - 1.00 - - jne .LBB5_1
+ - - - - - - 1.00 - mov rcx, qword ptr [rsi]
+ - - - - - - 0.99 0.01 mov rsi, qword ptr [rsi + 8]
+ - - 0.01 0.99 - - - - lea rdi, [rdx + 2*rdx]
+ - - - 0.96 - 0.04 - - add rdi, rcx
+ - - 0.03 - - 0.97 - - add rdi, 9
+ - - 0.01 0.03 - 0.96 - - sub rsi, rdx
+ - - - - 1.00 - 1.00 - mov qword ptr [rax + 8], rdx
+ - - - - 1.00 - - 1.00 mov qword ptr [rax + 16], rdi
+ - - - - 1.00 - 0.01 0.99 mov qword ptr [rax + 24], rsi
+ - - - - 1.00 - 0.99 0.01 mov qword ptr [rax], rcx
+ - - - - - 1.00 - - ret
+ - - - - - - - - xor ecx, ecx
+ - - - - 1.00 - 0.01 0.99 mov qword ptr [rax], rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.rs b/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.rs
new file mode 100644
index 000000000000..ce22de909aca
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.rs
@@ -0,0 +1,11 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_split_via_runtime_check_dynamic_size(
+ split: Split<&format::CocoPacket>,
+) -> Option<(&format::CocoPacket, &[[u8; 2]])> {
+ split.via_runtime_check().ok()
+}
diff --git a/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64 b/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64
new file mode 100644
index 000000000000..e54276bd85e8
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64
@@ -0,0 +1,13 @@
+bench_split_via_runtime_check_dynamic_size:
+ mov rax, rdi
+ mov rcx, qword ptr [rsi]
+ mov rdx, qword ptr [rsi + 8]
+ mov rsi, qword ptr [rsi + 16]
+ lea rdi, [rcx + 2*rsi]
+ add rdi, 4
+ sub rdx, rsi
+ mov qword ptr [rax], rcx
+ mov qword ptr [rax + 8], rsi
+ mov qword ptr [rax + 16], rdi
+ mov qword ptr [rax + 24], rdx
+ ret
diff --git a/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64.mca b/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..4549f20de53c
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_runtime_check_dynamic_size.x86-64.mca
@@ -0,0 +1,63 @@
+Iterations: 100
+Instructions: 1200
+Total Cycles: 509
+Total uOps: 1200
+
+Dispatch Width: 4
+uOps Per Cycle: 2.36
+IPC: 2.36
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 5 0.50 * mov rcx, qword ptr [rsi]
+ 1 5 0.50 * mov rdx, qword ptr [rsi + 8]
+ 1 5 0.50 * mov rsi, qword ptr [rsi + 16]
+ 1 1 0.50 lea rdi, [rcx + 2*rsi]
+ 1 1 0.33 add rdi, 4
+ 1 1 0.33 sub rdx, rsi
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 * mov qword ptr [rax + 8], rsi
+ 1 1 1.00 * mov qword ptr [rax + 16], rdi
+ 1 1 1.00 * mov qword ptr [rax + 24], rdx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.66 1.66 4.00 1.68 3.50 3.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.34 0.33 - 0.33 - - mov rax, rdi
+ - - - - - - 0.49 0.51 mov rcx, qword ptr [rsi]
+ - - - - - - 0.51 0.49 mov rdx, qword ptr [rsi + 8]
+ - - - - - - 0.01 0.99 mov rsi, qword ptr [rsi + 16]
+ - - 0.33 0.67 - - - - lea rdi, [rcx + 2*rsi]
+ - - 0.63 0.34 - 0.03 - - add rdi, 4
+ - - 0.36 0.32 - 0.32 - - sub rdx, rsi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax], rcx
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax + 8], rsi
+ - - - - 1.00 - 0.98 0.02 mov qword ptr [rax + 16], rdi
+ - - - - 1.00 - 0.51 0.49 mov qword ptr [rax + 24], rdx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.rs b/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.rs
new file mode 100644
index 000000000000..21d74dba176f
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.rs
@@ -0,0 +1,11 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+unsafe fn bench_split_via_unchecked_dynamic_padding(
+ split: Split<&format::CocoPacket>,
+) -> (&format::CocoPacket, &[[u8; 3]]) {
+ unsafe { split.via_unchecked() }
+}
diff --git a/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64 b/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64
new file mode 100644
index 000000000000..3c2c4ec9f6ed
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64
@@ -0,0 +1,14 @@
+bench_split_via_unchecked_dynamic_padding:
+ mov rax, rdi
+ mov rcx, qword ptr [rsi]
+ mov rdx, qword ptr [rsi + 8]
+ mov rsi, qword ptr [rsi + 16]
+ lea rdi, [rsi + 2*rsi]
+ add rdi, rcx
+ add rdi, 9
+ sub rdx, rsi
+ mov qword ptr [rax], rcx
+ mov qword ptr [rax + 8], rsi
+ mov qword ptr [rax + 16], rdi
+ mov qword ptr [rax + 24], rdx
+ ret
diff --git a/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..6ab4e838767e
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_unchecked_dynamic_padding.x86-64.mca
@@ -0,0 +1,65 @@
+Iterations: 100
+Instructions: 1300
+Total Cycles: 510
+Total uOps: 1300
+
+Dispatch Width: 4
+uOps Per Cycle: 2.55
+IPC: 2.55
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 5 0.50 * mov rcx, qword ptr [rsi]
+ 1 5 0.50 * mov rdx, qword ptr [rsi + 8]
+ 1 5 0.50 * mov rsi, qword ptr [rsi + 16]
+ 1 1 0.50 lea rdi, [rsi + 2*rsi]
+ 1 1 0.33 add rdi, rcx
+ 1 1 0.33 add rdi, 9
+ 1 1 0.33 sub rdx, rsi
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 * mov qword ptr [rax + 8], rsi
+ 1 1 1.00 * mov qword ptr [rax + 16], rdi
+ 1 1 1.00 * mov qword ptr [rax + 24], rdx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 2.00 2.00 4.00 2.00 3.50 3.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.03 0.93 - 0.04 - - mov rax, rdi
+ - - - - - - 0.49 0.51 mov rcx, qword ptr [rsi]
+ - - - - - - 1.00 - mov rdx, qword ptr [rsi + 8]
+ - - - - - - 0.01 0.99 mov rsi, qword ptr [rsi + 16]
+ - - 0.93 0.07 - - - - lea rdi, [rsi + 2*rsi]
+ - - 0.05 0.02 - 0.93 - - add rdi, rcx
+ - - 0.49 0.49 - 0.02 - - add rdi, 9
+ - - 0.50 0.49 - 0.01 - - sub rdx, rsi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax], rcx
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax + 8], rsi
+ - - - - 1.00 - 0.49 0.51 mov qword ptr [rax + 16], rdi
+ - - - - 1.00 - 0.51 0.49 mov qword ptr [rax + 24], rdx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/split_via_unchecked_dynamic_size.rs b/rust/zerocopy/benches/split_via_unchecked_dynamic_size.rs
new file mode 100644
index 000000000000..824e22d67a7b
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_unchecked_dynamic_size.rs
@@ -0,0 +1,11 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+unsafe fn bench_split_via_unchecked_dynamic_size(
+ split: Split<&format::CocoPacket>,
+) -> (&format::CocoPacket, &[[u8; 2]]) {
+ unsafe { split.via_unchecked() }
+}
diff --git a/rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64 b/rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64
new file mode 100644
index 000000000000..1e31268edc7f
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64
@@ -0,0 +1,13 @@
+bench_split_via_unchecked_dynamic_size:
+ mov rax, rdi
+ mov rcx, qword ptr [rsi]
+ mov rdx, qword ptr [rsi + 8]
+ mov rsi, qword ptr [rsi + 16]
+ lea rdi, [rcx + 2*rsi]
+ add rdi, 4
+ sub rdx, rsi
+ mov qword ptr [rax], rcx
+ mov qword ptr [rax + 8], rsi
+ mov qword ptr [rax + 16], rdi
+ mov qword ptr [rax + 24], rdx
+ ret
diff --git a/rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64.mca b/rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..4549f20de53c
--- /dev/null
+++ b/rust/zerocopy/benches/split_via_unchecked_dynamic_size.x86-64.mca
@@ -0,0 +1,63 @@
+Iterations: 100
+Instructions: 1200
+Total Cycles: 509
+Total uOps: 1200
+
+Dispatch Width: 4
+uOps Per Cycle: 2.36
+IPC: 2.36
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 5 0.50 * mov rcx, qword ptr [rsi]
+ 1 5 0.50 * mov rdx, qword ptr [rsi + 8]
+ 1 5 0.50 * mov rsi, qword ptr [rsi + 16]
+ 1 1 0.50 lea rdi, [rcx + 2*rsi]
+ 1 1 0.33 add rdi, 4
+ 1 1 0.33 sub rdx, rsi
+ 1 1 1.00 * mov qword ptr [rax], rcx
+ 1 1 1.00 * mov qword ptr [rax + 8], rsi
+ 1 1 1.00 * mov qword ptr [rax + 16], rdi
+ 1 1 1.00 * mov qword ptr [rax + 24], rdx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.66 1.66 4.00 1.68 3.50 3.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.34 0.33 - 0.33 - - mov rax, rdi
+ - - - - - - 0.49 0.51 mov rcx, qword ptr [rsi]
+ - - - - - - 0.51 0.49 mov rdx, qword ptr [rsi + 8]
+ - - - - - - 0.01 0.99 mov rsi, qword ptr [rsi + 16]
+ - - 0.33 0.67 - - - - lea rdi, [rcx + 2*rsi]
+ - - 0.63 0.34 - 0.03 - - add rdi, 4
+ - - 0.36 0.32 - 0.32 - - sub rdx, rsi
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax], rcx
+ - - - - 1.00 - 0.50 0.50 mov qword ptr [rax + 8], rsi
+ - - - - 1.00 - 0.98 0.02 mov qword ptr [rax + 16], rdi
+ - - - - 1.00 - 0.51 0.49 mov qword ptr [rax + 24], rdx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/transmute.rs b/rust/zerocopy/benches/transmute.rs
new file mode 100644
index 000000000000..e60bfb252f5c
--- /dev/null
+++ b/rust/zerocopy/benches/transmute.rs
@@ -0,0 +1,16 @@
+use zerocopy::Unalign;
+use zerocopy_derive::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[derive(IntoBytes, KnownLayout, Immutable)]
+#[repr(C)]
+struct MinimalViableSource {
+ bytes: [u8; 6],
+}
+
+#[unsafe(no_mangle)]
+fn bench_transmute(source: MinimalViableSource) -> Unalign<format::LocoPacket> {
+ zerocopy::transmute!(source)
+}
diff --git a/rust/zerocopy/benches/transmute.x86-64 b/rust/zerocopy/benches/transmute.x86-64
new file mode 100644
index 000000000000..a4c6299d1112
--- /dev/null
+++ b/rust/zerocopy/benches/transmute.x86-64
@@ -0,0 +1,3 @@
+bench_transmute:
+ mov rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/transmute.x86-64.mca b/rust/zerocopy/benches/transmute.x86-64.mca
new file mode 100644
index 000000000000..f297729c6523
--- /dev/null
+++ b/rust/zerocopy/benches/transmute.x86-64.mca
@@ -0,0 +1,43 @@
+Iterations: 100
+Instructions: 200
+Total Cycles: 104
+Total uOps: 200
+
+Dispatch Width: 4
+uOps Per Cycle: 1.92
+IPC: 1.92
+Block RThroughput: 1.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.49 0.50 - 1.01 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.49 0.50 - 0.01 - - mov rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/transmute_ref_dynamic_size.rs b/rust/zerocopy/benches/transmute_ref_dynamic_size.rs
new file mode 100644
index 000000000000..825f0f2bed24
--- /dev/null
+++ b/rust/zerocopy/benches/transmute_ref_dynamic_size.rs
@@ -0,0 +1,16 @@
+use zerocopy_derive::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[derive(IntoBytes, KnownLayout, Immutable)]
+#[repr(C, align(2))]
+struct MinimalViableSource {
+ header: [u8; 6],
+ trailer: [[u8; 2]],
+}
+
+#[unsafe(no_mangle)]
+fn bench_transmute_ref_dynamic_size(source: &MinimalViableSource) -> &format::LocoPacket {
+ zerocopy::transmute_ref!(source)
+}
diff --git a/rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64 b/rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64
new file mode 100644
index 000000000000..80a0f5906610
--- /dev/null
+++ b/rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64
@@ -0,0 +1,4 @@
+bench_transmute_ref_dynamic_size:
+ mov rax, rdi
+ lea rdx, [rsi + 1]
+ ret
diff --git a/rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64.mca b/rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..ef1bcdca5ea3
--- /dev/null
+++ b/rust/zerocopy/benches/transmute_ref_dynamic_size.x86-64.mca
@@ -0,0 +1,45 @@
+Iterations: 100
+Instructions: 300
+Total Cycles: 104
+Total uOps: 300
+
+Dispatch Width: 4
+uOps Per Cycle: 2.88
+IPC: 2.88
+Block RThroughput: 1.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.50 lea rdx, [rsi + 1]
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.99 1.00 - 1.01 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.99 - - 0.01 - - mov rax, rdi
+ - - - 1.00 - - - - lea rdx, [rsi + 1]
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/transmute_ref_static_size.rs b/rust/zerocopy/benches/transmute_ref_static_size.rs
new file mode 100644
index 000000000000..a6db611fde0d
--- /dev/null
+++ b/rust/zerocopy/benches/transmute_ref_static_size.rs
@@ -0,0 +1,15 @@
+use zerocopy_derive::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[derive(IntoBytes, KnownLayout, Immutable)]
+#[repr(C, align(2))]
+struct MinimalViableSource {
+ bytes: [u8; 6],
+}
+
+#[unsafe(no_mangle)]
+fn bench_transmute_ref_static_size(source: &MinimalViableSource) -> &format::LocoPacket {
+ zerocopy::transmute_ref!(source)
+}
diff --git a/rust/zerocopy/benches/transmute_ref_static_size.x86-64 b/rust/zerocopy/benches/transmute_ref_static_size.x86-64
new file mode 100644
index 000000000000..7a9229c129b0
--- /dev/null
+++ b/rust/zerocopy/benches/transmute_ref_static_size.x86-64
@@ -0,0 +1,3 @@
+bench_transmute_ref_static_size:
+ mov rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/transmute_ref_static_size.x86-64.mca b/rust/zerocopy/benches/transmute_ref_static_size.x86-64.mca
new file mode 100644
index 000000000000..f297729c6523
--- /dev/null
+++ b/rust/zerocopy/benches/transmute_ref_static_size.x86-64.mca
@@ -0,0 +1,43 @@
+Iterations: 100
+Instructions: 200
+Total Cycles: 104
+Total uOps: 200
+
+Dispatch Width: 4
+uOps Per Cycle: 1.92
+IPC: 1.92
+Block RThroughput: 1.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.49 0.50 - 1.01 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.49 0.50 - 0.01 - - mov rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_read_from_bytes.rs b/rust/zerocopy/benches/try_read_from_bytes.rs
new file mode 100644
index 000000000000..f8384b32f518
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_bytes.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_read_from_bytes_static_size(source: &[u8]) -> Option<format::CocoPacket> {
+ zerocopy::TryFromBytes::try_read_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/try_read_from_bytes.x86-64 b/rust/zerocopy/benches/try_read_from_bytes.x86-64
new file mode 100644
index 000000000000..08088a08fd85
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_bytes.x86-64
@@ -0,0 +1,23 @@
+bench_try_read_from_bytes_static_size:
+ mov ax, -16191
+ cmp rsi, 6
+ jne .LBB5_1
+ mov ecx, dword ptr [rdi]
+ movzx edx, cx
+ cmp edx, 49344
+ jne .LBB5_4
+ movzx eax, word ptr [rdi + 4]
+ shl rax, 32
+ or rcx, rax
+ shr rcx, 16
+ mov ax, -16192
+.LBB5_4:
+ shl rcx, 16
+ movzx eax, ax
+ or rax, rcx
+ ret
+.LBB5_1:
+ shl rcx, 16
+ movzx eax, ax
+ or rax, rcx
+ ret
diff --git a/rust/zerocopy/benches/try_read_from_bytes.x86-64.mca b/rust/zerocopy/benches/try_read_from_bytes.x86-64.mca
new file mode 100644
index 000000000000..385e6a480253
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_bytes.x86-64.mca
@@ -0,0 +1,79 @@
+Iterations: 100
+Instructions: 2000
+Total Cycles: 608
+Total uOps: 2000
+
+Dispatch Width: 4
+uOps Per Cycle: 3.29
+IPC: 3.29
+Block RThroughput: 5.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov ax, -16191
+ 1 1 0.33 cmp rsi, 6
+ 1 1 1.00 jne .LBB5_1
+ 1 5 0.50 * mov ecx, dword ptr [rdi]
+ 1 1 0.33 movzx edx, cx
+ 1 1 0.33 cmp edx, 49344
+ 1 1 1.00 jne .LBB5_4
+ 1 5 0.50 * movzx eax, word ptr [rdi + 4]
+ 1 1 0.50 shl rax, 32
+ 1 1 0.33 or rcx, rax
+ 1 1 0.50 shr rcx, 16
+ 1 1 0.33 mov ax, -16192
+ 1 1 0.50 shl rcx, 16
+ 1 1 0.33 movzx eax, ax
+ 1 1 0.33 or rax, rcx
+ 1 1 1.00 U ret
+ 1 1 0.50 shl rcx, 16
+ 1 1 0.33 movzx eax, ax
+ 1 1 0.33 or rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 5.99 5.99 - 6.02 1.00 1.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.99 - 0.01 - - mov ax, -16191
+ - - - 0.01 - 0.99 - - cmp rsi, 6
+ - - - - - 1.00 - - jne .LBB5_1
+ - - - - - - - 1.00 mov ecx, dword ptr [rdi]
+ - - 0.98 - - 0.02 - - movzx edx, cx
+ - - 0.99 0.01 - - - - cmp edx, 49344
+ - - - - - 1.00 - - jne .LBB5_4
+ - - - - - - 1.00 - movzx eax, word ptr [rdi + 4]
+ - - 0.01 - - 0.99 - - shl rax, 32
+ - - 0.02 0.98 - - - - or rcx, rax
+ - - 1.00 - - - - - shr rcx, 16
+ - - 0.99 0.01 - - - - mov ax, -16192
+ - - 1.00 - - - - - shl rcx, 16
+ - - - 1.00 - - - - movzx eax, ax
+ - - - 1.00 - - - - or rax, rcx
+ - - - - - 1.00 - - ret
+ - - 1.00 - - - - - shl rcx, 16
+ - - - 1.00 - - - - movzx eax, ax
+ - - - 0.99 - 0.01 - - or rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_read_from_prefix.rs b/rust/zerocopy/benches/try_read_from_prefix.rs
new file mode 100644
index 000000000000..fabbac3bd7d9
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_prefix.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_read_from_prefix_static_size(source: &[u8]) -> Option<format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_read_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_read_from_prefix.x86-64 b/rust/zerocopy/benches/try_read_from_prefix.x86-64
new file mode 100644
index 000000000000..d3e1edc3ea73
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_prefix.x86-64
@@ -0,0 +1,16 @@
+bench_try_read_from_prefix_static_size:
+ mov eax, 49345
+ cmp rsi, 6
+ jb .LBB5_2
+ mov eax, dword ptr [rdi]
+ movzx ecx, word ptr [rdi + 4]
+ shl rcx, 32
+ or rcx, rax
+ movzx eax, cx
+ and rcx, -65536
+ or rcx, 49344
+ cmp eax, 49344
+ mov eax, 49345
+ cmove rax, rcx
+.LBB5_2:
+ ret
diff --git a/rust/zerocopy/benches/try_read_from_prefix.x86-64.mca b/rust/zerocopy/benches/try_read_from_prefix.x86-64.mca
new file mode 100644
index 000000000000..40401d89e83d
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_prefix.x86-64.mca
@@ -0,0 +1,67 @@
+Iterations: 100
+Instructions: 1400
+Total Cycles: 442
+Total uOps: 1500
+
+Dispatch Width: 4
+uOps Per Cycle: 3.39
+IPC: 3.17
+Block RThroughput: 3.8
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov eax, 49345
+ 1 1 0.33 cmp rsi, 6
+ 1 1 1.00 jb .LBB5_2
+ 1 5 0.50 * mov eax, dword ptr [rdi]
+ 1 5 0.50 * movzx ecx, word ptr [rdi + 4]
+ 1 1 0.50 shl rcx, 32
+ 1 1 0.33 or rcx, rax
+ 1 1 0.33 movzx eax, cx
+ 1 1 0.33 and rcx, -65536
+ 1 1 0.33 or rcx, 49344
+ 1 1 0.33 cmp eax, 49344
+ 1 1 0.33 mov eax, 49345
+ 2 2 0.67 cmove rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.33 4.33 - 4.34 1.00 1.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.65 0.01 - 0.34 - - mov eax, 49345
+ - - 0.01 0.33 - 0.66 - - cmp rsi, 6
+ - - - - - 1.00 - - jb .LBB5_2
+ - - - - - - - 1.00 mov eax, dword ptr [rdi]
+ - - - - - - 1.00 - movzx ecx, word ptr [rdi + 4]
+ - - 0.65 - - 0.35 - - shl rcx, 32
+ - - - 0.67 - 0.33 - - or rcx, rax
+ - - 0.01 0.99 - - - - movzx eax, cx
+ - - 0.99 0.01 - - - - and rcx, -65536
+ - - 0.01 0.99 - - - - or rcx, 49344
+ - - 0.99 0.01 - - - - cmp eax, 49344
+ - - 0.02 0.33 - 0.65 - - mov eax, 49345
+ - - 1.00 0.99 - 0.01 - - cmove rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_read_from_suffix.rs b/rust/zerocopy/benches/try_read_from_suffix.rs
new file mode 100644
index 000000000000..1e961647b3f3
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_suffix.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_read_from_suffix_static_size(source: &[u8]) -> Option<format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_read_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_read_from_suffix.x86-64 b/rust/zerocopy/benches/try_read_from_suffix.x86-64
new file mode 100644
index 000000000000..095e326f0467
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_suffix.x86-64
@@ -0,0 +1,18 @@
+bench_try_read_from_suffix_static_size:
+ mov eax, 49345
+ cmp rsi, 6
+ jb .LBB5_2
+ mov eax, dword ptr [rdi + rsi - 6]
+ movzx ecx, word ptr [rdi + rsi - 2]
+ shl rcx, 32
+ or rcx, rax
+ movzx edx, cx
+ xor eax, eax
+ cmp edx, 49344
+ cmovne rcx, rsi
+ sete al
+ and rcx, -65536
+ xor rax, 49345
+ or rax, rcx
+.LBB5_2:
+ ret
diff --git a/rust/zerocopy/benches/try_read_from_suffix.x86-64.mca b/rust/zerocopy/benches/try_read_from_suffix.x86-64.mca
new file mode 100644
index 000000000000..d3eaadbb8a81
--- /dev/null
+++ b/rust/zerocopy/benches/try_read_from_suffix.x86-64.mca
@@ -0,0 +1,71 @@
+Iterations: 100
+Instructions: 1600
+Total Cycles: 478
+Total uOps: 1700
+
+Dispatch Width: 4
+uOps Per Cycle: 3.56
+IPC: 3.35
+Block RThroughput: 4.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov eax, 49345
+ 1 1 0.33 cmp rsi, 6
+ 1 1 1.00 jb .LBB5_2
+ 1 5 0.50 * mov eax, dword ptr [rdi + rsi - 6]
+ 1 5 0.50 * movzx ecx, word ptr [rdi + rsi - 2]
+ 1 1 0.50 shl rcx, 32
+ 1 1 0.33 or rcx, rax
+ 1 1 0.33 movzx edx, cx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp edx, 49344
+ 2 2 0.67 cmovne rcx, rsi
+ 1 1 0.50 sete al
+ 1 1 0.33 and rcx, -65536
+ 1 1 0.33 xor rax, 49345
+ 1 1 0.33 or rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.66 4.66 - 4.68 1.00 1.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.32 0.01 - 0.67 - - mov eax, 49345
+ - - 0.62 0.02 - 0.36 - - cmp rsi, 6
+ - - - - - 1.00 - - jb .LBB5_2
+ - - - - - - - 1.00 mov eax, dword ptr [rdi + rsi - 6]
+ - - - - - - 1.00 - movzx ecx, word ptr [rdi + rsi - 2]
+ - - 0.37 - - 0.63 - - shl rcx, 32
+ - - 0.99 0.01 - - - - or rcx, rax
+ - - 1.00 - - - - - movzx edx, cx
+ - - - - - - - - xor eax, eax
+ - - 0.35 0.64 - 0.01 - - cmp edx, 49344
+ - - 1.00 1.00 - - - - cmovne rcx, rsi
+ - - - - - 1.00 - - sete al
+ - - 0.01 0.99 - - - - and rcx, -65536
+ - - - 1.00 - - - - xor rax, 49345
+ - - - 0.99 - 0.01 - - or rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.rs b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.rs
new file mode 100644
index 000000000000..126009cd71d5
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_bytes_dynamic_padding(source: &[u8]) -> Option<&format::CocoPacket> {
+ zerocopy::TryFromBytes::try_ref_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64 b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64
new file mode 100644
index 000000000000..217c5fc61796
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64
@@ -0,0 +1,24 @@
+bench_try_ref_from_bytes_dynamic_padding:
+ test dil, 3
+ jne .LBB5_4
+ movabs rax, 9223372036854775804
+ and rax, rsi
+ cmp rax, 9
+ jb .LBB5_4
+ add rax, -9
+ movabs rcx, -6148914691236517205
+ mul rcx
+ shr rdx
+ lea rax, [rdx + 2*rdx]
+ or rax, 3
+ add rax, 9
+ cmp rsi, rax
+ jne .LBB5_4
+ cmp word ptr [rdi], -16192
+ je .LBB5_5
+.LBB5_4:
+ xor edi, edi
+ mov rdx, rsi
+.LBB5_5:
+ mov rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..95b993c7e0a8
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_padding.x86-64.mca
@@ -0,0 +1,81 @@
+Iterations: 100
+Instructions: 2100
+Total Cycles: 709
+Total uOps: 2300
+
+Dispatch Width: 4
+uOps Per Cycle: 3.24
+IPC: 2.96
+Block RThroughput: 5.8
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 test dil, 3
+ 1 1 1.00 jne .LBB5_4
+ 1 1 0.33 movabs rax, 9223372036854775804
+ 1 1 0.33 and rax, rsi
+ 1 1 0.33 cmp rax, 9
+ 1 1 1.00 jb .LBB5_4
+ 1 1 0.33 add rax, -9
+ 1 1 0.33 movabs rcx, -6148914691236517205
+ 2 4 1.00 mul rcx
+ 1 1 0.50 shr rdx
+ 1 1 0.50 lea rax, [rdx + 2*rdx]
+ 1 1 0.33 or rax, 3
+ 1 1 0.33 add rax, 9
+ 1 1 0.33 cmp rsi, rax
+ 1 1 1.00 jne .LBB5_4
+ 2 6 0.50 * cmp word ptr [rdi], -16192
+ 1 1 1.00 je .LBB5_5
+ 1 0 0.25 xor edi, edi
+ 1 1 0.33 mov rdx, rsi
+ 1 1 0.33 mov rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.98 6.99 - 7.03 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.48 0.51 - 0.01 - - test dil, 3
+ - - - - - 1.00 - - jne .LBB5_4
+ - - 0.51 0.49 - - - - movabs rax, 9223372036854775804
+ - - 0.01 0.99 - - - - and rax, rsi
+ - - 0.51 0.49 - - - - cmp rax, 9
+ - - - - - 1.00 - - jb .LBB5_4
+ - - 0.98 - - 0.02 - - add rax, -9
+ - - 0.98 0.02 - - - - movabs rcx, -6148914691236517205
+ - - 1.00 1.00 - - - - mul rcx
+ - - 0.99 - - 0.01 - - shr rdx
+ - - - 1.00 - - - - lea rax, [rdx + 2*rdx]
+ - - - 0.51 - 0.49 - - or rax, 3
+ - - 0.01 0.49 - 0.50 - - add rax, 9
+ - - - 0.02 - 0.98 - - cmp rsi, rax
+ - - - - - 1.00 - - jne .LBB5_4
+ - - 0.51 0.49 - - 0.50 0.50 cmp word ptr [rdi], -16192
+ - - - - - 1.00 - - je .LBB5_5
+ - - - - - - - - xor edi, edi
+ - - 0.50 0.50 - - - - mov rdx, rsi
+ - - 0.50 0.48 - 0.02 - - mov rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.rs b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.rs
new file mode 100644
index 000000000000..fc3cfbae27a2
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_bytes_dynamic_size(source: &[u8]) -> Option<&format::CocoPacket> {
+ zerocopy::TryFromBytes::try_ref_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64 b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64
new file mode 100644
index 000000000000..cf67afd31ce0
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64
@@ -0,0 +1,22 @@
+bench_try_ref_from_bytes_dynamic_size:
+ mov rdx, rsi
+ mov rax, rdi
+ cmp rsi, 4
+ setb cl
+ or cl, al
+ test cl, 1
+ jne .LBB5_4
+ lea rcx, [rdx - 4]
+ mov rsi, rcx
+ and rsi, -2
+ add rsi, 4
+ cmp rdx, rsi
+ jne .LBB5_4
+ cmp word ptr [rax], -16192
+ jne .LBB5_4
+ shr rcx
+ mov rdx, rcx
+ ret
+.LBB5_4:
+ xor eax, eax
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..ecd7a18f6d6d
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_dynamic_size.x86-64.mca
@@ -0,0 +1,79 @@
+Iterations: 100
+Instructions: 2000
+Total Cycles: 639
+Total uOps: 2100
+
+Dispatch Width: 4
+uOps Per Cycle: 3.29
+IPC: 3.13
+Block RThroughput: 5.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rdx, rsi
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 cmp rsi, 4
+ 1 1 0.50 setb cl
+ 1 1 0.33 or cl, al
+ 1 1 0.33 test cl, 1
+ 1 1 1.00 jne .LBB5_4
+ 1 1 0.50 lea rcx, [rdx - 4]
+ 1 1 0.33 mov rsi, rcx
+ 1 1 0.33 and rsi, -2
+ 1 1 0.33 add rsi, 4
+ 1 1 0.33 cmp rdx, rsi
+ 1 1 1.00 jne .LBB5_4
+ 2 6 0.50 * cmp word ptr [rax], -16192
+ 1 1 1.00 jne .LBB5_4
+ 1 1 0.50 shr rcx
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.32 6.32 - 6.36 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.33 0.66 - 0.01 - - mov rdx, rsi
+ - - 0.66 0.34 - - - - mov rax, rdi
+ - - 0.34 0.66 - - - - cmp rsi, 4
+ - - 0.99 - - 0.01 - - setb cl
+ - - 0.01 0.99 - - - - or cl, al
+ - - - 1.00 - - - - test cl, 1
+ - - - - - 1.00 - - jne .LBB5_4
+ - - 0.66 0.34 - - - - lea rcx, [rdx - 4]
+ - - 0.33 0.66 - 0.01 - - mov rsi, rcx
+ - - 1.00 - - - - - and rsi, -2
+ - - 0.66 0.34 - - - - add rsi, 4
+ - - - 1.00 - - - - cmp rdx, rsi
+ - - - - - 1.00 - - jne .LBB5_4
+ - - - - - 1.00 0.50 0.50 cmp word ptr [rax], -16192
+ - - - - - 1.00 - - jne .LBB5_4
+ - - 0.67 - - 0.33 - - shr rcx
+ - - 0.67 0.33 - - - - mov rdx, rcx
+ - - - - - 1.00 - - ret
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_static_size.rs b/rust/zerocopy/benches/try_ref_from_bytes_static_size.rs
new file mode 100644
index 000000000000..521557146324
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_static_size.rs
@@ -0,0 +1,7 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_bytes_static_size(source: &[u8]) -> Option<&format::CocoPacket> {
+ zerocopy::TryFromBytes::try_ref_from_bytes(source).ok()
+}
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64 b/rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64
new file mode 100644
index 000000000000..a11f27189e90
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64
@@ -0,0 +1,13 @@
+bench_try_ref_from_bytes_static_size:
+ mov rax, rdi
+ cmp rsi, 6
+ setne cl
+ or cl, al
+ test cl, 1
+ jne .LBB5_2
+ cmp word ptr [rax], -16192
+ je .LBB5_3
+.LBB5_2:
+ xor eax, eax
+.LBB5_3:
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64.mca
new file mode 100644
index 000000000000..e6bd20533a83
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_static_size.x86-64.mca
@@ -0,0 +1,59 @@
+Iterations: 100
+Instructions: 1000
+Total Cycles: 308
+Total uOps: 1100
+
+Dispatch Width: 4
+uOps Per Cycle: 3.57
+IPC: 3.25
+Block RThroughput: 3.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 cmp rsi, 6
+ 1 1 0.50 setne cl
+ 1 1 0.33 or cl, al
+ 1 1 0.33 test cl, 1
+ 1 1 1.00 jne .LBB5_2
+ 2 6 0.50 * cmp word ptr [rax], -16192
+ 1 1 1.00 je .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 2.98 2.98 - 3.04 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.02 0.97 - 0.01 - - mov rax, rdi
+ - - 0.02 0.98 - - - - cmp rsi, 6
+ - - 1.00 - - - - - setne cl
+ - - 0.97 0.02 - 0.01 - - or cl, al
+ - - 0.96 0.03 - 0.01 - - test cl, 1
+ - - - - - 1.00 - - jne .LBB5_2
+ - - 0.01 0.98 - 0.01 0.50 0.50 cmp word ptr [rax], -16192
+ - - - - - 1.00 - - je .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.rs b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.rs
new file mode 100644
index 000000000000..8b9e7355e367
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_bytes_with_elems_dynamic_padding(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::CocoPacket> {
+ zerocopy::TryFromBytes::try_ref_from_bytes_with_elems(source, count).ok()
+}
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64 b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64
new file mode 100644
index 000000000000..3ef8d1448a50
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64
@@ -0,0 +1,21 @@
+bench_try_ref_from_bytes_with_elems_dynamic_padding:
+ movabs rax, 3074457345618258598
+ cmp rdx, rax
+ seta cl
+ mov rax, rdi
+ test al, 3
+ setne dil
+ or dil, cl
+ jne .LBB5_3
+ lea rcx, [rdx + 2*rdx]
+ or rcx, 3
+ add rcx, 9
+ cmp rsi, rcx
+ jne .LBB5_3
+ cmp word ptr [rax], -16192
+ je .LBB5_4
+.LBB5_3:
+ xor eax, eax
+ mov rdx, rsi
+.LBB5_4:
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..8131f3bd549e
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_padding.x86-64.mca
@@ -0,0 +1,75 @@
+Iterations: 100
+Instructions: 1800
+Total Cycles: 607
+Total uOps: 2000
+
+Dispatch Width: 4
+uOps Per Cycle: 3.29
+IPC: 2.97
+Block RThroughput: 5.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 3074457345618258598
+ 1 1 0.33 cmp rdx, rax
+ 2 2 1.00 seta cl
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 test al, 3
+ 1 1 0.50 setne dil
+ 1 1 0.33 or dil, cl
+ 1 1 1.00 jne .LBB5_3
+ 1 1 0.50 lea rcx, [rdx + 2*rdx]
+ 1 1 0.33 or rcx, 3
+ 1 1 0.33 add rcx, 9
+ 1 1 0.33 cmp rsi, rcx
+ 1 1 1.00 jne .LBB5_3
+ 2 6 0.50 * cmp word ptr [rax], -16192
+ 1 1 1.00 je .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov rdx, rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 5.99 5.99 - 6.02 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.99 - 0.01 - - movabs rax, 3074457345618258598
+ - - - 1.00 - - - - cmp rdx, rax
+ - - - - - 2.00 - - seta cl
+ - - 1.00 - - - - - mov rax, rdi
+ - - 0.99 0.01 - - - - test al, 3
+ - - 1.00 - - - - - setne dil
+ - - - 0.99 - 0.01 - - or dil, cl
+ - - - - - 1.00 - - jne .LBB5_3
+ - - 0.01 0.99 - - - - lea rcx, [rdx + 2*rdx]
+ - - - 1.00 - - - - or rcx, 3
+ - - 0.99 0.01 - - - - add rcx, 9
+ - - - 1.00 - - - - cmp rsi, rcx
+ - - - - - 1.00 - - jne .LBB5_3
+ - - 1.00 - - - 0.50 0.50 cmp word ptr [rax], -16192
+ - - - - - 1.00 - - je .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - 1.00 - - - - - mov rdx, rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.rs b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.rs
new file mode 100644
index 000000000000..9ccd6fef558a
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_bytes_with_elems_dynamic_size(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::CocoPacket> {
+ zerocopy::TryFromBytes::try_ref_from_bytes_with_elems(source, count).ok()
+}
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64 b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64
new file mode 100644
index 000000000000..ba34b1855bf1
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64
@@ -0,0 +1,18 @@
+bench_try_ref_from_bytes_with_elems_dynamic_size:
+ movabs rax, 4611686018427387901
+ cmp rdx, rax
+ seta cl
+ mov rax, rdi
+ or dil, cl
+ test dil, 1
+ jne .LBB5_3
+ lea rcx, [2*rdx + 4]
+ cmp rsi, rcx
+ jne .LBB5_3
+ cmp word ptr [rax], -16192
+ je .LBB5_4
+.LBB5_3:
+ xor eax, eax
+ mov rdx, rsi
+.LBB5_4:
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..ae049c03dfde
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_bytes_with_elems_dynamic_size.x86-64.mca
@@ -0,0 +1,69 @@
+Iterations: 100
+Instructions: 1500
+Total Cycles: 507
+Total uOps: 1700
+
+Dispatch Width: 4
+uOps Per Cycle: 3.35
+IPC: 2.96
+Block RThroughput: 4.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 4611686018427387901
+ 1 1 0.33 cmp rdx, rax
+ 2 2 1.00 seta cl
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 or dil, cl
+ 1 1 0.33 test dil, 1
+ 1 1 1.00 jne .LBB5_3
+ 1 1 0.50 lea rcx, [2*rdx + 4]
+ 1 1 0.33 cmp rsi, rcx
+ 1 1 1.00 jne .LBB5_3
+ 2 6 0.50 * cmp word ptr [rax], -16192
+ 1 1 1.00 je .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov rdx, rsi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.98 4.99 - 5.03 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - 0.99 - 0.01 - - movabs rax, 4611686018427387901
+ - - 0.50 0.50 - - - - cmp rdx, rax
+ - - 1.96 - - 0.04 - - seta cl
+ - - 0.01 0.99 - - - - mov rax, rdi
+ - - 1.00 - - - - - or dil, cl
+ - - 0.99 0.01 - - - - test dil, 1
+ - - - - - 1.00 - - jne .LBB5_3
+ - - 0.01 0.99 - - - - lea rcx, [2*rdx + 4]
+ - - 0.02 0.49 - 0.49 - - cmp rsi, rcx
+ - - - - - 1.00 - - jne .LBB5_3
+ - - - 0.51 - 0.49 0.50 0.50 cmp word ptr [rax], -16192
+ - - - - - 1.00 - - je .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - 0.49 0.51 - - - - mov rdx, rsi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.rs b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.rs
new file mode 100644
index 000000000000..23b346f9c98d
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_prefix_dynamic_padding(source: &[u8]) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64 b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64
new file mode 100644
index 000000000000..d832cb7ecf7f
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64
@@ -0,0 +1,29 @@
+bench_try_ref_from_prefix_dynamic_padding:
+ xor edx, edx
+ mov eax, 0
+ test dil, 3
+ je .LBB5_1
+ ret
+.LBB5_1:
+ movabs rax, 9223372036854775804
+ and rsi, rax
+ cmp rsi, 9
+ jae .LBB5_3
+ mov edx, 1
+ xor eax, eax
+ ret
+.LBB5_3:
+ add rsi, -9
+ movabs rcx, -6148914691236517205
+ mov rax, rsi
+ mul rcx
+ mov rax, rdx
+ shr rax
+ movzx ecx, word ptr [rdi]
+ cmp cx, -16192
+ mov edx, 2
+ cmove rdx, rax
+ xor eax, eax
+ cmp ecx, 49344
+ cmove rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..482112a39b33
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_padding.x86-64.mca
@@ -0,0 +1,91 @@
+Iterations: 100
+Instructions: 2600
+Total Cycles: 843
+Total uOps: 2900
+
+Dispatch Width: 4
+uOps Per Cycle: 3.44
+IPC: 3.08
+Block RThroughput: 7.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 0 0.25 xor edx, edx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 3
+ 1 1 1.00 je .LBB5_1
+ 1 1 1.00 U ret
+ 1 1 0.33 movabs rax, 9223372036854775804
+ 1 1 0.33 and rsi, rax
+ 1 1 0.33 cmp rsi, 9
+ 1 1 1.00 jae .LBB5_3
+ 1 1 0.33 mov edx, 1
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.33 add rsi, -9
+ 1 1 0.33 movabs rcx, -6148914691236517205
+ 1 1 0.33 mov rax, rsi
+ 2 4 1.00 mul rcx
+ 1 1 0.33 mov rax, rdx
+ 1 1 0.50 shr rax
+ 1 5 0.50 * movzx ecx, word ptr [rdi]
+ 1 1 0.33 cmp cx, -16192
+ 1 1 0.33 mov edx, 2
+ 2 2 0.67 cmove rdx, rax
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp ecx, 49344
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 8.33 8.33 - 8.34 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - - - - - xor edx, edx
+ - - 0.32 0.34 - 0.34 - - mov eax, 0
+ - - 0.34 0.33 - 0.33 - - test dil, 3
+ - - - - - 1.00 - - je .LBB5_1
+ - - - - - 1.00 - - ret
+ - - 0.35 0.65 - - - - movabs rax, 9223372036854775804
+ - - 0.96 0.03 - 0.01 - - and rsi, rax
+ - - 0.01 0.97 - 0.02 - - cmp rsi, 9
+ - - - - - 1.00 - - jae .LBB5_3
+ - - 0.67 0.01 - 0.32 - - mov edx, 1
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.02 0.34 - 0.64 - - add rsi, -9
+ - - 0.33 0.66 - 0.01 - - movabs rcx, -6148914691236517205
+ - - 0.66 0.34 - - - - mov rax, rsi
+ - - 1.00 1.00 - - - - mul rcx
+ - - 0.01 0.99 - - - - mov rax, rdx
+ - - 0.99 - - 0.01 - - shr rax
+ - - - - - - 0.50 0.50 movzx ecx, word ptr [rdi]
+ - - 0.33 0.03 - 0.64 - - cmp cx, -16192
+ - - 0.01 0.31 - 0.68 - - mov edx, 2
+ - - 1.00 1.00 - - - - cmove rdx, rax
+ - - - - - - - - xor eax, eax
+ - - 0.33 0.33 - 0.34 - - cmp ecx, 49344
+ - - 1.00 1.00 - - - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.rs b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.rs
new file mode 100644
index 000000000000..41a466ec8e6a
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_prefix_dynamic_size(source: &[u8]) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64 b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64
new file mode 100644
index 000000000000..be7f34b9f8f5
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64
@@ -0,0 +1,22 @@
+bench_try_ref_from_prefix_dynamic_size:
+ xor edx, edx
+ mov eax, 0
+ test dil, 1
+ jne .LBB5_4
+ cmp rsi, 4
+ jae .LBB5_3
+ mov edx, 1
+ xor eax, eax
+ ret
+.LBB5_3:
+ add rsi, -4
+ shr rsi
+ movzx ecx, word ptr [rdi]
+ cmp ecx, 49344
+ mov edx, 2
+ cmove rdx, rsi
+ xor eax, eax
+ cmp cx, -16192
+ cmove rax, rdi
+.LBB5_4:
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..11706defe11e
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_dynamic_size.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 573
+Total uOps: 2100
+
+Dispatch Width: 4
+uOps Per Cycle: 3.66
+IPC: 3.32
+Block RThroughput: 5.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 0 0.25 xor edx, edx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 1
+ 1 1 1.00 jne .LBB5_4
+ 1 1 0.33 cmp rsi, 4
+ 1 1 1.00 jae .LBB5_3
+ 1 1 0.33 mov edx, 1
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.33 add rsi, -4
+ 1 1 0.50 shr rsi
+ 1 5 0.50 * movzx ecx, word ptr [rdi]
+ 1 1 0.33 cmp ecx, 49344
+ 1 1 0.33 mov edx, 2
+ 2 2 0.67 cmove rdx, rsi
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp cx, -16192
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 5.66 5.67 - 5.67 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - - - - - xor edx, edx
+ - - 0.30 0.37 - 0.33 - - mov eax, 0
+ - - 0.35 0.32 - 0.33 - - test dil, 1
+ - - - - - 1.00 - - jne .LBB5_4
+ - - 0.32 0.33 - 0.35 - - cmp rsi, 4
+ - - - - - 1.00 - - jae .LBB5_3
+ - - 0.33 0.35 - 0.32 - - mov edx, 1
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.34 0.64 - 0.02 - - add rsi, -4
+ - - 1.00 - - - - - shr rsi
+ - - - - - - 0.50 0.50 movzx ecx, word ptr [rdi]
+ - - 0.60 0.40 - - - - cmp ecx, 49344
+ - - 0.05 0.95 - - - - mov edx, 2
+ - - 1.00 1.00 - - - - cmove rdx, rsi
+ - - - - - - - - xor eax, eax
+ - - 0.37 0.31 - 0.32 - - cmp cx, -16192
+ - - 1.00 1.00 - - - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_static_size.rs b/rust/zerocopy/benches/try_ref_from_prefix_static_size.rs
new file mode 100644
index 000000000000..5f13d482b505
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_static_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_prefix_static_size(source: &[u8]) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_prefix(source) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64 b/rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64
new file mode 100644
index 000000000000..83212f776ea6
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64
@@ -0,0 +1,15 @@
+bench_try_ref_from_prefix_static_size:
+ cmp rsi, 6
+ setb al
+ or al, dil
+ test al, 1
+ jne .LBB5_2
+ movzx eax, word ptr [rdi]
+ cmp eax, 49344
+ mov eax, 2
+ cmove rax, rdi
+ je .LBB5_3
+.LBB5_2:
+ xor eax, eax
+.LBB5_3:
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64.mca
new file mode 100644
index 000000000000..5d02b863a741
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_static_size.x86-64.mca
@@ -0,0 +1,63 @@
+Iterations: 100
+Instructions: 1200
+Total Cycles: 374
+Total uOps: 1300
+
+Dispatch Width: 4
+uOps Per Cycle: 3.48
+IPC: 3.21
+Block RThroughput: 3.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 cmp rsi, 6
+ 1 1 0.50 setb al
+ 1 1 0.33 or al, dil
+ 1 1 0.33 test al, 1
+ 1 1 1.00 jne .LBB5_2
+ 1 5 0.50 * movzx eax, word ptr [rdi]
+ 1 1 0.33 cmp eax, 49344
+ 1 1 0.33 mov eax, 2
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 je .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 3.66 3.65 - 3.69 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.35 0.64 - 0.01 - - cmp rsi, 6
+ - - 1.00 - - - - - setb al
+ - - 0.02 0.66 - 0.32 - - or al, dil
+ - - 0.03 0.65 - 0.32 - - test al, 1
+ - - - - - 1.00 - - jne .LBB5_2
+ - - - - - - 0.50 0.50 movzx eax, word ptr [rdi]
+ - - 0.92 0.07 - 0.01 - - cmp eax, 49344
+ - - 0.37 0.63 - - - - mov eax, 2
+ - - 0.97 1.00 - 0.03 - - cmove rax, rdi
+ - - - - - 1.00 - - je .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.rs b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.rs
new file mode 100644
index 000000000000..1744a40759b1
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_prefix_with_elems_dynamic_padding(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_prefix_with_elems(source, count) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64 b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64
new file mode 100644
index 000000000000..80e66ba1601c
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64
@@ -0,0 +1,30 @@
+bench_try_ref_from_prefix_with_elems_dynamic_padding:
+ movabs rax, 3074457345618258598
+ cmp rdx, rax
+ ja .LBB5_1
+ xor ecx, ecx
+ mov eax, 0
+ test dil, 3
+ je .LBB5_3
+ mov rdx, rcx
+ ret
+.LBB5_3:
+ lea rax, [rdx + 2*rdx]
+ or rax, 3
+ add rax, 9
+ cmp rax, rsi
+ jbe .LBB5_4
+.LBB5_1:
+ xor eax, eax
+ mov edx, 1
+ ret
+.LBB5_4:
+ movzx esi, word ptr [rdi]
+ cmp si, -16192
+ mov ecx, 2
+ cmove rcx, rdx
+ xor eax, eax
+ cmp esi, 49344
+ cmove rax, rdi
+ mov rdx, rcx
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..512e8ce64393
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_padding.x86-64.mca
@@ -0,0 +1,91 @@
+Iterations: 100
+Instructions: 2600
+Total Cycles: 806
+Total uOps: 2800
+
+Dispatch Width: 4
+uOps Per Cycle: 3.47
+IPC: 3.23
+Block RThroughput: 7.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 3074457345618258598
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 0 0.25 xor ecx, ecx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 3
+ 1 1 1.00 je .LBB5_3
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rax, [rdx + 2*rdx]
+ 1 1 0.33 or rax, 3
+ 1 1 0.33 add rax, 9
+ 1 1 0.33 cmp rax, rsi
+ 1 1 1.00 jbe .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov edx, 1
+ 1 1 1.00 U ret
+ 1 5 0.50 * movzx esi, word ptr [rdi]
+ 1 1 0.33 cmp si, -16192
+ 1 1 0.33 mov ecx, 2
+ 2 2 0.67 cmove rcx, rdx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp esi, 49344
+ 2 2 0.67 cmove rax, rdi
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 7.98 7.99 - 8.03 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.98 - - 0.02 - - movabs rax, 3074457345618258598
+ - - - 1.00 - - - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - - - - - - - xor ecx, ecx
+ - - 0.99 0.01 - - - - mov eax, 0
+ - - 0.01 0.96 - 0.03 - - test dil, 3
+ - - - - - 1.00 - - je .LBB5_3
+ - - 0.97 0.01 - 0.02 - - mov rdx, rcx
+ - - - - - 1.00 - - ret
+ - - 0.03 0.97 - - - - lea rax, [rdx + 2*rdx]
+ - - 0.03 0.97 - - - - or rax, 3
+ - - 0.01 0.99 - - - - add rax, 9
+ - - - 1.00 - - - - cmp rax, rsi
+ - - - - - 1.00 - - jbe .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - 0.98 0.01 - 0.01 - - mov edx, 1
+ - - - - - 1.00 - - ret
+ - - - - - - 0.50 0.50 movzx esi, word ptr [rdi]
+ - - 0.97 0.03 - - - - cmp si, -16192
+ - - 0.98 0.01 - 0.01 - - mov ecx, 2
+ - - 1.00 0.03 - 0.97 - - cmove rcx, rdx
+ - - - - - - - - xor eax, eax
+ - - 0.03 0.97 - - - - cmp esi, 49344
+ - - 1.00 1.00 - - - - cmove rax, rdi
+ - - - 0.03 - 0.97 - - mov rdx, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.rs b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.rs
new file mode 100644
index 000000000000..ed0f50941194
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_prefix_with_elems_dynamic_size(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_prefix_with_elems(source, count) {
+ Ok((packet, _rest)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64 b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64
new file mode 100644
index 000000000000..c12e87c137c5
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64
@@ -0,0 +1,26 @@
+bench_try_ref_from_prefix_with_elems_dynamic_size:
+ movabs rax, 4611686018427387901
+ cmp rdx, rax
+ ja .LBB5_1
+ mov rcx, rdx
+ xor edx, edx
+ mov eax, 0
+ test dil, 1
+ jne .LBB5_5
+ lea rax, [2*rcx + 4]
+ cmp rax, rsi
+ jbe .LBB5_4
+.LBB5_1:
+ xor eax, eax
+ mov edx, 1
+ ret
+.LBB5_4:
+ movzx esi, word ptr [rdi]
+ cmp si, -16192
+ mov edx, 2
+ cmove rdx, rcx
+ xor eax, eax
+ cmp esi, 49344
+ cmove rax, rdi
+.LBB5_5:
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..6c3f1a1ec97a
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_prefix_with_elems_dynamic_size.x86-64.mca
@@ -0,0 +1,83 @@
+Iterations: 100
+Instructions: 2200
+Total Cycles: 674
+Total uOps: 2400
+
+Dispatch Width: 4
+uOps Per Cycle: 3.56
+IPC: 3.26
+Block RThroughput: 6.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 4611686018427387901
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.33 mov rcx, rdx
+ 1 0 0.25 xor edx, edx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test dil, 1
+ 1 1 1.00 jne .LBB5_5
+ 1 1 0.50 lea rax, [2*rcx + 4]
+ 1 1 0.33 cmp rax, rsi
+ 1 1 1.00 jbe .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov edx, 1
+ 1 1 1.00 U ret
+ 1 5 0.50 * movzx esi, word ptr [rdi]
+ 1 1 0.33 cmp si, -16192
+ 1 1 0.33 mov edx, 2
+ 2 2 0.67 cmove rdx, rcx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp esi, 49344
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.65 6.66 - 6.69 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.66 0.33 - 0.01 - - movabs rax, 4611686018427387901
+ - - 0.02 0.66 - 0.32 - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - 0.66 0.33 - 0.01 - - mov rcx, rdx
+ - - - - - - - - xor edx, edx
+ - - 0.33 0.01 - 0.66 - - mov eax, 0
+ - - 0.34 0.65 - 0.01 - - test dil, 1
+ - - - - - 1.00 - - jne .LBB5_5
+ - - 0.65 0.35 - - - - lea rax, [2*rcx + 4]
+ - - - 1.00 - - - - cmp rax, rsi
+ - - - - - 1.00 - - jbe .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - 0.34 0.01 - 0.65 - - mov edx, 1
+ - - - - - 1.00 - - ret
+ - - - - - - 0.50 0.50 movzx esi, word ptr [rdi]
+ - - 0.65 0.34 - 0.01 - - cmp si, -16192
+ - - 0.66 0.34 - - - - mov edx, 2
+ - - 1.00 0.99 - 0.01 - - cmove rdx, rcx
+ - - - - - - - - xor eax, eax
+ - - 0.34 0.66 - - - - cmp esi, 49344
+ - - 1.00 0.99 - 0.01 - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.rs b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.rs
new file mode 100644
index 000000000000..981feca3ca24
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_suffix_dynamic_padding(source: &[u8]) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64 b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64
new file mode 100644
index 000000000000..b3e924442865
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64
@@ -0,0 +1,26 @@
+bench_try_ref_from_suffix_dynamic_padding:
+ lea eax, [rsi + rdi]
+ test al, 3
+ jne .LBB5_1
+ movabs rax, 9223372036854775804
+ and rax, rsi
+ cmp rax, 9
+ jae .LBB5_3
+.LBB5_1:
+ xor eax, eax
+ ret
+.LBB5_3:
+ add rax, -9
+ movabs rcx, -6148914691236517205
+ mul rcx
+ shr rdx
+ lea rcx, [rdx + 2*rdx]
+ sub rsi, rcx
+ or rcx, -4
+ add rsi, rdi
+ lea rdi, [rcx + rsi]
+ add rdi, -8
+ xor eax, eax
+ cmp word ptr [rcx + rsi - 8], -16192
+ cmove rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..d56ae56d854a
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_padding.x86-64.mca
@@ -0,0 +1,85 @@
+Iterations: 100
+Instructions: 2300
+Total Cycles: 791
+Total uOps: 2600
+
+Dispatch Width: 4
+uOps Per Cycle: 3.29
+IPC: 2.91
+Block RThroughput: 6.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea eax, [rsi + rdi]
+ 1 1 0.33 test al, 3
+ 1 1 1.00 jne .LBB5_1
+ 1 1 0.33 movabs rax, 9223372036854775804
+ 1 1 0.33 and rax, rsi
+ 1 1 0.33 cmp rax, 9
+ 1 1 1.00 jae .LBB5_3
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.33 add rax, -9
+ 1 1 0.33 movabs rcx, -6148914691236517205
+ 2 4 1.00 mul rcx
+ 1 1 0.50 shr rdx
+ 1 1 0.50 lea rcx, [rdx + 2*rdx]
+ 1 1 0.33 sub rsi, rcx
+ 1 1 0.33 or rcx, -4
+ 1 1 0.33 add rsi, rdi
+ 1 1 0.50 lea rdi, [rcx + rsi]
+ 1 1 0.33 add rdi, -8
+ 1 0 0.25 xor eax, eax
+ 2 6 0.50 * cmp word ptr [rcx + rsi - 8], -16192
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 7.70 7.58 - 7.72 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.26 0.74 - - - - lea eax, [rsi + rdi]
+ - - 0.19 0.28 - 0.53 - - test al, 3
+ - - - - - 1.00 - - jne .LBB5_1
+ - - 0.93 0.06 - 0.01 - - movabs rax, 9223372036854775804
+ - - 0.81 0.14 - 0.05 - - and rax, rsi
+ - - 0.55 0.43 - 0.02 - - cmp rax, 9
+ - - - - - 1.00 - - jae .LBB5_3
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.42 0.56 - 0.02 - - add rax, -9
+ - - 0.67 0.30 - 0.03 - - movabs rcx, -6148914691236517205
+ - - 1.00 1.00 - - - - mul rcx
+ - - 0.71 - - 0.29 - - shr rdx
+ - - 0.32 0.68 - - - - lea rcx, [rdx + 2*rdx]
+ - - 0.57 0.04 - 0.39 - - sub rsi, rcx
+ - - 0.28 0.67 - 0.05 - - or rcx, -4
+ - - 0.29 0.29 - 0.42 - - add rsi, rdi
+ - - 0.02 0.98 - - - - lea rdi, [rcx + rsi]
+ - - 0.02 0.41 - 0.57 - - add rdi, -8
+ - - - - - - - - xor eax, eax
+ - - 0.57 0.01 - 0.42 0.50 0.50 cmp word ptr [rcx + rsi - 8], -16192
+ - - 0.09 0.99 - 0.92 - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.rs b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.rs
new file mode 100644
index 000000000000..c3d75ac9b3e1
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_suffix_dynamic_size(source: &[u8]) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64 b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64
new file mode 100644
index 000000000000..d51f7817e599
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64
@@ -0,0 +1,18 @@
+bench_try_ref_from_suffix_dynamic_size:
+ lea eax, [rsi + rdi]
+ cmp rsi, 4
+ setb cl
+ or cl, al
+ test cl, 1
+ je .LBB5_2
+ xor eax, eax
+ ret
+.LBB5_2:
+ lea rdx, [rsi - 4]
+ shr rdx
+ and esi, 1
+ lea rcx, [rdi + rsi]
+ xor eax, eax
+ cmp word ptr [rdi + rsi], -16192
+ cmove rax, rcx
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..6cf7f8e493f5
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_dynamic_size.x86-64.mca
@@ -0,0 +1,71 @@
+Iterations: 100
+Instructions: 1600
+Total Cycles: 510
+Total uOps: 1800
+
+Dispatch Width: 4
+uOps Per Cycle: 3.53
+IPC: 3.14
+Block RThroughput: 4.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea eax, [rsi + rdi]
+ 1 1 0.33 cmp rsi, 4
+ 1 1 0.50 setb cl
+ 1 1 0.33 or cl, al
+ 1 1 0.33 test cl, 1
+ 1 1 1.00 je .LBB5_2
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rdx, [rsi - 4]
+ 1 1 0.50 shr rdx
+ 1 1 0.33 and esi, 1
+ 1 1 0.50 lea rcx, [rdi + rsi]
+ 1 0 0.25 xor eax, eax
+ 2 6 0.50 * cmp word ptr [rdi + rsi], -16192
+ 2 2 0.67 cmove rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.99 5.00 - 5.01 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.98 0.02 - - - - lea eax, [rsi + rdi]
+ - - - 0.98 - 0.02 - - cmp rsi, 4
+ - - 1.00 - - - - - setb cl
+ - - 0.01 0.99 - - - - or cl, al
+ - - 0.01 0.07 - 0.92 - - test cl, 1
+ - - - - - 1.00 - - je .LBB5_2
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.93 0.07 - - - - lea rdx, [rsi - 4]
+ - - 0.93 - - 0.07 - - shr rdx
+ - - 0.06 0.93 - 0.01 - - and esi, 1
+ - - 0.07 0.93 - - - - lea rcx, [rdi + rsi]
+ - - - - - - - - xor eax, eax
+ - - - 0.01 - 0.99 0.50 0.50 cmp word ptr [rdi + rsi], -16192
+ - - 1.00 1.00 - - - - cmove rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_static_size.rs b/rust/zerocopy/benches/try_ref_from_suffix_static_size.rs
new file mode 100644
index 000000000000..d4b92f639a32
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_static_size.rs
@@ -0,0 +1,10 @@
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_suffix_static_size(source: &[u8]) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_suffix(source) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64 b/rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64
new file mode 100644
index 000000000000..cd39f70931bc
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64
@@ -0,0 +1,16 @@
+bench_try_ref_from_suffix_static_size:
+ lea eax, [rsi + rdi]
+ cmp rsi, 6
+ setb cl
+ or cl, al
+ test cl, 1
+ je .LBB5_2
+ xor eax, eax
+ ret
+.LBB5_2:
+ lea rcx, [rdi + rsi]
+ add rcx, -6
+ xor eax, eax
+ cmp word ptr [rdi + rsi - 6], -16192
+ cmove rax, rcx
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64.mca
new file mode 100644
index 000000000000..087d1e7ed971
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_static_size.x86-64.mca
@@ -0,0 +1,67 @@
+Iterations: 100
+Instructions: 1400
+Total Cycles: 443
+Total uOps: 1600
+
+Dispatch Width: 4
+uOps Per Cycle: 3.61
+IPC: 3.16
+Block RThroughput: 4.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea eax, [rsi + rdi]
+ 1 1 0.33 cmp rsi, 6
+ 1 1 0.50 setb cl
+ 1 1 0.33 or cl, al
+ 1 1 0.33 test cl, 1
+ 1 1 1.00 je .LBB5_2
+ 1 0 0.25 xor eax, eax
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rcx, [rdi + rsi]
+ 1 1 0.33 add rcx, -6
+ 1 0 0.25 xor eax, eax
+ 2 6 0.50 * cmp word ptr [rdi + rsi - 6], -16192
+ 2 2 0.67 cmove rax, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.33 4.33 - 4.34 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.32 0.68 - - - - lea eax, [rsi + rdi]
+ - - 0.05 0.94 - 0.01 - - cmp rsi, 6
+ - - 1.00 - - - - - setb cl
+ - - 0.95 0.05 - - - - or cl, al
+ - - 0.95 0.02 - 0.03 - - test cl, 1
+ - - - - - 1.00 - - je .LBB5_2
+ - - - - - - - - xor eax, eax
+ - - - - - 1.00 - - ret
+ - - 0.04 0.96 - - - - lea rcx, [rdi + rsi]
+ - - 0.02 0.97 - 0.01 - - add rcx, -6
+ - - - - - - - - xor eax, eax
+ - - 0.03 0.66 - 0.31 0.50 0.50 cmp word ptr [rdi + rsi - 6], -16192
+ - - 0.97 0.05 - 0.98 - - cmove rax, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.rs b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.rs
new file mode 100644
index 000000000000..1da455c9a238
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_suffix_with_elems_dynamic_padding(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_suffix_with_elems(source, count) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64 b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64
new file mode 100644
index 000000000000..c7530d8b6815
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64
@@ -0,0 +1,32 @@
+bench_try_ref_from_suffix_with_elems_dynamic_padding:
+ movabs rax, 3074457345618258598
+ cmp rdx, rax
+ ja .LBB5_1
+ lea r8d, [rsi + rdi]
+ xor ecx, ecx
+ mov eax, 0
+ test r8b, 3
+ je .LBB5_3
+ mov rdx, rcx
+ ret
+.LBB5_3:
+ lea rax, [rdx + 2*rdx]
+ or rax, 3
+ add rax, 9
+ sub rsi, rax
+ jae .LBB5_4
+.LBB5_1:
+ xor eax, eax
+ mov edx, 1
+ ret
+.LBB5_4:
+ lea r8, [rdi + rsi]
+ movzx esi, word ptr [rdi + rsi]
+ cmp si, -16192
+ mov ecx, 2
+ cmove rcx, rdx
+ xor eax, eax
+ cmp esi, 49344
+ cmove rax, r8
+ mov rdx, rcx
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..be736c00c250
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_padding.x86-64.mca
@@ -0,0 +1,95 @@
+Iterations: 100
+Instructions: 2800
+Total Cycles: 878
+Total uOps: 3000
+
+Dispatch Width: 4
+uOps Per Cycle: 3.42
+IPC: 3.19
+Block RThroughput: 7.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 3074457345618258598
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.50 lea r8d, [rsi + rdi]
+ 1 0 0.25 xor ecx, ecx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test r8b, 3
+ 1 1 1.00 je .LBB5_3
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+ 1 1 0.50 lea rax, [rdx + 2*rdx]
+ 1 1 0.33 or rax, 3
+ 1 1 0.33 add rax, 9
+ 1 1 0.33 sub rsi, rax
+ 1 1 1.00 jae .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov edx, 1
+ 1 1 1.00 U ret
+ 1 1 0.50 lea r8, [rdi + rsi]
+ 1 5 0.50 * movzx esi, word ptr [rdi + rsi]
+ 1 1 0.33 cmp si, -16192
+ 1 1 0.33 mov ecx, 2
+ 2 2 0.67 cmove rcx, rdx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp esi, 49344
+ 2 2 0.67 cmove rax, r8
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 8.65 8.65 - 8.70 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.67 0.30 - 0.03 - - movabs rax, 3074457345618258598
+ - - 0.01 0.99 - - - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - 0.99 0.01 - - - - lea r8d, [rsi + rdi]
+ - - - - - - - - xor ecx, ecx
+ - - 0.35 0.62 - 0.03 - - mov eax, 0
+ - - 0.99 0.01 - - - - test r8b, 3
+ - - - - - 1.00 - - je .LBB5_3
+ - - 0.68 0.30 - 0.02 - - mov rdx, rcx
+ - - - - - 1.00 - - ret
+ - - 0.07 0.93 - - - - lea rax, [rdx + 2*rdx]
+ - - 0.06 0.35 - 0.59 - - or rax, 3
+ - - 0.02 0.07 - 0.91 - - add rax, 9
+ - - 0.01 0.04 - 0.95 - - sub rsi, rax
+ - - - - - 1.00 - - jae .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - 0.92 0.01 - 0.07 - - mov edx, 1
+ - - - - - 1.00 - - ret
+ - - - 1.00 - - - - lea r8, [rdi + rsi]
+ - - - - - - 0.50 0.50 movzx esi, word ptr [rdi + rsi]
+ - - 0.01 0.99 - - - - cmp si, -16192
+ - - 0.88 0.04 - 0.08 - - mov ecx, 2
+ - - 1.00 0.99 - 0.01 - - cmove rcx, rdx
+ - - - - - - - - xor eax, eax
+ - - 0.99 0.01 - - - - cmp esi, 49344
+ - - 1.00 1.00 - - - - cmove rax, r8
+ - - - 0.99 - 0.01 - - mov rdx, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.rs b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.rs
new file mode 100644
index 000000000000..8c2b80f8762f
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.rs
@@ -0,0 +1,13 @@
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_try_ref_from_suffix_with_elems_dynamic_size(
+ source: &[u8],
+ count: usize,
+) -> Option<&format::CocoPacket> {
+ match zerocopy::TryFromBytes::try_ref_from_suffix_with_elems(source, count) {
+ Ok((_rest, packet)) => Some(packet),
+ _ => None,
+ }
+}
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64 b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64
new file mode 100644
index 000000000000..952eb12de8d6
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64
@@ -0,0 +1,28 @@
+bench_try_ref_from_suffix_with_elems_dynamic_size:
+ movabs rax, 4611686018427387901
+ cmp rdx, rax
+ ja .LBB5_1
+ lea r8d, [rsi + rdi]
+ xor ecx, ecx
+ mov eax, 0
+ test r8b, 1
+ jne .LBB5_5
+ lea rax, [2*rdx + 4]
+ sub rsi, rax
+ jae .LBB5_4
+.LBB5_1:
+ xor eax, eax
+ mov edx, 1
+ ret
+.LBB5_4:
+ lea r8, [rdi + rsi]
+ movzx esi, word ptr [rdi + rsi]
+ cmp si, -16192
+ mov ecx, 2
+ cmove rcx, rdx
+ xor eax, eax
+ cmp esi, 49344
+ cmove rax, r8
+.LBB5_5:
+ mov rdx, rcx
+ ret
diff --git a/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64.mca b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..d4f78f67a252
--- /dev/null
+++ b/rust/zerocopy/benches/try_ref_from_suffix_with_elems_dynamic_size.x86-64.mca
@@ -0,0 +1,87 @@
+Iterations: 100
+Instructions: 2400
+Total Cycles: 1107
+Total uOps: 2600
+
+Dispatch Width: 4
+uOps Per Cycle: 2.35
+IPC: 2.17
+Block RThroughput: 6.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movabs rax, 4611686018427387901
+ 1 1 0.33 cmp rdx, rax
+ 1 1 1.00 ja .LBB5_1
+ 1 1 0.50 lea r8d, [rsi + rdi]
+ 1 0 0.25 xor ecx, ecx
+ 1 1 0.33 mov eax, 0
+ 1 1 0.33 test r8b, 1
+ 1 1 1.00 jne .LBB5_5
+ 1 1 0.50 lea rax, [2*rdx + 4]
+ 1 1 0.33 sub rsi, rax
+ 1 1 1.00 jae .LBB5_4
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 mov edx, 1
+ 1 1 1.00 U ret
+ 1 1 0.50 lea r8, [rdi + rsi]
+ 1 5 0.50 * movzx esi, word ptr [rdi + rsi]
+ 1 1 0.33 cmp si, -16192
+ 1 1 0.33 mov ecx, 2
+ 2 2 0.67 cmove rcx, rdx
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp esi, 49344
+ 2 2 0.67 cmove rax, r8
+ 1 1 0.33 mov rdx, rcx
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 6.99 7.00 - 8.01 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.02 0.95 - 0.03 - - movabs rax, 4611686018427387901
+ - - 0.93 0.04 - 0.03 - - cmp rdx, rax
+ - - - - - 1.00 - - ja .LBB5_1
+ - - 0.96 0.04 - - - - lea r8d, [rsi + rdi]
+ - - - - - - - - xor ecx, ecx
+ - - 0.95 0.02 - 0.03 - - mov eax, 0
+ - - 0.95 0.05 - - - - test r8b, 1
+ - - - - - 1.00 - - jne .LBB5_5
+ - - 0.06 0.94 - - - - lea rax, [2*rdx + 4]
+ - - 0.93 0.07 - - - - sub rsi, rax
+ - - - - - 1.00 - - jae .LBB5_4
+ - - - - - - - - xor eax, eax
+ - - 0.03 0.95 - 0.02 - - mov edx, 1
+ - - - - - 1.00 - - ret
+ - - 0.97 0.03 - - - - lea r8, [rdi + rsi]
+ - - - - - - 0.50 0.50 movzx esi, word ptr [rdi + rsi]
+ - - 0.03 0.97 - - - - cmp si, -16192
+ - - 0.05 0.94 - 0.01 - - mov ecx, 2
+ - - 0.06 0.98 - 0.96 - - cmove rcx, rdx
+ - - - - - - - - xor eax, eax
+ - - 0.97 0.03 - - - - cmp esi, 49344
+ - - 0.06 0.96 - 0.98 - - cmove rax, r8
+ - - 0.02 0.03 - 0.95 - - mov rdx, rcx
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_transmute.rs b/rust/zerocopy/benches/try_transmute.rs
new file mode 100644
index 000000000000..c0de07a8d094
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute.rs
@@ -0,0 +1,16 @@
+use zerocopy::Unalign;
+use zerocopy_derive::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[derive(IntoBytes, KnownLayout, Immutable)]
+#[repr(C)]
+struct MinimalViableSource {
+ bytes: [u8; 6],
+}
+
+#[unsafe(no_mangle)]
+fn bench_try_transmute(source: MinimalViableSource) -> Option<Unalign<format::CocoPacket>> {
+ zerocopy::try_transmute!(source).ok()
+}
diff --git a/rust/zerocopy/benches/try_transmute.x86-64 b/rust/zerocopy/benches/try_transmute.x86-64
new file mode 100644
index 000000000000..9e16a663257c
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute.x86-64
@@ -0,0 +1,9 @@
+bench_try_transmute:
+ movzx ecx, di
+ xor eax, eax
+ cmp ecx, 49344
+ sete al
+ and rdi, -65536
+ xor rax, 49345
+ or rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/try_transmute.x86-64.mca b/rust/zerocopy/benches/try_transmute.x86-64.mca
new file mode 100644
index 000000000000..33abc3bf341e
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute.x86-64.mca
@@ -0,0 +1,55 @@
+Iterations: 100
+Instructions: 800
+Total Cycles: 238
+Total uOps: 800
+
+Dispatch Width: 4
+uOps Per Cycle: 3.36
+IPC: 3.36
+Block RThroughput: 2.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 movzx ecx, di
+ 1 0 0.25 xor eax, eax
+ 1 1 0.33 cmp ecx, 49344
+ 1 1 0.50 sete al
+ 1 1 0.33 and rdi, -65536
+ 1 1 0.33 xor rax, 49345
+ 1 1 0.33 or rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 2.33 2.33 - 2.34 - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.32 0.67 - 0.01 - - movzx ecx, di
+ - - - - - - - - xor eax, eax
+ - - 0.33 0.67 - - - - cmp ecx, 49344
+ - - 1.00 - - - - - sete al
+ - - 0.67 0.33 - - - - and rdi, -65536
+ - - - 0.66 - 0.34 - - xor rax, 49345
+ - - 0.01 - - 0.99 - - or rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_transmute_ref_dynamic_size.rs b/rust/zerocopy/benches/try_transmute_ref_dynamic_size.rs
new file mode 100644
index 000000000000..c9236e13d23c
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute_ref_dynamic_size.rs
@@ -0,0 +1,18 @@
+use zerocopy_derive::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[derive(IntoBytes, KnownLayout, Immutable)]
+#[repr(C, align(2))]
+struct MinimalViableSource {
+ header: [u8; 6],
+ trailer: [[u8; 2]],
+}
+
+#[unsafe(no_mangle)]
+fn bench_try_transmute_ref_dynamic_size(
+ source: &MinimalViableSource,
+) -> Option<&format::CocoPacket> {
+ zerocopy::try_transmute_ref!(source).ok()
+}
diff --git a/rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64 b/rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64
new file mode 100644
index 000000000000..d34d2b3eb3df
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64
@@ -0,0 +1,6 @@
+bench_try_transmute_ref_dynamic_size:
+ lea rdx, [rsi + 1]
+ xor eax, eax
+ cmp word ptr [rdi], -16192
+ cmove rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64.mca b/rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..bc771b504713
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute_ref_dynamic_size.x86-64.mca
@@ -0,0 +1,49 @@
+Iterations: 100
+Instructions: 500
+Total Cycles: 209
+Total uOps: 700
+
+Dispatch Width: 4
+uOps Per Cycle: 3.35
+IPC: 2.39
+Block RThroughput: 1.8
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea rdx, [rsi + 1]
+ 1 0 0.25 xor eax, eax
+ 2 6 0.50 * cmp word ptr [rdi], -16192
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.50 1.51 - 1.99 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.51 0.49 - - - - lea rdx, [rsi + 1]
+ - - - - - - - - xor eax, eax
+ - - - 0.02 - 0.98 0.50 0.50 cmp word ptr [rdi], -16192
+ - - 0.99 1.00 - 0.01 - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/try_transmute_ref_static_size.rs b/rust/zerocopy/benches/try_transmute_ref_static_size.rs
new file mode 100644
index 000000000000..631cce2b0bb9
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute_ref_static_size.rs
@@ -0,0 +1,17 @@
+use zerocopy_derive::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[derive(IntoBytes, KnownLayout, Immutable)]
+#[repr(C, align(2))]
+struct MinimalViableSource {
+ bytes: [u8; 6],
+}
+
+#[unsafe(no_mangle)]
+fn bench_try_transmute_ref_static_size(
+ source: &MinimalViableSource,
+) -> Option<&format::CocoPacket> {
+ zerocopy::try_transmute_ref!(source).ok()
+}
diff --git a/rust/zerocopy/benches/try_transmute_ref_static_size.x86-64 b/rust/zerocopy/benches/try_transmute_ref_static_size.x86-64
new file mode 100644
index 000000000000..8c16face9875
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute_ref_static_size.x86-64
@@ -0,0 +1,5 @@
+bench_try_transmute_ref_static_size:
+ xor eax, eax
+ cmp word ptr [rdi], -16192
+ cmove rax, rdi
+ ret
diff --git a/rust/zerocopy/benches/try_transmute_ref_static_size.x86-64.mca b/rust/zerocopy/benches/try_transmute_ref_static_size.x86-64.mca
new file mode 100644
index 000000000000..cf7384989536
--- /dev/null
+++ b/rust/zerocopy/benches/try_transmute_ref_static_size.x86-64.mca
@@ -0,0 +1,47 @@
+Iterations: 100
+Instructions: 400
+Total Cycles: 160
+Total uOps: 600
+
+Dispatch Width: 4
+uOps Per Cycle: 3.75
+IPC: 2.50
+Block RThroughput: 1.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 0 0.25 xor eax, eax
+ 2 6 0.50 * cmp word ptr [rdi], -16192
+ 2 2 0.67 cmove rax, rdi
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.02 1.48 - 1.50 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - - - - - xor eax, eax
+ - - 0.02 0.49 - 0.49 0.50 0.50 cmp word ptr [rdi], -16192
+ - - 1.00 0.99 - 0.01 - - cmove rax, rdi
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/write_to_dynamic_size.rs b/rust/zerocopy/benches/write_to_dynamic_size.rs
new file mode 100644
index 000000000000..c126a1468c9b
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_dynamic_size.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_write_to_dynamic_size(source: &format::CocoPacket, destination: &mut [u8]) -> Option<()> {
+ source.write_to(destination).ok()
+}
diff --git a/rust/zerocopy/benches/write_to_dynamic_size.x86-64 b/rust/zerocopy/benches/write_to_dynamic_size.x86-64
new file mode 100644
index 000000000000..c5abb17f7eba
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_dynamic_size.x86-64
@@ -0,0 +1,21 @@
+bench_write_to_dynamic_size:
+ push r14
+ push rbx
+ push rax
+ mov rbx, rcx
+ lea r14, [2*rsi + 5]
+ and r14, -2
+ cmp rcx, r14
+ jne .LBB5_2
+ mov rax, rdi
+ mov rdi, rdx
+ mov rsi, rax
+ mov rdx, rbx
+ call qword ptr [rip + memcpy@GOTPCREL]
+.LBB5_2:
+ cmp rbx, r14
+ sete al
+ add rsp, 8
+ pop rbx
+ pop r14
+ ret
diff --git a/rust/zerocopy/benches/write_to_dynamic_size.x86-64.mca b/rust/zerocopy/benches/write_to_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..5b2c08a31a29
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_dynamic_size.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 2890
+Total uOps: 2500
+
+Dispatch Width: 4
+uOps Per Cycle: 0.87
+IPC: 0.66
+Block RThroughput: 6.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push rbx
+ 2 5 1.00 * push rax
+ 1 1 0.33 mov rbx, rcx
+ 1 1 0.50 lea r14, [2*rsi + 5]
+ 1 1 0.33 and r14, -2
+ 1 1 0.33 cmp rcx, r14
+ 1 1 1.00 jne .LBB5_2
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 mov rdi, rdx
+ 1 1 0.33 mov rsi, rax
+ 1 1 0.33 mov rdx, rbx
+ 4 7 1.00 * call qword ptr [rip + memcpy@GOTPCREL]
+ 1 1 0.33 cmp rbx, r14
+ 1 1 0.50 sete al
+ 1 1 0.33 add rsp, 8
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r14
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.66 4.64 4.00 4.70 4.00 3.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - - 1.00 push r14
+ - - - - 1.00 - 1.00 - push rbx
+ - - - - 1.00 - - 1.00 push rax
+ - - 0.02 0.97 - 0.01 - - mov rbx, rcx
+ - - 0.97 0.03 - - - - lea r14, [2*rsi + 5]
+ - - 0.63 0.35 - 0.02 - - and r14, -2
+ - - 0.31 0.34 - 0.35 - - cmp rcx, r14
+ - - - - - 1.00 - - jne .LBB5_2
+ - - 0.33 0.33 - 0.34 - - mov rax, rdi
+ - - 0.36 0.31 - 0.33 - - mov rdi, rdx
+ - - 0.33 0.35 - 0.32 - - mov rsi, rax
+ - - 0.35 0.63 - 0.02 - - mov rdx, rbx
+ - - - - 1.00 1.00 2.00 - call qword ptr [rip + memcpy@GOTPCREL]
+ - - 0.65 0.35 - - - - cmp rbx, r14
+ - - 0.69 - - 0.31 - - sete al
+ - - 0.02 0.98 - - - - add rsp, 8
+ - - - - - - - 1.00 pop rbx
+ - - - - - - 1.00 - pop r14
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/write_to_prefix_dynamic_size.rs b/rust/zerocopy/benches/write_to_prefix_dynamic_size.rs
new file mode 100644
index 000000000000..a54d32773113
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_prefix_dynamic_size.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_write_to_prefix_dynamic_size(
+ source: &format::CocoPacket,
+ destination: &mut [u8],
+) -> Option<()> {
+ source.write_to_prefix(destination).ok()
+}
diff --git a/rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64 b/rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64
new file mode 100644
index 000000000000..d7779c6c9178
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64
@@ -0,0 +1,21 @@
+bench_write_to_prefix_dynamic_size:
+ push r14
+ push rbx
+ push rax
+ mov rbx, rcx
+ lea r14, [2*rsi + 5]
+ and r14, -2
+ cmp r14, rcx
+ ja .LBB5_2
+ mov rax, rdi
+ mov rdi, rdx
+ mov rsi, rax
+ mov rdx, r14
+ call qword ptr [rip + memcpy@GOTPCREL]
+.LBB5_2:
+ cmp r14, rbx
+ setbe al
+ add rsp, 8
+ pop rbx
+ pop r14
+ ret
diff --git a/rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64.mca b/rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..4cebe24d4f3b
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_prefix_dynamic_size.x86-64.mca
@@ -0,0 +1,77 @@
+Iterations: 100
+Instructions: 1900
+Total Cycles: 2890
+Total uOps: 2600
+
+Dispatch Width: 4
+uOps Per Cycle: 0.90
+IPC: 0.66
+Block RThroughput: 6.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push rbx
+ 2 5 1.00 * push rax
+ 1 1 0.33 mov rbx, rcx
+ 1 1 0.50 lea r14, [2*rsi + 5]
+ 1 1 0.33 and r14, -2
+ 1 1 0.33 cmp r14, rcx
+ 1 1 1.00 ja .LBB5_2
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 mov rdi, rdx
+ 1 1 0.33 mov rsi, rax
+ 1 1 0.33 mov rdx, r14
+ 4 7 1.00 * call qword ptr [rip + memcpy@GOTPCREL]
+ 1 1 0.33 cmp r14, rbx
+ 2 2 1.00 setbe al
+ 1 1 0.33 add rsp, 8
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r14
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 5.47 4.49 4.00 5.04 4.00 3.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - - 1.00 push r14
+ - - - - 1.00 - 1.00 - push rbx
+ - - - - 1.00 - - 1.00 push rax
+ - - 0.48 0.51 - 0.01 - - mov rbx, rcx
+ - - 0.51 0.49 - - - - lea r14, [2*rsi + 5]
+ - - 0.48 0.05 - 0.47 - - and r14, -2
+ - - 0.48 0.49 - 0.03 - - cmp r14, rcx
+ - - - - - 1.00 - - ja .LBB5_2
+ - - 0.04 0.47 - 0.49 - - mov rax, rdi
+ - - 0.49 0.03 - 0.48 - - mov rdi, rdx
+ - - 0.03 0.48 - 0.49 - - mov rsi, rax
+ - - 0.48 0.51 - 0.01 - - mov rdx, r14
+ - - - - 1.00 1.00 2.00 - call qword ptr [rip + memcpy@GOTPCREL]
+ - - 0.51 0.49 - - - - cmp r14, rbx
+ - - 1.94 - - 0.06 - - setbe al
+ - - 0.03 0.97 - - - - add rsp, 8
+ - - - - - - - 1.00 pop rbx
+ - - - - - - 1.00 - pop r14
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/write_to_prefix_static_size.rs b/rust/zerocopy/benches/write_to_prefix_static_size.rs
new file mode 100644
index 000000000000..826222c129fe
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_prefix_static_size.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_write_to_prefix_static_size(
+ source: &format::CocoPacket,
+ destination: &mut [u8],
+) -> Option<()> {
+ source.write_to_prefix(destination).ok()
+}
diff --git a/rust/zerocopy/benches/write_to_prefix_static_size.x86-64 b/rust/zerocopy/benches/write_to_prefix_static_size.x86-64
new file mode 100644
index 000000000000..9cf066295304
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_prefix_static_size.x86-64
@@ -0,0 +1,11 @@
+bench_write_to_prefix_static_size:
+ cmp rdx, 6
+ jb .LBB5_2
+ movzx eax, word ptr [rdi + 4]
+ mov word ptr [rsi + 4], ax
+ mov eax, dword ptr [rdi]
+ mov dword ptr [rsi], eax
+.LBB5_2:
+ cmp rdx, 6
+ setae al
+ ret
diff --git a/rust/zerocopy/benches/write_to_prefix_static_size.x86-64.mca b/rust/zerocopy/benches/write_to_prefix_static_size.x86-64.mca
new file mode 100644
index 000000000000..5d17200abd2d
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_prefix_static_size.x86-64.mca
@@ -0,0 +1,57 @@
+Iterations: 100
+Instructions: 900
+Total Cycles: 233
+Total uOps: 900
+
+Dispatch Width: 4
+uOps Per Cycle: 3.86
+IPC: 3.86
+Block RThroughput: 2.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 cmp rdx, 6
+ 1 1 1.00 jb .LBB5_2
+ 1 5 0.50 * movzx eax, word ptr [rdi + 4]
+ 1 1 1.00 * mov word ptr [rsi + 4], ax
+ 1 5 0.50 * mov eax, dword ptr [rdi]
+ 1 1 1.00 * mov dword ptr [rsi], eax
+ 1 1 0.33 cmp rdx, 6
+ 1 1 0.50 setae al
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.50 1.49 2.00 2.01 2.00 2.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.25 0.74 - 0.01 - - cmp rdx, 6
+ - - - - - 1.00 - - jb .LBB5_2
+ - - - - - - 0.50 0.50 movzx eax, word ptr [rdi + 4]
+ - - - - 1.00 - 0.48 0.52 mov word ptr [rsi + 4], ax
+ - - - - - - 0.52 0.48 mov eax, dword ptr [rdi]
+ - - - - 1.00 - 0.50 0.50 mov dword ptr [rsi], eax
+ - - 0.25 0.75 - - - - cmp rdx, 6
+ - - 1.00 - - - - - setae al
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/write_to_static_size.rs b/rust/zerocopy/benches/write_to_static_size.rs
new file mode 100644
index 000000000000..3bb9435c5ade
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_static_size.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_write_to_static_size(source: &format::CocoPacket, destination: &mut [u8]) -> Option<()> {
+ source.write_to(destination).ok()
+}
diff --git a/rust/zerocopy/benches/write_to_static_size.x86-64 b/rust/zerocopy/benches/write_to_static_size.x86-64
new file mode 100644
index 000000000000..d6413e0fd614
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_static_size.x86-64
@@ -0,0 +1,11 @@
+bench_write_to_static_size:
+ cmp rdx, 6
+ jne .LBB5_2
+ movzx eax, word ptr [rdi + 4]
+ mov word ptr [rsi + 4], ax
+ mov eax, dword ptr [rdi]
+ mov dword ptr [rsi], eax
+.LBB5_2:
+ cmp rdx, 6
+ sete al
+ ret
diff --git a/rust/zerocopy/benches/write_to_static_size.x86-64.mca b/rust/zerocopy/benches/write_to_static_size.x86-64.mca
new file mode 100644
index 000000000000..cc5bb1d26fc0
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_static_size.x86-64.mca
@@ -0,0 +1,57 @@
+Iterations: 100
+Instructions: 900
+Total Cycles: 233
+Total uOps: 900
+
+Dispatch Width: 4
+uOps Per Cycle: 3.86
+IPC: 3.86
+Block RThroughput: 2.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 cmp rdx, 6
+ 1 1 1.00 jne .LBB5_2
+ 1 5 0.50 * movzx eax, word ptr [rdi + 4]
+ 1 1 1.00 * mov word ptr [rsi + 4], ax
+ 1 5 0.50 * mov eax, dword ptr [rdi]
+ 1 1 1.00 * mov dword ptr [rsi], eax
+ 1 1 0.33 cmp rdx, 6
+ 1 1 0.50 sete al
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.50 1.49 2.00 2.01 2.00 2.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.25 0.74 - 0.01 - - cmp rdx, 6
+ - - - - - 1.00 - - jne .LBB5_2
+ - - - - - - 0.50 0.50 movzx eax, word ptr [rdi + 4]
+ - - - - 1.00 - 0.48 0.52 mov word ptr [rsi + 4], ax
+ - - - - - - 0.52 0.48 mov eax, dword ptr [rdi]
+ - - - - 1.00 - 0.50 0.50 mov dword ptr [rsi], eax
+ - - 0.25 0.75 - - - - cmp rdx, 6
+ - - 1.00 - - - - - sete al
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/write_to_suffix_dynamic_size.rs b/rust/zerocopy/benches/write_to_suffix_dynamic_size.rs
new file mode 100644
index 000000000000..9fa6b91cda41
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_suffix_dynamic_size.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_write_to_suffix_dynamic_size(
+ source: &format::CocoPacket,
+ destination: &mut [u8],
+) -> Option<()> {
+ source.write_to_suffix(destination).ok()
+}
diff --git a/rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64 b/rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64
new file mode 100644
index 000000000000..75f349562db6
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64
@@ -0,0 +1,22 @@
+bench_write_to_suffix_dynamic_size:
+ push r14
+ push rbx
+ push rax
+ mov rbx, rcx
+ lea r14, [2*rsi + 5]
+ and r14, -2
+ sub rcx, r14
+ jb .LBB5_2
+ mov rax, rdi
+ add rdx, rcx
+ mov rdi, rdx
+ mov rsi, rax
+ mov rdx, r14
+ call qword ptr [rip + memcpy@GOTPCREL]
+.LBB5_2:
+ cmp rbx, r14
+ setae al
+ add rsp, 8
+ pop rbx
+ pop r14
+ ret
diff --git a/rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64.mca b/rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..95cb9dfe2eb7
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_suffix_dynamic_size.x86-64.mca
@@ -0,0 +1,79 @@
+Iterations: 100
+Instructions: 2000
+Total Cycles: 2890
+Total uOps: 2600
+
+Dispatch Width: 4
+uOps Per Cycle: 0.90
+IPC: 0.69
+Block RThroughput: 6.5
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 2 5 1.00 * push r14
+ 2 5 1.00 * push rbx
+ 2 5 1.00 * push rax
+ 1 1 0.33 mov rbx, rcx
+ 1 1 0.50 lea r14, [2*rsi + 5]
+ 1 1 0.33 and r14, -2
+ 1 1 0.33 sub rcx, r14
+ 1 1 1.00 jb .LBB5_2
+ 1 1 0.33 mov rax, rdi
+ 1 1 0.33 add rdx, rcx
+ 1 1 0.33 mov rdi, rdx
+ 1 1 0.33 mov rsi, rax
+ 1 1 0.33 mov rdx, r14
+ 4 7 1.00 * call qword ptr [rip + memcpy@GOTPCREL]
+ 1 1 0.33 cmp rbx, r14
+ 1 1 0.50 setae al
+ 1 1 0.33 add rsp, 8
+ 1 6 0.50 * pop rbx
+ 1 6 0.50 * pop r14
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 4.98 4.98 4.00 5.04 4.00 3.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - - 1.00 push r14
+ - - - - 1.00 - 1.00 - push rbx
+ - - - - 1.00 - - 1.00 push rax
+ - - 0.94 0.05 - 0.01 - - mov rbx, rcx
+ - - 0.06 0.94 - - - - lea r14, [2*rsi + 5]
+ - - 0.93 0.02 - 0.05 - - and r14, -2
+ - - 0.05 0.94 - 0.01 - - sub rcx, r14
+ - - - - - 1.00 - - jb .LBB5_2
+ - - 0.02 0.04 - 0.94 - - mov rax, rdi
+ - - 0.03 0.97 - - - - add rdx, rcx
+ - - 0.95 0.05 - - - - mov rdi, rdx
+ - - 0.94 0.03 - 0.03 - - mov rsi, rax
+ - - 0.01 0.03 - 0.96 - - mov rdx, r14
+ - - - - 1.00 1.00 2.00 - call qword ptr [rip + memcpy@GOTPCREL]
+ - - 0.05 0.94 - 0.01 - - cmp rbx, r14
+ - - 0.97 - - 0.03 - - setae al
+ - - 0.03 0.97 - - - - add rsp, 8
+ - - - - - - - 1.00 pop rbx
+ - - - - - - 1.00 - pop r14
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/write_to_suffix_static_size.rs b/rust/zerocopy/benches/write_to_suffix_static_size.rs
new file mode 100644
index 000000000000..1c95aba4b16c
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_suffix_static_size.rs
@@ -0,0 +1,12 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_write_to_suffix_static_size(
+ source: &format::CocoPacket,
+ destination: &mut [u8],
+) -> Option<()> {
+ source.write_to_suffix(destination).ok()
+}
diff --git a/rust/zerocopy/benches/write_to_suffix_static_size.x86-64 b/rust/zerocopy/benches/write_to_suffix_static_size.x86-64
new file mode 100644
index 000000000000..934aa370d4d6
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_suffix_static_size.x86-64
@@ -0,0 +1,11 @@
+bench_write_to_suffix_static_size:
+ cmp rdx, 6
+ jb .LBB5_2
+ movzx eax, word ptr [rdi + 4]
+ mov word ptr [rsi + rdx - 2], ax
+ mov eax, dword ptr [rdi]
+ mov dword ptr [rsi + rdx - 6], eax
+.LBB5_2:
+ cmp rdx, 6
+ setae al
+ ret
diff --git a/rust/zerocopy/benches/write_to_suffix_static_size.x86-64.mca b/rust/zerocopy/benches/write_to_suffix_static_size.x86-64.mca
new file mode 100644
index 000000000000..6b18e4a44585
--- /dev/null
+++ b/rust/zerocopy/benches/write_to_suffix_static_size.x86-64.mca
@@ -0,0 +1,57 @@
+Iterations: 100
+Instructions: 900
+Total Cycles: 233
+Total uOps: 900
+
+Dispatch Width: 4
+uOps Per Cycle: 3.86
+IPC: 3.86
+Block RThroughput: 2.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.33 cmp rdx, 6
+ 1 1 1.00 jb .LBB5_2
+ 1 5 0.50 * movzx eax, word ptr [rdi + 4]
+ 1 1 1.00 * mov word ptr [rsi + rdx - 2], ax
+ 1 5 0.50 * mov eax, dword ptr [rdi]
+ 1 1 1.00 * mov dword ptr [rsi + rdx - 6], eax
+ 1 1 0.33 cmp rdx, 6
+ 1 1 0.50 setae al
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.50 1.49 2.00 2.01 2.00 2.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.25 0.74 - 0.01 - - cmp rdx, 6
+ - - - - - 1.00 - - jb .LBB5_2
+ - - - - - - 0.50 0.50 movzx eax, word ptr [rdi + 4]
+ - - - - 1.00 - 0.48 0.52 mov word ptr [rsi + rdx - 2], ax
+ - - - - - - 0.52 0.48 mov eax, dword ptr [rdi]
+ - - - - 1.00 - 0.50 0.50 mov dword ptr [rsi + rdx - 6], eax
+ - - 0.25 0.75 - - - - cmp rdx, 6
+ - - 1.00 - - - - - setae al
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/benches/zero_dynamic_padding.rs b/rust/zerocopy/benches/zero_dynamic_padding.rs
new file mode 100644
index 000000000000..8eda0953d1e6
--- /dev/null
+++ b/rust/zerocopy/benches/zero_dynamic_padding.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_padding.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_zero_dynamic_padding(source: &mut format::LocoPacket) {
+ source.zero()
+}
diff --git a/rust/zerocopy/benches/zero_dynamic_padding.x86-64 b/rust/zerocopy/benches/zero_dynamic_padding.x86-64
new file mode 100644
index 000000000000..7dccf1745f81
--- /dev/null
+++ b/rust/zerocopy/benches/zero_dynamic_padding.x86-64
@@ -0,0 +1,7 @@
+bench_zero_dynamic_padding:
+ lea rax, [rsi + 2*rsi]
+ movabs rdx, 9223372036854775804
+ and rdx, rax
+ add rdx, 12
+ xor esi, esi
+ jmp qword ptr [rip + memset@GOTPCREL]
diff --git a/rust/zerocopy/benches/zero_dynamic_padding.x86-64.mca b/rust/zerocopy/benches/zero_dynamic_padding.x86-64.mca
new file mode 100644
index 000000000000..098fc107875f
--- /dev/null
+++ b/rust/zerocopy/benches/zero_dynamic_padding.x86-64.mca
@@ -0,0 +1,51 @@
+Iterations: 100
+Instructions: 600
+Total Cycles: 209
+Total uOps: 700
+
+Dispatch Width: 4
+uOps Per Cycle: 3.35
+IPC: 2.87
+Block RThroughput: 1.8
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea rax, [rsi + 2*rsi]
+ 1 1 0.33 movabs rdx, 9223372036854775804
+ 1 1 0.33 and rdx, rax
+ 1 1 0.33 add rdx, 12
+ 1 0 0.25 xor esi, esi
+ 2 6 1.00 * jmp qword ptr [rip + memset@GOTPCREL]
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 1.66 1.66 - 1.68 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.33 0.67 - - - - lea rax, [rsi + 2*rsi]
+ - - 0.98 - - 0.02 - - movabs rdx, 9223372036854775804
+ - - 0.01 0.66 - 0.33 - - and rdx, rax
+ - - 0.34 0.33 - 0.33 - - add rdx, 12
+ - - - - - - - - xor esi, esi
+ - - - - - 1.00 0.50 0.50 jmp qword ptr [rip + memset@GOTPCREL]
diff --git a/rust/zerocopy/benches/zero_dynamic_size.rs b/rust/zerocopy/benches/zero_dynamic_size.rs
new file mode 100644
index 000000000000..536d800ebc76
--- /dev/null
+++ b/rust/zerocopy/benches/zero_dynamic_size.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_dynamic_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_zero_dynamic_size(source: &mut format::LocoPacket) {
+ source.zero()
+}
diff --git a/rust/zerocopy/benches/zero_dynamic_size.x86-64 b/rust/zerocopy/benches/zero_dynamic_size.x86-64
new file mode 100644
index 000000000000..2b31ed644eaf
--- /dev/null
+++ b/rust/zerocopy/benches/zero_dynamic_size.x86-64
@@ -0,0 +1,5 @@
+bench_zero_dynamic_size:
+ lea rdx, [2*rsi + 5]
+ and rdx, -2
+ xor esi, esi
+ jmp qword ptr [rip + memset@GOTPCREL]
diff --git a/rust/zerocopy/benches/zero_dynamic_size.x86-64.mca b/rust/zerocopy/benches/zero_dynamic_size.x86-64.mca
new file mode 100644
index 000000000000..0b086a29b0ed
--- /dev/null
+++ b/rust/zerocopy/benches/zero_dynamic_size.x86-64.mca
@@ -0,0 +1,47 @@
+Iterations: 100
+Instructions: 400
+Total Cycles: 142
+Total uOps: 500
+
+Dispatch Width: 4
+uOps Per Cycle: 3.52
+IPC: 2.82
+Block RThroughput: 1.3
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 0.50 lea rdx, [2*rsi + 5]
+ 1 1 0.33 and rdx, -2
+ 1 0 0.25 xor esi, esi
+ 2 6 1.00 * jmp qword ptr [rip + memset@GOTPCREL]
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - 0.99 1.00 - 1.01 0.50 0.50
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - 0.99 0.01 - - - - lea rdx, [2*rsi + 5]
+ - - - 0.99 - 0.01 - - and rdx, -2
+ - - - - - - - - xor esi, esi
+ - - - - - 1.00 0.50 0.50 jmp qword ptr [rip + memset@GOTPCREL]
diff --git a/rust/zerocopy/benches/zero_static_size.rs b/rust/zerocopy/benches/zero_static_size.rs
new file mode 100644
index 000000000000..fa7fa0839c15
--- /dev/null
+++ b/rust/zerocopy/benches/zero_static_size.rs
@@ -0,0 +1,9 @@
+use zerocopy::*;
+
+#[path = "formats/coco_static_size.rs"]
+mod format;
+
+#[unsafe(no_mangle)]
+fn bench_zero_static_size(source: &mut format::LocoPacket) {
+ source.zero()
+}
diff --git a/rust/zerocopy/benches/zero_static_size.x86-64 b/rust/zerocopy/benches/zero_static_size.x86-64
new file mode 100644
index 000000000000..ced8e18949b2
--- /dev/null
+++ b/rust/zerocopy/benches/zero_static_size.x86-64
@@ -0,0 +1,4 @@
+bench_zero_static_size:
+ mov word ptr [rdi + 4], 0
+ mov dword ptr [rdi], 0
+ ret
diff --git a/rust/zerocopy/benches/zero_static_size.x86-64.mca b/rust/zerocopy/benches/zero_static_size.x86-64.mca
new file mode 100644
index 000000000000..042897ef2dfe
--- /dev/null
+++ b/rust/zerocopy/benches/zero_static_size.x86-64.mca
@@ -0,0 +1,45 @@
+Iterations: 100
+Instructions: 300
+Total Cycles: 203
+Total uOps: 300
+
+Dispatch Width: 4
+uOps Per Cycle: 1.48
+IPC: 1.48
+Block RThroughput: 2.0
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects (U)
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 1 1.00 * mov word ptr [rdi + 4], 0
+ 1 1 1.00 * mov dword ptr [rdi], 0
+ 1 1 1.00 U ret
+
+
+Resources:
+[0] - SBDivider
+[1] - SBFPDivider
+[2] - SBPort0
+[3] - SBPort1
+[4] - SBPort4
+[5] - SBPort5
+[6.0] - SBPort23
+[6.1] - SBPort23
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1]
+ - - - - 2.00 1.00 1.00 1.00
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
+ - - - - 1.00 - - 1.00 mov word ptr [rdi + 4], 0
+ - - - - 1.00 - 1.00 - mov dword ptr [rdi], 0
+ - - - - - 1.00 - - ret
diff --git a/rust/zerocopy/rustdoc/style.css b/rust/zerocopy/rustdoc/style.css
new file mode 100644
index 000000000000..c5c8aeb743a1
--- /dev/null
+++ b/rust/zerocopy/rustdoc/style.css
@@ -0,0 +1,55 @@
+/*
+Copyright 2026 The Fuchsia Authors
+
+Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
+<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+This file may not be copied, modified, or distributed except according to
+those terms.
+*/
+
+.codegen-tabs {
+ display: grid;
+ grid-template-columns: repeat(var(--arity), minmax(200px, 1fr));
+ grid-template-rows: auto 1fr;
+ column-gap: 1rem;
+}
+
+.codegen-tabs:not(:has(> details[open]))::after {
+ grid-column: 1/-1;
+ content: 'Click one of the above headers to expand its contents.';
+ font-style: italic;
+ font-size: small;
+ text-align: center;
+}
+
+.codegen-tabs details {
+ display: grid;
+ grid-column: 1 / -1;
+ grid-row: 1 / span 2;
+ grid-template-columns: subgrid;
+ grid-template-rows: subgrid;
+}
+
+.codegen-tabs summary {
+ display: grid;
+ grid-column: var(--n) / span 1;
+ grid-row: 1;
+ z-index: 1;
+ border-bottom: 2px solid var(--headings-border-bottom-color);
+ cursor: pointer;
+}
+
+.codegen-tabs details[open] > summary {
+ background-color: var(--code-block-background-color);
+ border-bottom-color: var(--target-border-color);
+}
+
+.codegen-tabs details::details-content {
+ grid-column: 1 / -1;
+ grid-row: 2;
+}
+
+.codegen-tabs details:not([open])::details-content {
+ display: none;
+}
diff --git a/rust/zerocopy/src/byte_slice.rs b/rust/zerocopy/src/byte_slice.rs
new file mode 100644
index 000000000000..6f9ee9ac3336
--- /dev/null
+++ b/rust/zerocopy/src/byte_slice.rs
@@ -0,0 +1,432 @@
+// Copyright 2024 The Fuchsia Authors
+//
+// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+//! Traits for types that encapsulate a `[u8]`.
+//!
+//! These traits are used to bound the `B` parameter of [`Ref`].
+
+use core::{
+ cell,
+ ops::{Deref, DerefMut},
+};
+
+// For each trait polyfill, as soon as the corresponding feature is stable, the
+// polyfill import will be unused because method/function resolution will prefer
+// the inherent method/function over a trait method/function. Thus, we suppress
+// the `unused_imports` warning.
+//
+// See the documentation on `util::polyfills` for more information.
+#[allow(unused_imports)]
+use crate::util::polyfills::{self, NonNullExt as _, NumExt as _};
+#[cfg(doc)]
+use crate::Ref;
+
+/// A mutable or immutable reference to a byte slice.
+///
+/// `ByteSlice` abstracts over the mutability of a byte slice reference, and is
+/// implemented for various special reference types such as
+/// [`Ref<[u8]>`](core::cell::Ref) and [`RefMut<[u8]>`](core::cell::RefMut).
+///
+/// # Safety
+///
+/// Implementations of `ByteSlice` must promise that their implementations of
+/// [`Deref`] and [`DerefMut`] are "stable". In particular, given `B: ByteSlice`
+/// and `b: B`, two calls, each to either `b.deref()` or `b.deref_mut()`, must
+/// return a byte slice with the same address and length. This must hold even if
+/// the two calls are separated by an arbitrary sequence of calls to methods on
+/// `ByteSlice`, [`ByteSliceMut`], [`IntoByteSlice`], or [`IntoByteSliceMut`],
+/// or on their super-traits. This does *not* need to hold if the two calls are
+/// separated by any method calls, field accesses, or field modifications *other
+/// than* those from these traits.
+///
+/// Note that this also implies that, given `b: B`, the address and length
+/// cannot be modified via objects other than `b`, either on the same thread or
+/// on another thread.
+pub unsafe trait ByteSlice: Deref<Target = [u8]> + Sized {}
+
+/// A mutable reference to a byte slice.
+///
+/// `ByteSliceMut` abstracts over various ways of storing a mutable reference to
+/// a byte slice, and is implemented for various special reference types such as
+/// `RefMut<[u8]>`.
+///
+/// `ByteSliceMut` is a shorthand for [`ByteSlice`] and [`DerefMut`].
+pub trait ByteSliceMut: ByteSlice + DerefMut {}
+impl<B: ByteSlice + DerefMut> ByteSliceMut for B {}
+
+/// A [`ByteSlice`] which can be copied without violating dereference stability.
+///
+/// # Safety
+///
+/// If `B: CopyableByteSlice`, then the dereference stability properties
+/// required by [`ByteSlice`] (see that trait's safety documentation) do not
+/// only hold regarding two calls to `b.deref()` or `b.deref_mut()`, but also
+/// hold regarding `c.deref()` or `c.deref_mut()`, where `c` is produced by
+/// copying `b`.
+pub unsafe trait CopyableByteSlice: ByteSlice + Copy + CloneableByteSlice {}
+
+/// A [`ByteSlice`] which can be cloned without violating dereference stability.
+///
+/// # Safety
+///
+/// If `B: CloneableByteSlice`, then the dereference stability properties
+/// required by [`ByteSlice`] (see that trait's safety documentation) do not
+/// only hold regarding two calls to `b.deref()` or `b.deref_mut()`, but also
+/// hold regarding `c.deref()` or `c.deref_mut()`, where `c` is produced by
+/// `b.clone()`, `b.clone().clone()`, etc.
+pub unsafe trait CloneableByteSlice: ByteSlice + Clone {}
+
+/// A [`ByteSlice`] that can be split in two.
+///
+/// # Safety
+///
+/// Unsafe code may depend for its soundness on the assumption that `split_at`
+/// and `split_at_unchecked` are implemented correctly. In particular, given `B:
+/// SplitByteSlice` and `b: B`, if `b.deref()` returns a byte slice with address
+/// `addr` and length `len`, then if `split <= len`, both of these
+/// invocations:
+/// - `b.split_at(split)`
+/// - `b.split_at_unchecked(split)`
+///
+/// ...will return `(first, second)` such that:
+/// - `first`'s address is `addr` and its length is `split`
+/// - `second`'s address is `addr + split` and its length is `len - split`
+pub unsafe trait SplitByteSlice: ByteSlice {
+ /// Attempts to split `self` at the midpoint.
+ ///
+ /// `s.split_at(mid)` returns `Ok((s[..mid], s[mid..]))` if `mid <=
+ /// s.deref().len()` and otherwise returns `Err(s)`.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe code may rely on this function correctly implementing the above
+ /// functionality.
+ #[inline]
+ fn split_at(self, mid: usize) -> Result<(Self, Self), Self> {
+ if mid <= self.deref().len() {
+ // SAFETY: Above, we ensure that `mid <= self.deref().len()`. By
+ // invariant on `ByteSlice`, a supertrait of `SplitByteSlice`,
+ // `.deref()` is guaranteed to be "stable"; i.e., it will always
+ // dereference to a byte slice of the same address and length. Thus,
+ // we can be sure that the above precondition remains satisfied
+ // through the call to `split_at_unchecked`.
+ unsafe { Ok(self.split_at_unchecked(mid)) }
+ } else {
+ Err(self)
+ }
+ }
+
+ /// Splits the slice at the midpoint, possibly omitting bounds checks.
+ ///
+ /// `s.split_at_unchecked(mid)` returns `s[..mid]` and `s[mid..]`.
+ ///
+ /// # Safety
+ ///
+ /// `mid` must not be greater than `self.deref().len()`.
+ ///
+ /// # Panics
+ ///
+ /// Implementations of this method may choose to perform a bounds check and
+ /// panic if `mid > self.deref().len()`. They may also panic for any other
+ /// reason. Since it is optional, callers must not rely on this behavior for
+ /// soundness.
+ #[must_use]
+ unsafe fn split_at_unchecked(self, mid: usize) -> (Self, Self);
+}
+
+/// A shorthand for [`SplitByteSlice`] and [`ByteSliceMut`].
+pub trait SplitByteSliceMut: SplitByteSlice + ByteSliceMut {}
+impl<B: SplitByteSlice + ByteSliceMut> SplitByteSliceMut for B {}
+
+#[allow(clippy::missing_safety_doc)] // There's a `Safety` section on `into_byte_slice`.
+/// A [`ByteSlice`] that conveys no ownership, and so can be converted into a
+/// byte slice.
+///
+/// Some `ByteSlice` types (notably, the standard library's [`Ref`] type) convey
+/// ownership, and so they cannot soundly be moved by-value into a byte slice
+/// type (`&[u8]`). Some methods in this crate's API (such as [`Ref::into_ref`])
+/// are only compatible with `ByteSlice` types without these ownership
+/// semantics.
+///
+/// [`Ref`]: core::cell::Ref
+pub unsafe trait IntoByteSlice<'a>: ByteSlice {
+ /// Coverts `self` into a `&[u8]`.
+ ///
+ /// # Safety
+ ///
+ /// The returned reference has the same address and length as `self.deref()`
+ /// and `self.deref_mut()`.
+ ///
+ /// Note that, combined with the safety invariant on [`ByteSlice`], this
+ /// safety invariant implies that the returned reference is "stable" in the
+ /// sense described in the `ByteSlice` docs.
+ fn into_byte_slice(self) -> &'a [u8];
+}
+
+#[allow(clippy::missing_safety_doc)] // There's a `Safety` section on `into_byte_slice_mut`.
+/// A [`ByteSliceMut`] that conveys no ownership, and so can be converted into a
+/// mutable byte slice.
+///
+/// Some `ByteSliceMut` types (notably, the standard library's [`RefMut`] type)
+/// convey ownership, and so they cannot soundly be moved by-value into a byte
+/// slice type (`&mut [u8]`). Some methods in this crate's API (such as
+/// [`Ref::into_mut`]) are only compatible with `ByteSliceMut` types without
+/// these ownership semantics.
+///
+/// [`RefMut`]: core::cell::RefMut
+pub unsafe trait IntoByteSliceMut<'a>: IntoByteSlice<'a> + ByteSliceMut {
+ /// Coverts `self` into a `&mut [u8]`.
+ ///
+ /// # Safety
+ ///
+ /// The returned reference has the same address and length as `self.deref()`
+ /// and `self.deref_mut()`.
+ ///
+ /// Note that, combined with the safety invariant on [`ByteSlice`], this
+ /// safety invariant implies that the returned reference is "stable" in the
+ /// sense described in the `ByteSlice` docs.
+ fn into_byte_slice_mut(self) -> &'a mut [u8];
+}
+
+// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+#[allow(clippy::undocumented_unsafe_blocks)]
+unsafe impl ByteSlice for &[u8] {}
+
+// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+#[allow(clippy::undocumented_unsafe_blocks)]
+unsafe impl CopyableByteSlice for &[u8] {}
+
+// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+#[allow(clippy::undocumented_unsafe_blocks)]
+unsafe impl CloneableByteSlice for &[u8] {}
+
+// SAFETY: This delegates to `polyfills:split_at_unchecked`, which is documented
+// to correctly split `self` into two slices at the given `mid` point.
+unsafe impl SplitByteSlice for &[u8] {
+ #[inline]
+ unsafe fn split_at_unchecked(self, mid: usize) -> (Self, Self) {
+ // SAFETY: By contract on caller, `mid` is not greater than
+ // `self.len()`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ unsafe {
+ (<[u8]>::get_unchecked(self, ..mid), <[u8]>::get_unchecked(self, mid..))
+ }
+ }
+}
+
+// SAFETY: See inline.
+unsafe impl<'a> IntoByteSlice<'a> for &'a [u8] {
+ #[inline(always)]
+ fn into_byte_slice(self) -> &'a [u8] {
+ // SAFETY: It would be patently insane to implement `<Deref for
+ // &[u8]>::deref` as anything other than `fn deref(&self) -> &[u8] {
+ // *self }`. Assuming this holds, then `self` is stable as required by
+ // `into_byte_slice`.
+ self
+ }
+}
+
+// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+#[allow(clippy::undocumented_unsafe_blocks)]
+unsafe impl ByteSlice for &mut [u8] {}
+
+// SAFETY: This delegates to `polyfills:split_at_mut_unchecked`, which is
+// documented to correctly split `self` into two slices at the given `mid`
+// point.
+unsafe impl SplitByteSlice for &mut [u8] {
+ #[inline]
+ unsafe fn split_at_unchecked(self, mid: usize) -> (Self, Self) {
+ use core::slice::from_raw_parts_mut;
+
+ // `l_ptr` is non-null, because `self` is non-null, by invariant on
+ // `&mut [u8]`.
+ let l_ptr = self.as_mut_ptr();
+
+ // SAFETY: By contract on caller, `mid` is not greater than
+ // `self.len()`.
+ let r_ptr = unsafe { l_ptr.add(mid) };
+
+ let l_len = mid;
+
+ // SAFETY: By contract on caller, `mid` is not greater than
+ // `self.len()`.
+ //
+ // FIXME(#67): Remove this allow. See NumExt for more details.
+ #[allow(unstable_name_collisions)]
+ let r_len = unsafe { self.len().unchecked_sub(mid) };
+
+ // SAFETY: These invocations of `from_raw_parts_mut` satisfy its
+ // documented safety preconditions [1]:
+ // - The data `l_ptr` and `r_ptr` are valid for both reads and writes of
+ // `l_len` and `r_len` bytes, respectively, and they are trivially
+ // aligned. In particular:
+ // - The entire memory range of each slice is contained within a
+ // single allocated object, since `l_ptr` and `r_ptr` are both
+ // derived from within the address range of `self`.
+ // - Both `l_ptr` and `r_ptr` are non-null and trivially aligned.
+ // `self` is non-null by invariant on `&mut [u8]`, and the
+ // operations that derive `l_ptr` and `r_ptr` from `self` do not
+ // nullify either pointer.
+ // - The data `l_ptr` and `r_ptr` point to `l_len` and `r_len`,
+ // respectively, consecutive properly initialized values of type `u8`.
+ // This is true for `self` by invariant on `&mut [u8]`, and remains
+ // true for these two sub-slices of `self`.
+ // - The memory referenced by the returned slice cannot be accessed
+ // through any other pointer (not derived from the return value) for
+ // the duration of lifetime `'a``, because:
+ // - `split_at_unchecked` consumes `self` (which is not `Copy`),
+ // - `split_at_unchecked` does not exfiltrate any references to this
+ // memory, besides those references returned below,
+ // - the returned slices are non-overlapping.
+ // - The individual sizes of the sub-slices of `self` are no larger than
+ // `isize::MAX`, because their combined sizes are no larger than
+ // `isize::MAX`, by invariant on `self`.
+ //
+ // [1] https://doc.rust-lang.org/std/slice/fn.from_raw_parts_mut.html#safety
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ unsafe {
+ (from_raw_parts_mut(l_ptr, l_len), from_raw_parts_mut(r_ptr, r_len))
+ }
+ }
+}
+
+// SAFETY: See inline.
+unsafe impl<'a> IntoByteSlice<'a> for &'a mut [u8] {
+ #[inline(always)]
+ fn into_byte_slice(self) -> &'a [u8] {
+ // SAFETY: It would be patently insane to implement `<Deref for &mut
+ // [u8]>::deref` as anything other than `fn deref(&self) -> &[u8] {
+ // *self }`. Assuming this holds, then `self` is stable as required by
+ // `into_byte_slice`.
+ self
+ }
+}
+
+// SAFETY: See inline.
+unsafe impl<'a> IntoByteSliceMut<'a> for &'a mut [u8] {
+ #[inline(always)]
+ fn into_byte_slice_mut(self) -> &'a mut [u8] {
+ // SAFETY: It would be patently insane to implement `<DerefMut for &mut
+ // [u8]>::deref` as anything other than `fn deref_mut(&mut self) -> &mut
+ // [u8] { *self }`. Assuming this holds, then `self` is stable as
+ // required by `into_byte_slice_mut`.
+ self
+ }
+}
+
+// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+#[allow(clippy::undocumented_unsafe_blocks)]
+unsafe impl ByteSlice for cell::Ref<'_, [u8]> {}
+
+// SAFETY: This delegates to stdlib implementation of `Ref::map_split`, which is
+// assumed to be correct, and `SplitByteSlice::split_at_unchecked`, which is
+// documented to correctly split `self` into two slices at the given `mid`
+// point.
+unsafe impl SplitByteSlice for cell::Ref<'_, [u8]> {
+ #[inline]
+ unsafe fn split_at_unchecked(self, mid: usize) -> (Self, Self) {
+ cell::Ref::map_split(self, |slice|
+ // SAFETY: By precondition on caller, `mid` is not greater than
+ // `slice.len()`.
+ unsafe {
+ SplitByteSlice::split_at_unchecked(slice, mid)
+ })
+ }
+}
+
+// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+#[allow(clippy::undocumented_unsafe_blocks)]
+unsafe impl ByteSlice for cell::RefMut<'_, [u8]> {}
+
+// SAFETY: This delegates to stdlib implementation of `RefMut::map_split`, which
+// is assumed to be correct, and `SplitByteSlice::split_at_unchecked`, which is
+// documented to correctly split `self` into two slices at the given `mid`
+// point.
+unsafe impl SplitByteSlice for cell::RefMut<'_, [u8]> {
+ #[inline]
+ unsafe fn split_at_unchecked(self, mid: usize) -> (Self, Self) {
+ cell::RefMut::map_split(self, |slice|
+ // SAFETY: By precondition on caller, `mid` is not greater than
+ // `slice.len()`
+ unsafe {
+ SplitByteSlice::split_at_unchecked(slice, mid)
+ })
+ }
+}
+
+#[cfg(kani)]
+mod proofs {
+ use super::*;
+
+ fn any_vec() -> Vec<u8> {
+ let len = kani::any();
+ kani::assume(len <= crate::DstLayout::MAX_SIZE);
+ vec![0u8; len]
+ }
+
+ #[kani::proof]
+ fn prove_split_at_unchecked() {
+ let v = any_vec();
+ let slc = v.as_slice();
+ let mid = kani::any();
+ kani::assume(mid <= slc.len());
+ let (l, r) = unsafe { slc.split_at_unchecked(mid) };
+ assert_eq!(l.len() + r.len(), slc.len());
+
+ let slc: *const _ = slc;
+ let l: *const _ = l;
+ let r: *const _ = r;
+
+ assert_eq!(slc.cast::<u8>(), l.cast::<u8>());
+ assert_eq!(unsafe { slc.cast::<u8>().add(mid) }, r.cast::<u8>());
+
+ let mut v = any_vec();
+ let slc = v.as_mut_slice();
+ let len = slc.len();
+ let mid = kani::any();
+ kani::assume(mid <= slc.len());
+ let (l, r) = unsafe { slc.split_at_unchecked(mid) };
+ assert_eq!(l.len() + r.len(), len);
+
+ let l: *mut _ = l;
+ let r: *mut _ = r;
+ let slc: *mut _ = slc;
+
+ assert_eq!(slc.cast::<u8>(), l.cast::<u8>());
+ assert_eq!(unsafe { slc.cast::<u8>().add(mid) }, r.cast::<u8>());
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::cell::RefCell;
+
+ use super::*;
+
+ #[test]
+ fn test_ref_split_at_unchecked() {
+ let cell = RefCell::new([1, 2, 3, 4]);
+ let borrow = cell.borrow();
+ let slice_ref: cell::Ref<'_, [u8]> = cell::Ref::map(borrow, |a| &a[..]);
+ // SAFETY: 2 is within bounds of [1, 2, 3, 4]
+ let (l, r) = unsafe { slice_ref.split_at_unchecked(2) };
+ assert_eq!(*l, [1, 2]);
+ assert_eq!(*r, [3, 4]);
+ }
+
+ #[test]
+ fn test_ref_mut_split_at_unchecked() {
+ let cell = RefCell::new([1, 2, 3, 4]);
+ let borrow_mut = cell.borrow_mut();
+ let slice_ref_mut: cell::RefMut<'_, [u8]> = cell::RefMut::map(borrow_mut, |a| &mut a[..]);
+ // SAFETY: 2 is within bounds of [1, 2, 3, 4]
+ let (l, r) = unsafe { slice_ref_mut.split_at_unchecked(2) };
+ assert_eq!(*l, [1, 2]);
+ assert_eq!(*r, [3, 4]);
+ }
+}
diff --git a/rust/zerocopy/src/byteorder.rs b/rust/zerocopy/src/byteorder.rs
new file mode 100644
index 000000000000..257505203415
--- /dev/null
+++ b/rust/zerocopy/src/byteorder.rs
@@ -0,0 +1,1563 @@
+// Copyright 2019 The Fuchsia Authors
+//
+// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+//! Byte order-aware numeric primitives.
+//!
+//! This module contains equivalents of the native multi-byte integer types with
+//! no alignment requirement and supporting byte order conversions.
+//!
+//! For each native multi-byte integer type - `u16`, `i16`, `u32`, etc - and
+//! floating point type - `f32` and `f64` - an equivalent type is defined by
+//! this module - [`U16`], [`I16`], [`U32`], [`F32`], [`F64`], etc. Unlike their
+//! native counterparts, these types have alignment 1, and take a type parameter
+//! specifying the byte order in which the bytes are stored in memory. Each type
+//! implements this crate's relevant conversion and marker traits.
+//!
+//! These two properties, taken together, make these types useful for defining
+//! data structures whose memory layout matches a wire format such as that of a
+//! network protocol or a file format. Such formats often have multi-byte values
+//! at offsets that do not respect the alignment requirements of the equivalent
+//! native types, and stored in a byte order not necessarily the same as that of
+//! the target platform.
+//!
+//! Type aliases are provided for common byte orders in the [`big_endian`],
+//! [`little_endian`], [`network_endian`], and [`native_endian`] submodules.
+//! Note that network-endian is a synonym for big-endian.
+//!
+//! # Example
+//!
+//! One use of these types is for representing network packet formats, such as
+//! UDP:
+//!
+//! ```rust
+//! use zerocopy::{*, byteorder::network_endian::U16};
+//! # use zerocopy_derive::*;
+//!
+//! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
+//! #[repr(C)]
+//! struct UdpHeader {
+//! src_port: U16,
+//! dst_port: U16,
+//! length: U16,
+//! checksum: U16,
+//! }
+//!
+//! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
+//! #[repr(C, packed)]
+//! struct UdpPacket {
+//! header: UdpHeader,
+//! body: [u8],
+//! }
+//!
+//! impl UdpPacket {
+//! fn parse(bytes: &[u8]) -> Option<&UdpPacket> {
+//! UdpPacket::ref_from_bytes(bytes).ok()
+//! }
+//! }
+//! ```
+
+use core::{
+ convert::{TryFrom, TryInto},
+ fmt::{Binary, Debug, LowerHex, Octal, UpperHex},
+ hash::Hash,
+ num::TryFromIntError,
+};
+
+use super::*;
+
+/// A type-level representation of byte order.
+///
+/// This type is implemented by [`BigEndian`] and [`LittleEndian`], which
+/// represent big-endian and little-endian byte order respectively. This module
+/// also provides a number of useful aliases for those types: [`NativeEndian`],
+/// [`NetworkEndian`], [`BE`], and [`LE`].
+///
+/// `ByteOrder` types can be used to specify the byte order of the types in this
+/// module - for example, [`U32<BigEndian>`] is a 32-bit integer stored in
+/// big-endian byte order.
+///
+/// [`U32<BigEndian>`]: U32
+pub trait ByteOrder:
+ Copy + Clone + Debug + Display + Eq + PartialEq + Ord + PartialOrd + Hash + private::Sealed
+{
+ #[doc(hidden)]
+ const ORDER: Order;
+}
+
+mod private {
+ pub trait Sealed {}
+
+ impl Sealed for super::BigEndian {}
+ impl Sealed for super::LittleEndian {}
+}
+
+#[allow(missing_copy_implementations, missing_debug_implementations)]
+#[doc(hidden)]
+pub enum Order {
+ BigEndian,
+ LittleEndian,
+}
+
+/// Big-endian byte order.
+///
+/// See [`ByteOrder`] for more details.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub enum BigEndian {}
+
+impl ByteOrder for BigEndian {
+ const ORDER: Order = Order::BigEndian;
+}
+
+impl Display for BigEndian {
+ #[inline]
+ fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
+ match *self {}
+ }
+}
+
+/// Little-endian byte order.
+///
+/// See [`ByteOrder`] for more details.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub enum LittleEndian {}
+
+impl ByteOrder for LittleEndian {
+ const ORDER: Order = Order::LittleEndian;
+}
+
+impl Display for LittleEndian {
+ #[inline]
+ fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
+ match *self {}
+ }
+}
+
+/// The endianness used by this platform.
+///
+/// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the
+/// endianness of the target platform.
+#[cfg(target_endian = "big")]
+pub type NativeEndian = BigEndian;
+
+/// The endianness used by this platform.
+///
+/// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the
+/// endianness of the target platform.
+#[cfg(target_endian = "little")]
+pub type NativeEndian = LittleEndian;
+
+/// The endianness used in many network protocols.
+///
+/// This is a type alias for [`BigEndian`].
+pub type NetworkEndian = BigEndian;
+
+/// A type alias for [`BigEndian`].
+pub type BE = BigEndian;
+
+/// A type alias for [`LittleEndian`].
+pub type LE = LittleEndian;
+
+macro_rules! impl_fmt_trait {
+ ($name:ident, $native:ident, $trait:ident) => {
+ impl<O: ByteOrder> $trait for $name<O> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ $trait::fmt(&self.get(), f)
+ }
+ }
+ };
+}
+
+macro_rules! impl_fmt_traits {
+ ($name:ident, $native:ident, "floating point number") => {
+ impl_fmt_trait!($name, $native, Display);
+ };
+ ($name:ident, $native:ident, "unsigned integer") => {
+ impl_fmt_traits!($name, $native, @all_types);
+ };
+ ($name:ident, $native:ident, "signed integer") => {
+ impl_fmt_traits!($name, $native, @all_types);
+ };
+ ($name:ident, $native:ident, @all_types) => {
+ impl_fmt_trait!($name, $native, Display);
+ impl_fmt_trait!($name, $native, Octal);
+ impl_fmt_trait!($name, $native, LowerHex);
+ impl_fmt_trait!($name, $native, UpperHex);
+ impl_fmt_trait!($name, $native, Binary);
+ };
+}
+
+macro_rules! impl_ops_traits {
+ ($name:ident, $native:ident, "floating point number") => {
+ impl_ops_traits!($name, $native, @all_types);
+ impl_ops_traits!($name, $native, @signed_integer_floating_point);
+
+ impl<O: ByteOrder> PartialOrd for $name<O> {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.get().partial_cmp(&other.get())
+ }
+ }
+ };
+ ($name:ident, $native:ident, "unsigned integer") => {
+ impl_ops_traits!($name, $native, @signed_unsigned_integer);
+ impl_ops_traits!($name, $native, @all_types);
+ };
+ ($name:ident, $native:ident, "signed integer") => {
+ impl_ops_traits!($name, $native, @signed_unsigned_integer);
+ impl_ops_traits!($name, $native, @signed_integer_floating_point);
+ impl_ops_traits!($name, $native, @all_types);
+ };
+ ($name:ident, $native:ident, @signed_unsigned_integer) => {
+ impl_ops_traits!(@without_byteorder_swap $name, $native, BitAnd, bitand, BitAndAssign, bitand_assign);
+ impl_ops_traits!(@without_byteorder_swap $name, $native, BitOr, bitor, BitOrAssign, bitor_assign);
+ impl_ops_traits!(@without_byteorder_swap $name, $native, BitXor, bitxor, BitXorAssign, bitxor_assign);
+ impl_ops_traits!(@with_byteorder_swap $name, $native, Shl, shl, ShlAssign, shl_assign);
+ impl_ops_traits!(@with_byteorder_swap $name, $native, Shr, shr, ShrAssign, shr_assign);
+
+ impl<O> core::ops::Not for $name<O> {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn not(self) -> $name<O> {
+ let self_native = $native::from_ne_bytes(self.0);
+ $name((!self_native).to_ne_bytes(), PhantomData)
+ }
+ }
+
+ impl<O: ByteOrder> PartialOrd for $name<O> {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+ }
+
+ impl<O: ByteOrder> Ord for $name<O> {
+ #[inline(always)]
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.get().cmp(&other.get())
+ }
+ }
+
+ impl<O: ByteOrder> PartialOrd<$native> for $name<O> {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &$native) -> Option<Ordering> {
+ self.get().partial_cmp(other)
+ }
+ }
+ };
+ ($name:ident, $native:ident, @signed_integer_floating_point) => {
+ impl<O: ByteOrder> core::ops::Neg for $name<O> {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn neg(self) -> $name<O> {
+ let self_native: $native = self.get();
+ #[allow(clippy::arithmetic_side_effects)]
+ $name::<O>::new(-self_native)
+ }
+ }
+ };
+ ($name:ident, $native:ident, @all_types) => {
+ impl_ops_traits!(@with_byteorder_swap $name, $native, Add, add, AddAssign, add_assign);
+ impl_ops_traits!(@with_byteorder_swap $name, $native, Div, div, DivAssign, div_assign);
+ impl_ops_traits!(@with_byteorder_swap $name, $native, Mul, mul, MulAssign, mul_assign);
+ impl_ops_traits!(@with_byteorder_swap $name, $native, Rem, rem, RemAssign, rem_assign);
+ impl_ops_traits!(@with_byteorder_swap $name, $native, Sub, sub, SubAssign, sub_assign);
+ };
+ (@with_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => {
+ impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn $method(self, rhs: $name<O>) -> $name<O> {
+ let self_native: $native = self.get();
+ let rhs_native: $native = rhs.get();
+ let result_native = core::ops::$trait::$method(self_native, rhs_native);
+ $name::<O>::new(result_native)
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn $method(self, rhs: $name<O>) -> $name<O> {
+ let rhs_native: $native = rhs.get();
+ let result_native = core::ops::$trait::$method(self, rhs_native);
+ $name::<O>::new(result_native)
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn $method(self, rhs: $native) -> $name<O> {
+ let self_native: $native = self.get();
+ let result_native = core::ops::$trait::$method(self_native, rhs);
+ $name::<O>::new(result_native)
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> {
+ #[inline(always)]
+ fn $method_assign(&mut self, rhs: $name<O>) {
+ *self = core::ops::$trait::$method(*self, rhs);
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native {
+ #[inline(always)]
+ fn $method_assign(&mut self, rhs: $name<O>) {
+ let rhs_native: $native = rhs.get();
+ *self = core::ops::$trait::$method(*self, rhs_native);
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> {
+ #[inline(always)]
+ fn $method_assign(&mut self, rhs: $native) {
+ *self = core::ops::$trait::$method(*self, rhs);
+ }
+ }
+ };
+ // Implement traits in terms of the same trait on the native type, but
+ // without performing a byte order swap when both operands are byteorder
+ // types. This only works for bitwise operations like `&`, `|`, etc.
+ //
+ // When only one operand is a byteorder type, we still need to perform a
+ // byteorder swap.
+ (@without_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => {
+ impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn $method(self, rhs: $name<O>) -> $name<O> {
+ let self_native = $native::from_ne_bytes(self.0);
+ let rhs_native = $native::from_ne_bytes(rhs.0);
+ let result_native = core::ops::$trait::$method(self_native, rhs_native);
+ $name(result_native.to_ne_bytes(), PhantomData)
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn $method(self, rhs: $name<O>) -> $name<O> {
+ // No runtime cost - just byte packing
+ let rhs_native = $native::from_ne_bytes(rhs.0);
+ // (Maybe) runtime cost - byte order swap
+ let slf_byteorder = $name::<O>::new(self);
+ // No runtime cost - just byte packing
+ let slf_native = $native::from_ne_bytes(slf_byteorder.0);
+ // Runtime cost - perform the operation
+ let result_native = core::ops::$trait::$method(slf_native, rhs_native);
+ // No runtime cost - just byte unpacking
+ $name(result_native.to_ne_bytes(), PhantomData)
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> {
+ type Output = $name<O>;
+
+ #[inline(always)]
+ fn $method(self, rhs: $native) -> $name<O> {
+ // (Maybe) runtime cost - byte order swap
+ let rhs_byteorder = $name::<O>::new(rhs);
+ // No runtime cost - just byte packing
+ let rhs_native = $native::from_ne_bytes(rhs_byteorder.0);
+ // No runtime cost - just byte packing
+ let slf_native = $native::from_ne_bytes(self.0);
+ // Runtime cost - perform the operation
+ let result_native = core::ops::$trait::$method(slf_native, rhs_native);
+ // No runtime cost - just byte unpacking
+ $name(result_native.to_ne_bytes(), PhantomData)
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> {
+ #[inline(always)]
+ fn $method_assign(&mut self, rhs: $name<O>) {
+ *self = core::ops::$trait::$method(*self, rhs);
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native {
+ #[inline(always)]
+ fn $method_assign(&mut self, rhs: $name<O>) {
+ // (Maybe) runtime cost - byte order swap
+ let rhs_native = rhs.get();
+ // Runtime cost - perform the operation
+ *self = core::ops::$trait::$method(*self, rhs_native);
+ }
+ }
+
+ impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> {
+ #[inline(always)]
+ fn $method_assign(&mut self, rhs: $native) {
+ *self = core::ops::$trait::$method(*self, rhs);
+ }
+ }
+ };
+}
+
+macro_rules! doc_comment {
+ ($x:expr, $($tt:tt)*) => {
+ #[doc = $x]
+ $($tt)*
+ };
+}
+
+macro_rules! define_max_value_constant {
+ ($name:ident, $bytes:expr, "unsigned integer") => {
+ /// The maximum value.
+ ///
+ /// This constant should be preferred to constructing a new value using
+ /// `new`, as `new` may perform an endianness swap depending on the
+ /// endianness `O` and the endianness of the platform.
+ pub const MAX_VALUE: $name<O> = $name([0xFFu8; $bytes], PhantomData);
+ };
+ // We don't provide maximum and minimum value constants for signed values
+ // and floats because there's no way to do it generically - it would require
+ // a different value depending on the value of the `ByteOrder` type
+ // parameter. Currently, one workaround would be to provide implementations
+ // for concrete implementations of that trait. In the long term, if we are
+ // ever able to make the `new` constructor a const fn, we could use that
+ // instead.
+ ($name:ident, $bytes:expr, "signed integer") => {};
+ ($name:ident, $bytes:expr, "floating point number") => {};
+}
+
+macro_rules! define_type {
+ (
+ $article:ident,
+ $description:expr,
+ $name:ident,
+ $native:ident,
+ $bits:expr,
+ $bytes:expr,
+ $from_be_fn:path,
+ $to_be_fn:path,
+ $from_le_fn:path,
+ $to_le_fn:path,
+ $number_kind:tt,
+ [$($larger_native:ty),*],
+ [$($larger_native_try:ty),*],
+ [$($larger_byteorder:ident),*],
+ [$($larger_byteorder_try:ident),*]
+ ) => {
+ doc_comment! {
+ concat!($description, " stored in a given byte order.
+
+`", stringify!($name), "` is like the native `", stringify!($native), "` type with
+two major differences: First, it has no alignment requirement (its alignment is 1).
+Second, the endianness of its memory layout is given by the type parameter `O`,
+which can be any type which implements [`ByteOrder`]. In particular, this refers
+to [`BigEndian`], [`LittleEndian`], [`NativeEndian`], and [`NetworkEndian`].
+
+", stringify!($article), " `", stringify!($name), "` can be constructed using
+the [`new`] method, and its contained value can be obtained as a native
+`",stringify!($native), "` using the [`get`] method, or updated in place with
+the [`set`] method. In all cases, if the endianness `O` is not the same as the
+endianness of the current platform, an endianness swap will be performed in
+order to uphold the invariants that a) the layout of `", stringify!($name), "`
+has endianness `O` and that, b) the layout of `", stringify!($native), "` has
+the platform's native endianness.
+
+`", stringify!($name), "` implements [`FromBytes`], [`IntoBytes`], and [`Unaligned`],
+making it useful for parsing and serialization. See the module documentation for an
+example of how it can be used for parsing UDP packets.
+
+[`new`]: crate::byteorder::", stringify!($name), "::new
+[`get`]: crate::byteorder::", stringify!($name), "::get
+[`set`]: crate::byteorder::", stringify!($name), "::set
+[`FromBytes`]: crate::FromBytes
+[`IntoBytes`]: crate::IntoBytes
+[`Unaligned`]: crate::Unaligned"),
+ #[derive(Copy, Clone, Eq, PartialEq, Hash)]
+ #[cfg_attr(any(feature = "derive", test), derive(KnownLayout, Immutable, FromBytes, IntoBytes, Unaligned))]
+ #[repr(transparent)]
+ pub struct $name<O>([u8; $bytes], PhantomData<O>);
+ }
+
+ #[cfg(not(any(feature = "derive", test)))]
+ impl_known_layout!(O => $name<O>);
+
+ #[allow(unused_unsafe)] // Unused when `feature = "derive"`.
+ // SAFETY: `$name<O>` is `repr(transparent)`, and so it has the same
+ // layout as its only non-zero field, which is a `u8` array. `u8` arrays
+ // are `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`,
+ // `IntoBytes`, and `Unaligned`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ impl_or_verify!(O => Immutable for $name<O>);
+ impl_or_verify!(O => TryFromBytes for $name<O>);
+ impl_or_verify!(O => FromZeros for $name<O>);
+ impl_or_verify!(O => FromBytes for $name<O>);
+ impl_or_verify!(O => IntoBytes for $name<O>);
+ impl_or_verify!(O => Unaligned for $name<O>);
+ };
+
+ impl<O> Default for $name<O> {
+ #[inline(always)]
+ fn default() -> $name<O> {
+ $name::ZERO
+ }
+ }
+
+ impl<O> $name<O> {
+ /// The value zero.
+ ///
+ /// This constant should be preferred to constructing a new value
+ /// using `new`, as `new` may perform an endianness swap depending
+ /// on the endianness and platform.
+ pub const ZERO: $name<O> = $name([0u8; $bytes], PhantomData);
+
+ define_max_value_constant!($name, $bytes, $number_kind);
+
+ /// Constructs a new value from bytes which are already in `O` byte
+ /// order.
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub const fn from_bytes(bytes: [u8; $bytes]) -> $name<O> {
+ $name(bytes, PhantomData)
+ }
+
+ /// Extracts the bytes of `self` without swapping the byte order.
+ ///
+ /// The returned bytes will be in `O` byte order.
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub const fn to_bytes(self) -> [u8; $bytes] {
+ self.0
+ }
+ }
+
+ impl<O: ByteOrder> $name<O> {
+ maybe_const_trait_bounded_fn! {
+ /// Constructs a new value, possibly performing an endianness
+ /// swap to guarantee that the returned value has endianness
+ /// `O`.
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub const fn new(n: $native) -> $name<O> {
+ let bytes = match O::ORDER {
+ Order::BigEndian => $to_be_fn(n),
+ Order::LittleEndian => $to_le_fn(n),
+ };
+
+ $name(bytes, PhantomData)
+ }
+ }
+
+ maybe_const_trait_bounded_fn! {
+ /// Returns the value as a primitive type, possibly performing
+ /// an endianness swap to guarantee that the return value has
+ /// the endianness of the native platform.
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub const fn get(self) -> $native {
+ match O::ORDER {
+ Order::BigEndian => $from_be_fn(self.0),
+ Order::LittleEndian => $from_le_fn(self.0),
+ }
+ }
+ }
+
+ /// Updates the value in place as a primitive type, possibly
+ /// performing an endianness swap to guarantee that the stored value
+ /// has the endianness `O`.
+ #[inline(always)]
+ pub fn set(&mut self, n: $native) {
+ *self = Self::new(n);
+ }
+ }
+
+ // The reasoning behind which traits to implement here is to only
+ // implement traits which won't cause inference issues. Notably,
+ // comparison traits like PartialEq and PartialOrd tend to cause
+ // inference issues.
+
+ impl<O: ByteOrder> From<$name<O>> for [u8; $bytes] {
+ #[inline(always)]
+ fn from(x: $name<O>) -> [u8; $bytes] {
+ x.0
+ }
+ }
+
+ impl<O: ByteOrder> From<[u8; $bytes]> for $name<O> {
+ #[inline(always)]
+ fn from(bytes: [u8; $bytes]) -> $name<O> {
+ $name(bytes, PhantomData)
+ }
+ }
+
+ impl<O: ByteOrder> From<$name<O>> for $native {
+ #[inline(always)]
+ fn from(x: $name<O>) -> $native {
+ x.get()
+ }
+ }
+
+ impl<O: ByteOrder> From<$native> for $name<O> {
+ #[inline(always)]
+ fn from(x: $native) -> $name<O> {
+ $name::new(x)
+ }
+ }
+
+ $(
+ impl<O: ByteOrder> From<$name<O>> for $larger_native {
+ #[inline(always)]
+ fn from(x: $name<O>) -> $larger_native {
+ x.get().into()
+ }
+ }
+ )*
+
+ $(
+ impl<O: ByteOrder> TryFrom<$larger_native_try> for $name<O> {
+ type Error = TryFromIntError;
+ #[inline(always)]
+ fn try_from(x: $larger_native_try) -> Result<$name<O>, TryFromIntError> {
+ $native::try_from(x).map($name::new)
+ }
+ }
+ )*
+
+ $(
+ impl<O: ByteOrder, P: ByteOrder> From<$name<O>> for $larger_byteorder<P> {
+ #[inline(always)]
+ fn from(x: $name<O>) -> $larger_byteorder<P> {
+ $larger_byteorder::new(x.get().into())
+ }
+ }
+ )*
+
+ $(
+ impl<O: ByteOrder, P: ByteOrder> TryFrom<$larger_byteorder_try<P>> for $name<O> {
+ type Error = TryFromIntError;
+ #[inline(always)]
+ fn try_from(x: $larger_byteorder_try<P>) -> Result<$name<O>, TryFromIntError> {
+ x.get().try_into().map($name::new)
+ }
+ }
+ )*
+
+ impl<O> AsRef<[u8; $bytes]> for $name<O> {
+ #[inline(always)]
+ fn as_ref(&self) -> &[u8; $bytes] {
+ &self.0
+ }
+ }
+
+ impl<O> AsMut<[u8; $bytes]> for $name<O> {
+ #[inline(always)]
+ fn as_mut(&mut self) -> &mut [u8; $bytes] {
+ &mut self.0
+ }
+ }
+
+ impl<O> PartialEq<$name<O>> for [u8; $bytes] {
+ #[inline(always)]
+ fn eq(&self, other: &$name<O>) -> bool {
+ self.eq(&other.0)
+ }
+ }
+
+ impl<O> PartialEq<[u8; $bytes]> for $name<O> {
+ #[inline(always)]
+ fn eq(&self, other: &[u8; $bytes]) -> bool {
+ self.0.eq(other)
+ }
+ }
+
+ impl<O: ByteOrder> PartialEq<$native> for $name<O> {
+ #[inline(always)]
+ fn eq(&self, other: &$native) -> bool {
+ self.get().eq(other)
+ }
+ }
+
+ impl_fmt_traits!($name, $native, $number_kind);
+ impl_ops_traits!($name, $native, $number_kind);
+
+ impl<O: ByteOrder> Debug for $name<O> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ // This results in a format like "U16(42)".
+ f.debug_tuple(stringify!($name)).field(&self.get()).finish()
+ }
+ }
+ };
+}
+
+define_type!(
+ A,
+ "A 16-bit unsigned integer",
+ U16,
+ u16,
+ 16,
+ 2,
+ u16::from_be_bytes,
+ u16::to_be_bytes,
+ u16::from_le_bytes,
+ u16::to_le_bytes,
+ "unsigned integer",
+ [u32, u64, u128, usize],
+ [u32, u64, u128, usize],
+ [U32, U64, U128, Usize],
+ [U32, U64, U128, Usize]
+);
+define_type!(
+ A,
+ "A 32-bit unsigned integer",
+ U32,
+ u32,
+ 32,
+ 4,
+ u32::from_be_bytes,
+ u32::to_be_bytes,
+ u32::from_le_bytes,
+ u32::to_le_bytes,
+ "unsigned integer",
+ [u64, u128],
+ [u64, u128],
+ [U64, U128],
+ [U64, U128]
+);
+define_type!(
+ A,
+ "A 64-bit unsigned integer",
+ U64,
+ u64,
+ 64,
+ 8,
+ u64::from_be_bytes,
+ u64::to_be_bytes,
+ u64::from_le_bytes,
+ u64::to_le_bytes,
+ "unsigned integer",
+ [u128],
+ [u128],
+ [U128],
+ [U128]
+);
+define_type!(
+ A,
+ "A 128-bit unsigned integer",
+ U128,
+ u128,
+ 128,
+ 16,
+ u128::from_be_bytes,
+ u128::to_be_bytes,
+ u128::from_le_bytes,
+ u128::to_le_bytes,
+ "unsigned integer",
+ [],
+ [],
+ [],
+ []
+);
+define_type!(
+ A,
+ "A word-sized unsigned integer",
+ Usize,
+ usize,
+ mem::size_of::<usize>() * 8,
+ mem::size_of::<usize>(),
+ usize::from_be_bytes,
+ usize::to_be_bytes,
+ usize::from_le_bytes,
+ usize::to_le_bytes,
+ "unsigned integer",
+ [],
+ [],
+ [],
+ []
+);
+define_type!(
+ An,
+ "A 16-bit signed integer",
+ I16,
+ i16,
+ 16,
+ 2,
+ i16::from_be_bytes,
+ i16::to_be_bytes,
+ i16::from_le_bytes,
+ i16::to_le_bytes,
+ "signed integer",
+ [i32, i64, i128, isize],
+ [i32, i64, i128, isize],
+ [I32, I64, I128, Isize],
+ [I32, I64, I128, Isize]
+);
+define_type!(
+ An,
+ "A 32-bit signed integer",
+ I32,
+ i32,
+ 32,
+ 4,
+ i32::from_be_bytes,
+ i32::to_be_bytes,
+ i32::from_le_bytes,
+ i32::to_le_bytes,
+ "signed integer",
+ [i64, i128],
+ [i64, i128],
+ [I64, I128],
+ [I64, I128]
+);
+define_type!(
+ An,
+ "A 64-bit signed integer",
+ I64,
+ i64,
+ 64,
+ 8,
+ i64::from_be_bytes,
+ i64::to_be_bytes,
+ i64::from_le_bytes,
+ i64::to_le_bytes,
+ "signed integer",
+ [i128],
+ [i128],
+ [I128],
+ [I128]
+);
+define_type!(
+ An,
+ "A 128-bit signed integer",
+ I128,
+ i128,
+ 128,
+ 16,
+ i128::from_be_bytes,
+ i128::to_be_bytes,
+ i128::from_le_bytes,
+ i128::to_le_bytes,
+ "signed integer",
+ [],
+ [],
+ [],
+ []
+);
+define_type!(
+ An,
+ "A word-sized signed integer",
+ Isize,
+ isize,
+ mem::size_of::<isize>() * 8,
+ mem::size_of::<isize>(),
+ isize::from_be_bytes,
+ isize::to_be_bytes,
+ isize::from_le_bytes,
+ isize::to_le_bytes,
+ "signed integer",
+ [],
+ [],
+ [],
+ []
+);
+
+// FIXME(https://github.com/rust-lang/rust/issues/72447): Use the endianness
+// conversion methods directly once those are const-stable.
+macro_rules! define_float_conversion {
+ ($ty:ty, $bits:ident, $bytes:expr, $mod:ident) => {
+ mod $mod {
+ use super::*;
+
+ define_float_conversion!($ty, $bits, $bytes, from_be_bytes, to_be_bytes);
+ define_float_conversion!($ty, $bits, $bytes, from_le_bytes, to_le_bytes);
+ }
+ };
+ ($ty:ty, $bits:ident, $bytes:expr, $from:ident, $to:ident) => {
+ // Clippy: The suggestion of using `from_bits()` instead doesn't work
+ // because `from_bits` is not const-stable on our MSRV.
+ #[allow(clippy::unnecessary_transmutes)]
+ pub(crate) const fn $from(bytes: [u8; $bytes]) -> $ty {
+ transmute!($bits::$from(bytes))
+ }
+
+ pub(crate) const fn $to(f: $ty) -> [u8; $bytes] {
+ // Clippy: The suggestion of using `f.to_bits()` instead doesn't
+ // work because `to_bits` is not const-stable on our MSRV.
+ #[allow(clippy::unnecessary_transmutes)]
+ let bits: $bits = transmute!(f);
+ bits.$to()
+ }
+ };
+}
+
+define_float_conversion!(f32, u32, 4, f32_ext);
+define_float_conversion!(f64, u64, 8, f64_ext);
+
+define_type!(
+ An,
+ "A 32-bit floating point number",
+ F32,
+ f32,
+ 32,
+ 4,
+ f32_ext::from_be_bytes,
+ f32_ext::to_be_bytes,
+ f32_ext::from_le_bytes,
+ f32_ext::to_le_bytes,
+ "floating point number",
+ [f64],
+ [],
+ [F64],
+ []
+);
+define_type!(
+ An,
+ "A 64-bit floating point number",
+ F64,
+ f64,
+ 64,
+ 8,
+ f64_ext::from_be_bytes,
+ f64_ext::to_be_bytes,
+ f64_ext::from_le_bytes,
+ f64_ext::to_le_bytes,
+ "floating point number",
+ [],
+ [],
+ [],
+ []
+);
+
+macro_rules! module {
+ ($name:ident, $trait:ident, $endianness_str:expr) => {
+ /// Numeric primitives stored in
+ #[doc = $endianness_str]
+ /// byte order.
+ pub mod $name {
+ use super::$trait;
+
+ module!(@ty U16, $trait, "16-bit unsigned integer", $endianness_str);
+ module!(@ty U32, $trait, "32-bit unsigned integer", $endianness_str);
+ module!(@ty U64, $trait, "64-bit unsigned integer", $endianness_str);
+ module!(@ty U128, $trait, "128-bit unsigned integer", $endianness_str);
+ module!(@ty I16, $trait, "16-bit signed integer", $endianness_str);
+ module!(@ty I32, $trait, "32-bit signed integer", $endianness_str);
+ module!(@ty I64, $trait, "64-bit signed integer", $endianness_str);
+ module!(@ty I128, $trait, "128-bit signed integer", $endianness_str);
+ module!(@ty F32, $trait, "32-bit floating point number", $endianness_str);
+ module!(@ty F64, $trait, "64-bit floating point number", $endianness_str);
+ }
+ };
+ (@ty $ty:ident, $trait:ident, $desc_str:expr, $endianness_str:expr) => {
+ /// A
+ #[doc = $desc_str]
+ /// stored in
+ #[doc = $endianness_str]
+ /// byte order.
+ pub type $ty = crate::byteorder::$ty<$trait>;
+ };
+}
+
+module!(big_endian, BigEndian, "big-endian");
+module!(little_endian, LittleEndian, "little-endian");
+module!(network_endian, NetworkEndian, "network-endian");
+module!(native_endian, NativeEndian, "native-endian");
+
+#[cfg(any(test, kani))]
+mod tests {
+ use super::*;
+
+ #[cfg(not(kani))]
+ mod compatibility {
+ pub(super) use rand::{
+ distributions::{Distribution, Standard},
+ rngs::SmallRng,
+ Rng, SeedableRng,
+ };
+
+ pub(crate) trait Arbitrary {}
+
+ impl<T> Arbitrary for T {}
+ }
+
+ #[cfg(kani)]
+ mod compatibility {
+ pub(crate) use kani::Arbitrary;
+
+ pub(crate) struct SmallRng;
+
+ impl SmallRng {
+ pub(crate) fn seed_from_u64(_state: u64) -> Self {
+ Self
+ }
+ }
+
+ pub(crate) trait Rng {
+ fn sample<T, D: Distribution<T>>(&mut self, _distr: D) -> T
+ where
+ T: Arbitrary,
+ {
+ kani::any()
+ }
+ }
+
+ impl Rng for SmallRng {}
+
+ pub(crate) trait Distribution<T> {}
+ impl<T, U> Distribution<T> for U {}
+
+ pub(crate) struct Standard;
+ }
+
+ use compatibility::*;
+
+ // A native integer type (u16, i32, etc).
+ trait Native: Arbitrary + FromBytes + IntoBytes + Immutable + Copy + PartialEq + Debug {
+ const ZERO: Self;
+ const MAX_VALUE: Self;
+
+ type Distribution: Distribution<Self>;
+ const DIST: Self::Distribution;
+
+ fn rand<R: Rng>(rng: &mut R) -> Self {
+ rng.sample(Self::DIST)
+ }
+
+ #[cfg_attr(kani, allow(unused))]
+ fn checked_add(self, rhs: Self) -> Option<Self>;
+
+ #[cfg_attr(kani, allow(unused))]
+ fn checked_div(self, rhs: Self) -> Option<Self>;
+
+ #[cfg_attr(kani, allow(unused))]
+ fn checked_mul(self, rhs: Self) -> Option<Self>;
+
+ #[cfg_attr(kani, allow(unused))]
+ fn checked_rem(self, rhs: Self) -> Option<Self>;
+
+ #[cfg_attr(kani, allow(unused))]
+ fn checked_sub(self, rhs: Self) -> Option<Self>;
+
+ #[cfg_attr(kani, allow(unused))]
+ fn checked_shl(self, rhs: Self) -> Option<Self>;
+
+ #[cfg_attr(kani, allow(unused))]
+ fn checked_shr(self, rhs: Self) -> Option<Self>;
+
+ fn is_nan(self) -> bool;
+
+ /// For `f32` and `f64`, NaN values are not considered equal to
+ /// themselves. This method is like `assert_eq!`, but it treats NaN
+ /// values as equal.
+ fn assert_eq_or_nan(self, other: Self) {
+ let slf = (!self.is_nan()).then(|| self);
+ let other = (!other.is_nan()).then(|| other);
+ assert_eq!(slf, other);
+ }
+ }
+
+ trait ByteArray:
+ FromBytes + IntoBytes + Immutable + Copy + AsRef<[u8]> + AsMut<[u8]> + Debug + Default + Eq
+ {
+ /// Invert the order of the bytes in the array.
+ fn invert(self) -> Self;
+ }
+
+ trait ByteOrderType:
+ FromBytes + IntoBytes + Unaligned + Copy + Eq + Debug + Hash + From<Self::Native>
+ {
+ type Native: Native;
+ type ByteArray: ByteArray;
+
+ const ZERO: Self;
+
+ fn new(native: Self::Native) -> Self;
+ fn get(self) -> Self::Native;
+ fn set(&mut self, native: Self::Native);
+ fn from_bytes(bytes: Self::ByteArray) -> Self;
+ fn into_bytes(self) -> Self::ByteArray;
+
+ /// For `f32` and `f64`, NaN values are not considered equal to
+ /// themselves. This method is like `assert_eq!`, but it treats NaN
+ /// values as equal.
+ fn assert_eq_or_nan(self, other: Self) {
+ let slf = (!self.get().is_nan()).then(|| self);
+ let other = (!other.get().is_nan()).then(|| other);
+ assert_eq!(slf, other);
+ }
+ }
+
+ trait ByteOrderTypeUnsigned: ByteOrderType {
+ const MAX_VALUE: Self;
+ }
+
+ macro_rules! impl_byte_array {
+ ($bytes:expr) => {
+ impl ByteArray for [u8; $bytes] {
+ fn invert(mut self) -> [u8; $bytes] {
+ self.reverse();
+ self
+ }
+ }
+ };
+ }
+
+ impl_byte_array!(2);
+ impl_byte_array!(4);
+ impl_byte_array!(8);
+ impl_byte_array!(16);
+
+ macro_rules! impl_byte_order_type_unsigned {
+ ($name:ident, unsigned) => {
+ impl<O: ByteOrder> ByteOrderTypeUnsigned for $name<O> {
+ const MAX_VALUE: $name<O> = $name::MAX_VALUE;
+ }
+ };
+ ($name:ident, signed) => {};
+ }
+
+ macro_rules! impl_traits {
+ ($name:ident, $native:ident, $sign:ident $(, @$float:ident)?) => {
+ impl Native for $native {
+ // For some types, `0 as $native` is required (for example, when
+ // `$native` is a floating-point type; `0` is an integer), but
+ // for other types, it's a trivial cast. In all cases, Clippy
+ // thinks it's dangerous.
+ #[allow(trivial_numeric_casts, clippy::as_conversions)]
+ const ZERO: $native = 0 as $native;
+ const MAX_VALUE: $native = $native::MAX;
+
+ type Distribution = Standard;
+ const DIST: Standard = Standard;
+
+ impl_traits!(@float_dependent_methods $(@$float)?);
+ }
+
+ impl<O: ByteOrder> ByteOrderType for $name<O> {
+ type Native = $native;
+ type ByteArray = [u8; mem::size_of::<$native>()];
+
+ const ZERO: $name<O> = $name::ZERO;
+
+ fn new(native: $native) -> $name<O> {
+ $name::new(native)
+ }
+
+ fn get(self) -> $native {
+ $name::get(self)
+ }
+
+ fn set(&mut self, native: $native) {
+ $name::set(self, native)
+ }
+
+ fn from_bytes(bytes: [u8; mem::size_of::<$native>()]) -> $name<O> {
+ $name::from(bytes)
+ }
+
+ fn into_bytes(self) -> [u8; mem::size_of::<$native>()] {
+ <[u8; mem::size_of::<$native>()]>::from(self)
+ }
+ }
+
+ impl_byte_order_type_unsigned!($name, $sign);
+ };
+ (@float_dependent_methods) => {
+ fn checked_add(self, rhs: Self) -> Option<Self> { self.checked_add(rhs) }
+ fn checked_div(self, rhs: Self) -> Option<Self> { self.checked_div(rhs) }
+ fn checked_mul(self, rhs: Self) -> Option<Self> { self.checked_mul(rhs) }
+ fn checked_rem(self, rhs: Self) -> Option<Self> { self.checked_rem(rhs) }
+ fn checked_sub(self, rhs: Self) -> Option<Self> { self.checked_sub(rhs) }
+ fn checked_shl(self, rhs: Self) -> Option<Self> { self.checked_shl(rhs.try_into().unwrap_or(u32::MAX)) }
+ fn checked_shr(self, rhs: Self) -> Option<Self> { self.checked_shr(rhs.try_into().unwrap_or(u32::MAX)) }
+ fn is_nan(self) -> bool { false }
+ };
+ (@float_dependent_methods @float) => {
+ fn checked_add(self, rhs: Self) -> Option<Self> { Some(self + rhs) }
+ fn checked_div(self, rhs: Self) -> Option<Self> { Some(self / rhs) }
+ fn checked_mul(self, rhs: Self) -> Option<Self> { Some(self * rhs) }
+ fn checked_rem(self, rhs: Self) -> Option<Self> { Some(self % rhs) }
+ fn checked_sub(self, rhs: Self) -> Option<Self> { Some(self - rhs) }
+ fn checked_shl(self, _rhs: Self) -> Option<Self> { unimplemented!() }
+ fn checked_shr(self, _rhs: Self) -> Option<Self> { unimplemented!() }
+ fn is_nan(self) -> bool { self.is_nan() }
+ };
+ }
+
+ impl_traits!(U16, u16, unsigned);
+ impl_traits!(U32, u32, unsigned);
+ impl_traits!(U64, u64, unsigned);
+ impl_traits!(U128, u128, unsigned);
+ impl_traits!(Usize, usize, unsigned);
+ impl_traits!(I16, i16, signed);
+ impl_traits!(I32, i32, signed);
+ impl_traits!(I64, i64, signed);
+ impl_traits!(I128, i128, signed);
+ impl_traits!(Isize, isize, unsigned);
+ impl_traits!(F32, f32, signed, @float);
+ impl_traits!(F64, f64, signed, @float);
+
+ macro_rules! call_for_unsigned_types {
+ ($fn:ident, $byteorder:ident) => {
+ $fn::<U16<$byteorder>>();
+ $fn::<U32<$byteorder>>();
+ $fn::<U64<$byteorder>>();
+ $fn::<U128<$byteorder>>();
+ $fn::<Usize<$byteorder>>();
+ };
+ }
+
+ macro_rules! call_for_signed_types {
+ ($fn:ident, $byteorder:ident) => {
+ $fn::<I16<$byteorder>>();
+ $fn::<I32<$byteorder>>();
+ $fn::<I64<$byteorder>>();
+ $fn::<I128<$byteorder>>();
+ $fn::<Isize<$byteorder>>();
+ };
+ }
+
+ macro_rules! call_for_float_types {
+ ($fn:ident, $byteorder:ident) => {
+ $fn::<F32<$byteorder>>();
+ $fn::<F64<$byteorder>>();
+ };
+ }
+
+ macro_rules! call_for_all_types {
+ ($fn:ident, $byteorder:ident) => {
+ call_for_unsigned_types!($fn, $byteorder);
+ call_for_signed_types!($fn, $byteorder);
+ call_for_float_types!($fn, $byteorder);
+ };
+ }
+
+ #[cfg(target_endian = "big")]
+ type NonNativeEndian = LittleEndian;
+ #[cfg(target_endian = "little")]
+ type NonNativeEndian = BigEndian;
+
+ // We use a `u64` seed so that we can use `SeedableRng::seed_from_u64`.
+ // `SmallRng`'s `SeedableRng::Seed` differs by platform, so if we wanted to
+ // call `SeedableRng::from_seed`, which takes a `Seed`, we would need
+ // conditional compilation by `target_pointer_width`.
+ const RNG_SEED: u64 = 0x7A03CAE2F32B5B8F;
+
+ const RAND_ITERS: usize = if cfg!(any(miri, kani)) {
+ // The tests below which use this constant used to take a very long time
+ // on Miri, which slows down local development and CI jobs. We're not
+ // using Miri to check for the correctness of our code, but rather its
+ // soundness, and at least in the context of these particular tests, a
+ // single loop iteration is just as good for surfacing UB as multiple
+ // iterations are.
+ //
+ // As of the writing of this comment, here's one set of measurements:
+ //
+ // $ # RAND_ITERS == 1
+ // $ cargo miri test -- -Z unstable-options --report-time endian
+ // test byteorder::tests::test_native_endian ... ok <0.049s>
+ // test byteorder::tests::test_non_native_endian ... ok <0.061s>
+ //
+ // $ # RAND_ITERS == 1024
+ // $ cargo miri test -- -Z unstable-options --report-time endian
+ // test byteorder::tests::test_native_endian ... ok <25.716s>
+ // test byteorder::tests::test_non_native_endian ... ok <38.127s>
+ 1
+ } else {
+ 1024
+ };
+
+ #[test]
+ fn test_const_methods() {
+ use big_endian::*;
+
+ #[rustversion::since(1.61.0)]
+ const _U: U16 = U16::new(0);
+ #[rustversion::since(1.61.0)]
+ const _NATIVE: u16 = _U.get();
+ const _FROM_BYTES: U16 = U16::from_bytes([0, 1]);
+ const _BYTES: [u8; 2] = _FROM_BYTES.to_bytes();
+ }
+
+ #[cfg_attr(test, test)]
+ #[cfg_attr(kani, kani::proof)]
+ fn test_zero() {
+ fn test_zero<T: ByteOrderType>() {
+ assert_eq!(T::ZERO.get(), T::Native::ZERO);
+ }
+
+ call_for_all_types!(test_zero, NativeEndian);
+ call_for_all_types!(test_zero, NonNativeEndian);
+ }
+
+ #[cfg_attr(test, test)]
+ #[cfg_attr(kani, kani::proof)]
+ fn test_max_value() {
+ fn test_max_value<T: ByteOrderTypeUnsigned>() {
+ assert_eq!(T::MAX_VALUE.get(), T::Native::MAX_VALUE);
+ }
+
+ call_for_unsigned_types!(test_max_value, NativeEndian);
+ call_for_unsigned_types!(test_max_value, NonNativeEndian);
+ }
+
+ #[cfg_attr(test, test)]
+ #[cfg_attr(kani, kani::proof)]
+ fn test_endian() {
+ fn test<T: ByteOrderType>(invert: bool) {
+ let mut r = SmallRng::seed_from_u64(RNG_SEED);
+ for _ in 0..RAND_ITERS {
+ let native = T::Native::rand(&mut r);
+ let mut bytes = T::ByteArray::default();
+ bytes.as_mut_bytes().copy_from_slice(native.as_bytes());
+ if invert {
+ bytes = bytes.invert();
+ }
+ let mut from_native = T::new(native);
+ let from_bytes = T::from_bytes(bytes);
+
+ from_native.assert_eq_or_nan(from_bytes);
+ from_native.get().assert_eq_or_nan(native);
+ from_bytes.get().assert_eq_or_nan(native);
+
+ assert_eq!(from_native.into_bytes(), bytes);
+ assert_eq!(from_bytes.into_bytes(), bytes);
+
+ let updated = T::Native::rand(&mut r);
+ from_native.set(updated);
+ from_native.get().assert_eq_or_nan(updated);
+ }
+ }
+
+ fn test_native<T: ByteOrderType>() {
+ test::<T>(false);
+ }
+
+ fn test_non_native<T: ByteOrderType>() {
+ test::<T>(true);
+ }
+
+ call_for_all_types!(test_native, NativeEndian);
+ call_for_all_types!(test_non_native, NonNativeEndian);
+ }
+
+ #[test]
+ fn test_ops_impls() {
+ // Test implementations of traits in `core::ops`. Some of these are
+ // fairly banal, but some are optimized to perform the operation without
+ // swapping byte order (namely, bit-wise operations which are identical
+ // regardless of byte order). These are important to test, and while
+ // we're testing those anyway, it's trivial to test all of the impls.
+
+ fn test<T, FTT, FTN, FNT, FNN, FNNChecked, FATT, FATN, FANT>(
+ op_t_t: FTT,
+ op_t_n: FTN,
+ op_n_t: FNT,
+ op_n_n: FNN,
+ op_n_n_checked: Option<FNNChecked>,
+ op_assign: Option<(FATT, FATN, FANT)>,
+ ) where
+ T: ByteOrderType,
+ FTT: Fn(T, T) -> T,
+ FTN: Fn(T, T::Native) -> T,
+ FNT: Fn(T::Native, T) -> T,
+ FNN: Fn(T::Native, T::Native) -> T::Native,
+ FNNChecked: Fn(T::Native, T::Native) -> Option<T::Native>,
+ FATT: Fn(&mut T, T),
+ FATN: Fn(&mut T, T::Native),
+ FANT: Fn(&mut T::Native, T),
+ {
+ let mut r = SmallRng::seed_from_u64(RNG_SEED);
+ for _ in 0..RAND_ITERS {
+ let n0 = T::Native::rand(&mut r);
+ let n1 = T::Native::rand(&mut r);
+ let t0 = T::new(n0);
+ let t1 = T::new(n1);
+
+ // If this operation would overflow/underflow, skip it rather
+ // than attempt to catch and recover from panics.
+ if matches!(&op_n_n_checked, Some(checked) if checked(n0, n1).is_none()) {
+ continue;
+ }
+
+ let t_t_res = op_t_t(t0, t1);
+ let t_n_res = op_t_n(t0, n1);
+ let n_t_res = op_n_t(n0, t1);
+ let n_n_res = op_n_n(n0, n1);
+
+ // For `f32` and `f64`, NaN values are not considered equal to
+ // themselves. We store `Option<f32>`/`Option<f64>` and store
+ // NaN as `None` so they can still be compared.
+ let val_or_none = |t: T| (!T::Native::is_nan(t.get())).then(|| t.get());
+ let t_t_res = val_or_none(t_t_res);
+ let t_n_res = val_or_none(t_n_res);
+ let n_t_res = val_or_none(n_t_res);
+ let n_n_res = (!T::Native::is_nan(n_n_res)).then(|| n_n_res);
+ assert_eq!(t_t_res, n_n_res);
+ assert_eq!(t_n_res, n_n_res);
+ assert_eq!(n_t_res, n_n_res);
+
+ if let Some((op_assign_t_t, op_assign_t_n, op_assign_n_t)) = &op_assign {
+ let mut t_t_res = t0;
+ op_assign_t_t(&mut t_t_res, t1);
+ let mut t_n_res = t0;
+ op_assign_t_n(&mut t_n_res, n1);
+ let mut n_t_res = n0;
+ op_assign_n_t(&mut n_t_res, t1);
+
+ // For `f32` and `f64`, NaN values are not considered equal to
+ // themselves. We store `Option<f32>`/`Option<f64>` and store
+ // NaN as `None` so they can still be compared.
+ let t_t_res = val_or_none(t_t_res);
+ let t_n_res = val_or_none(t_n_res);
+ let n_t_res = (!T::Native::is_nan(n_t_res)).then(|| n_t_res);
+ assert_eq!(t_t_res, n_n_res);
+ assert_eq!(t_n_res, n_n_res);
+ assert_eq!(n_t_res, n_n_res);
+ }
+ }
+ }
+
+ macro_rules! test {
+ (
+ @binary
+ $trait:ident,
+ $method:ident $([$checked_method:ident])?,
+ $trait_assign:ident,
+ $method_assign:ident,
+ $($call_for_macros:ident),*
+ ) => {{
+ fn t<T>()
+ where
+ T: ByteOrderType,
+ T: core::ops::$trait<T, Output = T>,
+ T: core::ops::$trait<T::Native, Output = T>,
+ T::Native: core::ops::$trait<T, Output = T>,
+ T::Native: core::ops::$trait<T::Native, Output = T::Native>,
+
+ T: core::ops::$trait_assign<T>,
+ T: core::ops::$trait_assign<T::Native>,
+ T::Native: core::ops::$trait_assign<T>,
+ T::Native: core::ops::$trait_assign<T::Native>,
+ {
+ test::<T, _, _, _, _, _, _, _, _>(
+ core::ops::$trait::$method,
+ core::ops::$trait::$method,
+ core::ops::$trait::$method,
+ core::ops::$trait::$method,
+ {
+ #[allow(unused_mut, unused_assignments)]
+ let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>;
+ $(
+ op_native_checked = Some(T::Native::$checked_method);
+ )?
+ op_native_checked
+ },
+ Some((
+ <T as core::ops::$trait_assign<T>>::$method_assign,
+ <T as core::ops::$trait_assign::<T::Native>>::$method_assign,
+ <T::Native as core::ops::$trait_assign::<T>>::$method_assign
+ )),
+ );
+ }
+
+ $(
+ $call_for_macros!(t, NativeEndian);
+ $call_for_macros!(t, NonNativeEndian);
+ )*
+ }};
+ (
+ @unary
+ $trait:ident,
+ $method:ident,
+ $($call_for_macros:ident),*
+ ) => {{
+ fn t<T>()
+ where
+ T: ByteOrderType,
+ T: core::ops::$trait<Output = T>,
+ T::Native: core::ops::$trait<Output = T::Native>,
+ {
+ test::<T, _, _, _, _, _, _, _, _>(
+ |slf, _rhs| core::ops::$trait::$method(slf),
+ |slf, _rhs| core::ops::$trait::$method(slf),
+ |slf, _rhs| core::ops::$trait::$method(slf).into(),
+ |slf, _rhs| core::ops::$trait::$method(slf),
+ None::<fn(T::Native, T::Native) -> Option<T::Native>>,
+ None::<(fn(&mut T, T), fn(&mut T, T::Native), fn(&mut T::Native, T))>,
+ );
+ }
+
+ $(
+ $call_for_macros!(t, NativeEndian);
+ $call_for_macros!(t, NonNativeEndian);
+ )*
+ }};
+ }
+
+ test!(@binary Add, add[checked_add], AddAssign, add_assign, call_for_all_types);
+ test!(@binary Div, div[checked_div], DivAssign, div_assign, call_for_all_types);
+ test!(@binary Mul, mul[checked_mul], MulAssign, mul_assign, call_for_all_types);
+ test!(@binary Rem, rem[checked_rem], RemAssign, rem_assign, call_for_all_types);
+ test!(@binary Sub, sub[checked_sub], SubAssign, sub_assign, call_for_all_types);
+
+ test!(@binary BitAnd, bitand, BitAndAssign, bitand_assign, call_for_unsigned_types, call_for_signed_types);
+ test!(@binary BitOr, bitor, BitOrAssign, bitor_assign, call_for_unsigned_types, call_for_signed_types);
+ test!(@binary BitXor, bitxor, BitXorAssign, bitxor_assign, call_for_unsigned_types, call_for_signed_types);
+ test!(@binary Shl, shl[checked_shl], ShlAssign, shl_assign, call_for_unsigned_types, call_for_signed_types);
+ test!(@binary Shr, shr[checked_shr], ShrAssign, shr_assign, call_for_unsigned_types, call_for_signed_types);
+
+ test!(@unary Not, not, call_for_signed_types, call_for_unsigned_types);
+ test!(@unary Neg, neg, call_for_signed_types, call_for_float_types);
+ }
+
+ #[test]
+ fn test_debug_impl() {
+ // Ensure that Debug applies format options to the inner value.
+ let val = U16::<LE>::new(10);
+ assert_eq!(format!("{:?}", val), "U16(10)");
+ assert_eq!(format!("{:03?}", val), "U16(010)");
+ assert_eq!(format!("{:x?}", val), "U16(a)");
+ }
+
+ #[test]
+ fn test_byteorder_traits_coverage() {
+ let val_be = U16::<BigEndian>::from_bytes([0, 1]);
+ let val_le = U16::<LittleEndian>::from_bytes([1, 0]);
+
+ assert_eq!(val_be.get(), 1);
+ assert_eq!(val_le.get(), 1);
+
+ // Debug
+ assert_eq!(format!("{:?}", val_be), "U16(1)");
+ assert_eq!(format!("{:?}", val_le), "U16(1)");
+
+ // PartialOrd, Ord with same type
+ assert!(val_be >= val_be);
+ assert!(val_be <= val_be);
+ assert_eq!(val_be.cmp(&val_be), core::cmp::Ordering::Equal);
+
+ // PartialOrd with native
+ assert!(val_be == 1u16);
+ assert!(val_be >= 1u16);
+
+ // Default
+ let default_be: U16<BigEndian> = Default::default();
+ assert_eq!(default_be.get(), 0);
+
+ // I16
+ let val_be_i16 = I16::<BigEndian>::from_bytes([0, 1]);
+ assert_eq!(val_be_i16.get(), 1);
+ assert_eq!(format!("{:?}", val_be_i16), "I16(1)");
+ assert_eq!(val_be_i16.cmp(&val_be_i16), core::cmp::Ordering::Equal);
+ }
+}
diff --git a/rust/zerocopy/src/deprecated.rs b/rust/zerocopy/src/deprecated.rs
new file mode 100644
index 000000000000..61b29aa5f485
--- /dev/null
+++ b/rust/zerocopy/src/deprecated.rs
@@ -0,0 +1,279 @@
+// Copyright 2024 The Fuchsia Authors
+//
+// Licensed under the 2-Clause BSD License <LICENSE-BSD or
+// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+//! Deprecated items. These are kept separate so that they don't clutter up
+//! other modules.
+
+use super::*;
+
+impl<B, T> Ref<B, T>
+where
+ B: ByteSlice,
+ T: KnownLayout + Immutable + ?Sized,
+{
+ #[deprecated(since = "0.8.0", note = "renamed to `Ref::from_bytes`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new(bytes: B) -> Option<Ref<B, T>> {
+ Self::from_bytes(bytes).ok()
+ }
+}
+
+impl<B, T> Ref<B, T>
+where
+ B: SplitByteSlice,
+ T: KnownLayout + Immutable + ?Sized,
+{
+ #[deprecated(since = "0.8.0", note = "renamed to `Ref::from_prefix`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new_from_prefix(bytes: B) -> Option<(Ref<B, T>, B)> {
+ Self::from_prefix(bytes).ok()
+ }
+}
+
+impl<B, T> Ref<B, T>
+where
+ B: SplitByteSlice,
+ T: KnownLayout + Immutable + ?Sized,
+{
+ #[deprecated(since = "0.8.0", note = "renamed to `Ref::from_suffix`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new_from_suffix(bytes: B) -> Option<(B, Ref<B, T>)> {
+ Self::from_suffix(bytes).ok()
+ }
+}
+
+impl<B, T> Ref<B, T>
+where
+ B: ByteSlice,
+ T: Unaligned + KnownLayout + Immutable + ?Sized,
+{
+ #[deprecated(
+ since = "0.8.0",
+ note = "use `Ref::from_bytes`; for `T: Unaligned`, the returned `CastError` implements `Into<SizeError>`"
+ )]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new_unaligned(bytes: B) -> Option<Ref<B, T>> {
+ Self::from_bytes(bytes).ok()
+ }
+}
+
+impl<B, T> Ref<B, T>
+where
+ B: SplitByteSlice,
+ T: Unaligned + KnownLayout + Immutable + ?Sized,
+{
+ #[deprecated(
+ since = "0.8.0",
+ note = "use `Ref::from_prefix`; for `T: Unaligned`, the returned `CastError` implements `Into<SizeError>`"
+ )]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new_unaligned_from_prefix(bytes: B) -> Option<(Ref<B, T>, B)> {
+ Self::from_prefix(bytes).ok()
+ }
+}
+
+impl<B, T> Ref<B, T>
+where
+ B: SplitByteSlice,
+ T: Unaligned + KnownLayout + Immutable + ?Sized,
+{
+ #[deprecated(
+ since = "0.8.0",
+ note = "use `Ref::from_suffix`; for `T: Unaligned`, the returned `CastError` implements `Into<SizeError>`"
+ )]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new_unaligned_from_suffix(bytes: B) -> Option<(B, Ref<B, T>)> {
+ Self::from_suffix(bytes).ok()
+ }
+}
+
+impl<B, T> Ref<B, [T]>
+where
+ B: ByteSlice,
+ T: Immutable,
+{
+ #[deprecated(since = "0.8.0", note = "`Ref::from_bytes` now supports slices")]
+ #[doc(hidden)]
+ #[inline(always)]
+ pub fn new_slice(bytes: B) -> Option<Ref<B, [T]>> {
+ Self::from_bytes(bytes).ok()
+ }
+}
+
+impl<B, T> Ref<B, [T]>
+where
+ B: ByteSlice,
+ T: Unaligned + Immutable,
+{
+ #[deprecated(
+ since = "0.8.0",
+ note = "`Ref::from_bytes` now supports slices; for `T: Unaligned`, the returned `CastError` implements `Into<SizeError>`"
+ )]
+ #[doc(hidden)]
+ #[inline(always)]
+ pub fn new_slice_unaligned(bytes: B) -> Option<Ref<B, [T]>> {
+ Ref::from_bytes(bytes).ok()
+ }
+}
+
+impl<'a, B, T> Ref<B, [T]>
+where
+ B: 'a + IntoByteSlice<'a>,
+ T: FromBytes + Immutable,
+{
+ #[deprecated(since = "0.8.0", note = "`Ref::into_ref` now supports slices")]
+ #[doc(hidden)]
+ #[inline(always)]
+ pub fn into_slice(self) -> &'a [T] {
+ Ref::into_ref(self)
+ }
+}
+
+impl<'a, B, T> Ref<B, [T]>
+where
+ B: 'a + IntoByteSliceMut<'a>,
+ T: FromBytes + IntoBytes + Immutable,
+{
+ #[deprecated(since = "0.8.0", note = "`Ref::into_mut` now supports slices")]
+ #[doc(hidden)]
+ #[inline(always)]
+ pub fn into_mut_slice(self) -> &'a mut [T] {
+ Ref::into_mut(self)
+ }
+}
+
+impl<B, T> Ref<B, [T]>
+where
+ B: SplitByteSlice,
+ T: Immutable,
+{
+ #[deprecated(since = "0.8.0", note = "replaced by `Ref::from_prefix_with_elems`")]
+ #[must_use = "has no side effects"]
+ #[doc(hidden)]
+ #[inline(always)]
+ pub fn new_slice_from_prefix(bytes: B, count: usize) -> Option<(Ref<B, [T]>, B)> {
+ Ref::from_prefix_with_elems(bytes, count).ok()
+ }
+
+ #[deprecated(since = "0.8.0", note = "replaced by `Ref::from_suffix_with_elems`")]
+ #[must_use = "has no side effects"]
+ #[doc(hidden)]
+ #[inline(always)]
+ pub fn new_slice_from_suffix(bytes: B, count: usize) -> Option<(B, Ref<B, [T]>)> {
+ Ref::from_suffix_with_elems(bytes, count).ok()
+ }
+}
+
+impl<B, T> Ref<B, [T]>
+where
+ B: SplitByteSlice,
+ T: Unaligned + Immutable,
+{
+ #[deprecated(
+ since = "0.8.0",
+ note = "use `Ref::from_prefix_with_elems`; for `T: Unaligned`, the returned `CastError` implements `Into<SizeError>`"
+ )]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new_slice_unaligned_from_prefix(bytes: B, count: usize) -> Option<(Ref<B, [T]>, B)> {
+ Ref::from_prefix_with_elems(bytes, count).ok()
+ }
+
+ #[deprecated(
+ since = "0.8.0",
+ note = "use `Ref::from_suffix_with_elems`; for `T: Unaligned`, the returned `CastError` implements `Into<SizeError>`"
+ )]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ pub fn new_slice_unaligned_from_suffix(bytes: B, count: usize) -> Option<(B, Ref<B, [T]>)> {
+ Ref::from_suffix_with_elems(bytes, count).ok()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ #[allow(deprecated)]
+ fn test_deprecated_ref_methods() {
+ let bytes = &[0u8; 1][..];
+ let bytes_slice = &[0u8; 4][..];
+
+ let r: Option<Ref<&[u8], u8>> = Ref::new(bytes);
+ assert!(r.is_some());
+
+ let r: Option<(Ref<&[u8], u8>, &[u8])> = Ref::new_from_prefix(bytes);
+ assert!(r.is_some());
+
+ let r: Option<(&[u8], Ref<&[u8], u8>)> = Ref::new_from_suffix(bytes);
+ assert!(r.is_some());
+
+ let r: Option<Ref<&[u8], u8>> = Ref::new_unaligned(bytes);
+ assert!(r.is_some());
+
+ let r: Option<(Ref<&[u8], u8>, &[u8])> = Ref::new_unaligned_from_prefix(bytes);
+ assert!(r.is_some());
+
+ let r: Option<(&[u8], Ref<&[u8], u8>)> = Ref::new_unaligned_from_suffix(bytes);
+ assert!(r.is_some());
+
+ let r: Option<Ref<&[u8], [u8]>> = Ref::new_slice(bytes_slice);
+ assert!(r.is_some());
+
+ let r: Option<Ref<&[u8], [u8]>> = Ref::new_slice_unaligned(bytes_slice);
+ assert!(r.is_some());
+
+ let r: Option<(Ref<&[u8], [u8]>, &[u8])> = Ref::new_slice_from_prefix(bytes_slice, 1);
+ assert!(r.is_some());
+
+ let r: Option<(&[u8], Ref<&[u8], [u8]>)> = Ref::new_slice_from_suffix(bytes_slice, 1);
+ assert!(r.is_some());
+
+ let r: Option<(Ref<&[u8], [u8]>, &[u8])> =
+ Ref::new_slice_unaligned_from_prefix(bytes_slice, 1);
+ assert!(r.is_some());
+
+ let r: Option<(&[u8], Ref<&[u8], [u8]>)> =
+ Ref::new_slice_unaligned_from_suffix(bytes_slice, 1);
+ assert!(r.is_some());
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn test_deprecated_into_slice() {
+ let bytes = &[0u8; 4][..];
+ let r: Ref<&[u8], [u8]> = Ref::from_bytes(bytes).unwrap();
+ let slice: &[u8] = r.into_slice();
+ assert_eq!(slice.len(), 4);
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn test_deprecated_into_mut_slice() {
+ let mut bytes = [0u8; 4];
+ let r: Ref<&mut [u8], [u8]> = Ref::from_bytes(&mut bytes[..]).unwrap();
+ let slice: &mut [u8] = r.into_mut_slice();
+ assert_eq!(slice.len(), 4);
+ }
+}
diff --git a/rust/zerocopy/src/error.rs b/rust/zerocopy/src/error.rs
new file mode 100644
index 000000000000..05fdfe3b91f8
--- /dev/null
+++ b/rust/zerocopy/src/error.rs
@@ -0,0 +1,1348 @@
+// Copyright 2024 The Fuchsia Authors
+//
+// Licensed under the 2-Clause BSD License <LICENSE-BSD or
+// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+//! Types related to error reporting.
+//!
+//! ## Single failure mode errors
+//!
+//! Generally speaking, zerocopy's conversions may fail for one of up to three
+//! reasons:
+//! - [`AlignmentError`]: the conversion source was improperly aligned
+//! - [`SizeError`]: the conversion source was of incorrect size
+//! - [`ValidityError`]: the conversion source contained invalid data
+//!
+//! Methods that only have one failure mode, like
+//! [`FromBytes::read_from_bytes`], return that mode's corresponding error type
+//! directly.
+//!
+//! ## Compound errors
+//!
+//! Conversion methods that have either two or three possible failure modes
+//! return one of these error types:
+//! - [`CastError`]: the error type of reference conversions
+//! - [`TryCastError`]: the error type of fallible reference conversions
+//! - [`TryReadError`]: the error type of fallible read conversions
+//!
+//! ## [`Unaligned`] destination types
+//!
+//! For [`Unaligned`] destination types, alignment errors are impossible. All
+//! compound error types support infallibly discarding the alignment error via
+//! [`From`] so long as `Dst: Unaligned`. For example, see [`<SizeError as
+//! From<ConvertError>>::from`][size-error-from].
+//!
+//! [size-error-from]: struct.SizeError.html#method.from-1
+//!
+//! ## Accessing the conversion source
+//!
+//! All error types provide an `into_src` method that converts the error into
+//! the source value underlying the failed conversion.
+//!
+//! ## Display formatting
+//!
+//! All error types provide a `Display` implementation that produces a
+//! human-readable error message. When `debug_assertions` are enabled, these
+//! error messages are verbose and may include potentially sensitive
+//! information, including:
+//!
+//! - the names of the involved types
+//! - the sizes of the involved types
+//! - the addresses of the involved types
+//! - the contents of the involved types
+//!
+//! When `debug_assertions` are disabled (as is default for `release` builds),
+//! such potentially sensitive information is excluded.
+//!
+//! In the future, we may support manually configuring this behavior. If you are
+//! interested in this feature, [let us know on GitHub][issue-1457] so we know
+//! to prioritize it.
+//!
+//! [issue-1457]: https://github.com/google/zerocopy/issues/1457
+//!
+//! ## Validation order
+//!
+//! Our conversion methods typically check alignment, then size, then bit
+//! validity. However, we do not guarantee that this is always the case, and
+//! this behavior may change between releases.
+//!
+//! ## `Send`, `Sync`, and `'static`
+//!
+//! Our error types are `Send`, `Sync`, and `'static` when their `Src` parameter
+//! is `Send`, `Sync`, or `'static`, respectively. This can cause issues when an
+//! error is sent or synchronized across threads; e.g.:
+//!
+//! ```compile_fail,E0515
+//! use zerocopy::*;
+//!
+//! let result: SizeError<&[u8], u32> = std::thread::spawn(|| {
+//! let source = &mut [0u8, 1, 2][..];
+//! // Try (and fail) to read a `u32` from `source`.
+//! u32::read_from_bytes(source).unwrap_err()
+//! }).join().unwrap();
+//! ```
+//!
+//! To work around this, use [`map_src`][CastError::map_src] to convert the
+//! source parameter to an unproblematic type; e.g.:
+//!
+//! ```
+//! use zerocopy::*;
+//!
+//! let result: SizeError<(), u32> = std::thread::spawn(|| {
+//! let source = &mut [0u8, 1, 2][..];
+//! // Try (and fail) to read a `u32` from `source`.
+//! u32::read_from_bytes(source).unwrap_err()
+//! // Erase the error source.
+//! .map_src(drop)
+//! }).join().unwrap();
+//! ```
+//!
+//! Alternatively, use `.to_string()` to eagerly convert the error into a
+//! human-readable message; e.g.:
+//!
+//! ```
+//! use zerocopy::*;
+//!
+//! let result: Result<u32, String> = std::thread::spawn(|| {
+//! let source = &mut [0u8, 1, 2][..];
+//! // Try (and fail) to read a `u32` from `source`.
+//! u32::read_from_bytes(source)
+//! // Eagerly render the error message.
+//! .map_err(|err| err.to_string())
+//! }).join().unwrap();
+//! ```
+#[cfg(not(no_zerocopy_core_error_1_81_0))]
+use core::error::Error;
+use core::{
+ convert::Infallible,
+ fmt::{self, Debug, Write},
+ ops::Deref,
+};
+#[cfg(all(no_zerocopy_core_error_1_81_0, any(feature = "std", test)))]
+use std::error::Error;
+
+use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned};
+#[cfg(doc)]
+use crate::{FromBytes, Ref};
+
+/// Zerocopy's generic error type.
+///
+/// Generally speaking, zerocopy's conversions may fail for one of up to three
+/// reasons:
+/// - [`AlignmentError`]: the conversion source was improperly aligned
+/// - [`SizeError`]: the conversion source was of incorrect size
+/// - [`ValidityError`]: the conversion source contained invalid data
+///
+/// However, not all conversions produce all errors. For instance,
+/// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but
+/// not validity issues. This generic error type captures these
+/// (im)possibilities via parameterization: `A` is parameterized with
+/// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is
+/// parameterized with [`Infallible`].
+///
+/// Zerocopy never uses this type directly in its API. Rather, we provide three
+/// pre-parameterized aliases:
+/// - [`CastError`]: the error type of reference conversions
+/// - [`TryCastError`]: the error type of fallible reference conversions
+/// - [`TryReadError`]: the error type of fallible read conversions
+#[derive(PartialEq, Eq, Clone)]
+pub enum ConvertError<A, S, V> {
+ /// The conversion source was improperly aligned.
+ Alignment(A),
+ /// The conversion source was of incorrect size.
+ Size(S),
+ /// The conversion source contained invalid data.
+ Validity(V),
+}
+
+impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>>
+ for ConvertError<Infallible, S, V>
+{
+ /// Infallibly discards the alignment error from this `ConvertError` since
+ /// `Dst` is unaligned.
+ ///
+ /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
+ /// error. This method permits discarding that alignment error infallibly
+ /// and replacing it with [`Infallible`].
+ ///
+ /// [`Dst: Unaligned`]: crate::Unaligned
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use core::convert::Infallible;
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
+ /// #[repr(C, packed)]
+ /// struct Bools {
+ /// one: bool,
+ /// two: bool,
+ /// many: [bool],
+ /// }
+ ///
+ /// impl Bools {
+ /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
+ /// // Since `Bools: Unaligned`, we can infallibly discard
+ /// // the alignment error.
+ /// Bools::try_ref_from_bytes(bytes).map_err(Into::into)
+ /// }
+ /// }
+ /// ```
+ #[inline]
+ fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> {
+ match err {
+ ConvertError::Alignment(e) => {
+ #[allow(unreachable_code)]
+ return ConvertError::Alignment(Infallible::from(e));
+ }
+ ConvertError::Size(e) => ConvertError::Size(e),
+ ConvertError::Validity(e) => ConvertError::Validity(e),
+ }
+ }
+}
+
+impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(),
+ Self::Size(e) => f.debug_tuple("Size").field(e).finish(),
+ Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(),
+ }
+ }
+}
+
+/// Produces a human-readable error message.
+///
+/// The message differs between debug and release builds. When
+/// `debug_assertions` are enabled, this message is verbose and includes
+/// potentially sensitive information.
+impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Alignment(e) => e.fmt(f),
+ Self::Size(e) => e.fmt(f),
+ Self::Validity(e) => e.fmt(f),
+ }
+ }
+}
+
+#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
+#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
+impl<A, S, V> Error for ConvertError<A, S, V>
+where
+ A: fmt::Display + fmt::Debug,
+ S: fmt::Display + fmt::Debug,
+ V: fmt::Display + fmt::Debug,
+{
+}
+
+/// The error emitted if the conversion source is improperly aligned.
+pub struct AlignmentError<Src, Dst: ?Sized> {
+ /// The source value involved in the conversion.
+ src: Src,
+ /// The inner destination type involved in the conversion.
+ ///
+ /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s
+ /// alignment requirement is greater than one.
+ _dst: SendSyncPhantomData<Dst>,
+}
+
+impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> {
+ /// # Safety
+ ///
+ /// The caller must ensure that `Dst`'s alignment requirement is greater
+ /// than one.
+ pub(crate) unsafe fn new_unchecked(src: Src) -> Self {
+ // INVARIANT: The caller guarantees that `Dst`'s alignment requirement
+ // is greater than one.
+ Self { src, _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Produces the source underlying the failed conversion.
+ #[inline]
+ pub fn into_src(self) -> Src {
+ self.src
+ }
+
+ pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> {
+ // INVARIANT: `with_src` doesn't change the type of `Dst`, so the
+ // invariant that `Dst`'s alignment requirement is greater than one is
+ // preserved.
+ AlignmentError { src: new_src, _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Maps the source value associated with the conversion error.
+ ///
+ /// This can help mitigate [issues with `Send`, `Sync` and `'static`
+ /// bounds][self#send-sync-and-static].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::*;
+ ///
+ /// let unaligned = Unalign::new(0u16);
+ ///
+ /// // Attempt to deref `unaligned`. This might fail with an alignment error.
+ /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref();
+ ///
+ /// // Map the error's source to its address as a usize.
+ /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| {
+ /// err.map_src(|src| src as *const _ as usize)
+ /// });
+ /// ```
+ #[inline]
+ pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> {
+ AlignmentError { src: f(self.src), _dst: SendSyncPhantomData::default() }
+ }
+
+ pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> {
+ ConvertError::Alignment(self)
+ }
+
+ /// Format extra details for a verbose, human-readable error message.
+ ///
+ /// This formatting may include potentially sensitive information.
+ fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ where
+ Src: Deref,
+ Dst: KnownLayout,
+ {
+ #[allow(clippy::as_conversions)]
+ let addr = self.src.deref() as *const _ as *const ();
+ let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros());
+
+ f.write_str("\n\nSource type: ")?;
+ f.write_str(core::any::type_name::<Src>())?;
+
+ f.write_str("\nSource address: ")?;
+ addr.fmt(f)?;
+ f.write_str(" (a multiple of ")?;
+ addr_align.fmt(f)?;
+ f.write_str(")")?;
+
+ f.write_str("\nDestination type: ")?;
+ f.write_str(core::any::type_name::<Dst>())?;
+
+ f.write_str("\nDestination alignment: ")?;
+ <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?;
+
+ Ok(())
+ }
+}
+
+impl<Src: Clone, Dst: ?Sized> Clone for AlignmentError<Src, Dst> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() }
+ }
+}
+
+impl<Src: PartialEq, Dst: ?Sized> PartialEq for AlignmentError<Src, Dst> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.src == other.src
+ }
+}
+
+impl<Src: Eq, Dst: ?Sized> Eq for AlignmentError<Src, Dst> {}
+
+impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible {
+ #[inline(always)]
+ fn from(_: AlignmentError<Src, Dst>) -> Infallible {
+ // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s
+ // alignment requirement is greater than one. In this block, `Dst:
+ // Unaligned`, which means that its alignment requirement is equal to
+ // one. Thus, it's not possible to reach here at runtime.
+ unsafe { core::hint::unreachable_unchecked() }
+ }
+}
+
+#[cfg(test)]
+impl<Src, Dst> AlignmentError<Src, Dst> {
+ // A convenience constructor so that test code doesn't need to write
+ // `unsafe`.
+ fn new_checked(src: Src) -> AlignmentError<Src, Dst> {
+ assert_ne!(core::mem::align_of::<Dst>(), 1);
+ // SAFETY: The preceding assertion guarantees that `Dst`'s alignment
+ // requirement is greater than one.
+ unsafe { AlignmentError::new_unchecked(src) }
+ }
+}
+
+impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("AlignmentError").finish()
+ }
+}
+
+/// Produces a human-readable error message.
+///
+/// The message differs between debug and release builds. When
+/// `debug_assertions` are enabled, this message is verbose and includes
+/// potentially sensitive information.
+impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>
+where
+ Src: Deref,
+ Dst: KnownLayout,
+{
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.")?;
+
+ if cfg!(debug_assertions) {
+ self.display_verbose_extras(f)
+ } else {
+ Ok(())
+ }
+ }
+}
+
+#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
+#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
+impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst>
+where
+ Src: Deref,
+ Dst: KnownLayout,
+{
+}
+
+impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>>
+ for ConvertError<AlignmentError<Src, Dst>, S, V>
+{
+ #[inline(always)]
+ fn from(err: AlignmentError<Src, Dst>) -> Self {
+ Self::Alignment(err)
+ }
+}
+
+/// The error emitted if the conversion source is of incorrect size.
+pub struct SizeError<Src, Dst: ?Sized> {
+ /// The source value involved in the conversion.
+ src: Src,
+ /// The inner destination type involved in the conversion.
+ _dst: SendSyncPhantomData<Dst>,
+}
+
+impl<Src, Dst: ?Sized> SizeError<Src, Dst> {
+ pub(crate) fn new(src: Src) -> Self {
+ Self { src, _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Produces the source underlying the failed conversion.
+ #[inline]
+ pub fn into_src(self) -> Src {
+ self.src
+ }
+
+ /// Sets the source value associated with the conversion error.
+ pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> {
+ SizeError { src: new_src, _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Maps the source value associated with the conversion error.
+ ///
+ /// This can help mitigate [issues with `Send`, `Sync` and `'static`
+ /// bounds][self#send-sync-and-static].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::*;
+ ///
+ /// let source: [u8; 3] = [0, 1, 2];
+ ///
+ /// // Try to read a `u32` from `source`. This will fail because there are insufficient
+ /// // bytes in `source`.
+ /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]);
+ ///
+ /// // Map the error's source to its size.
+ /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| {
+ /// err.map_src(|src| src.len())
+ /// });
+ /// ```
+ #[inline]
+ pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> {
+ SizeError { src: f(self.src), _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Sets the destination type associated with the conversion error.
+ pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> {
+ SizeError { src: self.src, _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Converts the error into a general [`ConvertError`].
+ pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> {
+ ConvertError::Size(self)
+ }
+
+ /// Format extra details for a verbose, human-readable error message.
+ ///
+ /// This formatting may include potentially sensitive information.
+ fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ where
+ Src: Deref,
+ Dst: KnownLayout,
+ {
+ // include the source type
+ f.write_str("\nSource type: ")?;
+ f.write_str(core::any::type_name::<Src>())?;
+
+ // include the source.deref() size
+ let src_size = core::mem::size_of_val(&*self.src);
+ f.write_str("\nSource size: ")?;
+ src_size.fmt(f)?;
+ f.write_str(" byte")?;
+ if src_size != 1 {
+ f.write_char('s')?;
+ }
+
+ // if `Dst` is `Sized`, include the `Dst` size
+ if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info {
+ f.write_str("\nDestination size: ")?;
+ size.fmt(f)?;
+ f.write_str(" byte")?;
+ if size != 1 {
+ f.write_char('s')?;
+ }
+ }
+
+ // include the destination type
+ f.write_str("\nDestination type: ")?;
+ f.write_str(core::any::type_name::<Dst>())?;
+
+ Ok(())
+ }
+}
+
+impl<Src: Clone, Dst: ?Sized> Clone for SizeError<Src, Dst> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() }
+ }
+}
+
+impl<Src: PartialEq, Dst: ?Sized> PartialEq for SizeError<Src, Dst> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.src == other.src
+ }
+}
+
+impl<Src: Eq, Dst: ?Sized> Eq for SizeError<Src, Dst> {}
+
+impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SizeError").finish()
+ }
+}
+
+/// Produces a human-readable error message.
+///
+/// The message differs between debug and release builds. When
+/// `debug_assertions` are enabled, this message is verbose and includes
+/// potentially sensitive information.
+impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst>
+where
+ Src: Deref,
+ Dst: KnownLayout,
+{
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.")?;
+ if cfg!(debug_assertions) {
+ f.write_str("\n")?;
+ self.display_verbose_extras(f)?;
+ }
+ Ok(())
+ }
+}
+
+#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
+#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
+impl<Src, Dst: ?Sized> Error for SizeError<Src, Dst>
+where
+ Src: Deref,
+ Dst: KnownLayout,
+{
+}
+
+impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
+ #[inline(always)]
+ fn from(err: SizeError<Src, Dst>) -> Self {
+ Self::Size(err)
+ }
+}
+
+/// The error emitted if the conversion source contains invalid data.
+pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> {
+ /// The source value involved in the conversion.
+ pub(crate) src: Src,
+ /// The inner destination type involved in the conversion.
+ _dst: SendSyncPhantomData<Dst>,
+}
+
+impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
+ pub(crate) fn new(src: Src) -> Self {
+ Self { src, _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Produces the source underlying the failed conversion.
+ #[inline]
+ pub fn into_src(self) -> Src {
+ self.src
+ }
+
+ /// Maps the source value associated with the conversion error.
+ ///
+ /// This can help mitigate [issues with `Send`, `Sync` and `'static`
+ /// bounds][self#send-sync-and-static].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::*;
+ ///
+ /// let source: u8 = 42;
+ ///
+ /// // Try to transmute the `source` to a `bool`. This will fail.
+ /// let maybe_bool: Result<bool, ValidityError<u8, bool>> = try_transmute!(source);
+ ///
+ /// // Drop the error's source.
+ /// let maybe_bool: Result<bool, ValidityError<(), bool>> = maybe_bool.map_err(|err| {
+ /// err.map_src(drop)
+ /// });
+ /// ```
+ #[inline]
+ pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> {
+ ValidityError { src: f(self.src), _dst: SendSyncPhantomData::default() }
+ }
+
+ /// Converts the error into a general [`ConvertError`].
+ pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> {
+ ConvertError::Validity(self)
+ }
+
+ /// Format extra details for a verbose, human-readable error message.
+ ///
+ /// This formatting may include potentially sensitive information.
+ fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ where
+ Dst: KnownLayout,
+ {
+ f.write_str("Destination type: ")?;
+ f.write_str(core::any::type_name::<Dst>())?;
+ Ok(())
+ }
+}
+
+impl<Src: Clone, Dst: ?Sized + TryFromBytes> Clone for ValidityError<Src, Dst> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() }
+ }
+}
+
+// SAFETY: `ValidityError` contains a single `Self::Inner = Src`, and no other
+// non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` to `f`.
+unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc>
+ for crate::ValidityError<Src, Dst>
+where
+ Dst: TryFromBytes + ?Sized,
+{
+ type Inner = Src;
+ type Mapped = crate::ValidityError<NewSrc, Dst>;
+ #[inline]
+ fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped {
+ self.map_src(f)
+ }
+}
+
+impl<Src: PartialEq, Dst: ?Sized + TryFromBytes> PartialEq for ValidityError<Src, Dst> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.src == other.src
+ }
+}
+
+impl<Src: Eq, Dst: ?Sized + TryFromBytes> Eq for ValidityError<Src, Dst> {}
+
+impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ValidityError").finish()
+ }
+}
+
+/// Produces a human-readable error message.
+///
+/// The message differs between debug and release builds. When
+/// `debug_assertions` are enabled, this message is verbose and includes
+/// potentially sensitive information.
+impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst>
+where
+ Dst: KnownLayout + TryFromBytes,
+{
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("The conversion failed because the source bytes are not a valid value of the destination type.")?;
+ if cfg!(debug_assertions) {
+ f.write_str("\n\n")?;
+ self.display_verbose_extras(f)?;
+ }
+ Ok(())
+ }
+}
+
+#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
+#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
+impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst> where Dst: KnownLayout + TryFromBytes {}
+
+impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
+ for ConvertError<A, S, ValidityError<Src, Dst>>
+{
+ #[inline(always)]
+ fn from(err: ValidityError<Src, Dst>) -> Self {
+ Self::Validity(err)
+ }
+}
+
+/// The error type of reference conversions.
+///
+/// Reference conversions, like [`FromBytes::ref_from_bytes`] may emit
+/// [alignment](AlignmentError) and [size](SizeError) errors.
+// Bounds on generic parameters are not enforced in type aliases, but they do
+// appear in rustdoc.
+#[allow(type_alias_bounds)]
+pub type CastError<Src, Dst: ?Sized> =
+ ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>;
+
+impl<Src, Dst: ?Sized> CastError<Src, Dst> {
+ /// Produces the source underlying the failed conversion.
+ #[inline]
+ pub fn into_src(self) -> Src {
+ match self {
+ Self::Alignment(e) => e.src,
+ Self::Size(e) => e.src,
+ Self::Validity(i) => match i {},
+ }
+ }
+
+ /// Sets the source value associated with the conversion error.
+ pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> {
+ match self {
+ Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)),
+ Self::Size(e) => CastError::Size(e.with_src(new_src)),
+ Self::Validity(i) => match i {},
+ }
+ }
+
+ /// Maps the source value associated with the conversion error.
+ ///
+ /// This can help mitigate [issues with `Send`, `Sync` and `'static`
+ /// bounds][self#send-sync-and-static].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::*;
+ ///
+ /// let source: [u8; 3] = [0, 1, 2];
+ ///
+ /// // Try to read a `u32` from `source`. This will fail because there are insufficient
+ /// // bytes in `source`.
+ /// let maybe_u32: Result<&u32, CastError<&[u8], u32>> = u32::ref_from_bytes(&source[..]);
+ ///
+ /// // Map the error's source to its size and address.
+ /// let maybe_u32: Result<&u32, CastError<(usize, usize), u32>> = maybe_u32.map_err(|err| {
+ /// err.map_src(|src| (src.len(), src.as_ptr() as usize))
+ /// });
+ /// ```
+ #[inline]
+ pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst> {
+ match self {
+ Self::Alignment(e) => CastError::Alignment(e.map_src(f)),
+ Self::Size(e) => CastError::Size(e.map_src(f)),
+ Self::Validity(i) => match i {},
+ }
+ }
+
+ /// Converts the error into a general [`ConvertError`].
+ pub(crate) fn into(self) -> TryCastError<Src, Dst>
+ where
+ Dst: TryFromBytes,
+ {
+ match self {
+ Self::Alignment(e) => TryCastError::Alignment(e),
+ Self::Size(e) => TryCastError::Size(e),
+ Self::Validity(i) => match i {},
+ }
+ }
+}
+
+// SAFETY: `CastError` is either a single `AlignmentError` or a single
+// `SizeError`. In either case, it contains a single `Self::Inner = Src`, and no
+// other non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner`
+// to `f`.
+unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc> for crate::CastError<Src, Dst>
+where
+ Dst: ?Sized,
+{
+ type Inner = Src;
+ type Mapped = crate::CastError<NewSrc, Dst>;
+
+ #[inline]
+ fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped {
+ self.map_src(f)
+ }
+}
+
+impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src, Dst> {
+ /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst`
+ /// is unaligned.
+ ///
+ /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
+ /// error, and so the only error that can be encountered at runtime is a
+ /// [`SizeError`]. This method permits extracting that `SizeError`
+ /// infallibly.
+ ///
+ /// [`Dst: Unaligned`]: crate::Unaligned
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
+ /// #[repr(C)]
+ /// struct UdpHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
+ /// #[repr(C, packed)]
+ /// struct UdpPacket {
+ /// header: UdpHeader,
+ /// body: [u8],
+ /// }
+ ///
+ /// impl UdpPacket {
+ /// pub fn parse(bytes: &[u8]) -> Result<&UdpPacket, SizeError<&[u8], UdpPacket>> {
+ /// // Since `UdpPacket: Unaligned`, we can map the `CastError` to a `SizeError`.
+ /// UdpPacket::ref_from_bytes(bytes).map_err(Into::into)
+ /// }
+ /// }
+ /// ```
+ #[inline(always)]
+ fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> {
+ match err {
+ #[allow(unreachable_code)]
+ CastError::Alignment(e) => match Infallible::from(e) {},
+ CastError::Size(e) => e,
+ CastError::Validity(i) => match i {},
+ }
+ }
+}
+
+/// The error type of fallible reference conversions.
+///
+/// Fallible reference conversions, like [`TryFromBytes::try_ref_from_bytes`]
+/// may emit [alignment](AlignmentError), [size](SizeError), and
+/// [validity](ValidityError) errors.
+// Bounds on generic parameters are not enforced in type aliases, but they do
+// appear in rustdoc.
+#[allow(type_alias_bounds)]
+pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> =
+ ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
+
+// FIXME(#1139): Remove the `TryFromBytes` here and in other downstream
+// locations (all the way to `ValidityError`) if we determine it's not necessary
+// for rich validity errors.
+impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> {
+ /// Produces the source underlying the failed conversion.
+ #[inline]
+ pub fn into_src(self) -> Src {
+ match self {
+ Self::Alignment(e) => e.src,
+ Self::Size(e) => e.src,
+ Self::Validity(e) => e.src,
+ }
+ }
+
+ /// Maps the source value associated with the conversion error.
+ ///
+ /// This can help mitigate [issues with `Send`, `Sync` and `'static`
+ /// bounds][self#send-sync-and-static].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use core::num::NonZeroU32;
+ /// use zerocopy::*;
+ ///
+ /// let source: [u8; 3] = [0, 0, 0];
+ ///
+ /// // Try to read a `NonZeroU32` from `source`.
+ /// let maybe_u32: Result<&NonZeroU32, TryCastError<&[u8], NonZeroU32>>
+ /// = NonZeroU32::try_ref_from_bytes(&source[..]);
+ ///
+ /// // Map the error's source to its size and address.
+ /// let maybe_u32: Result<&NonZeroU32, TryCastError<(usize, usize), NonZeroU32>> =
+ /// maybe_u32.map_err(|err| {
+ /// err.map_src(|src| (src.len(), src.as_ptr() as usize))
+ /// });
+ /// ```
+ #[inline]
+ pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> {
+ match self {
+ Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)),
+ Self::Size(e) => TryCastError::Size(e.map_src(f)),
+ Self::Validity(e) => TryCastError::Validity(e.map_src(f)),
+ }
+ }
+}
+
+impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> {
+ #[inline]
+ fn from(value: CastError<Src, Dst>) -> Self {
+ match value {
+ CastError::Alignment(e) => Self::Alignment(e),
+ CastError::Size(e) => Self::Size(e),
+ CastError::Validity(i) => match i {},
+ }
+ }
+}
+
+/// The error type of fallible read-conversions.
+///
+/// Fallible read-conversions, like [`TryFromBytes::try_read_from_bytes`] may
+/// emit [size](SizeError) and [validity](ValidityError) errors, but not
+/// alignment errors.
+// Bounds on generic parameters are not enforced in type aliases, but they do
+// appear in rustdoc.
+#[allow(type_alias_bounds)]
+pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> =
+ ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
+
+impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
+ /// Produces the source underlying the failed conversion.
+ #[inline]
+ pub fn into_src(self) -> Src {
+ match self {
+ Self::Alignment(i) => match i {},
+ Self::Size(e) => e.src,
+ Self::Validity(e) => e.src,
+ }
+ }
+
+ /// Maps the source value associated with the conversion error.
+ ///
+ /// This can help mitigate [issues with `Send`, `Sync` and `'static`
+ /// bounds][self#send-sync-and-static].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use core::num::NonZeroU32;
+ /// use zerocopy::*;
+ ///
+ /// let source: [u8; 3] = [0, 0, 0];
+ ///
+ /// // Try to read a `NonZeroU32` from `source`.
+ /// let maybe_u32: Result<NonZeroU32, TryReadError<&[u8], NonZeroU32>>
+ /// = NonZeroU32::try_read_from_bytes(&source[..]);
+ ///
+ /// // Map the error's source to its size.
+ /// let maybe_u32: Result<NonZeroU32, TryReadError<usize, NonZeroU32>> =
+ /// maybe_u32.map_err(|err| {
+ /// err.map_src(|src| src.len())
+ /// });
+ /// ```
+ #[inline]
+ pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> {
+ match self {
+ Self::Alignment(i) => match i {},
+ Self::Size(e) => TryReadError::Size(e.map_src(f)),
+ Self::Validity(e) => TryReadError::Validity(e.map_src(f)),
+ }
+ }
+}
+
+/// The error type of well-aligned, fallible casts.
+///
+/// This is like [`TryCastError`], but for casts that are always well-aligned.
+/// It is identical to `TryCastError`, except that its alignment error is
+/// [`Infallible`].
+///
+/// As of this writing, none of zerocopy's API produces this error directly.
+/// However, it is useful since it permits users to infallibly discard alignment
+/// errors when they can prove statically that alignment errors are impossible.
+///
+/// # Examples
+///
+/// ```
+/// use core::convert::Infallible;
+/// use zerocopy::*;
+/// # use zerocopy_derive::*;
+///
+/// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
+/// #[repr(C, packed)]
+/// struct Bools {
+/// one: bool,
+/// two: bool,
+/// many: [bool],
+/// }
+///
+/// impl Bools {
+/// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
+/// // Since `Bools: Unaligned`, we can infallibly discard
+/// // the alignment error.
+/// Bools::try_ref_from_bytes(bytes).map_err(Into::into)
+/// }
+/// }
+/// ```
+#[allow(type_alias_bounds)]
+pub type AlignedTryCastError<Src, Dst: ?Sized + TryFromBytes> =
+ ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
+
+/// The error type of a failed allocation.
+///
+/// This type is intended to be deprecated in favor of the standard library's
+/// [`AllocError`] type once it is stabilized. When that happens, this type will
+/// be replaced by a type alias to the standard library type. We do not intend
+/// to treat this as a breaking change; users who wish to avoid breakage should
+/// avoid writing code which assumes that this is *not* such an alias. For
+/// example, implementing the same trait for both types will result in an impl
+/// conflict once this type is an alias.
+///
+/// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct AllocError;
+
+#[cfg(test)]
+mod tests {
+ use core::convert::Infallible;
+
+ use super::*;
+
+ #[test]
+ fn test_send_sync() {
+ // Test that all error types are `Send + Sync` even if `Dst: !Send +
+ // !Sync`.
+
+ #[allow(dead_code)]
+ fn is_send_sync<T: Send + Sync>(_t: T) {}
+
+ #[allow(dead_code)]
+ fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) {
+ is_send_sync(err)
+ }
+
+ #[allow(dead_code)]
+ fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) {
+ is_send_sync(err)
+ }
+
+ #[allow(dead_code)]
+ fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
+ err: ValidityError<Src, Dst>,
+ ) {
+ is_send_sync(err)
+ }
+
+ #[allow(dead_code)]
+ fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
+ err: ConvertError<
+ AlignmentError<Src, Dst>,
+ SizeError<Src, Dst>,
+ ValidityError<Src, Dst>,
+ >,
+ ) {
+ is_send_sync(err)
+ }
+ }
+
+ #[test]
+ fn test_eq_partial_eq_clone() {
+ // Test that all error types implement `Eq`, `PartialEq`
+ // and `Clone` if src does
+ // even if `Dst: !Eq`, `!PartialEq`, `!Clone`.
+
+ #[allow(dead_code)]
+ fn is_eq_partial_eq_clone<T: Eq + PartialEq + Clone>(_t: T) {}
+
+ #[allow(dead_code)]
+ fn alignment_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst>(
+ err: AlignmentError<Src, Dst>,
+ ) {
+ is_eq_partial_eq_clone(err)
+ }
+
+ #[allow(dead_code)]
+ fn size_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst>(
+ err: SizeError<Src, Dst>,
+ ) {
+ is_eq_partial_eq_clone(err)
+ }
+
+ #[allow(dead_code)]
+ fn validity_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst: TryFromBytes>(
+ err: ValidityError<Src, Dst>,
+ ) {
+ is_eq_partial_eq_clone(err)
+ }
+
+ #[allow(dead_code)]
+ fn convert_error_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst: TryFromBytes>(
+ err: ConvertError<
+ AlignmentError<Src, Dst>,
+ SizeError<Src, Dst>,
+ ValidityError<Src, Dst>,
+ >,
+ ) {
+ is_eq_partial_eq_clone(err)
+ }
+ }
+
+ #[test]
+ fn alignment_display() {
+ #[repr(C, align(128))]
+ struct Aligned {
+ bytes: [u8; 128],
+ }
+
+ impl_known_layout!(elain::Align::<8>);
+
+ let aligned = Aligned { bytes: [0; 128] };
+
+ let bytes = &aligned.bytes[1..];
+ let addr = crate::util::AsAddress::addr(bytes);
+ assert_eq!(
+ AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
+ format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
+ \nSource type: &[u8]\
+ \nSource address: 0x{:x} (a multiple of 1)\
+ \nDestination type: elain::Align<8>\
+ \nDestination alignment: 8", addr)
+ );
+
+ let bytes = &aligned.bytes[2..];
+ let addr = crate::util::AsAddress::addr(bytes);
+ assert_eq!(
+ AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
+ format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
+ \nSource type: &[u8]\
+ \nSource address: 0x{:x} (a multiple of 2)\
+ \nDestination type: elain::Align<8>\
+ \nDestination alignment: 8", addr)
+ );
+
+ let bytes = &aligned.bytes[3..];
+ let addr = crate::util::AsAddress::addr(bytes);
+ assert_eq!(
+ AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
+ format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
+ \nSource type: &[u8]\
+ \nSource address: 0x{:x} (a multiple of 1)\
+ \nDestination type: elain::Align<8>\
+ \nDestination alignment: 8", addr)
+ );
+
+ let bytes = &aligned.bytes[4..];
+ let addr = crate::util::AsAddress::addr(bytes);
+ assert_eq!(
+ AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
+ format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
+ \nSource type: &[u8]\
+ \nSource address: 0x{:x} (a multiple of 4)\
+ \nDestination type: elain::Align<8>\
+ \nDestination alignment: 8", addr)
+ );
+ }
+
+ #[test]
+ fn size_display() {
+ assert_eq!(
+ SizeError::<_, [u8]>::new(&[0u8; 2][..]).to_string(),
+ "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
+ \nSource type: &[u8]\
+ \nSource size: 2 bytes\
+ \nDestination type: [u8]"
+ );
+
+ assert_eq!(
+ SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(),
+ "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
+ \nSource type: &[u8]\
+ \nSource size: 1 byte\
+ \nDestination size: 2 bytes\
+ \nDestination type: [u8; 2]"
+ );
+ }
+
+ #[test]
+ fn validity_display() {
+ assert_eq!(
+ ValidityError::<_, bool>::new(&[2u8; 1][..]).to_string(),
+ "The conversion failed because the source bytes are not a valid value of the destination type.\n\
+ \n\
+ Destination type: bool"
+ );
+ }
+
+ #[test]
+ fn test_convert_error_debug() {
+ let err: ConvertError<
+ AlignmentError<&[u8], u16>,
+ SizeError<&[u8], u16>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Alignment(AlignmentError::new_checked(&[0u8]));
+ assert_eq!(format!("{:?}", err), "Alignment(AlignmentError)");
+
+ let err: ConvertError<
+ AlignmentError<&[u8], u16>,
+ SizeError<&[u8], u16>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Size(SizeError::new(&[0u8]));
+ assert_eq!(format!("{:?}", err), "Size(SizeError)");
+
+ let err: ConvertError<
+ AlignmentError<&[u8], u16>,
+ SizeError<&[u8], u16>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Validity(ValidityError::new(&[0u8]));
+ assert_eq!(format!("{:?}", err), "Validity(ValidityError)");
+ }
+
+ #[test]
+ fn test_convert_error_from_unaligned() {
+ // u8 is Unaligned
+ let err: ConvertError<
+ AlignmentError<&[u8], u8>,
+ SizeError<&[u8], u8>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Size(SizeError::new(&[0u8]));
+ let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> =
+ ConvertError::from(err);
+ match converted {
+ ConvertError::Size(_) => {}
+ _ => panic!("Expected Size error"),
+ }
+ }
+
+ #[test]
+ fn test_alignment_error_display_debug() {
+ let err: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[0u8]);
+ assert!(format!("{:?}", err).contains("AlignmentError"));
+ assert!(format!("{}", err).contains("address of the source is not a multiple"));
+ }
+
+ #[test]
+ fn test_size_error_display_debug() {
+ let err: SizeError<&[u8], u16> = SizeError::new(&[0u8]);
+ assert!(format!("{:?}", err).contains("SizeError"));
+ assert!(format!("{}", err).contains("source was incorrectly sized"));
+ }
+
+ #[test]
+ fn test_validity_error_display_debug() {
+ let err: ValidityError<&[u8], bool> = ValidityError::new(&[0u8]);
+ assert!(format!("{:?}", err).contains("ValidityError"));
+ assert!(format!("{}", err).contains("source bytes are not a valid value"));
+ }
+
+ #[test]
+ fn test_convert_error_display_debug_more() {
+ let err: ConvertError<
+ AlignmentError<&[u8], u16>,
+ SizeError<&[u8], u16>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Alignment(AlignmentError::new_checked(&[0u8]));
+ assert!(format!("{}", err).contains("address of the source is not a multiple"));
+
+ let err: ConvertError<
+ AlignmentError<&[u8], u16>,
+ SizeError<&[u8], u16>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Size(SizeError::new(&[0u8]));
+ assert!(format!("{}", err).contains("source was incorrectly sized"));
+
+ let err: ConvertError<
+ AlignmentError<&[u8], u16>,
+ SizeError<&[u8], u16>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Validity(ValidityError::new(&[0u8]));
+ assert!(format!("{}", err).contains("source bytes are not a valid value"));
+ }
+
+ #[test]
+ fn test_alignment_error_methods() {
+ let err: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[0u8]);
+
+ // into_src
+ let src = err.clone().into_src();
+ assert_eq!(src, &[0u8]);
+
+ // into
+ let converted: ConvertError<
+ AlignmentError<&[u8], u16>,
+ SizeError<&[u8], u16>,
+ ValidityError<&[u8], bool>,
+ > = err.clone().into();
+ match converted {
+ ConvertError::Alignment(_) => {}
+ _ => panic!("Expected Alignment error"),
+ }
+
+ // clone
+ let cloned = err.clone();
+ assert_eq!(err, cloned);
+
+ // eq
+ assert_eq!(err, cloned);
+ let err2: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[1u8]);
+ assert_ne!(err, err2);
+ }
+
+ #[test]
+ fn test_convert_error_from_unaligned_variants() {
+ // u8 is Unaligned
+ let err: ConvertError<
+ AlignmentError<&[u8], u8>,
+ SizeError<&[u8], u8>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Validity(ValidityError::new(&[0u8]));
+ let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> =
+ ConvertError::from(err);
+ match converted {
+ ConvertError::Validity(_) => {}
+ _ => panic!("Expected Validity error"),
+ }
+
+ let err: ConvertError<
+ AlignmentError<&[u8], u8>,
+ SizeError<&[u8], u8>,
+ ValidityError<&[u8], bool>,
+ > = ConvertError::Size(SizeError::new(&[0u8]));
+ let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> =
+ ConvertError::from(err);
+ match converted {
+ ConvertError::Size(_) => {}
+ _ => panic!("Expected Size error"),
+ }
+ }
+}
diff --git a/rust/zerocopy/src/impls.rs b/rust/zerocopy/src/impls.rs
new file mode 100644
index 000000000000..80538bfc8a26
--- /dev/null
+++ b/rust/zerocopy/src/impls.rs
@@ -0,0 +1,2387 @@
+// Copyright 2024 The Fuchsia Authors
+//
+// Licensed under the 2-Clause BSD License <LICENSE-BSD or
+// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+use core::{
+ cell::{Cell, UnsafeCell},
+ mem::MaybeUninit as CoreMaybeUninit,
+ ptr::NonNull,
+};
+
+use super::*;
+use crate::pointer::cast::{CastSizedExact, CastUnsized};
+
+// SAFETY: Per the reference [1], "the unit tuple (`()`) ... is guaranteed as a
+// zero-sized type to have a size of 0 and an alignment of 1."
+// - `Immutable`: `()` self-evidently does not contain any `UnsafeCell`s.
+// - `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`: There is only
+// one possible sequence of 0 bytes, and `()` is inhabited.
+// - `IntoBytes`: Since `()` has size 0, it contains no padding bytes.
+// - `Unaligned`: `()` has alignment 1.
+//
+// [1] https://doc.rust-lang.org/1.81.0/reference/type-layout.html#tuple-layout
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!((): Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ assert_unaligned!(());
+};
+
+// SAFETY:
+// - `Immutable`: These types self-evidently do not contain any `UnsafeCell`s.
+// - `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`: all bit
+// patterns are valid for numeric types [1]
+// - `IntoBytes`: numeric types have no padding bytes [1]
+// - `Unaligned` (`u8` and `i8` only): The reference [2] specifies the size of
+// `u8` and `i8` as 1 byte. We also know that:
+// - Alignment is >= 1 [3]
+// - Size is an integer multiple of alignment [4]
+// - The only value >= 1 for which 1 is an integer multiple is 1 Therefore,
+// the only possible alignment for `u8` and `i8` is 1.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/reference/types/numeric.html#bit-validity:
+//
+// For every numeric type, `T`, the bit validity of `T` is equivalent to
+// the bit validity of `[u8; size_of::<T>()]`. An uninitialized byte is
+// not a valid `u8`.
+//
+// [2] https://doc.rust-lang.org/1.81.0/reference/type-layout.html#primitive-data-layout
+//
+// [3] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#size-and-alignment:
+//
+// Alignment is measured in bytes, and must be at least 1.
+//
+// [4] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#size-and-alignment:
+//
+// The size of a value is always a multiple of its alignment.
+//
+// FIXME(#278): Once we've updated the trait docs to refer to `u8`s rather than
+// bits or bytes, update this comment, especially the reference to [1].
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(u8: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ unsafe_impl!(i8: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ assert_unaligned!(u8, i8);
+ unsafe_impl!(u16: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(i16: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(u32: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(i32: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(u64: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(i64: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(u128: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(i128: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(usize: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(isize: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(f32: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(f64: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ #[cfg(feature = "float-nightly")]
+ unsafe_impl!(#[cfg_attr(doc_cfg, doc(cfg(feature = "float-nightly")))] f16: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ #[cfg(feature = "float-nightly")]
+ unsafe_impl!(#[cfg_attr(doc_cfg, doc(cfg(feature = "float-nightly")))] f128: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes);
+};
+
+// SAFETY:
+// - `Immutable`: `bool` self-evidently does not contain any `UnsafeCell`s.
+// - `FromZeros`: Valid since "[t]he value false has the bit pattern 0x00" [1].
+// - `IntoBytes`: Since "the boolean type has a size and alignment of 1 each"
+// and "The value false has the bit pattern 0x00 and the value true has the
+// bit pattern 0x01" [1]. Thus, the only byte of the bool is always
+// initialized.
+// - `Unaligned`: Per the reference [1], "[a]n object with the boolean type has
+// a size and alignment of 1 each."
+//
+// [1] https://doc.rust-lang.org/1.81.0/reference/types/boolean.html
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe { unsafe_impl!(bool: Immutable, FromZeros, IntoBytes, Unaligned) };
+assert_unaligned!(bool);
+
+// SAFETY: The impl must only return `true` for its argument if the original
+// `Maybe<bool>` refers to a valid `bool`. We only return true if the `u8` value
+// is 0 or 1, and both of these are valid values for `bool` [1].
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/reference/types/boolean.html:
+//
+// The value false has the bit pattern 0x00 and the value true has the bit
+// pattern 0x01.
+const _: () = unsafe {
+ unsafe_impl!(=> TryFromBytes for bool; |byte| {
+ let byte = byte.transmute_with::<u8, invariant::Valid, CastSizedExact, BecauseImmutable>();
+ *byte.unaligned_as_ref() < 2
+ })
+};
+
+// SAFETY:
+// - `Immutable`: `char` self-evidently does not contain any `UnsafeCell`s.
+// - `FromZeros`: Per reference [1], "[a] value of type char is a Unicode scalar
+// value (i.e. a code point that is not a surrogate), represented as a 32-bit
+// unsigned word in the 0x0000 to 0xD7FF or 0xE000 to 0x10FFFF range" which
+// contains 0x0000.
+// - `IntoBytes`: `char` is per reference [1] "represented as a 32-bit unsigned
+// word" (`u32`) which is `IntoBytes`. Note that unlike `u32`, not all bit
+// patterns are valid for `char`.
+//
+// [1] https://doc.rust-lang.org/1.81.0/reference/types/textual.html
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) };
+
+// SAFETY: The impl must only return `true` for its argument if the original
+// `Maybe<char>` refers to a valid `char`. `char::from_u32` guarantees that it
+// returns `None` if its input is not a valid `char` [1].
+//
+// [1] Per https://doc.rust-lang.org/core/primitive.char.html#method.from_u32:
+//
+// `from_u32()` will return `None` if the input is not a valid value for a
+// `char`.
+const _: () = unsafe {
+ unsafe_impl!(=> TryFromBytes for char; |c| {
+ let c = c.transmute_with::<Unalign<u32>, invariant::Valid, CastSizedExact, BecauseImmutable>();
+ let c = c.read().into_inner();
+ char::from_u32(c).is_some()
+ });
+};
+
+// SAFETY: Per the Reference [1], `str` has the same layout as `[u8]`.
+// - `Immutable`: `[u8]` does not contain any `UnsafeCell`s.
+// - `FromZeros`, `IntoBytes`, `Unaligned`: `[u8]` is `FromZeros`, `IntoBytes`,
+// and `Unaligned`.
+//
+// Note that we don't `assert_unaligned!(str)` because `assert_unaligned!` uses
+// `align_of`, which only works for `Sized` types.
+//
+// FIXME(#429): Improve safety proof for `FromZeros` and `IntoBytes`; having the same
+// layout as `[u8]` isn't sufficient.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#str-layout:
+//
+// String slices are a UTF-8 representation of characters that have the same
+// layout as slices of type `[u8]`.
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unaligned) };
+
+// SAFETY: The impl must only return `true` for its argument if the original
+// `Maybe<str>` refers to a valid `str`. `str::from_utf8` guarantees that it
+// returns `Err` if its input is not a valid `str` [1].
+//
+// [1] Per https://doc.rust-lang.org/core/str/fn.from_utf8.html#errors:
+//
+// Returns `Err` if the slice is not UTF-8.
+const _: () = unsafe {
+ unsafe_impl!(=> TryFromBytes for str; |c| {
+ let c = c.transmute_with::<[u8], invariant::Valid, CastUnsized, BecauseImmutable>();
+ let c = c.unaligned_as_ref();
+ core::str::from_utf8(c).is_ok()
+ })
+};
+
+macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
+ ($($nonzero:ident[$prim:ty]),*) => {
+ $(
+ unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
+ let n = n.transmute_with::<Unalign<$prim>, invariant::Valid, CastSizedExact, BecauseImmutable>();
+ $nonzero::new(n.read().into_inner()).is_some()
+ });
+ )*
+ }
+}
+
+// `NonZeroXxx` is `IntoBytes`, but not `FromZeros` or `FromBytes`.
+//
+// SAFETY:
+// - `IntoBytes`: `NonZeroXxx` has the same layout as its associated primitive.
+// Since it is the same size, this guarantees it has no padding - integers
+// have no padding, and there's no room for padding if it can represent all
+// of the same values except 0.
+// - `Unaligned`: `NonZeroU8` and `NonZeroI8` document that `Option<NonZeroU8>`
+// and `Option<NonZeroI8>` both have size 1. [1] [2] This is worded in a way
+// that makes it unclear whether it's meant as a guarantee, but given the
+// purpose of those types, it's virtually unthinkable that that would ever
+// change. `Option` cannot be smaller than its contained type, which implies
+// that, and `NonZeroX8` are of size 1 or 0. `NonZeroX8` can represent
+// multiple states, so they cannot be 0 bytes, which means that they must be 1
+// byte. The only valid alignment for a 1-byte type is 1.
+//
+// FIXME(#429):
+// - Add quotes from documentation.
+// - Add safety comment for `Immutable`. How can we prove that `NonZeroXxx`
+// doesn't contain any `UnsafeCell`s? It's obviously true, but it's not clear
+// how we'd prove it short of adding text to the stdlib docs that says so
+// explicitly, which likely wouldn't be accepted.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/std/num/type.NonZeroU8.html:
+//
+// `NonZeroU8` is guaranteed to have the same layout and bit validity as `u8` with
+// the exception that 0 is not a valid instance.
+//
+// [2] Per https://doc.rust-lang.org/1.81.0/std/num/type.NonZeroI8.html:
+//
+// `NonZeroI8` is guaranteed to have the same layout and bit validity as `i8` with
+// the exception that 0 is not a valid instance.
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(NonZeroU8: Immutable, IntoBytes, Unaligned);
+ unsafe_impl!(NonZeroI8: Immutable, IntoBytes, Unaligned);
+ assert_unaligned!(NonZeroU8, NonZeroI8);
+ unsafe_impl!(NonZeroU16: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroI16: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroU32: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroI32: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroU64: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroI64: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroU128: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroI128: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroUsize: Immutable, IntoBytes);
+ unsafe_impl!(NonZeroIsize: Immutable, IntoBytes);
+ unsafe_impl_try_from_bytes_for_nonzero!(
+ NonZeroU8[u8],
+ NonZeroI8[i8],
+ NonZeroU16[u16],
+ NonZeroI16[i16],
+ NonZeroU32[u32],
+ NonZeroI32[i32],
+ NonZeroU64[u64],
+ NonZeroI64[i64],
+ NonZeroU128[u128],
+ NonZeroI128[i128],
+ NonZeroUsize[usize],
+ NonZeroIsize[isize]
+ );
+};
+
+// SAFETY:
+// - `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`, `IntoBytes`:
+// The Rust compiler reuses `0` value to represent `None`, so
+// `size_of::<Option<NonZeroXxx>>() == size_of::<xxx>()`; see `NonZeroXxx`
+// documentation.
+// - `Unaligned`: `NonZeroU8` and `NonZeroI8` document that `Option<NonZeroU8>`
+// and `Option<NonZeroI8>` both have size 1. [1] [2] This is worded in a way
+// that makes it unclear whether it's meant as a guarantee, but given the
+// purpose of those types, it's virtually unthinkable that that would ever
+// change. The only valid alignment for a 1-byte type is 1.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/std/num/type.NonZeroU8.html:
+//
+// `Option<NonZeroU8>` is guaranteed to be compatible with `u8`, including in FFI.
+//
+// Thanks to the null pointer optimization, `NonZeroU8` and `Option<NonZeroU8>`
+// are guaranteed to have the same size and alignment:
+//
+// [2] Per https://doc.rust-lang.org/1.81.0/std/num/type.NonZeroI8.html:
+//
+// `Option<NonZeroI8>` is guaranteed to be compatible with `i8`, including in FFI.
+//
+// Thanks to the null pointer optimization, `NonZeroI8` and `Option<NonZeroI8>`
+// are guaranteed to have the same size and alignment:
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(Option<NonZeroU8>: TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ unsafe_impl!(Option<NonZeroI8>: TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ assert_unaligned!(Option<NonZeroU8>, Option<NonZeroI8>);
+ unsafe_impl!(Option<NonZeroU16>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroI16>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroU32>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroI32>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroU64>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroI64>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroU128>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroI128>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroUsize>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+ unsafe_impl!(Option<NonZeroIsize>: TryFromBytes, FromZeros, FromBytes, IntoBytes);
+};
+
+// SAFETY: While it's not fully documented, the consensus is that `Box<T>` does
+// not contain any `UnsafeCell`s for `T: Sized` [1]. This is not a complete
+// proof, but we are accepting this as a known risk per #1358.
+//
+// [1] https://github.com/rust-lang/unsafe-code-guidelines/issues/492
+#[cfg(feature = "alloc")]
+const _: () = unsafe {
+ unsafe_impl!(
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+ T: Sized => Immutable for Box<T>
+ )
+};
+
+// SAFETY: The following types can be transmuted from `[0u8; size_of::<T>()]`. [1]
+//
+// [1] Per https://doc.rust-lang.org/1.89.0/core/option/index.html#representation:
+//
+// Rust guarantees to optimize the following types `T` such that [`Option<T>`]
+// has the same size and alignment as `T`. In some of these cases, Rust
+// further guarantees that `transmute::<_, Option<T>>([0u8; size_of::<T>()])`
+// is sound and produces `Option::<T>::None`. These cases are identified by
+// the second column:
+//
+// | `T` | `transmute::<_, Option<T>>([0u8; size_of::<T>()])` sound? |
+// |-----------------------------------|-----------------------------------------------------------|
+// | [`Box<U>`] | when `U: Sized` |
+// | `&U` | when `U: Sized` |
+// | `&mut U` | when `U: Sized` |
+// | [`ptr::NonNull<U>`] | when `U: Sized` |
+// | `fn`, `extern "C" fn`[^extern_fn] | always |
+//
+// [^extern_fn]: this remains true for `unsafe` variants, any argument/return
+// types, and any other ABI: `[unsafe] extern "abi" fn` (_e.g._, `extern
+// "system" fn`)
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ #[cfg(feature = "alloc")]
+ unsafe_impl!(
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+ T => TryFromBytes for Option<Box<T>>; |c| pointer::is_zeroed(c)
+ );
+ #[cfg(feature = "alloc")]
+ unsafe_impl!(
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+ T => FromZeros for Option<Box<T>>
+ );
+ unsafe_impl!(
+ T => TryFromBytes for Option<&'_ T>; |c| pointer::is_zeroed(c)
+ );
+ unsafe_impl!(T => FromZeros for Option<&'_ T>);
+ unsafe_impl!(
+ T => TryFromBytes for Option<&'_ mut T>; |c| pointer::is_zeroed(c)
+ );
+ unsafe_impl!(T => FromZeros for Option<&'_ mut T>);
+ unsafe_impl!(
+ T => TryFromBytes for Option<NonNull<T>>; |c| pointer::is_zeroed(c)
+ );
+ unsafe_impl!(T => FromZeros for Option<NonNull<T>>);
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_fn!(...));
+ unsafe_impl_for_power_set!(
+ A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_fn!(...);
+ |c| pointer::is_zeroed(c)
+ );
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_unsafe_fn!(...));
+ unsafe_impl_for_power_set!(
+ A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_unsafe_fn!(...);
+ |c| pointer::is_zeroed(c)
+ );
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_extern_c_fn!(...));
+ unsafe_impl_for_power_set!(
+ A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_extern_c_fn!(...);
+ |c| pointer::is_zeroed(c)
+ );
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_unsafe_extern_c_fn!(...));
+ unsafe_impl_for_power_set!(
+ A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_unsafe_extern_c_fn!(...);
+ |c| pointer::is_zeroed(c)
+ );
+};
+
+// SAFETY: `[unsafe] [extern "C"] fn()` self-evidently do not contain
+// `UnsafeCell`s. This is not a proof, but we are accepting this as a known risk
+// per #1358.
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable for opt_fn!(...));
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable for opt_unsafe_fn!(...));
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable for opt_extern_c_fn!(...));
+ unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable for opt_unsafe_extern_c_fn!(...));
+};
+
+#[cfg(all(
+ not(no_zerocopy_target_has_atomics_1_60_0),
+ any(
+ target_has_atomic = "8",
+ target_has_atomic = "16",
+ target_has_atomic = "32",
+ target_has_atomic = "64",
+ target_has_atomic = "ptr"
+ )
+))]
+#[cfg_attr(doc_cfg, doc(cfg(rust = "1.60.0")))]
+mod atomics {
+ use super::*;
+
+ macro_rules! impl_traits_for_atomics {
+ ($($atomics:tt [$primitives:ty]),* $(,)?) => {
+ $(
+ impl_known_layout!($atomics);
+ impl_for_transmute_from!(=> FromZeros for $atomics [$primitives]);
+ impl_for_transmute_from!(=> FromBytes for $atomics [$primitives]);
+ impl_for_transmute_from!(=> TryFromBytes for $atomics [$primitives]);
+ impl_for_transmute_from!(=> IntoBytes for $atomics [$primitives]);
+ )*
+ };
+ }
+
+ /// Implements `TransmuteFrom` for `$atomic`, `$prim`, and
+ /// `UnsafeCell<$prim>`.
+ ///
+ /// # Safety
+ ///
+ /// `$atomic` must have the same size and bit validity as `$prim`.
+ macro_rules! unsafe_impl_transmute_from_for_atomic {
+ ($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{
+ crate::util::macros::__unsafe();
+
+ use crate::pointer::{SizeEq, TransmuteFrom, invariant::Valid};
+
+ $(
+ // SAFETY: The caller promised that `$atomic` and `$prim` have
+ // the same size and bit validity.
+ unsafe impl<$($tyvar)?> TransmuteFrom<$atomic, Valid, Valid> for $prim {}
+ // SAFETY: The caller promised that `$atomic` and `$prim` have
+ // the same size and bit validity.
+ unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {}
+
+ impl<$($tyvar)?> SizeEq<ReadOnly<$atomic>> for ReadOnly<$prim> {
+ type CastFrom = $crate::pointer::cast::CastSizedExact;
+ }
+
+ // SAFETY: The caller promised that `$atomic` and `$prim` have
+ // the same bit validity. `UnsafeCell<T>` has the same bit
+ // validity as `T` [1].
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
+ //
+ // `UnsafeCell<T>` has the same in-memory representation as
+ // its inner type `T`. A consequence of this guarantee is that
+ // it is possible to convert between `T` and `UnsafeCell<T>`.
+ unsafe impl<$($tyvar)?> TransmuteFrom<$atomic, Valid, Valid> for core::cell::UnsafeCell<$prim> {}
+ // SAFETY: See previous safety comment.
+ unsafe impl<$($tyvar)?> TransmuteFrom<core::cell::UnsafeCell<$prim>, Valid, Valid> for $atomic {}
+ )*
+ }};
+ }
+
+ #[cfg(target_has_atomic = "8")]
+ #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))]
+ mod atomic_8 {
+ use core::sync::atomic::{AtomicBool, AtomicI8, AtomicU8};
+
+ use super::*;
+
+ impl_traits_for_atomics!(AtomicU8[u8], AtomicI8[i8]);
+
+ impl_known_layout!(AtomicBool);
+ impl_for_transmute_from!(=> FromZeros for AtomicBool [bool]);
+ impl_for_transmute_from!(=> TryFromBytes for AtomicBool [bool]);
+ impl_for_transmute_from!(=> IntoBytes for AtomicBool [bool]);
+
+ // SAFETY: Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the
+ // same size as `bool`, `u8`, and `i8` respectively. Since a type's
+ // alignment cannot be smaller than 1 [2], and since its alignment
+ // cannot be greater than its size [3], the only possible value for the
+ // alignment is 1. Thus, it is sound to implement `Unaligned`.
+ //
+ // [1] Per (for example) https://doc.rust-lang.org/1.81.0/std/sync/atomic/struct.AtomicU8.html:
+ //
+ // This type has the same size, alignment, and bit validity as the
+ // underlying integer type
+ //
+ // [2] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#size-and-alignment:
+ //
+ // Alignment is measured in bytes, and must be at least 1.
+ //
+ // [3] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#size-and-alignment:
+ //
+ // The size of a value is always a multiple of its alignment.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ unsafe_impl!(AtomicBool: Unaligned);
+ unsafe_impl!(AtomicU8: Unaligned);
+ unsafe_impl!(AtomicI8: Unaligned);
+ assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
+ };
+
+ // SAFETY: `AtomicU8`, `AtomicI8`, and `AtomicBool` have the same size
+ // and bit validity as `u8`, `i8`, and `bool` respectively [1][2][3].
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicU8.html:
+ //
+ // This type has the same size, alignment, and bit validity as the
+ // underlying integer type, `u8`.
+ //
+ // [2] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicI8.html:
+ //
+ // This type has the same size, alignment, and bit validity as the
+ // underlying integer type, `i8`.
+ //
+ // [3] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicBool.html:
+ //
+ // This type has the same size, alignment, and bit validity a `bool`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ unsafe_impl_transmute_from_for_atomic!(
+ => AtomicU8 [u8],
+ => AtomicI8 [i8],
+ => AtomicBool [bool]
+ )
+ };
+ }
+
+ #[cfg(target_has_atomic = "16")]
+ #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))]
+ mod atomic_16 {
+ use core::sync::atomic::{AtomicI16, AtomicU16};
+
+ use super::*;
+
+ impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]);
+
+ // SAFETY: `AtomicU16` and `AtomicI16` have the same size and bit
+ // validity as `u16` and `i16` respectively [1][2].
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicU16.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `u16`.
+ //
+ // [2] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicI16.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `i16`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ unsafe_impl_transmute_from_for_atomic!(=> AtomicU16 [u16], => AtomicI16 [i16])
+ };
+ }
+
+ #[cfg(target_has_atomic = "32")]
+ #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))]
+ mod atomic_32 {
+ use core::sync::atomic::{AtomicI32, AtomicU32};
+
+ use super::*;
+
+ impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]);
+
+ // SAFETY: `AtomicU32` and `AtomicI32` have the same size and bit
+ // validity as `u32` and `i32` respectively [1][2].
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicU32.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `u32`.
+ //
+ // [2] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicI32.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `i32`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ unsafe_impl_transmute_from_for_atomic!(=> AtomicU32 [u32], => AtomicI32 [i32])
+ };
+ }
+
+ #[cfg(target_has_atomic = "64")]
+ #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))]
+ mod atomic_64 {
+ use core::sync::atomic::{AtomicI64, AtomicU64};
+
+ use super::*;
+
+ impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]);
+
+ // SAFETY: `AtomicU64` and `AtomicI64` have the same size and bit
+ // validity as `u64` and `i64` respectively [1][2].
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicU64.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `u64`.
+ //
+ // [2] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicI64.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `i64`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ unsafe_impl_transmute_from_for_atomic!(=> AtomicU64 [u64], => AtomicI64 [i64])
+ };
+ }
+
+ #[cfg(target_has_atomic = "ptr")]
+ #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))]
+ mod atomic_ptr {
+ use core::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize};
+
+ use super::*;
+
+ impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]);
+
+ // FIXME(#170): Implement `FromBytes` and `IntoBytes` once we implement
+ // those traits for `*mut T`.
+ impl_known_layout!(T => AtomicPtr<T>);
+ impl_for_transmute_from!(T => TryFromBytes for AtomicPtr<T> [*mut T]);
+ impl_for_transmute_from!(T => FromZeros for AtomicPtr<T> [*mut T]);
+
+ // SAFETY: `AtomicUsize` and `AtomicIsize` have the same size and bit
+ // validity as `usize` and `isize` respectively [1][2].
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicUsize.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `usize`.
+ //
+ // [2] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicIsize.html:
+ //
+ // This type has the same size and bit validity as the underlying
+ // integer type, `isize`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ unsafe_impl_transmute_from_for_atomic!(=> AtomicUsize [usize], => AtomicIsize [isize])
+ };
+
+ // SAFETY: Per
+ // https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicPtr.html:
+ //
+ // This type has the same size and bit validity as a `*mut T`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe { unsafe_impl_transmute_from_for_atomic!(T => AtomicPtr<T> [*mut T]) };
+ }
+}
+
+// SAFETY: Per reference [1]: "For all T, the following are guaranteed:
+// size_of::<PhantomData<T>>() == 0 align_of::<PhantomData<T>>() == 1". This
+// gives:
+// - `Immutable`: `PhantomData` has no fields.
+// - `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`: There is only
+// one possible sequence of 0 bytes, and `PhantomData` is inhabited.
+// - `IntoBytes`: Since `PhantomData` has size 0, it contains no padding bytes.
+// - `Unaligned`: Per the preceding reference, `PhantomData` has alignment 1.
+//
+// [1] https://doc.rust-lang.org/1.81.0/std/marker/struct.PhantomData.html#layout-1
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(T: ?Sized => Immutable for PhantomData<T>);
+ unsafe_impl!(T: ?Sized => TryFromBytes for PhantomData<T>);
+ unsafe_impl!(T: ?Sized => FromZeros for PhantomData<T>);
+ unsafe_impl!(T: ?Sized => FromBytes for PhantomData<T>);
+ unsafe_impl!(T: ?Sized => IntoBytes for PhantomData<T>);
+ unsafe_impl!(T: ?Sized => Unaligned for PhantomData<T>);
+ assert_unaligned!(PhantomData<()>, PhantomData<u8>, PhantomData<u64>);
+};
+
+impl_for_transmute_from!(T: TryFromBytes => TryFromBytes for Wrapping<T>[T]);
+impl_for_transmute_from!(T: FromZeros => FromZeros for Wrapping<T>[T]);
+impl_for_transmute_from!(T: FromBytes => FromBytes for Wrapping<T>[T]);
+impl_for_transmute_from!(T: IntoBytes => IntoBytes for Wrapping<T>[T]);
+assert_unaligned!(Wrapping<()>, Wrapping<u8>);
+
+// SAFETY: Per [1], `Wrapping<T>` has the same layout as `T`. Since its single
+// field (of type `T`) is public, it would be a breaking change to add or remove
+// fields. Thus, we know that `Wrapping<T>` contains a `T` (as opposed to just
+// having the same size and alignment as `T`) with no pre- or post-padding.
+// Thus, `Wrapping<T>` must have `UnsafeCell`s covering the same byte ranges as
+// `Inner = T`.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/std/num/struct.Wrapping.html#layout-1:
+//
+// `Wrapping<T>` is guaranteed to have the same layout and ABI as `T`
+const _: () = unsafe { unsafe_impl!(T: Immutable => Immutable for Wrapping<T>) };
+
+// SAFETY: Per [1] in the preceding safety comment, `Wrapping<T>` has the same
+// alignment as `T`.
+const _: () = unsafe { unsafe_impl!(T: Unaligned => Unaligned for Wrapping<T>) };
+
+// SAFETY: `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`:
+// `MaybeUninit<T>` has no restrictions on its contents.
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(T => TryFromBytes for CoreMaybeUninit<T>);
+ unsafe_impl!(T => FromZeros for CoreMaybeUninit<T>);
+ unsafe_impl!(T => FromBytes for CoreMaybeUninit<T>);
+};
+
+// SAFETY: `MaybeUninit<T>` has `UnsafeCell`s covering the same byte ranges as
+// `Inner = T`. This is not explicitly documented, but it can be inferred. Per
+// [1], `MaybeUninit<T>` has the same size as `T`. Further, note the signature
+// of `MaybeUninit::assume_init_ref` [2]:
+//
+// pub unsafe fn assume_init_ref(&self) -> &T
+//
+// If the argument `&MaybeUninit<T>` and the returned `&T` had `UnsafeCell`s at
+// different offsets, this would be unsound. Its existence is proof that this is
+// not the case.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#layout-1:
+//
+// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and ABI as
+// `T`.
+//
+// [2] https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#method.assume_init_ref
+const _: () = unsafe { unsafe_impl!(T: Immutable => Immutable for CoreMaybeUninit<T>) };
+
+// SAFETY: Per [1] in the preceding safety comment, `MaybeUninit<T>` has the
+// same alignment as `T`.
+const _: () = unsafe { unsafe_impl!(T: Unaligned => Unaligned for CoreMaybeUninit<T>) };
+assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit<u8>);
+
+// SAFETY: `ManuallyDrop<T>` has the same layout as `T` [1]. This strongly
+// implies, but does not guarantee, that it contains `UnsafeCell`s covering the
+// same byte ranges as in `T`. However, it also implements `Defer<Target = T>`
+// [2], which provides the ability to convert `&ManuallyDrop<T> -> &T`. This,
+// combined with having the same size as `T`, implies that `ManuallyDrop<T>`
+// exactly contains a `T` with the same fields and `UnsafeCell`s covering the
+// same byte ranges, or else the `Deref` impl would permit safe code to obtain
+// different shared references to the same region of memory with different
+// `UnsafeCell` coverage, which would in turn permit interior mutation that
+// would violate the invariants of a shared reference.
+//
+// [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
+//
+// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
+// `T`
+//
+// [2] https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html#impl-Deref-for-ManuallyDrop%3CT%3E
+const _: () = unsafe { unsafe_impl!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>) };
+
+impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>[T]);
+impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop<T>[T]);
+impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>[T]);
+impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop<T>[T]);
+// SAFETY: `ManuallyDrop<T>` has the same layout as `T` [1], and thus has the
+// same alignment as `T`.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/struct.ManuallyDrop.html:
+//
+// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
+// `T`
+const _: () = unsafe { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<T>) };
+assert_unaligned!(ManuallyDrop<()>, ManuallyDrop<u8>);
+
+const _: () = {
+ #[allow(
+ non_camel_case_types,
+ missing_copy_implementations,
+ missing_debug_implementations,
+ missing_docs
+ )]
+ pub enum value {}
+
+ // SAFETY: See safety comment on `ProjectToTag`.
+ unsafe impl<T: ?Sized> HasTag for ManuallyDrop<T> {
+ #[inline]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
+
+ type Tag = ();
+
+ // SAFETY: It is trivially sound to project any pointer to a pointer to
+ // a type of size zero and alignment 1 (which `()` is [1]). Such a
+ // pointer will trivially satisfy its aliasing and validity requirements
+ // (since it has a zero-sized referent), and its alignment requirement
+ // (since it is aligned to 1).
+ //
+ // [1] Per https://doc.rust-lang.org/1.92.0/reference/type-layout.html#r-layout.tuple.unit:
+ //
+ // [T]he unit tuple (`()`)... is guaranteed as a zero-sized type to
+ // have a size of 0 and an alignment of 1.
+ type ProjectToTag = crate::pointer::cast::CastToUnit;
+ }
+
+ // SAFETY: `ManuallyDrop<T>` has a field of type `T` at offset `0` without
+ // any safety invariants beyond those of `T`. Its existence is not
+ // explicitly documented, but it can be inferred; per [1] `ManuallyDrop<T>`
+ // has the same size and bit validity as `T`. This field is not literally
+ // public, but is effectively so; the field can be transparently:
+ //
+ // - initialized via `ManuallyDrop::new`
+ // - moved via `ManuallyDrop::into_inner`
+ // - referenced via `ManuallyDrop::deref`
+ // - exclusively referenced via `ManuallyDrop::deref_mut`
+ //
+ // We call this field `value`, both because that is both the name of this
+ // private field, and because it is the name it is referred to in the public
+ // documentation of `ManuallyDrop::new`, `ManuallyDrop::into_inner`,
+ // `ManuallyDrop::take` and `ManuallyDrop::drop`.
+ unsafe impl<T: ?Sized>
+ HasField<value, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!(value) }>
+ for ManuallyDrop<T>
+ {
+ #[inline]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
+
+ type Type = T;
+
+ #[inline(always)]
+ fn project(slf: PtrInner<'_, Self>) -> *mut T {
+ // SAFETY: `ManuallyDrop<T>` has the same layout and bit validity as
+ // `T` [1].
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
+ //
+ // `ManuallyDrop<T>` is guaranteed to have the same layout and bit
+ // validity as `T`
+ #[allow(clippy::as_conversions)]
+ return slf.as_ptr() as *mut T;
+ }
+ }
+};
+
+impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for Cell<T>[T]);
+impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for Cell<T>[T]);
+impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for Cell<T>[T]);
+impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for Cell<T>[T]);
+// SAFETY: `Cell<T>` has the same in-memory representation as `T` [1], and thus
+// has the same alignment as `T`.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.Cell.html#memory-layout:
+//
+// `Cell<T>` has the same in-memory representation as its inner type `T`.
+const _: () = unsafe { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for Cell<T>) };
+
+impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for UnsafeCell<T>[T]);
+impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for UnsafeCell<T>[T]);
+impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for UnsafeCell<T>[T]);
+// SAFETY: `UnsafeCell<T>` has the same in-memory representation as `T` [1], and
+// thus has the same alignment as `T`.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
+//
+// `UnsafeCell<T>` has the same in-memory representation as its inner type
+// `T`.
+const _: () = unsafe { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for UnsafeCell<T>) };
+assert_unaligned!(UnsafeCell<()>, UnsafeCell<u8>);
+
+// SAFETY: See safety comment in `is_bit_valid` impl.
+unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
+ #[allow(clippy::missing_inline_in_public_items)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
+
+ #[inline(always)]
+ fn is_bit_valid<A>(candidate: Maybe<'_, Self, A>) -> bool
+ where
+ A: invariant::Alignment,
+ {
+ T::is_bit_valid(candidate.transmute::<_, _, BecauseImmutable>())
+ }
+}
+
+// SAFETY: Per the reference [1]:
+//
+// An array of `[T; N]` has a size of `size_of::<T>() * N` and the same
+// alignment of `T`. Arrays are laid out so that the zero-based `nth` element
+// of the array is offset from the start of the array by `n * size_of::<T>()`
+// bytes.
+//
+// ...
+//
+// Slices have the same layout as the section of the array they slice.
+//
+// In other words, the layout of a `[T]` or `[T; N]` is a sequence of `T`s laid
+// out back-to-back with no bytes in between. Therefore, `[T]` or `[T; N]` are
+// `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`, and `IntoBytes` if `T`
+// is (respectively). Furthermore, since an array/slice has "the same alignment
+// of `T`", `[T]` and `[T; N]` are `Unaligned` if `T` is.
+//
+// Note that we don't `assert_unaligned!` for slice types because
+// `assert_unaligned!` uses `align_of`, which only works for `Sized` types.
+//
+// [1] https://doc.rust-lang.org/1.81.0/reference/type-layout.html#array-layout
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(const N: usize, T: Immutable => Immutable for [T; N]);
+ unsafe_impl!(const N: usize, T: TryFromBytes => TryFromBytes for [T; N]; |c| {
+ let c: Ptr<'_, [ReadOnly<T>; N], _> = c.cast::<_, crate::pointer::cast::CastSized, _>();
+ let c: Ptr<'_, [ReadOnly<T>], _> = c.as_slice();
+ let c: Ptr<'_, ReadOnly<[T]>, _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>();
+
+ // Note that this call may panic, but it would still be sound even if it
+ // did. `is_bit_valid` does not promise that it will not panic (in fact,
+ // it explicitly warns that it's a possibility), and we have not
+ // violated any safety invariants that we must fix before returning.
+ <[T] as TryFromBytes>::is_bit_valid(c)
+ });
+ unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]);
+ unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]);
+ unsafe_impl!(const N: usize, T: IntoBytes => IntoBytes for [T; N]);
+ unsafe_impl!(const N: usize, T: Unaligned => Unaligned for [T; N]);
+ assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]);
+ unsafe_impl!(T: Immutable => Immutable for [T]);
+ unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c| {
+ let c: Ptr<'_, [ReadOnly<T>], _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>();
+
+ // SAFETY: Per the reference [1]:
+ //
+ // An array of `[T; N]` has a size of `size_of::<T>() * N` and the
+ // same alignment of `T`. Arrays are laid out so that the zero-based
+ // `nth` element of the array is offset from the start of the array by
+ // `n * size_of::<T>()` bytes.
+ //
+ // ...
+ //
+ // Slices have the same layout as the section of the array they slice.
+ //
+ // In other words, the layout of a `[T] is a sequence of `T`s laid out
+ // back-to-back with no bytes in between. If all elements in `candidate`
+ // are `is_bit_valid`, so too is `candidate`.
+ //
+ // Note that any of the below calls may panic, but it would still be
+ // sound even if it did. `is_bit_valid` does not promise that it will
+ // not panic (in fact, it explicitly warns that it's a possibility), and
+ // we have not violated any safety invariants that we must fix before
+ // returning.
+ c.iter().all(<T as TryFromBytes>::is_bit_valid)
+ });
+ unsafe_impl!(T: FromZeros => FromZeros for [T]);
+ unsafe_impl!(T: FromBytes => FromBytes for [T]);
+ unsafe_impl!(T: IntoBytes => IntoBytes for [T]);
+ unsafe_impl!(T: Unaligned => Unaligned for [T]);
+};
+
+// SAFETY:
+// - `Immutable`: Raw pointers do not contain any `UnsafeCell`s.
+// - `FromZeros`: For thin pointers (note that `T: Sized`), the zero pointer is
+// considered "null". [1] No operations which require provenance are legal on
+// null pointers, so this is not a footgun.
+// - `TryFromBytes`: By the same reasoning as for `FromZeroes`, we can implement
+// `TryFromBytes` for thin pointers provided that
+// [`TryFromByte::is_bit_valid`] only produces `true` for zeroed bytes.
+//
+// NOTE(#170): Implementing `FromBytes` and `IntoBytes` for raw pointers would
+// be sound, but carries provenance footguns. We want to support `FromBytes` and
+// `IntoBytes` for raw pointers eventually, but we are holding off until we can
+// figure out how to address those footguns.
+//
+// [1] Per https://doc.rust-lang.org/1.81.0/std/ptr/fn.null.html:
+//
+// Creates a null raw pointer.
+//
+// This function is equivalent to zero-initializing the pointer:
+// `MaybeUninit::<*const T>::zeroed().assume_init()`.
+//
+// The resulting pointer has the address 0.
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(T: ?Sized => Immutable for *const T);
+ unsafe_impl!(T: ?Sized => Immutable for *mut T);
+ unsafe_impl!(T => TryFromBytes for *const T; |c| pointer::is_zeroed(c));
+ unsafe_impl!(T => FromZeros for *const T);
+ unsafe_impl!(T => TryFromBytes for *mut T; |c| pointer::is_zeroed(c));
+ unsafe_impl!(T => FromZeros for *mut T);
+};
+
+// SAFETY: `NonNull<T>` self-evidently does not contain `UnsafeCell`s. This is
+// not a proof, but we are accepting this as a known risk per #1358.
+const _: () = unsafe { unsafe_impl!(T: ?Sized => Immutable for NonNull<T>) };
+
+// SAFETY: Reference types do not contain any `UnsafeCell`s.
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl!(T: ?Sized => Immutable for &'_ T);
+ unsafe_impl!(T: ?Sized => Immutable for &'_ mut T);
+};
+
+// SAFETY: `Option` is not `#[non_exhaustive]` [1], which means that the types
+// in its variants cannot change, and no new variants can be added. `Option<T>`
+// does not contain any `UnsafeCell`s outside of `T`. [1]
+//
+// [1] https://doc.rust-lang.org/core/option/enum.Option.html
+const _: () = unsafe { unsafe_impl!(T: Immutable => Immutable for Option<T>) };
+
+mod tuples {
+ use super::*;
+
+ /// Generates various trait implementations for tuples.
+ ///
+ /// # Safety
+ ///
+ /// `impl_tuple!` should be provided name-number pairs, where each number is
+ /// the ordinal of the preceding type name.
+ macro_rules! impl_tuple {
+ // Entry point.
+ ($($T:ident $I:tt),+ $(,)?) => {
+ crate::util::macros::__unsafe();
+ impl_tuple!(@all [] [$($T $I)+]);
+ };
+
+ // Build up the set of tuple types (i.e., `(A,)`, `(A, B)`, `(A, B, C)`,
+ // etc.) Trait implementations that do not depend on field index may be
+ // added to this branch.
+ (@all [$($head_T:ident $head_I:tt)*] [$next_T:ident $next_I:tt $($tail:tt)*]) => {
+ // SAFETY: If all fields of the tuple `Self` are `Immutable`, so too is `Self`.
+ unsafe_impl!($($head_T: Immutable,)* $next_T: Immutable => Immutable for ($($head_T,)* $next_T,));
+
+ // SAFETY: If all fields in `c` are `is_bit_valid`, so too is `c`.
+ unsafe_impl!($($head_T: TryFromBytes,)* $next_T: TryFromBytes => TryFromBytes for ($($head_T,)* $next_T,); |c| {
+ let mut c = c;
+ $(TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($head_I) }>())) &&)*
+ TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($next_I) }>()))
+ });
+
+ // SAFETY: If all fields in `Self` are `FromZeros`, so too is `Self`.
+ unsafe_impl!($($head_T: FromZeros,)* $next_T: FromZeros => FromZeros for ($($head_T,)* $next_T,));
+
+ // SAFETY: If all fields in `Self` are `FromBytes`, so too is `Self`.
+ unsafe_impl!($($head_T: FromBytes,)* $next_T: FromBytes => FromBytes for ($($head_T,)* $next_T,));
+
+ // SAFETY: See safety comment on `ProjectToTag`.
+ unsafe impl<$($head_T,)* $next_T> crate::HasTag for ($($head_T,)* $next_T,) {
+ #[inline]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized
+ {}
+
+ type Tag = ();
+
+ // SAFETY: It is trivially sound to project any pointer to a
+ // pointer to a type of size zero and alignment 1 (which `()` is
+ // [1]). Such a pointer will trivially satisfy its aliasing and
+ // validity requirements (since it has a zero-sized referent),
+ // and its alignment requirement (since it is aligned to 1).
+ //
+ // [1] Per https://doc.rust-lang.org/1.92.0/reference/type-layout.html#r-layout.tuple.unit:
+ //
+ // [T]he unit tuple (`()`)... is guaranteed as a zero-sized
+ // type to have a size of 0 and an alignment of 1.
+ type ProjectToTag = crate::pointer::cast::CastToUnit;
+ }
+
+ // Generate impls that depend on tuple index.
+ impl_tuple!(@variants
+ [$($head_T $head_I)* $next_T $next_I]
+ []
+ [$($head_T $head_I)* $next_T $next_I]
+ );
+
+ // Recurse to next tuple size
+ impl_tuple!(@all [$($head_T $head_I)* $next_T $next_I] [$($tail)*]);
+ };
+ (@all [$($head_T:ident $head_I:tt)*] []) => {};
+
+ // Emit trait implementations that depend on field index.
+ (@variants
+ // The full tuple definition in type–index pairs.
+ [$($AllT:ident $AllI:tt)+]
+ // Types before the current index.
+ [$($BeforeT:ident)*]
+ // The types and indices at and after the current index.
+ [$CurrT:ident $CurrI:tt $($AfterT:ident $AfterI:tt)*]
+ ) => {
+ // SAFETY:
+ // - `Self` is a struct (albeit anonymous), so `VARIANT_ID` is
+ // `STRUCT_VARIANT_ID`.
+ // - `$CurrI` is the field at index `$CurrI`, so `FIELD_ID` is
+ // `zerocopy::ident_id!($CurrI)`
+ // - `()` has the same visibility as the `.$CurrI` field (ie, `.0`,
+ // `.1`, etc)
+ // - `Type` has the same type as `$CurrI`; i.e., `$CurrT`.
+ unsafe impl<$($AllT),+> crate::HasField<
+ (),
+ { crate::STRUCT_VARIANT_ID },
+ { crate::ident_id!($CurrI)}
+ > for ($($AllT,)+) {
+ #[inline]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized
+ {}
+
+ type Type = $CurrT;
+
+ #[inline(always)]
+ fn project(slf: crate::PtrInner<'_, Self>) -> *mut Self::Type {
+ let slf = slf.as_non_null().as_ptr();
+ // SAFETY: `PtrInner` promises it references either a zero-sized
+ // byte range, or else will reference a byte range that is
+ // entirely contained within an allocated object. In either
+ // case, this guarantees that `(*slf).$CurrI` is in-bounds of
+ // `slf`.
+ unsafe { core::ptr::addr_of_mut!((*slf).$CurrI) }
+ }
+ }
+
+ // SAFETY: See comments on items.
+ unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
+ (),
+ (Aliasing, Alignment, crate::invariant::Uninit),
+ { crate::STRUCT_VARIANT_ID },
+ { crate::ident_id!($CurrI)}
+ > for ($($AllT,)+)
+ where
+ Aliasing: crate::invariant::Aliasing,
+ Alignment: crate::invariant::Alignment,
+ {
+ #[inline]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized
+ {}
+
+ // SAFETY: Tuples are product types whose fields are
+ // well-aligned, so projection preserves both the alignment and
+ // validity invariants of the outer pointer.
+ type Invariants = (Aliasing, Alignment, crate::invariant::Uninit);
+
+ // SAFETY: Tuples are product types and so projection is infallible;
+ type Error = core::convert::Infallible;
+ }
+
+ // SAFETY: See comments on items.
+ unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
+ (),
+ (Aliasing, Alignment, crate::invariant::Initialized),
+ { crate::STRUCT_VARIANT_ID },
+ { crate::ident_id!($CurrI)}
+ > for ($($AllT,)+)
+ where
+ Aliasing: crate::invariant::Aliasing,
+ Alignment: crate::invariant::Alignment,
+ {
+ #[inline]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized
+ {}
+
+ // SAFETY: Tuples are product types whose fields are
+ // well-aligned, so projection preserves both the alignment and
+ // validity invariants of the outer pointer.
+ type Invariants = (Aliasing, Alignment, crate::invariant::Initialized);
+
+ // SAFETY: Tuples are product types and so projection is infallible;
+ type Error = core::convert::Infallible;
+ }
+
+ // SAFETY: See comments on items.
+ unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
+ (),
+ (Aliasing, Alignment, crate::invariant::Valid),
+ { crate::STRUCT_VARIANT_ID },
+ { crate::ident_id!($CurrI)}
+ > for ($($AllT,)+)
+ where
+ Aliasing: crate::invariant::Aliasing,
+ Alignment: crate::invariant::Alignment,
+ {
+ #[inline]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized
+ {}
+
+ // SAFETY: Tuples are product types whose fields are
+ // well-aligned, so projection preserves both the alignment and
+ // validity invariants of the outer pointer.
+ type Invariants = (Aliasing, Alignment, crate::invariant::Valid);
+
+ // SAFETY: Tuples are product types and so projection is infallible;
+ type Error = core::convert::Infallible;
+ }
+
+ // Recurse to the next index.
+ impl_tuple!(@variants [$($AllT $AllI)+] [$($BeforeT)* $CurrT] [$($AfterT $AfterI)*]);
+ };
+ (@variants [$($AllT:ident $AllI:tt)+] [$($BeforeT:ident)*] []) => {};
+ }
+
+ // SAFETY: `impl_tuple` is provided name-number pairs, where number is the
+ // ordinal of the name.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ impl_tuple! {
+ A 0,
+ B 1,
+ C 2,
+ D 3,
+ E 4,
+ F 5,
+ G 6,
+ H 7,
+ I 8,
+ J 9,
+ K 10,
+ L 11,
+ M 12,
+ N 13,
+ O 14,
+ P 15,
+ Q 16,
+ R 17,
+ S 18,
+ T 19,
+ U 20,
+ V 21,
+ W 22,
+ X 23,
+ Y 24,
+ Z 25,
+ };
+ };
+}
+
+// SIMD support
+//
+// Per the Unsafe Code Guidelines Reference [1]:
+//
+// Packed SIMD vector types are `repr(simd)` homogeneous tuple-structs
+// containing `N` elements of type `T` where `N` is a power-of-two and the
+// size and alignment requirements of `T` are equal:
+//
+// ```rust
+// #[repr(simd)]
+// struct Vector<T, N>(T_0, ..., T_(N - 1));
+// ```
+//
+// ...
+//
+// The size of `Vector` is `N * size_of::<T>()` and its alignment is an
+// implementation-defined function of `T` and `N` greater than or equal to
+// `align_of::<T>()`.
+//
+// ...
+//
+// Vector elements are laid out in source field order, enabling random access
+// to vector elements by reinterpreting the vector as an array:
+//
+// ```rust
+// union U {
+// vec: Vector<T, N>,
+// arr: [T; N]
+// }
+//
+// assert_eq!(size_of::<Vector<T, N>>(), size_of::<[T; N]>());
+// assert!(align_of::<Vector<T, N>>() >= align_of::<[T; N]>());
+//
+// unsafe {
+// let u = U { vec: Vector<T, N>(t_0, ..., t_(N - 1)) };
+//
+// assert_eq!(u.vec.0, u.arr[0]);
+// // ...
+// assert_eq!(u.vec.(N - 1), u.arr[N - 1]);
+// }
+// ```
+//
+// Given this background, we can observe that:
+// - The size and bit pattern requirements of a SIMD type are equivalent to the
+// equivalent array type. Thus, for any SIMD type whose primitive `T` is
+// `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`, or `IntoBytes`, that
+// SIMD type is also `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`, or
+// `IntoBytes` respectively.
+// - Since no upper bound is placed on the alignment, no SIMD type can be
+// guaranteed to be `Unaligned`.
+//
+// Also per [1]:
+//
+// This chapter represents the consensus from issue #38. The statements in
+// here are not (yet) "guaranteed" not to change until an RFC ratifies them.
+//
+// See issue #38 [2]. While this behavior is not technically guaranteed, the
+// likelihood that the behavior will change such that SIMD types are no longer
+// `TryFromBytes`, `FromZeros`, `FromBytes`, or `IntoBytes` is next to zero, as
+// that would defeat the entire purpose of SIMD types. Nonetheless, we put this
+// behavior behind the `simd` Cargo feature, which requires consumers to opt
+// into this stability hazard.
+//
+// [1] https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html
+// [2] https://github.com/rust-lang/unsafe-code-guidelines/issues/38
+#[cfg(feature = "simd")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "simd")))]
+mod simd {
+ /// Defines a module which implements `TryFromBytes`, `FromZeros`,
+ /// `FromBytes`, and `IntoBytes` for a set of types from a module in
+ /// `core::arch`.
+ ///
+ /// `$arch` is both the name of the defined module and the name of the
+ /// module in `core::arch`, and `$typ` is the list of items from that module
+ /// to implement `FromZeros`, `FromBytes`, and `IntoBytes` for.
+ #[allow(unused_macros)] // `allow(unused_macros)` is needed because some
+ // target/feature combinations don't emit any impls
+ // and thus don't use this macro.
+ macro_rules! simd_arch_mod {
+ ($(#[cfg $cfg:tt])* $(#[cfg_attr $cfg_attr:tt])? $arch:ident, $mod:ident, $($typ:ident),*) => {
+ $(#[cfg $cfg])*
+ #[cfg_attr(doc_cfg, doc(cfg $($cfg)*))]
+ $(#[cfg_attr $cfg_attr])?
+ mod $mod {
+ use core::arch::$arch::{$($typ),*};
+
+ use crate::*;
+ impl_known_layout!($($typ),*);
+ // SAFETY: See comment on module definition for justification.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ const _: () = unsafe {
+ $( unsafe_impl!($typ: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes); )*
+ };
+ }
+ };
+ }
+
+ #[rustfmt::skip]
+ const _: () = {
+ simd_arch_mod!(
+ #[cfg(target_arch = "x86")]
+ x86, x86, __m128, __m128d, __m128i, __m256, __m256d, __m256i
+ );
+ #[cfg(not(no_zerocopy_simd_x86_avx12_1_89_0))]
+ simd_arch_mod!(
+ #[cfg(target_arch = "x86")]
+ #[cfg_attr(doc_cfg, doc(cfg(rust = "1.89.0")))]
+ x86, x86_nightly, __m512bh, __m512, __m512d, __m512i
+ );
+ simd_arch_mod!(
+ #[cfg(target_arch = "x86_64")]
+ x86_64, x86_64, __m128, __m128d, __m128i, __m256, __m256d, __m256i
+ );
+ #[cfg(not(no_zerocopy_simd_x86_avx12_1_89_0))]
+ simd_arch_mod!(
+ #[cfg(target_arch = "x86_64")]
+ #[cfg_attr(doc_cfg, doc(cfg(rust = "1.89.0")))]
+ x86_64, x86_64_nightly, __m512bh, __m512, __m512d, __m512i
+ );
+ simd_arch_mod!(
+ #[cfg(target_arch = "wasm32")]
+ wasm32, wasm32, v128
+ );
+ simd_arch_mod!(
+ #[cfg(all(feature = "simd-nightly", target_arch = "powerpc"))]
+ powerpc, powerpc, vector_bool_long, vector_double, vector_signed_long, vector_unsigned_long
+ );
+ simd_arch_mod!(
+ #[cfg(all(feature = "simd-nightly", target_arch = "powerpc64"))]
+ powerpc64, powerpc64, vector_bool_long, vector_double, vector_signed_long, vector_unsigned_long
+ );
+ // NOTE: NEON intrinsics were broken on big-endian platforms from their stabilization up to
+ // Rust 1.87. (Context in https://github.com/rust-lang/stdarch/issues/1484). Support is
+ // split in two different version ranges on top of the base configuration, requiring either
+ // little endian or the more recent version to be detected as well.
+ #[cfg(not(no_zerocopy_aarch64_simd_1_59_0))]
+ simd_arch_mod!(
+ #[cfg(all(
+ target_arch = "aarch64",
+ any(
+ target_endian = "little",
+ not(no_zerocopy_aarch64_simd_be_1_87_0)
+ )
+ ))]
+ #[cfg_attr(
+ doc_cfg,
+ doc(cfg(all(target_arch = "aarch64", any(
+ all(rust = "1.59.0", target_endian = "little"),
+ rust = "1.87.0",
+ ))))
+ )]
+ aarch64, aarch64, float32x2_t, float32x4_t, float64x1_t, float64x2_t, int8x8_t, int8x8x2_t,
+ int8x8x3_t, int8x8x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int16x4_t,
+ int16x8_t, int32x2_t, int32x4_t, int64x1_t, int64x2_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t,
+ poly8x8x4_t, poly8x16_t, poly8x16x2_t, poly8x16x3_t, poly8x16x4_t, poly16x4_t, poly16x8_t,
+ poly64x1_t, poly64x2_t, uint8x8_t, uint8x8x2_t, uint8x8x3_t, uint8x8x4_t, uint8x16_t,
+ uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint16x4_t, uint16x4x2_t, uint16x4x3_t,
+ uint16x4x4_t, uint16x8_t, uint32x2_t, uint32x4_t, uint64x1_t, uint64x2_t
+ );
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_impls() {
+ // A type that can supply test cases for testing
+ // `TryFromBytes::is_bit_valid`. All types passed to `assert_impls!`
+ // must implement this trait; that macro uses it to generate runtime
+ // tests for `TryFromBytes` impls.
+ //
+ // All `T: FromBytes` types are provided with a blanket impl. Other
+ // types must implement `TryFromBytesTestable` directly (ie using
+ // `impl_try_from_bytes_testable!`).
+ trait TryFromBytesTestable {
+ fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(f: F);
+ fn with_failing_test_cases<F: Fn(&mut [u8])>(f: F);
+ }
+
+ impl<T: FromBytes> TryFromBytesTestable for T {
+ fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(f: F) {
+ // Test with a zeroed value.
+ f(ReadOnly::<Self>::new_box_zeroed().unwrap());
+
+ let ffs = {
+ let mut t = ReadOnly::new(Self::new_zeroed());
+ let ptr: *mut T = ReadOnly::as_mut(&mut t);
+ // SAFETY: `T: FromBytes`
+ unsafe { ptr::write_bytes(ptr.cast::<u8>(), 0xFF, mem::size_of::<T>()) };
+ t
+ };
+
+ // Test with a value initialized with 0xFF.
+ f(Box::new(ffs));
+ }
+
+ fn with_failing_test_cases<F: Fn(&mut [u8])>(_f: F) {}
+ }
+
+ macro_rules! impl_try_from_bytes_testable_for_null_pointer_optimization {
+ ($($tys:ty),*) => {
+ $(
+ impl TryFromBytesTestable for Option<$tys> {
+ fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(f: F) {
+ // Test with a zeroed value.
+ f(Box::new(ReadOnly::new(None)));
+ }
+
+ fn with_failing_test_cases<F: Fn(&mut [u8])>(f: F) {
+ for pos in 0..mem::size_of::<Self>() {
+ let mut bytes = [0u8; mem::size_of::<Self>()];
+ bytes[pos] = 0x01;
+ f(&mut bytes[..]);
+ }
+ }
+ }
+ )*
+ };
+ }
+
+ // Implements `TryFromBytesTestable`.
+ macro_rules! impl_try_from_bytes_testable {
+ // Base case for recursion (when the list of types has run out).
+ (=> @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => {};
+ // Implements for type(s) with no type parameters.
+ ($ty:ty $(,$tys:ty)* => @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => {
+ impl TryFromBytesTestable for $ty {
+ impl_try_from_bytes_testable!(
+ @methods @success $($success_case),*
+ $(, @failure $($failure_case),*)?
+ );
+ }
+ impl_try_from_bytes_testable!($($tys),* => @success $($success_case),* $(, @failure $($failure_case),*)?);
+ };
+ // Implements for multiple types with no type parameters.
+ ($($($ty:ty),* => @success $($success_case:expr), * $(, @failure $($failure_case:expr),*)?;)*) => {
+ $(
+ impl_try_from_bytes_testable!($($ty),* => @success $($success_case),* $(, @failure $($failure_case),*)*);
+ )*
+ };
+ // Implements only the methods; caller must invoke this from inside
+ // an impl block.
+ (@methods @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => {
+ fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(_f: F) {
+ $(
+ let bx = Box::<Self>::from($success_case);
+ let ro: Box<ReadOnly<_>> = {
+ let raw = Box::into_raw(bx);
+ // SAFETY: `ReadOnly<T>` has the same layout and bit
+ // validity as `T`.
+ #[allow(clippy::as_conversions)]
+ unsafe { Box::from_raw(raw as *mut _) }
+ };
+ _f(ro);
+ )*
+ }
+
+ fn with_failing_test_cases<F: Fn(&mut [u8])>(_f: F) {
+ $($(
+ let mut case = $failure_case;
+ _f(case.as_mut_bytes());
+ )*)?
+ }
+ };
+ }
+
+ impl_try_from_bytes_testable_for_null_pointer_optimization!(
+ Box<UnsafeCell<NotZerocopy>>,
+ &'static UnsafeCell<NotZerocopy>,
+ &'static mut UnsafeCell<NotZerocopy>,
+ NonNull<UnsafeCell<NotZerocopy>>,
+ fn(),
+ FnManyArgs,
+ extern "C" fn(),
+ ECFnManyArgs
+ );
+
+ macro_rules! bx {
+ ($e:expr) => {
+ Box::new($e)
+ };
+ }
+
+ // Note that these impls are only for types which are not `FromBytes`.
+ // `FromBytes` types are covered by a preceding blanket impl.
+ impl_try_from_bytes_testable!(
+ bool => @success true, false,
+ @failure 2u8, 3u8, 0xFFu8;
+ char => @success '\u{0}', '\u{D7FF}', '\u{E000}', '\u{10FFFF}',
+ @failure 0xD800u32, 0xDFFFu32, 0x110000u32;
+ str => @success "", "hello", "❤️🧡💛💚💙💜",
+ @failure [0, 159, 146, 150];
+ [u8] => @success vec![].into_boxed_slice(), vec![0, 1, 2].into_boxed_slice();
+ NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32,
+ NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128,
+ NonZeroUsize, NonZeroIsize
+ => @success Self::new(1).unwrap(),
+ // Doing this instead of `0` ensures that we always satisfy
+ // the size and alignment requirements of `Self` (whereas `0`
+ // may be any integer type with a different size or alignment
+ // than some `NonZeroXxx` types).
+ @failure Option::<Self>::None;
+ [bool; 0] => @success [];
+ [bool; 1]
+ => @success [true], [false],
+ @failure [2u8], [3u8], [0xFFu8];
+ [bool]
+ => @success vec![true, false].into_boxed_slice(), vec![false, true].into_boxed_slice(),
+ @failure [2u8], [3u8], [0xFFu8], [0u8, 1u8, 2u8];
+ Unalign<bool>
+ => @success Unalign::new(false), Unalign::new(true),
+ @failure 2u8, 0xFFu8;
+ ManuallyDrop<bool>
+ => @success ManuallyDrop::new(false), ManuallyDrop::new(true),
+ @failure 2u8, 0xFFu8;
+ ManuallyDrop<[u8]>
+ => @success bx!(ManuallyDrop::new([])), bx!(ManuallyDrop::new([0u8])), bx!(ManuallyDrop::new([0u8, 1u8]));
+ ManuallyDrop<[bool]>
+ => @success bx!(ManuallyDrop::new([])), bx!(ManuallyDrop::new([false])), bx!(ManuallyDrop::new([false, true])),
+ @failure [2u8], [3u8], [0xFFu8], [0u8, 1u8, 2u8];
+ ManuallyDrop<[UnsafeCell<u8>]>
+ => @success bx!(ManuallyDrop::new([UnsafeCell::new(0)])), bx!(ManuallyDrop::new([UnsafeCell::new(0), UnsafeCell::new(1)]));
+ ManuallyDrop<[UnsafeCell<bool>]>
+ => @success bx!(ManuallyDrop::new([UnsafeCell::new(false)])), bx!(ManuallyDrop::new([UnsafeCell::new(false), UnsafeCell::new(true)])),
+ @failure [2u8], [3u8], [0xFFu8], [0u8, 1u8, 2u8];
+ Wrapping<bool>
+ => @success Wrapping(false), Wrapping(true),
+ @failure 2u8, 0xFFu8;
+ *const NotZerocopy
+ => @success ptr::null::<NotZerocopy>(),
+ @failure [0x01; mem::size_of::<*const NotZerocopy>()];
+ *mut NotZerocopy
+ => @success ptr::null_mut::<NotZerocopy>(),
+ @failure [0x01; mem::size_of::<*mut NotZerocopy>()];
+ );
+
+ // Use the trick described in [1] to allow us to call methods
+ // conditional on certain trait bounds.
+ //
+ // In all of these cases, methods return `Option<R>`, where `R` is the
+ // return type of the method we're conditionally calling. The "real"
+ // implementations (the ones defined in traits using `&self`) return
+ // `Some`, and the default implementations (the ones defined as inherent
+ // methods using `&mut self`) return `None`.
+ //
+ // [1] https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
+ mod autoref_trick {
+ use super::*;
+
+ pub(super) struct AutorefWrapper<T: ?Sized>(pub(super) PhantomData<T>);
+
+ pub(super) trait TestIsBitValidShared<T: ?Sized> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_is_bit_valid_shared<'ptr>(&self, candidate: Maybe<'ptr, T>)
+ -> Option<bool>;
+ }
+
+ impl<T: TryFromBytes + Immutable + ?Sized> TestIsBitValidShared<T> for AutorefWrapper<T> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_is_bit_valid_shared<'ptr>(
+ &self,
+ candidate: Maybe<'ptr, T>,
+ ) -> Option<bool> {
+ Some(T::is_bit_valid(candidate))
+ }
+ }
+
+ pub(super) trait TestTryFromRef<T: ?Sized> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_try_from_ref<'bytes>(
+ &self,
+ bytes: &'bytes [u8],
+ ) -> Option<Option<&'bytes T>>;
+ }
+
+ impl<T: TryFromBytes + Immutable + KnownLayout + ?Sized> TestTryFromRef<T> for AutorefWrapper<T> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_try_from_ref<'bytes>(
+ &self,
+ bytes: &'bytes [u8],
+ ) -> Option<Option<&'bytes T>> {
+ Some(T::try_ref_from_bytes(bytes).ok())
+ }
+ }
+
+ pub(super) trait TestTryFromMut<T: ?Sized> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_try_from_mut<'bytes>(
+ &self,
+ bytes: &'bytes mut [u8],
+ ) -> Option<Option<&'bytes mut T>>;
+ }
+
+ impl<T: TryFromBytes + IntoBytes + KnownLayout + ?Sized> TestTryFromMut<T> for AutorefWrapper<T> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_try_from_mut<'bytes>(
+ &self,
+ bytes: &'bytes mut [u8],
+ ) -> Option<Option<&'bytes mut T>> {
+ Some(T::try_mut_from_bytes(bytes).ok())
+ }
+ }
+
+ pub(super) trait TestTryReadFrom<T> {
+ fn test_try_read_from(&self, bytes: &[u8]) -> Option<Option<T>>;
+ }
+
+ impl<T: TryFromBytes> TestTryReadFrom<T> for AutorefWrapper<T> {
+ fn test_try_read_from(&self, bytes: &[u8]) -> Option<Option<T>> {
+ Some(T::try_read_from_bytes(bytes).ok())
+ }
+ }
+
+ pub(super) trait TestAsBytes<T: ?Sized> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_as_bytes<'slf, 't>(&'slf self, t: &'t ReadOnly<T>) -> Option<&'t [u8]>;
+ }
+
+ impl<T: IntoBytes + Immutable + ?Sized> TestAsBytes<T> for AutorefWrapper<T> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_as_bytes<'slf, 't>(&'slf self, t: &'t ReadOnly<T>) -> Option<&'t [u8]> {
+ Some(t.as_bytes())
+ }
+ }
+ }
+
+ use autoref_trick::*;
+
+ // Asserts that `$ty` is one of a list of types which are allowed to not
+ // provide a "real" implementation for `$fn_name`. Since the
+ // `autoref_trick` machinery fails silently, this allows us to ensure
+ // that the "default" impls are only being used for types which we
+ // expect.
+ //
+ // Note that, since this is a runtime test, it is possible to have an
+ // allowlist which is too restrictive if the function in question is
+ // never called for a particular type. For example, if `as_bytes` is not
+ // supported for a particular type, and so `test_as_bytes` returns
+ // `None`, methods such as `test_try_from_ref` may never be called for
+ // that type. As a result, it's possible that, for example, adding
+ // `as_bytes` support for a type would cause other allowlist assertions
+ // to fail. This means that allowlist assertion failures should not
+ // automatically be taken as a sign of a bug.
+ macro_rules! assert_on_allowlist {
+ ($fn_name:ident($ty:ty) $(: $($tys:ty),*)?) => {{
+ use core::any::TypeId;
+
+ let allowlist: &[TypeId] = &[ $($(TypeId::of::<$tys>()),*)? ];
+ let allowlist_names: &[&str] = &[ $($(stringify!($tys)),*)? ];
+
+ let id = TypeId::of::<$ty>();
+ assert!(allowlist.contains(&id), "{} is not on allowlist for {}: {:?}", stringify!($ty), stringify!($fn_name), allowlist_names);
+ }};
+ }
+
+ // Asserts that `$ty` implements any `$trait` and doesn't implement any
+ // `!$trait`. Note that all `$trait`s must come before any `!$trait`s.
+ //
+ // For `T: TryFromBytes`, uses `TryFromBytesTestable` to test success
+ // and failure cases.
+ macro_rules! assert_impls {
+ ($ty:ty: TryFromBytes) => {
+ // "Default" implementations that match the "real"
+ // implementations defined in the `autoref_trick` module above.
+ #[allow(unused, non_local_definitions)]
+ impl AutorefWrapper<$ty> {
+ #[allow(clippy::needless_lifetimes)]
+ fn test_is_bit_valid_shared<'ptr>(
+ &mut self,
+ candidate: Maybe<'ptr, $ty>,
+ ) -> Option<bool> {
+ assert_on_allowlist!(
+ test_is_bit_valid_shared($ty):
+ ManuallyDrop<UnsafeCell<()>>,
+ ManuallyDrop<[UnsafeCell<u8>]>,
+ ManuallyDrop<[UnsafeCell<bool>]>,
+ CoreMaybeUninit<NotZerocopy>,
+ CoreMaybeUninit<UnsafeCell<()>>,
+ Wrapping<UnsafeCell<()>>
+ );
+
+ None
+ }
+
+ #[allow(clippy::needless_lifetimes)]
+ fn test_try_from_ref<'bytes>(&mut self, _bytes: &'bytes [u8]) -> Option<Option<&'bytes $ty>> {
+ assert_on_allowlist!(
+ test_try_from_ref($ty):
+ ManuallyDrop<[UnsafeCell<bool>]>
+ );
+
+ None
+ }
+
+ #[allow(clippy::needless_lifetimes)]
+ fn test_try_from_mut<'bytes>(&mut self, _bytes: &'bytes mut [u8]) -> Option<Option<&'bytes mut $ty>> {
+ assert_on_allowlist!(
+ test_try_from_mut($ty):
+ Option<Box<UnsafeCell<NotZerocopy>>>,
+ Option<&'static UnsafeCell<NotZerocopy>>,
+ Option<&'static mut UnsafeCell<NotZerocopy>>,
+ Option<NonNull<UnsafeCell<NotZerocopy>>>,
+ Option<fn()>,
+ Option<FnManyArgs>,
+ Option<extern "C" fn()>,
+ Option<ECFnManyArgs>,
+ *const NotZerocopy,
+ *mut NotZerocopy
+ );
+
+ None
+ }
+
+ fn test_try_read_from(&mut self, _bytes: &[u8]) -> Option<Option<&$ty>> {
+ assert_on_allowlist!(
+ test_try_read_from($ty):
+ str,
+ ManuallyDrop<[u8]>,
+ ManuallyDrop<[bool]>,
+ ManuallyDrop<[UnsafeCell<bool>]>,
+ [u8],
+ [bool]
+ );
+
+ None
+ }
+
+ fn test_as_bytes(&mut self, _t: &ReadOnly<$ty>) -> Option<&[u8]> {
+ assert_on_allowlist!(
+ test_as_bytes($ty):
+ Option<&'static UnsafeCell<NotZerocopy>>,
+ Option<&'static mut UnsafeCell<NotZerocopy>>,
+ Option<NonNull<UnsafeCell<NotZerocopy>>>,
+ Option<Box<UnsafeCell<NotZerocopy>>>,
+ Option<fn()>,
+ Option<FnManyArgs>,
+ Option<extern "C" fn()>,
+ Option<ECFnManyArgs>,
+ CoreMaybeUninit<u8>,
+ CoreMaybeUninit<NotZerocopy>,
+ CoreMaybeUninit<UnsafeCell<()>>,
+ ManuallyDrop<UnsafeCell<()>>,
+ ManuallyDrop<[UnsafeCell<u8>]>,
+ ManuallyDrop<[UnsafeCell<bool>]>,
+ Wrapping<UnsafeCell<()>>,
+ *const NotZerocopy,
+ *mut NotZerocopy
+ );
+
+ None
+ }
+ }
+
+ <$ty as TryFromBytesTestable>::with_passing_test_cases(|mut val| {
+ // FIXME(#494): These tests only get exercised for types
+ // which are `IntoBytes`. Once we implement #494, we should
+ // be able to support non-`IntoBytes` types by zeroing
+ // padding.
+
+ // We define `w` and `ww` since, in the case of the inherent
+ // methods, Rust thinks they're both borrowed mutably at the
+ // same time (given how we use them below). If we just
+ // defined a single `w` and used it for multiple operations,
+ // this would conflict.
+ //
+ // We `#[allow(unused_mut]` for the cases where the "real"
+ // impls are used, which take `&self`.
+ #[allow(unused_mut)]
+ let (mut w, mut ww) = (AutorefWrapper::<$ty>(PhantomData), AutorefWrapper::<$ty>(PhantomData));
+
+ let c = Ptr::from_ref(&*val);
+ let c = c.forget_aligned();
+ // SAFETY: FIXME(#899): This is unsound. `$ty` is not
+ // necessarily `IntoBytes`, but that's the corner we've
+ // backed ourselves into by using `Ptr::from_ref`.
+ let c = unsafe { c.assume_initialized() };
+ let res = w.test_is_bit_valid_shared(c);
+ if let Some(res) = res {
+ assert!(res, "{}::is_bit_valid (shared `Ptr`): got false, expected true", stringify!($ty));
+ }
+
+ let c = Ptr::from_mut(&mut *val);
+ let c = c.forget_aligned();
+ // SAFETY: FIXME(#899): This is unsound. `$ty` is not
+ // necessarily `IntoBytes`, but that's the corner we've
+ // backed ourselves into by using `Ptr::from_ref`.
+ let mut c = unsafe { c.assume_initialized() };
+ let res = <$ty as TryFromBytes>::is_bit_valid(c.reborrow_shared());
+ assert!(res, "{}::is_bit_valid (exclusive `Ptr`): got false, expected true", stringify!($ty));
+
+ // `bytes` is `Some(val.as_bytes())` if `$ty: IntoBytes +
+ // Immutable` and `None` otherwise.
+ let bytes = w.test_as_bytes(&*val);
+
+ // The inner closure returns
+ // `Some($ty::try_ref_from_bytes(bytes))` if `$ty:
+ // Immutable` and `None` otherwise.
+ let res = bytes.and_then(|bytes| ww.test_try_from_ref(bytes));
+ if let Some(res) = res {
+ assert!(res.is_some(), "{}::try_ref_from_bytes: got `None`, expected `Some`", stringify!($ty));
+ }
+
+ if let Some(bytes) = bytes {
+ // We need to get a mutable byte slice, and so we clone
+ // into a `Vec`. However, we also need these bytes to
+ // satisfy `$ty`'s alignment requirement, which isn't
+ // guaranteed for `Vec<u8>`. In order to get around
+ // this, we create a `Vec` which is twice as long as we
+ // need. There is guaranteed to be an aligned byte range
+ // of size `size_of_val(val)` within that range.
+ let val = &*val;
+ let size = mem::size_of_val(val);
+ let align = mem::align_of_val(val);
+
+ let mut vec = bytes.to_vec();
+ vec.extend(bytes);
+ let slc = vec.as_slice();
+ let offset = slc.as_ptr().align_offset(align);
+ let bytes_mut = &mut vec.as_mut_slice()[offset..offset+size];
+ bytes_mut.copy_from_slice(bytes);
+
+ let res = ww.test_try_from_mut(bytes_mut);
+ if let Some(res) = res {
+ assert!(res.is_some(), "{}::try_mut_from_bytes: got `None`, expected `Some`", stringify!($ty));
+ }
+ }
+
+ let res = bytes.and_then(|bytes| ww.test_try_read_from(bytes));
+ if let Some(res) = res {
+ assert!(res.is_some(), "{}::try_read_from_bytes: got `None`, expected `Some`", stringify!($ty));
+ }
+ });
+ #[allow(clippy::as_conversions)]
+ <$ty as TryFromBytesTestable>::with_failing_test_cases(|c| {
+ #[allow(unused_mut)] // For cases where the "real" impls are used, which take `&self`.
+ let mut w = AutorefWrapper::<$ty>(PhantomData);
+
+ // This is `Some($ty::try_ref_from_bytes(c))` if `$ty:
+ // Immutable` and `None` otherwise.
+ let res = w.test_try_from_ref(c);
+ if let Some(res) = res {
+ assert!(res.is_none(), "{}::try_ref_from_bytes({:?}): got Some, expected None", stringify!($ty), c);
+ }
+
+ let res = w.test_try_from_mut(c);
+ if let Some(res) = res {
+ assert!(res.is_none(), "{}::try_mut_from_bytes({:?}): got Some, expected None", stringify!($ty), c);
+ }
+
+
+ let res = w.test_try_read_from(c);
+ if let Some(res) = res {
+ assert!(res.is_none(), "{}::try_read_from_bytes({:?}): got Some, expected None", stringify!($ty), c);
+ }
+ });
+
+ #[allow(dead_code)]
+ const _: () = { static_assertions::assert_impl_all!($ty: TryFromBytes); };
+ };
+ ($ty:ty: $trait:ident) => {
+ #[allow(dead_code)]
+ const _: () = { static_assertions::assert_impl_all!($ty: $trait); };
+ };
+ ($ty:ty: !$trait:ident) => {
+ #[allow(dead_code)]
+ const _: () = { static_assertions::assert_not_impl_any!($ty: $trait); };
+ };
+ ($ty:ty: $($trait:ident),* $(,)? $(!$negative_trait:ident),*) => {
+ $(
+ assert_impls!($ty: $trait);
+ )*
+
+ $(
+ assert_impls!($ty: !$negative_trait);
+ )*
+ };
+ }
+
+ // NOTE: The negative impl assertions here are not necessarily
+ // prescriptive. They merely serve as change detectors to make sure
+ // we're aware of what trait impls are getting added with a given
+ // change. Of course, some impls would be invalid (e.g., `bool:
+ // FromBytes`), and so this change detection is very important.
+
+ assert_impls!(
+ (): KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ Unaligned
+ );
+ assert_impls!(
+ u8: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ Unaligned
+ );
+ assert_impls!(
+ i8: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ Unaligned
+ );
+ assert_impls!(
+ u16: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ i16: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ u32: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ i32: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ u64: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ i64: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ u128: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ i128: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ usize: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ isize: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ #[cfg(feature = "float-nightly")]
+ assert_impls!(
+ f16: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ f32: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ f64: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ #[cfg(feature = "float-nightly")]
+ assert_impls!(
+ f128: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ bool: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ IntoBytes,
+ Unaligned,
+ !FromBytes
+ );
+ assert_impls!(
+ char: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ str: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ IntoBytes,
+ Unaligned,
+ !FromBytes
+ );
+
+ assert_impls!(
+ NonZeroU8: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ Unaligned,
+ !FromZeros,
+ !FromBytes
+ );
+ assert_impls!(
+ NonZeroI8: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ Unaligned,
+ !FromZeros,
+ !FromBytes
+ );
+ assert_impls!(
+ NonZeroU16: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroI16: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroU32: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroI32: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroU64: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroI64: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroU128: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroI128: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroUsize: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ NonZeroIsize: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ IntoBytes,
+ !FromBytes,
+ !Unaligned
+ );
+
+ assert_impls!(Option<NonZeroU8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ assert_impls!(Option<NonZeroI8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ assert_impls!(Option<NonZeroU16>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroI16>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroU32>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroI32>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroU64>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroI64>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroU128>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroI128>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroUsize>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+ assert_impls!(Option<NonZeroIsize>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned);
+
+ // Implements none of the ZC traits.
+ struct NotZerocopy;
+
+ #[rustfmt::skip]
+ type FnManyArgs = fn(
+ NotZerocopy, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8,
+ ) -> (NotZerocopy, NotZerocopy);
+
+ // Allowed, because we're not actually using this type for FFI.
+ #[allow(improper_ctypes_definitions)]
+ #[rustfmt::skip]
+ type ECFnManyArgs = extern "C" fn(
+ NotZerocopy, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8,
+ ) -> (NotZerocopy, NotZerocopy);
+
+ #[cfg(feature = "alloc")]
+ assert_impls!(Option<Box<UnsafeCell<NotZerocopy>>>: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<Box<[UnsafeCell<NotZerocopy>]>>: KnownLayout, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<&'static UnsafeCell<NotZerocopy>>: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<&'static [UnsafeCell<NotZerocopy>]>: KnownLayout, Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<&'static mut UnsafeCell<NotZerocopy>>: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<&'static mut [UnsafeCell<NotZerocopy>]>: KnownLayout, Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<NonNull<UnsafeCell<NotZerocopy>>>: KnownLayout, TryFromBytes, FromZeros, Immutable, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<NonNull<[UnsafeCell<NotZerocopy>]>>: KnownLayout, Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<fn()>: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<FnManyArgs>: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<extern "C" fn()>: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Option<ECFnManyArgs>: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+
+ assert_impls!(PhantomData<NotZerocopy>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ assert_impls!(PhantomData<UnsafeCell<()>>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ assert_impls!(PhantomData<[u8]>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+
+ assert_impls!(ManuallyDrop<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ // This test is important because it allows us to test our hand-rolled
+ // implementation of `<ManuallyDrop<T> as TryFromBytes>::is_bit_valid`.
+ assert_impls!(ManuallyDrop<bool>: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes);
+ assert_impls!(ManuallyDrop<[u8]>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ // This test is important because it allows us to test our hand-rolled
+ // implementation of `<ManuallyDrop<T> as TryFromBytes>::is_bit_valid`.
+ assert_impls!(ManuallyDrop<[bool]>: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes);
+ assert_impls!(ManuallyDrop<NotZerocopy>: !Immutable, !TryFromBytes, !KnownLayout, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(ManuallyDrop<[NotZerocopy]>: KnownLayout, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(ManuallyDrop<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable);
+ assert_impls!(ManuallyDrop<[UnsafeCell<u8>]>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable);
+ assert_impls!(ManuallyDrop<[UnsafeCell<bool>]>: KnownLayout, TryFromBytes, FromZeros, IntoBytes, Unaligned, !Immutable, !FromBytes);
+
+ assert_impls!(CoreMaybeUninit<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, Unaligned, !IntoBytes);
+ assert_impls!(CoreMaybeUninit<NotZerocopy>: KnownLayout, TryFromBytes, FromZeros, FromBytes, !Immutable, !IntoBytes, !Unaligned);
+ assert_impls!(CoreMaybeUninit<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, Unaligned, !Immutable, !IntoBytes);
+
+ assert_impls!(Wrapping<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ // This test is important because it allows us to test our hand-rolled
+ // implementation of `<Wrapping<T> as TryFromBytes>::is_bit_valid`.
+ assert_impls!(Wrapping<bool>: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes);
+ assert_impls!(Wrapping<NotZerocopy>: KnownLayout, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(Wrapping<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable);
+
+ assert_impls!(Unalign<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
+ // This test is important because it allows us to test our hand-rolled
+ // implementation of `<Unalign<T> as TryFromBytes>::is_bit_valid`.
+ assert_impls!(Unalign<bool>: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes);
+ assert_impls!(Unalign<NotZerocopy>: KnownLayout, Unaligned, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes);
+
+ assert_impls!(
+ [u8]: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ Unaligned
+ );
+ assert_impls!(
+ [bool]: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ IntoBytes,
+ Unaligned,
+ !FromBytes
+ );
+ assert_impls!([NotZerocopy]: KnownLayout, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(
+ [u8; 0]: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ Unaligned,
+ );
+ assert_impls!(
+ [NotZerocopy; 0]: KnownLayout,
+ !Immutable,
+ !TryFromBytes,
+ !FromZeros,
+ !FromBytes,
+ !IntoBytes,
+ !Unaligned
+ );
+ assert_impls!(
+ [u8; 1]: KnownLayout,
+ Immutable,
+ TryFromBytes,
+ FromZeros,
+ FromBytes,
+ IntoBytes,
+ Unaligned,
+ );
+ assert_impls!(
+ [NotZerocopy; 1]: KnownLayout,
+ !Immutable,
+ !TryFromBytes,
+ !FromZeros,
+ !FromBytes,
+ !IntoBytes,
+ !Unaligned
+ );
+
+ assert_impls!(*const NotZerocopy: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(*mut NotZerocopy: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(*const [NotZerocopy]: KnownLayout, Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(*mut [NotZerocopy]: KnownLayout, Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(*const dyn Debug: KnownLayout, Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+ assert_impls!(*mut dyn Debug: KnownLayout, Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
+
+ #[cfg(feature = "simd")]
+ {
+ #[allow(unused_macros)]
+ macro_rules! test_simd_arch_mod {
+ ($arch:ident, $($typ:ident),*) => {
+ {
+ use core::arch::$arch::{$($typ),*};
+ use crate::*;
+ $( assert_impls!($typ: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); )*
+ }
+ };
+ }
+ #[cfg(target_arch = "x86")]
+ test_simd_arch_mod!(x86, __m128, __m128d, __m128i, __m256, __m256d, __m256i);
+
+ #[cfg(all(not(no_zerocopy_simd_x86_avx12_1_89_0), target_arch = "x86"))]
+ test_simd_arch_mod!(x86, __m512bh, __m512, __m512d, __m512i);
+
+ #[cfg(target_arch = "x86_64")]
+ test_simd_arch_mod!(x86_64, __m128, __m128d, __m128i, __m256, __m256d, __m256i);
+
+ #[cfg(all(not(no_zerocopy_simd_x86_avx12_1_89_0), target_arch = "x86_64"))]
+ test_simd_arch_mod!(x86_64, __m512bh, __m512, __m512d, __m512i);
+
+ #[cfg(target_arch = "wasm32")]
+ test_simd_arch_mod!(wasm32, v128);
+
+ #[cfg(all(feature = "simd-nightly", target_arch = "powerpc"))]
+ test_simd_arch_mod!(
+ powerpc,
+ vector_bool_long,
+ vector_double,
+ vector_signed_long,
+ vector_unsigned_long
+ );
+
+ #[cfg(all(feature = "simd-nightly", target_arch = "powerpc64"))]
+ test_simd_arch_mod!(
+ powerpc64,
+ vector_bool_long,
+ vector_double,
+ vector_signed_long,
+ vector_unsigned_long
+ );
+ #[cfg(all(target_arch = "aarch64", not(no_zerocopy_aarch64_simd_1_59_0)))]
+ #[rustfmt::skip]
+ test_simd_arch_mod!(
+ aarch64, float32x2_t, float32x4_t, float64x1_t, float64x2_t, int8x8_t, int8x8x2_t,
+ int8x8x3_t, int8x8x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int16x4_t,
+ int16x8_t, int32x2_t, int32x4_t, int64x1_t, int64x2_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t,
+ poly8x8x4_t, poly8x16_t, poly8x16x2_t, poly8x16x3_t, poly8x16x4_t, poly16x4_t, poly16x8_t,
+ poly64x1_t, poly64x2_t, uint8x8_t, uint8x8x2_t, uint8x8x3_t, uint8x8x4_t, uint8x16_t,
+ uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint16x4_t, uint16x4x2_t, uint16x4x3_t,
+ uint16x4x4_t, uint16x8_t, uint32x2_t, uint32x4_t, uint64x1_t, uint64x2_t
+ );
+ }
+ }
+}
diff --git a/rust/zerocopy/src/layout.rs b/rust/zerocopy/src/layout.rs
new file mode 100644
index 000000000000..19ad5ca85f74
--- /dev/null
+++ b/rust/zerocopy/src/layout.rs
@@ -0,0 +1,2223 @@
+// Copyright 2024 The Fuchsia Authors
+//
+// Licensed under the 2-Clause BSD License <LICENSE-BSD or
+// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+use core::{mem, num::NonZeroUsize};
+
+use crate::util;
+
+/// The target pointer width, counted in bits.
+const POINTER_WIDTH_BITS: usize = mem::size_of::<usize>() * 8;
+
+/// The layout of a type which might be dynamically-sized.
+///
+/// `DstLayout` describes the layout of sized types, slice types, and "slice
+/// DSTs" - ie, those that are known by the type system to have a trailing slice
+/// (as distinguished from `dyn Trait` types - such types *might* have a
+/// trailing slice type, but the type system isn't aware of it).
+///
+/// Note that `DstLayout` does not have any internal invariants, so no guarantee
+/// is made that a `DstLayout` conforms to any of Rust's requirements regarding
+/// the layout of real Rust types or instances of types.
+#[doc(hidden)]
+#[allow(missing_debug_implementations, missing_copy_implementations)]
+#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]
+#[derive(Copy, Clone)]
+pub struct DstLayout {
+ pub(crate) align: NonZeroUsize,
+ pub(crate) size_info: SizeInfo,
+ // Is it guaranteed statically (without knowing a value's runtime metadata)
+ // that the top-level type contains no padding? This does *not* apply
+ // recursively - for example, `[(u8, u16)]` has `statically_shallow_unpadded
+ // = true` even though this type likely has padding inside each `(u8, u16)`.
+ pub(crate) statically_shallow_unpadded: bool,
+}
+
+#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]
+#[derive(Copy, Clone)]
+pub(crate) enum SizeInfo<E = usize> {
+ Sized { size: usize },
+ SliceDst(TrailingSliceLayout<E>),
+}
+
+#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]
+#[derive(Copy, Clone)]
+pub(crate) struct TrailingSliceLayout<E = usize> {
+ // The offset of the first byte of the trailing slice field. Note that this
+ // is NOT the same as the minimum size of the type. For example, consider
+ // the following type:
+ //
+ // struct Foo {
+ // a: u16,
+ // b: u8,
+ // c: [u8],
+ // }
+ //
+ // In `Foo`, `c` is at byte offset 3. When `c.len() == 0`, `c` is followed
+ // by a padding byte.
+ pub(crate) offset: usize,
+ // The size of the element type of the trailing slice field.
+ pub(crate) elem_size: E,
+}
+
+impl SizeInfo {
+ /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a
+ /// `NonZeroUsize`. If `elem_size` is 0, returns `None`.
+ #[allow(unused)]
+ const fn try_to_nonzero_elem_size(&self) -> Option<SizeInfo<NonZeroUsize>> {
+ Some(match *self {
+ SizeInfo::Sized { size } => SizeInfo::Sized { size },
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
+ if let Some(elem_size) = NonZeroUsize::new(elem_size) {
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
+ } else {
+ return None;
+ }
+ }
+ })
+ }
+}
+
+#[doc(hidden)]
+#[derive(Copy, Clone)]
+#[cfg_attr(test, derive(Debug))]
+#[allow(missing_debug_implementations)]
+pub enum CastType {
+ Prefix,
+ Suffix,
+}
+
+#[cfg_attr(test, derive(Debug))]
+pub(crate) enum MetadataCastError {
+ Alignment,
+ Size,
+}
+
+impl DstLayout {
+ /// The minimum possible alignment of a type.
+ const MIN_ALIGN: NonZeroUsize = match NonZeroUsize::new(1) {
+ Some(min_align) => min_align,
+ None => const_unreachable!(),
+ };
+
+ /// The maximum theoretic possible alignment of a type.
+ ///
+ /// For compatibility with future Rust versions, this is defined as the
+ /// maximum power-of-two that fits into a `usize`. See also
+ /// [`DstLayout::CURRENT_MAX_ALIGN`].
+ pub(crate) const THEORETICAL_MAX_ALIGN: NonZeroUsize =
+ match NonZeroUsize::new(1 << (POINTER_WIDTH_BITS - 1)) {
+ Some(max_align) => max_align,
+ None => const_unreachable!(),
+ };
+
+ /// The current, documented max alignment of a type \[1\].
+ ///
+ /// \[1\] Per <https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers>:
+ ///
+ /// The alignment value must be a power of two from 1 up to
+ /// 2<sup>29</sup>.
+ #[cfg(not(kani))]
+ #[cfg(not(target_pointer_width = "16"))]
+ pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 28) {
+ Some(max_align) => max_align,
+ None => const_unreachable!(),
+ };
+
+ #[cfg(not(kani))]
+ #[cfg(target_pointer_width = "16")]
+ pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 15) {
+ Some(max_align) => max_align,
+ None => const_unreachable!(),
+ };
+
+ /// The maximum size of an allocation \[1\].
+ ///
+ /// \[1\] Per <https://doc.rust-lang.org/1.91.1/std/ptr/index.html#allocation>:
+ ///
+ /// For any allocation with base `address`, `size`, and a set of `addresses`,
+ /// the following are guaranteed: [..]
+ ///
+ /// - `size <= isize::MAX`
+ ///
+ #[allow(clippy::as_conversions)]
+ pub(crate) const MAX_SIZE: usize = isize::MAX as usize;
+
+ /// Assumes that this layout lacks static shallow padding.
+ ///
+ /// # Panics
+ ///
+ /// This method does not panic.
+ ///
+ /// # Safety
+ ///
+ /// If `self` describes the size and alignment of type that lacks static
+ /// shallow padding, unsafe code may assume that the result of this method
+ /// accurately reflects the size, alignment, and lack of static shallow
+ /// padding of that type.
+ const fn assume_shallow_unpadded(self) -> Self {
+ Self { statically_shallow_unpadded: true, ..self }
+ }
+
+ /// Constructs a `DstLayout` for a zero-sized type with `repr_align`
+ /// alignment (or 1). If `repr_align` is provided, then it must be a power
+ /// of two.
+ ///
+ /// # Panics
+ ///
+ /// This function panics if the supplied `repr_align` is not a power of two.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe code may assume that the contract of this function is satisfied.
+ #[doc(hidden)]
+ #[must_use]
+ #[inline]
+ pub const fn new_zst(repr_align: Option<NonZeroUsize>) -> DstLayout {
+ let align = match repr_align {
+ Some(align) => align,
+ None => Self::MIN_ALIGN,
+ };
+
+ const_assert!(align.get().is_power_of_two());
+
+ DstLayout {
+ align,
+ size_info: SizeInfo::Sized { size: 0 },
+ statically_shallow_unpadded: true,
+ }
+ }
+
+ /// Constructs a `DstLayout` which describes `T` and assumes `T` may contain
+ /// padding.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe code may assume that `DstLayout` is the correct layout for `T`.
+ #[doc(hidden)]
+ #[must_use]
+ #[inline]
+ pub const fn for_type<T>() -> DstLayout {
+ // SAFETY: `align` is correct by construction. `T: Sized`, and so it is
+ // sound to initialize `size_info` to `SizeInfo::Sized { size }`; the
+ // `size` field is also correct by construction. `unpadded` can safely
+ // default to `false`.
+ DstLayout {
+ align: match NonZeroUsize::new(mem::align_of::<T>()) {
+ Some(align) => align,
+ None => const_unreachable!(),
+ },
+ size_info: SizeInfo::Sized { size: mem::size_of::<T>() },
+ statically_shallow_unpadded: false,
+ }
+ }
+
+ /// Constructs a `DstLayout` which describes a `T` that does not contain
+ /// padding.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe code may assume that `DstLayout` is the correct layout for `T`.
+ #[doc(hidden)]
+ #[must_use]
+ #[inline]
+ pub const fn for_unpadded_type<T>() -> DstLayout {
+ Self::for_type::<T>().assume_shallow_unpadded()
+ }
+
+ /// Constructs a `DstLayout` which describes `[T]`.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`.
+ pub(crate) const fn for_slice<T>() -> DstLayout {
+ // SAFETY: The alignment of a slice is equal to the alignment of its
+ // element type, and so `align` is initialized correctly.
+ //
+ // Since this is just a slice type, there is no offset between the
+ // beginning of the type and the beginning of the slice, so it is
+ // correct to set `offset: 0`. The `elem_size` is correct by
+ // construction. Since `[T]` is a (degenerate case of a) slice DST, it
+ // is correct to initialize `size_info` to `SizeInfo::SliceDst`.
+ DstLayout {
+ align: match NonZeroUsize::new(mem::align_of::<T>()) {
+ Some(align) => align,
+ None => const_unreachable!(),
+ },
+ size_info: SizeInfo::SliceDst(TrailingSliceLayout {
+ offset: 0,
+ elem_size: mem::size_of::<T>(),
+ }),
+ statically_shallow_unpadded: true,
+ }
+ }
+
+ /// Constructs a complete `DstLayout` reflecting a `repr(C)` struct with the
+ /// given alignment modifiers and fields.
+ ///
+ /// This method cannot be used to match the layout of a record with the
+ /// default representation, as that representation is mostly unspecified.
+ ///
+ /// # Safety
+ ///
+ /// For any definition of a `repr(C)` struct, if this method is invoked with
+ /// alignment modifiers and fields corresponding to that definition, the
+ /// resulting `DstLayout` will correctly encode the layout of that struct.
+ ///
+ /// We make no guarantees to the behavior of this method when it is invoked
+ /// with arguments that cannot correspond to a valid `repr(C)` struct.
+ #[must_use]
+ #[inline]
+ pub const fn for_repr_c_struct(
+ repr_align: Option<NonZeroUsize>,
+ repr_packed: Option<NonZeroUsize>,
+ fields: &[DstLayout],
+ ) -> DstLayout {
+ let mut layout = DstLayout::new_zst(repr_align);
+
+ let mut i = 0;
+ #[allow(clippy::arithmetic_side_effects)]
+ while i < fields.len() {
+ #[allow(clippy::indexing_slicing)]
+ let field = fields[i];
+ layout = layout.extend(field, repr_packed);
+ i += 1;
+ }
+
+ layout = layout.pad_to_align();
+
+ // SAFETY: `layout` accurately describes the layout of a `repr(C)`
+ // struct with `repr_align` or `repr_packed` alignment modifications and
+ // the given `fields`. The `layout` is constructed using a sequence of
+ // invocations of `DstLayout::{new_zst,extend,pad_to_align}`. The
+ // documentation of these items vows that invocations in this manner
+ // will accurately describe a type, so long as:
+ //
+ // - that type is `repr(C)`,
+ // - its fields are enumerated in the order they appear,
+ // - the presence of `repr_align` and `repr_packed` are correctly accounted for.
+ //
+ // We respect all three of these preconditions above.
+ layout
+ }
+
+ /// Like `Layout::extend`, this creates a layout that describes a record
+ /// whose layout consists of `self` followed by `next` that includes the
+ /// necessary inter-field padding, but not any trailing padding.
+ ///
+ /// In order to match the layout of a `#[repr(C)]` struct, this method
+ /// should be invoked for each field in declaration order. To add trailing
+ /// padding, call `DstLayout::pad_to_align` after extending the layout for
+ /// all fields. If `self` corresponds to a type marked with
+ /// `repr(packed(N))`, then `repr_packed` should be set to `Some(N)`,
+ /// otherwise `None`.
+ ///
+ /// This method cannot be used to match the layout of a record with the
+ /// default representation, as that representation is mostly unspecified.
+ ///
+ /// # Safety
+ ///
+ /// If a (potentially hypothetical) valid `repr(C)` Rust type begins with
+ /// fields whose layout are `self`, and those fields are immediately
+ /// followed by a field whose layout is `field`, then unsafe code may rely
+ /// on `self.extend(field, repr_packed)` producing a layout that correctly
+ /// encompasses those two components.
+ ///
+ /// We make no guarantees to the behavior of this method if these fragments
+ /// cannot appear in a valid Rust type (e.g., the concatenation of the
+ /// layouts would lead to a size larger than `isize::MAX`).
+ #[doc(hidden)]
+ #[must_use]
+ #[inline]
+ pub const fn extend(self, field: DstLayout, repr_packed: Option<NonZeroUsize>) -> Self {
+ use util::{max, min, padding_needed_for};
+
+ // If `repr_packed` is `None`, there are no alignment constraints, and
+ // the value can be defaulted to `THEORETICAL_MAX_ALIGN`.
+ let max_align = match repr_packed {
+ Some(max_align) => max_align,
+ None => Self::THEORETICAL_MAX_ALIGN,
+ };
+
+ const_assert!(max_align.get().is_power_of_two());
+
+ // We use Kani to prove that this method is robust to future increases
+ // in Rust's maximum allowed alignment. However, if such a change ever
+ // actually occurs, we'd like to be notified via assertion failures.
+ #[cfg(not(kani))]
+ {
+ const_debug_assert!(self.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
+ const_debug_assert!(field.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
+ if let Some(repr_packed) = repr_packed {
+ const_debug_assert!(repr_packed.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
+ }
+ }
+
+ // The field's alignment is clamped by `repr_packed` (i.e., the
+ // `repr(packed(N))` attribute, if any) [1].
+ //
+ // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
+ //
+ // The alignments of each field, for the purpose of positioning
+ // fields, is the smaller of the specified alignment and the alignment
+ // of the field's type.
+ let field_align = min(field.align, max_align);
+
+ // The struct's alignment is the maximum of its previous alignment and
+ // `field_align`.
+ let align = max(self.align, field_align);
+
+ let (interfield_padding, size_info) = match self.size_info {
+ // If the layout is already a DST, we panic; DSTs cannot be extended
+ // with additional fields.
+ SizeInfo::SliceDst(..) => const_panic!("Cannot extend a DST with additional fields."),
+
+ SizeInfo::Sized { size: preceding_size } => {
+ // Compute the minimum amount of inter-field padding needed to
+ // satisfy the field's alignment, and offset of the trailing
+ // field. [1]
+ //
+ // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
+ //
+ // Inter-field padding is guaranteed to be the minimum
+ // required in order to satisfy each field's (possibly
+ // altered) alignment.
+ let padding = padding_needed_for(preceding_size, field_align);
+
+ // This will not panic (and is proven to not panic, with Kani)
+ // if the layout components can correspond to a leading layout
+ // fragment of a valid Rust type, but may panic otherwise (e.g.,
+ // combining or aligning the components would create a size
+ // exceeding `isize::MAX`).
+ let offset = match preceding_size.checked_add(padding) {
+ Some(offset) => offset,
+ None => const_panic!("Adding padding to `self`'s size overflows `usize`."),
+ };
+
+ (
+ padding,
+ match field.size_info {
+ SizeInfo::Sized { size: field_size } => {
+ // If the trailing field is sized, the resulting layout
+ // will be sized. Its size will be the sum of the
+ // preceding layout, the size of the new field, and the
+ // size of inter-field padding between the two.
+ //
+ // This will not panic (and is proven with Kani to not
+ // panic) if the layout components can correspond to a
+ // leading layout fragment of a valid Rust type, but may
+ // panic otherwise (e.g., combining or aligning the
+ // components would create a size exceeding
+ // `usize::MAX`).
+ let size = match offset.checked_add(field_size) {
+ Some(size) => size,
+ None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"),
+ };
+ SizeInfo::Sized { size }
+ }
+ SizeInfo::SliceDst(TrailingSliceLayout {
+ offset: trailing_offset,
+ elem_size,
+ }) => {
+ // If the trailing field is dynamically sized, so too
+ // will the resulting layout. The offset of the trailing
+ // slice component is the sum of the offset of the
+ // trailing field and the trailing slice offset within
+ // that field.
+ //
+ // This will not panic (and is proven with Kani to not
+ // panic) if the layout components can correspond to a
+ // leading layout fragment of a valid Rust type, but may
+ // panic otherwise (e.g., combining or aligning the
+ // components would create a size exceeding
+ // `usize::MAX`).
+ let offset = match offset.checked_add(trailing_offset) {
+ Some(offset) => offset,
+ None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"),
+ };
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
+ }
+ },
+ )
+ }
+ };
+
+ let statically_shallow_unpadded = self.statically_shallow_unpadded
+ && field.statically_shallow_unpadded
+ && interfield_padding == 0;
+
+ DstLayout { align, size_info, statically_shallow_unpadded }
+ }
+
+ /// Like `Layout::pad_to_align`, this routine rounds the size of this layout
+ /// up to the nearest multiple of this type's alignment or `repr_packed`
+ /// (whichever is less). This method leaves DST layouts unchanged, since the
+ /// trailing padding of DSTs is computed at runtime.
+ ///
+ /// The accompanying boolean is `true` if the resulting composition of
+ /// fields necessitated static (as opposed to dynamic) padding; otherwise
+ /// `false`.
+ ///
+ /// In order to match the layout of a `#[repr(C)]` struct, this method
+ /// should be invoked after the invocations of [`DstLayout::extend`]. If
+ /// `self` corresponds to a type marked with `repr(packed(N))`, then
+ /// `repr_packed` should be set to `Some(N)`, otherwise `None`.
+ ///
+ /// This method cannot be used to match the layout of a record with the
+ /// default representation, as that representation is mostly unspecified.
+ ///
+ /// # Safety
+ ///
+ /// If a (potentially hypothetical) valid `repr(C)` type begins with fields
+ /// whose layout are `self` followed only by zero or more bytes of trailing
+ /// padding (not included in `self`), then unsafe code may rely on
+ /// `self.pad_to_align(repr_packed)` producing a layout that correctly
+ /// encapsulates the layout of that type.
+ ///
+ /// We make no guarantees to the behavior of this method if `self` cannot
+ /// appear in a valid Rust type (e.g., because the addition of trailing
+ /// padding would lead to a size larger than `isize::MAX`).
+ #[doc(hidden)]
+ #[must_use]
+ #[inline]
+ pub const fn pad_to_align(self) -> Self {
+ use util::padding_needed_for;
+
+ let (static_padding, size_info) = match self.size_info {
+ // For sized layouts, we add the minimum amount of trailing padding
+ // needed to satisfy alignment.
+ SizeInfo::Sized { size: unpadded_size } => {
+ let padding = padding_needed_for(unpadded_size, self.align);
+ let size = match unpadded_size.checked_add(padding) {
+ Some(size) => size,
+ None => const_panic!("Adding padding caused size to overflow `usize`."),
+ };
+ (padding, SizeInfo::Sized { size })
+ }
+ // For DST layouts, trailing padding depends on the length of the
+ // trailing DST and is computed at runtime. This does not alter the
+ // offset or element size of the layout, so we leave `size_info`
+ // unchanged.
+ size_info @ SizeInfo::SliceDst(_) => (0, size_info),
+ };
+
+ let statically_shallow_unpadded = self.statically_shallow_unpadded && static_padding == 0;
+
+ DstLayout { align: self.align, size_info, statically_shallow_unpadded }
+ }
+
+ /// Produces `true` if `self` requires static padding; otherwise `false`.
+ #[must_use]
+ #[inline(always)]
+ pub const fn requires_static_padding(self) -> bool {
+ !self.statically_shallow_unpadded
+ }
+
+ /// Produces `true` if there exists any metadata for which a type of layout
+ /// `self` would require dynamic trailing padding; otherwise `false`.
+ #[must_use]
+ #[inline(always)]
+ pub const fn requires_dynamic_padding(self) -> bool {
+ // A `% self.align.get()` cannot panic, since `align` is non-zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ match self.size_info {
+ SizeInfo::Sized { .. } => false,
+ SizeInfo::SliceDst(trailing_slice_layout) => {
+ // SAFETY: This predicate is formally proved sound by
+ // `proofs::prove_requires_dynamic_padding`.
+ trailing_slice_layout.offset % self.align.get() != 0
+ || trailing_slice_layout.elem_size % self.align.get() != 0
+ }
+ }
+ }
+
+ /// Validates that a cast is sound from a layout perspective.
+ ///
+ /// Validates that the size and alignment requirements of a type with the
+ /// layout described in `self` would not be violated by performing a
+ /// `cast_type` cast from a pointer with address `addr` which refers to a
+ /// memory region of size `bytes_len`.
+ ///
+ /// If the cast is valid, `validate_cast_and_convert_metadata` returns
+ /// `(elems, split_at)`. If `self` describes a dynamically-sized type, then
+ /// `elems` is the maximum number of trailing slice elements for which a
+ /// cast would be valid (for sized types, `elem` is meaningless and should
+ /// be ignored). `split_at` is the index at which to split the memory region
+ /// in order for the prefix (suffix) to contain the result of the cast, and
+ /// in order for the remaining suffix (prefix) to contain the leftover
+ /// bytes.
+ ///
+ /// There are three conditions under which a cast can fail:
+ /// - The smallest possible value for the type is larger than the provided
+ /// memory region
+ /// - A prefix cast is requested, and `addr` does not satisfy `self`'s
+ /// alignment requirement
+ /// - A suffix cast is requested, and `addr + bytes_len` does not satisfy
+ /// `self`'s alignment requirement (as a consequence, since all instances
+ /// of the type are a multiple of its alignment, no size for the type will
+ /// result in a starting address which is properly aligned)
+ ///
+ /// # Safety
+ ///
+ /// The caller may assume that this implementation is correct, and may rely
+ /// on that assumption for the soundness of their code. In particular, the
+ /// caller may assume that, if `validate_cast_and_convert_metadata` returns
+ /// `Some((elems, split_at))`, then:
+ /// - A pointer to the type (for dynamically sized types, this includes
+ /// `elems` as its pointer metadata) describes an object of size `size <=
+ /// bytes_len`
+ /// - If this is a prefix cast:
+ /// - `addr` satisfies `self`'s alignment
+ /// - `size == split_at`
+ /// - If this is a suffix cast:
+ /// - `split_at == bytes_len - size`
+ /// - `addr + split_at` satisfies `self`'s alignment
+ ///
+ /// Note that this method does *not* ensure that a pointer constructed from
+ /// its return values will be a valid pointer. In particular, this method
+ /// does not reason about `isize` overflow, which is a requirement of many
+ /// Rust pointer APIs, and may at some point be determined to be a validity
+ /// invariant of pointer types themselves. This should never be a problem so
+ /// long as the arguments to this method are derived from a known-valid
+ /// pointer (e.g., one derived from a safe Rust reference), but it is
+ /// nonetheless the caller's responsibility to justify that pointer
+ /// arithmetic will not overflow based on a safety argument *other than* the
+ /// mere fact that this method returned successfully.
+ ///
+ /// # Panics
+ ///
+ /// `validate_cast_and_convert_metadata` will panic if `self` describes a
+ /// DST whose trailing slice element is zero-sized.
+ ///
+ /// If `addr + bytes_len` overflows `usize`,
+ /// `validate_cast_and_convert_metadata` may panic, or it may return
+ /// incorrect results. No guarantees are made about when
+ /// `validate_cast_and_convert_metadata` will panic. The caller should not
+ /// rely on `validate_cast_and_convert_metadata` panicking in any particular
+ /// condition, even if `debug_assertions` are enabled.
+ #[allow(unused)]
+ #[inline(always)]
+ pub(crate) const fn validate_cast_and_convert_metadata(
+ &self,
+ addr: usize,
+ bytes_len: usize,
+ cast_type: CastType,
+ ) -> Result<(usize, usize), MetadataCastError> {
+ // `debug_assert!`, but with `#[allow(clippy::arithmetic_side_effects)]`.
+ macro_rules! __const_debug_assert {
+ ($e:expr $(, $msg:expr)?) => {
+ const_debug_assert!({
+ #[allow(clippy::arithmetic_side_effects)]
+ let e = $e;
+ e
+ } $(, $msg)?);
+ };
+ }
+
+ // Note that, in practice, `self` is always a compile-time constant. We
+ // do this check earlier than needed to ensure that we always panic as a
+ // result of bugs in the program (such as calling this function on an
+ // invalid type) instead of allowing this panic to be hidden if the cast
+ // would have failed anyway for runtime reasons (such as a too-small
+ // memory region).
+ //
+ // FIXME(#67): Once our MSRV is 1.65, use let-else:
+ // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements
+ let size_info = match self.size_info.try_to_nonzero_elem_size() {
+ Some(size_info) => size_info,
+ None => const_panic!("attempted to cast to slice type with zero-sized element"),
+ };
+
+ // Precondition
+ __const_debug_assert!(
+ addr.checked_add(bytes_len).is_some(),
+ "`addr` + `bytes_len` > usize::MAX"
+ );
+
+ // Alignment checks go in their own block to avoid introducing variables
+ // into the top-level scope.
+ {
+ // We check alignment for `addr` (for prefix casts) or `addr +
+ // bytes_len` (for suffix casts). For a prefix cast, the correctness
+ // of this check is trivial - `addr` is the address the object will
+ // live at.
+ //
+ // For a suffix cast, we know that all valid sizes for the type are
+ // a multiple of the alignment (and by safety precondition, we know
+ // `DstLayout` may only describe valid Rust types). Thus, a
+ // validly-sized instance which lives at a validly-aligned address
+ // must also end at a validly-aligned address. Thus, if the end
+ // address for a suffix cast (`addr + bytes_len`) is not aligned,
+ // then no valid start address will be aligned either.
+ let offset = match cast_type {
+ CastType::Prefix => 0,
+ CastType::Suffix => bytes_len,
+ };
+
+ // Addition is guaranteed not to overflow because `offset <=
+ // bytes_len`, and `addr + bytes_len <= usize::MAX` is a
+ // precondition of this method. Modulus is guaranteed not to divide
+ // by 0 because `align` is non-zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ if (addr + offset) % self.align.get() != 0 {
+ return Err(MetadataCastError::Alignment);
+ }
+ }
+
+ let (elems, self_bytes) = match size_info {
+ SizeInfo::Sized { size } => {
+ if size > bytes_len {
+ return Err(MetadataCastError::Size);
+ }
+ (0, size)
+ }
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
+ // Calculate the maximum number of bytes that could be consumed
+ // - any number of bytes larger than this will either not be a
+ // multiple of the alignment, or will be larger than
+ // `bytes_len`.
+ let max_total_bytes =
+ util::round_down_to_next_multiple_of_alignment(bytes_len, self.align);
+ // Calculate the maximum number of bytes that could be consumed
+ // by the trailing slice.
+ //
+ // FIXME(#67): Once our MSRV is 1.65, use let-else:
+ // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements
+ let max_slice_and_padding_bytes = match max_total_bytes.checked_sub(offset) {
+ Some(max) => max,
+ // `bytes_len` too small even for 0 trailing slice elements.
+ None => return Err(MetadataCastError::Size),
+ };
+
+ // Calculate the number of elements that fit in
+ // `max_slice_and_padding_bytes`; any remaining bytes will be
+ // considered padding.
+ //
+ // Guaranteed not to divide by zero: `elem_size` is non-zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ let elems = max_slice_and_padding_bytes / elem_size.get();
+ // Guaranteed not to overflow on multiplication: `usize::MAX >=
+ // max_slice_and_padding_bytes >= (max_slice_and_padding_bytes /
+ // elem_size) * elem_size`.
+ //
+ // Guaranteed not to overflow on addition:
+ // - max_slice_and_padding_bytes == max_total_bytes - offset
+ // - elems * elem_size <= max_slice_and_padding_bytes == max_total_bytes - offset
+ // - elems * elem_size + offset <= max_total_bytes <= usize::MAX
+ #[allow(clippy::arithmetic_side_effects)]
+ let without_padding = offset + elems * elem_size.get();
+ // `self_bytes` is equal to the offset bytes plus the bytes
+ // consumed by the trailing slice plus any padding bytes
+ // required to satisfy the alignment. Note that we have computed
+ // the maximum number of trailing slice elements that could fit
+ // in `self_bytes`, so any padding is guaranteed to be less than
+ // the size of an extra element.
+ //
+ // Guaranteed not to overflow:
+ // - By previous comment: without_padding == elems * elem_size +
+ // offset <= max_total_bytes
+ // - By construction, `max_total_bytes` is a multiple of
+ // `self.align`.
+ // - At most, adding padding needed to round `without_padding`
+ // up to the next multiple of the alignment will bring
+ // `self_bytes` up to `max_total_bytes`.
+ #[allow(clippy::arithmetic_side_effects)]
+ let self_bytes =
+ without_padding + util::padding_needed_for(without_padding, self.align);
+ (elems, self_bytes)
+ }
+ };
+
+ __const_debug_assert!(self_bytes <= bytes_len);
+
+ let split_at = match cast_type {
+ CastType::Prefix => self_bytes,
+ // Guaranteed not to underflow:
+ // - In the `Sized` branch, only returns `size` if `size <=
+ // bytes_len`.
+ // - In the `SliceDst` branch, calculates `self_bytes <=
+ // max_toatl_bytes`, which is upper-bounded by `bytes_len`.
+ #[allow(clippy::arithmetic_side_effects)]
+ CastType::Suffix => bytes_len - self_bytes,
+ };
+
+ Ok((elems, split_at))
+ }
+}
+
+pub(crate) use cast_from::CastFrom;
+mod cast_from {
+ use crate::*;
+
+ pub(crate) struct CastFrom<Dst: ?Sized> {
+ _never: core::convert::Infallible,
+ _marker: PhantomData<Dst>,
+ }
+
+ // SAFETY: The implementation of `Project::project` preserves the address
+ // of the referent – it only modifies pointer metadata.
+ unsafe impl<Src, Dst> crate::pointer::cast::Cast<Src, Dst> for CastFrom<Dst>
+ where
+ Src: KnownLayout + ?Sized,
+ Dst: KnownLayout + ?Sized,
+ {
+ }
+
+ // SAFETY: The implementation of `Project::project` preserves the size of
+ // the referent (see inline comments for a more detailed proof of this).
+ unsafe impl<Src, Dst> crate::pointer::cast::CastExact<Src, Dst> for CastFrom<Dst>
+ where
+ Src: KnownLayout + ?Sized,
+ Dst: KnownLayout + ?Sized,
+ {
+ }
+
+ // SAFETY: `project` produces a pointer which refers to the same referent
+ // bytes as its input, or to a subset of them (see inline comments for a
+ // more detailed proof of this). It does this using provenance-preserving
+ // operations.
+ unsafe impl<Src, Dst> crate::pointer::cast::Project<Src, Dst> for CastFrom<Dst>
+ where
+ Src: KnownLayout + ?Sized,
+ Dst: KnownLayout + ?Sized,
+ {
+ /// # PME
+ ///
+ /// Generates a post-monomorphization error if it is not possible to
+ /// implement soundly.
+ //
+ // FIXME(#1817): Support Sized->Unsized and Unsized->Sized casts
+ fn project(src: PtrInner<'_, Src>) -> *mut Dst {
+ /// The parameters required in order to perform a pointer cast from
+ /// `Src` to `Dst`.
+ ///
+ /// These are a compile-time function of the layouts of `Src`
+ /// and `Dst`.
+ ///
+ /// # Safety
+ ///
+ /// `Src`'s alignment must not be smaller than `Dst`'s alignment.
+ struct CastParams<Src: ?Sized, Dst: ?Sized> {
+ inner: CastParamsInner,
+ _src: PhantomData<Src>,
+ _dst: PhantomData<Dst>,
+ }
+
+ #[derive(Copy, Clone)]
+ enum CastParamsInner {
+ // At compile time (specifically, post-monomorphization time),
+ // we need to compute two things:
+ // - Whether, given *any* `*Src`, it is possible to construct a
+ // `*Dst` which addresses the same number of bytes (ie,
+ // whether, for any `Src` pointer metadata, there exists `Dst`
+ // pointer metadata that addresses the same number of bytes)
+ // - If this is possible, any information necessary to perform
+ // the `Src`->`Dst` metadata conversion at runtime.
+ //
+ // Assume that `Src` and `Dst` are slice DSTs, and define:
+ // - `S_OFF = Src::LAYOUT.size_info.offset`
+ // - `S_ELEM = Src::LAYOUT.size_info.elem_size`
+ // - `D_OFF = Dst::LAYOUT.size_info.offset`
+ // - `D_ELEM = Dst::LAYOUT.size_info.elem_size`
+ //
+ // We are trying to solve the following equation:
+ //
+ // D_OFF + d_meta * D_ELEM = S_OFF + s_meta * S_ELEM
+ //
+ // At runtime, we will be attempting to compute `d_meta`, given
+ // `s_meta` (a runtime value) and all other parameters (which
+ // are compile-time values). We can solve like so:
+ //
+ // D_OFF + d_meta * D_ELEM = S_OFF + s_meta * S_ELEM
+ //
+ // d_meta * D_ELEM = S_OFF - D_OFF + s_meta * S_ELEM
+ //
+ // d_meta = (S_OFF - D_OFF + s_meta * S_ELEM)/D_ELEM
+ //
+ // Since `d_meta` will be a `usize`, we need the right-hand side
+ // to be an integer, and this needs to hold for *any* value of
+ // `s_meta` (in order for our conversion to be infallible - ie,
+ // to not have to reject certain values of `s_meta` at runtime).
+ // This means that:
+ //
+ // - `s_meta * S_ELEM` must be a multiple of `D_ELEM`
+ // - Since this must hold for any value of `s_meta`, `S_ELEM`
+ // must be a multiple of `D_ELEM`
+ // - `S_OFF - D_OFF` must be a multiple of `D_ELEM`
+ //
+ // Thus, let `OFFSET_DELTA_ELEMS = (S_OFF - D_OFF)/D_ELEM` and
+ // `ELEM_MULTIPLE = S_ELEM/D_ELEM`. We can rewrite the above
+ // expression as:
+ //
+ // d_meta = (S_OFF - D_OFF + s_meta * S_ELEM)/D_ELEM
+ //
+ // d_meta = OFFSET_DELTA_ELEMS + s_meta * ELEM_MULTIPLE
+ //
+ // Thus, we just need to compute the following and confirm that
+ // they have integer solutions in order to both a) determine
+ // whether infallible `Src` -> `Dst` casts are possible and, b)
+ // pre-compute the parameters necessary to perform those casts
+ // at runtime. These parameters are encapsulated in
+ // `CastParams`, which acts as a witness that such infallible
+ // casts are possible.
+ /// The parameters required in order to perform an
+ /// unsized-to-unsized pointer cast from `Src` to `Dst` as
+ /// described above.
+ ///
+ /// # Safety
+ ///
+ /// `Src` and `Dst` must both be slice DSTs.
+ ///
+ /// `offset_delta_elems` and `elem_multiple` must be valid as
+ /// described above.
+ UnsizedToUnsized { offset_delta_elems: usize, elem_multiple: usize },
+
+ /// The metadata of a `Dst` which has the same size as `Src:
+ /// Sized`.
+ ///
+ /// # Safety
+ ///
+ /// `Src: Sized` and `Dst` must be a slice DST.
+ ///
+ /// A raw `Dst` pointer with metadata `dst_meta` must address
+ /// `size_of::<Src>()` bytes.
+ SizedToUnsized { dst_meta: usize },
+
+ /// The metadata of a `Dst` which has the same size as `Src:
+ /// Sized`.
+ ///
+ /// # Safety
+ ///
+ /// `Src` and `Dst` must both be `Sized` and `size_of::<Src>()
+ /// == size_of::<Dst>()`.
+ SizedToSized,
+ }
+
+ impl<Src: ?Sized, Dst: ?Sized> Copy for CastParams<Src, Dst> {}
+ impl<Src: ?Sized, Dst: ?Sized> Clone for CastParams<Src, Dst> {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+
+ impl<Src: ?Sized, Dst: ?Sized> CastParams<Src, Dst> {
+ const fn try_compute(
+ src: &DstLayout,
+ dst: &DstLayout,
+ ) -> Option<CastParams<Src, Dst>> {
+ if src.align.get() < dst.align.get() {
+ return None;
+ }
+
+ let inner = match (src.size_info, dst.size_info) {
+ (
+ SizeInfo::Sized { size: src_size },
+ SizeInfo::Sized { size: dst_size },
+ ) => {
+ if src_size != dst_size {
+ return None;
+ }
+
+ // SAFETY: We checked above that `src_size ==
+ // dst_size`.
+ CastParamsInner::SizedToSized
+ }
+ (SizeInfo::Sized { size: src_size }, SizeInfo::SliceDst(dst)) => {
+ let offset_delta = if let Some(od) = src_size.checked_sub(dst.offset) {
+ od
+ } else {
+ return None;
+ };
+
+ let dst_elem_size = if let Some(e) = NonZeroUsize::new(dst.elem_size) {
+ e
+ } else {
+ return None;
+ };
+
+ // PANICS: `dst_elem_size: NonZeroUsize`, so this won't
+ // divide by zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ let delta_mod_other_elem = offset_delta % dst_elem_size.get();
+
+ if delta_mod_other_elem != 0 {
+ return None;
+ }
+
+ // PANICS: `dst_elem_size: NonZeroUsize`, so this won't
+ // divide by zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ let dst_meta = offset_delta / dst_elem_size.get();
+
+ // SAFETY: The preceding math ensures that a `Dst`
+ // with `dst_meta` addresses `src_size` bytes.
+ CastParamsInner::SizedToUnsized { dst_meta }
+ }
+ (SizeInfo::SliceDst(src), SizeInfo::SliceDst(dst)) => {
+ let offset_delta = if let Some(od) = src.offset.checked_sub(dst.offset)
+ {
+ od
+ } else {
+ return None;
+ };
+
+ let dst_elem_size = if let Some(e) = NonZeroUsize::new(dst.elem_size) {
+ e
+ } else {
+ return None;
+ };
+
+ // PANICS: `dst_elem_size: NonZeroUsize`, so this won't
+ // divide by zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ let delta_mod_other_elem = offset_delta % dst_elem_size.get();
+
+ // PANICS: `dst_elem_size: NonZeroUsize`, so this won't
+ // divide by zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ let elem_remainder = src.elem_size % dst_elem_size.get();
+
+ if delta_mod_other_elem != 0
+ || src.elem_size < dst.elem_size
+ || elem_remainder != 0
+ {
+ return None;
+ }
+
+ // PANICS: `dst_elem_size: NonZeroUsize`, so this won't
+ // divide by zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ let offset_delta_elems = offset_delta / dst_elem_size.get();
+
+ // PANICS: `dst_elem_size: NonZeroUsize`, so this won't
+ // divide by zero.
+ #[allow(clippy::arithmetic_side_effects)]
+ let elem_multiple = src.elem_size / dst_elem_size.get();
+
+ CastParamsInner::UnsizedToUnsized {
+ // SAFETY: We checked above that this is an exact ratio.
+ offset_delta_elems,
+ // SAFETY: We checked above that this is an exact ratio.
+ elem_multiple,
+ }
+ }
+ _ => return None,
+ };
+
+ // SAFETY: We checked above that `src.align >= dst.align`.
+ Some(CastParams { inner, _src: PhantomData, _dst: PhantomData })
+ }
+ }
+
+ impl<Src: KnownLayout + ?Sized, Dst: KnownLayout + ?Sized> CastParams<Src, Dst> {
+ /// # Safety
+ ///
+ /// `src_meta` describes a `Src` whose size is no larger than
+ /// `isize::MAX`.
+ ///
+ /// The returned metadata describes a `Dst` of the same size as
+ /// the original `Src`.
+ #[inline(always)]
+ unsafe fn cast_metadata(
+ self,
+ src_meta: Src::PointerMetadata,
+ ) -> Dst::PointerMetadata {
+ #[allow(unused)]
+ use crate::util::polyfills::*;
+
+ let dst_meta = match self.inner {
+ CastParamsInner::UnsizedToUnsized { offset_delta_elems, elem_multiple } => {
+ let src_meta = src_meta.to_elem_count();
+ #[allow(
+ unstable_name_collisions,
+ clippy::multiple_unsafe_ops_per_block
+ )]
+ // SAFETY: `self` is a witness that the following
+ // equation holds:
+ //
+ // D_OFF + d_meta * D_ELEM = S_OFF + s_meta * S_ELEM
+ //
+ // Since the caller promises that `src_meta` is
+ // valid `Src` metadata, this math will not
+ // overflow, and the returned value will describe a
+ // `Dst` of the same size.
+ unsafe {
+ offset_delta_elems
+ .unchecked_add(src_meta.unchecked_mul(elem_multiple))
+ }
+ }
+ CastParamsInner::SizedToUnsized { dst_meta } => dst_meta,
+ CastParamsInner::SizedToSized => 0,
+ };
+ Dst::PointerMetadata::from_elem_count(dst_meta)
+ }
+ }
+
+ trait Params<Src: ?Sized> {
+ const CAST_PARAMS: CastParams<Src, Self>;
+ }
+
+ impl<Src, Dst> Params<Src> for Dst
+ where
+ Src: KnownLayout + ?Sized,
+ Dst: KnownLayout + ?Sized,
+ {
+ const CAST_PARAMS: CastParams<Src, Dst> =
+ match CastParams::try_compute(&Src::LAYOUT, &Dst::LAYOUT) {
+ Some(params) => params,
+ None => const_panic!(
+ "cannot `transmute_ref!` or `transmute_mut!` between incompatible types"
+ ),
+ };
+ }
+
+ let src_meta = <Src as KnownLayout>::pointer_to_metadata(src.as_ptr());
+ let params = <Dst as Params<Src>>::CAST_PARAMS;
+
+ // SAFETY: `src: PtrInner` guarantees that `src`'s referent is zero
+ // bytes or lives in a single allocation, which means that it is no
+ // larger than `isize::MAX` bytes [1].
+ //
+ // [1] https://doc.rust-lang.org/1.92.0/std/ptr/index.html#allocation
+ let dst_meta = unsafe { params.cast_metadata(src_meta) };
+
+ <Dst as KnownLayout>::raw_from_ptr_len(src.as_non_null().cast(), dst_meta).as_ptr()
+ }
+ }
+}
+
+// FIXME(#67): For some reason, on our MSRV toolchain, this `allow` isn't
+// enforced despite having `#![allow(unknown_lints)]` at the crate root, but
+// putting it here works. Once our MSRV is high enough that this bug has been
+// fixed, remove this `allow`.
+#[allow(unknown_lints)]
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_dst_layout_for_slice() {
+ let layout = DstLayout::for_slice::<u32>();
+ match layout.size_info {
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
+ assert_eq!(offset, 0);
+ assert_eq!(elem_size, 4);
+ }
+ _ => panic!("Expected SliceDst"),
+ }
+ assert_eq!(layout.align.get(), 4);
+ }
+
+ /// Tests of when a sized `DstLayout` is extended with a sized field.
+ #[allow(clippy::decimal_literal_representation)]
+ #[test]
+ fn test_dst_layout_extend_sized_with_sized() {
+ // This macro constructs a layout corresponding to a `u8` and extends it
+ // with a zero-sized trailing field of given alignment `n`. The macro
+ // tests that the resulting layout has both size and alignment `min(n,
+ // P)` for all valid values of `repr(packed(P))`.
+ macro_rules! test_align_is_size {
+ ($n:expr) => {
+ let base = DstLayout::for_type::<u8>();
+ let trailing_field = DstLayout::for_type::<elain::Align<$n>>();
+
+ let packs =
+ core::iter::once(None).chain((0..29).map(|p| NonZeroUsize::new(2usize.pow(p))));
+
+ for pack in packs {
+ let composite = base.extend(trailing_field, pack);
+ let max_align = pack.unwrap_or(DstLayout::CURRENT_MAX_ALIGN);
+ let align = $n.min(max_align.get());
+ assert_eq!(
+ composite,
+ DstLayout {
+ align: NonZeroUsize::new(align).unwrap(),
+ size_info: SizeInfo::Sized { size: align },
+ statically_shallow_unpadded: false,
+ }
+ )
+ }
+ };
+ }
+
+ test_align_is_size!(1);
+ test_align_is_size!(2);
+ test_align_is_size!(4);
+ test_align_is_size!(8);
+ test_align_is_size!(16);
+ test_align_is_size!(32);
+ test_align_is_size!(64);
+ test_align_is_size!(128);
+ test_align_is_size!(256);
+ test_align_is_size!(512);
+ test_align_is_size!(1024);
+ test_align_is_size!(2048);
+ test_align_is_size!(4096);
+ test_align_is_size!(8192);
+ test_align_is_size!(16384);
+ test_align_is_size!(32768);
+ test_align_is_size!(65536);
+ test_align_is_size!(131072);
+ test_align_is_size!(262144);
+ test_align_is_size!(524288);
+ test_align_is_size!(1048576);
+ test_align_is_size!(2097152);
+ test_align_is_size!(4194304);
+ test_align_is_size!(8388608);
+ test_align_is_size!(16777216);
+ test_align_is_size!(33554432);
+ test_align_is_size!(67108864);
+ test_align_is_size!(33554432);
+ test_align_is_size!(134217728);
+ test_align_is_size!(268435456);
+ }
+
+ /// Tests of when a sized `DstLayout` is extended with a DST field.
+ #[test]
+ fn test_dst_layout_extend_sized_with_dst() {
+ // Test that for all combinations of real-world alignments and
+ // `repr_packed` values, that the extension of a sized `DstLayout`` with
+ // a DST field correctly computes the trailing offset in the composite
+ // layout.
+
+ let aligns = (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap());
+ let packs = core::iter::once(None).chain(aligns.clone().map(Some));
+
+ for align in aligns {
+ for pack in packs.clone() {
+ let base = DstLayout::for_type::<u8>();
+ let elem_size = 42;
+ let trailing_field_offset = 11;
+
+ let trailing_field = DstLayout {
+ align,
+ size_info: SizeInfo::SliceDst(TrailingSliceLayout { elem_size, offset: 11 }),
+ statically_shallow_unpadded: false,
+ };
+
+ let composite = base.extend(trailing_field, pack);
+
+ let max_align = pack.unwrap_or(DstLayout::CURRENT_MAX_ALIGN).get();
+
+ let align = align.get().min(max_align);
+
+ assert_eq!(
+ composite,
+ DstLayout {
+ align: NonZeroUsize::new(align).unwrap(),
+ size_info: SizeInfo::SliceDst(TrailingSliceLayout {
+ elem_size,
+ offset: align + trailing_field_offset,
+ }),
+ statically_shallow_unpadded: false,
+ }
+ )
+ }
+ }
+ }
+
+ /// Tests that calling `pad_to_align` on a sized `DstLayout` adds the
+ /// expected amount of trailing padding.
+ #[test]
+ fn test_dst_layout_pad_to_align_with_sized() {
+ // For all valid alignments `align`, construct a one-byte layout aligned
+ // to `align`, call `pad_to_align`, and assert that the size of the
+ // resulting layout is equal to `align`.
+ for align in (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap()) {
+ let layout = DstLayout {
+ align,
+ size_info: SizeInfo::Sized { size: 1 },
+ statically_shallow_unpadded: true,
+ };
+
+ assert_eq!(
+ layout.pad_to_align(),
+ DstLayout {
+ align,
+ size_info: SizeInfo::Sized { size: align.get() },
+ statically_shallow_unpadded: align.get() == 1
+ }
+ );
+ }
+
+ // Test explicitly-provided combinations of unpadded and padded
+ // counterparts.
+
+ macro_rules! test {
+ (unpadded { size: $unpadded_size:expr, align: $unpadded_align:expr }
+ => padded { size: $padded_size:expr, align: $padded_align:expr }) => {
+ let unpadded = DstLayout {
+ align: NonZeroUsize::new($unpadded_align).unwrap(),
+ size_info: SizeInfo::Sized { size: $unpadded_size },
+ statically_shallow_unpadded: false,
+ };
+ let padded = unpadded.pad_to_align();
+
+ assert_eq!(
+ padded,
+ DstLayout {
+ align: NonZeroUsize::new($padded_align).unwrap(),
+ size_info: SizeInfo::Sized { size: $padded_size },
+ statically_shallow_unpadded: false,
+ }
+ );
+ };
+ }
+
+ test!(unpadded { size: 0, align: 4 } => padded { size: 0, align: 4 });
+ test!(unpadded { size: 1, align: 4 } => padded { size: 4, align: 4 });
+ test!(unpadded { size: 2, align: 4 } => padded { size: 4, align: 4 });
+ test!(unpadded { size: 3, align: 4 } => padded { size: 4, align: 4 });
+ test!(unpadded { size: 4, align: 4 } => padded { size: 4, align: 4 });
+ test!(unpadded { size: 5, align: 4 } => padded { size: 8, align: 4 });
+ test!(unpadded { size: 6, align: 4 } => padded { size: 8, align: 4 });
+ test!(unpadded { size: 7, align: 4 } => padded { size: 8, align: 4 });
+ test!(unpadded { size: 8, align: 4 } => padded { size: 8, align: 4 });
+
+ let current_max_align = DstLayout::CURRENT_MAX_ALIGN.get();
+
+ test!(unpadded { size: 1, align: current_max_align }
+ => padded { size: current_max_align, align: current_max_align });
+
+ test!(unpadded { size: current_max_align + 1, align: current_max_align }
+ => padded { size: current_max_align * 2, align: current_max_align });
+ }
+
+ /// Tests that calling `pad_to_align` on a DST `DstLayout` is a no-op.
+ #[test]
+ fn test_dst_layout_pad_to_align_with_dst() {
+ for align in (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap()) {
+ for offset in 0..10 {
+ for elem_size in 0..10 {
+ let layout = DstLayout {
+ align,
+ size_info: SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }),
+ statically_shallow_unpadded: false,
+ };
+ assert_eq!(layout.pad_to_align(), layout);
+ }
+ }
+ }
+ }
+
+ // This test takes a long time when running under Miri, so we skip it in
+ // that case. This is acceptable because this is a logic test that doesn't
+ // attempt to expose UB.
+ #[test]
+ #[cfg_attr(miri, ignore)]
+ fn test_validate_cast_and_convert_metadata() {
+ #[allow(non_local_definitions)]
+ impl From<usize> for SizeInfo {
+ fn from(size: usize) -> SizeInfo {
+ SizeInfo::Sized { size }
+ }
+ }
+
+ #[allow(non_local_definitions)]
+ impl From<(usize, usize)> for SizeInfo {
+ fn from((offset, elem_size): (usize, usize)) -> SizeInfo {
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
+ }
+ }
+
+ fn layout<S: Into<SizeInfo>>(s: S, align: usize) -> DstLayout {
+ DstLayout {
+ size_info: s.into(),
+ align: NonZeroUsize::new(align).unwrap(),
+ statically_shallow_unpadded: false,
+ }
+ }
+
+ /// This macro accepts arguments in the form of:
+ ///
+ /// layout(_, _).validate(_, _, _), Ok(Some((_, _)))
+ /// | | | | | | |
+ /// size ---------+ | | | | | |
+ /// align -----------+ | | | | |
+ /// addr ------------------------+ | | | |
+ /// bytes_len ----------------------+ | | |
+ /// cast_type -------------------------+ | |
+ /// elems ------------------------------------------+ |
+ /// split_at ------------------------------------------+
+ ///
+ /// `.validate` is shorthand for `.validate_cast_and_convert_metadata`
+ /// for brevity.
+ ///
+ /// Each argument can either be an iterator or a wildcard. Each
+ /// wildcarded variable is implicitly replaced by an iterator over a
+ /// representative sample of values for that variable. Each `test!`
+ /// invocation iterates over every combination of values provided by
+ /// each variable's iterator (ie, the cartesian product) and validates
+ /// that the results are expected.
+ ///
+ /// The final argument uses the same syntax, but it has a different
+ /// meaning:
+ /// - If it is `Ok(pat)`, then the pattern `pat` is supplied to
+ /// a matching assert to validate the computed result for each
+ /// combination of input values.
+ /// - If it is `Err(Some(msg) | None)`, then `test!` validates that the
+ /// call to `validate_cast_and_convert_metadata` panics with the given
+ /// panic message or, if the current Rust toolchain version is too
+ /// early to support panicking in `const fn`s, panics with *some*
+ /// message. In the latter case, the `const_panic!` macro is used,
+ /// which emits code which causes a non-panicking error at const eval
+ /// time, but which does panic when invoked at runtime. Thus, it is
+ /// merely difficult to predict the *value* of this panic. We deem
+ /// that testing against the real panic strings on stable and nightly
+ /// toolchains is enough to ensure correctness.
+ ///
+ /// Note that the meta-variables that match these variables have the
+ /// `tt` type, and some valid expressions are not valid `tt`s (such as
+ /// `a..b`). In this case, wrap the expression in parentheses, and it
+ /// will become valid `tt`.
+ macro_rules! test {
+ (
+ layout($size:tt, $align:tt)
+ .validate($addr:tt, $bytes_len:tt, $cast_type:tt), $expect:pat $(,)?
+ ) => {
+ itertools::iproduct!(
+ test!(@generate_size $size),
+ test!(@generate_align $align),
+ test!(@generate_usize $addr),
+ test!(@generate_usize $bytes_len),
+ test!(@generate_cast_type $cast_type)
+ ).for_each(|(size_info, align, addr, bytes_len, cast_type)| {
+ // Temporarily disable the panic hook installed by the test
+ // harness. If we don't do this, all panic messages will be
+ // kept in an internal log. On its own, this isn't a
+ // problem, but if a non-caught panic ever happens (ie, in
+ // code later in this test not in this macro), all of the
+ // previously-buffered messages will be dumped, hiding the
+ // real culprit.
+ let previous_hook = std::panic::take_hook();
+ // I don't understand why, but this seems to be required in
+ // addition to the previous line.
+ std::panic::set_hook(Box::new(|_| {}));
+ let actual = std::panic::catch_unwind(|| {
+ layout(size_info, align).validate_cast_and_convert_metadata(addr, bytes_len, cast_type)
+ }).map_err(|d| {
+ let msg = d.downcast::<&'static str>().ok().map(|s| *s.as_ref());
+ assert!(msg.is_some() || cfg!(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0), "non-string panic messages are not permitted when usage of panic in const fn is enabled");
+ msg
+ });
+ std::panic::set_hook(previous_hook);
+
+ assert!(
+ matches!(actual, $expect),
+ "layout({:?}, {}).validate_cast_and_convert_metadata({}, {}, {:?})" ,size_info, align, addr, bytes_len, cast_type
+ );
+ });
+ };
+ (@generate_usize _) => { 0..8 };
+ // Generate sizes for both Sized and !Sized types.
+ (@generate_size _) => {
+ test!(@generate_size (_)).chain(test!(@generate_size (_, _)))
+ };
+ // Generate sizes for both Sized and !Sized types by chaining
+ // specified iterators for each.
+ (@generate_size ($sized_sizes:tt | $unsized_sizes:tt)) => {
+ test!(@generate_size ($sized_sizes)).chain(test!(@generate_size $unsized_sizes))
+ };
+ // Generate sizes for Sized types.
+ (@generate_size (_)) => { test!(@generate_size (0..8)) };
+ (@generate_size ($sizes:expr)) => { $sizes.into_iter().map(Into::<SizeInfo>::into) };
+ // Generate sizes for !Sized types.
+ (@generate_size ($min_sizes:tt, $elem_sizes:tt)) => {
+ itertools::iproduct!(
+ test!(@generate_min_size $min_sizes),
+ test!(@generate_elem_size $elem_sizes)
+ ).map(Into::<SizeInfo>::into)
+ };
+ (@generate_fixed_size _) => { (0..8).into_iter().map(Into::<SizeInfo>::into) };
+ (@generate_min_size _) => { 0..8 };
+ (@generate_elem_size _) => { 1..8 };
+ (@generate_align _) => { [1, 2, 4, 8, 16] };
+ (@generate_opt_usize _) => { [None].into_iter().chain((0..8).map(Some).into_iter()) };
+ (@generate_cast_type _) => { [CastType::Prefix, CastType::Suffix] };
+ (@generate_cast_type $variant:ident) => { [CastType::$variant] };
+ // Some expressions need to be wrapped in parentheses in order to be
+ // valid `tt`s (required by the top match pattern). See the comment
+ // below for more details. This arm removes these parentheses to
+ // avoid generating an `unused_parens` warning.
+ (@$_:ident ($vals:expr)) => { $vals };
+ (@$_:ident $vals:expr) => { $vals };
+ }
+
+ const EVENS: [usize; 8] = [0, 2, 4, 6, 8, 10, 12, 14];
+ const ODDS: [usize; 8] = [1, 3, 5, 7, 9, 11, 13, 15];
+
+ // base_size is too big for the memory region.
+ test!(
+ layout(((1..8) | ((1..8), (1..8))), _).validate([0], [0], _),
+ Ok(Err(MetadataCastError::Size))
+ );
+ test!(
+ layout(((2..8) | ((2..8), (2..8))), _).validate([0], [1], Prefix),
+ Ok(Err(MetadataCastError::Size))
+ );
+ test!(
+ layout(((2..8) | ((2..8), (2..8))), _).validate([0x1000_0000 - 1], [1], Suffix),
+ Ok(Err(MetadataCastError::Size))
+ );
+
+ // addr is unaligned for prefix cast
+ test!(layout(_, [2]).validate(ODDS, _, Prefix), Ok(Err(MetadataCastError::Alignment)));
+ test!(layout(_, [2]).validate(ODDS, _, Prefix), Ok(Err(MetadataCastError::Alignment)));
+
+ // addr is aligned, but end of buffer is unaligned for suffix cast
+ test!(layout(_, [2]).validate(EVENS, ODDS, Suffix), Ok(Err(MetadataCastError::Alignment)));
+ test!(layout(_, [2]).validate(EVENS, ODDS, Suffix), Ok(Err(MetadataCastError::Alignment)));
+
+ // Unfortunately, these constants cannot easily be used in the
+ // implementation of `validate_cast_and_convert_metadata`, since
+ // `panic!` consumes a string literal, not an expression.
+ //
+ // It's important that these messages be in a separate module. If they
+ // were at the function's top level, we'd pass them to `test!` as, e.g.,
+ // `Err(TRAILING)`, which would run into a subtle Rust footgun - the
+ // `TRAILING` identifier would be treated as a pattern to match rather
+ // than a value to check for equality.
+ mod msgs {
+ pub(super) const TRAILING: &str =
+ "attempted to cast to slice type with zero-sized element";
+ pub(super) const OVERFLOW: &str = "`addr` + `bytes_len` > usize::MAX";
+ }
+
+ // casts with ZST trailing element types are unsupported
+ test!(layout((_, [0]), _).validate(_, _, _), Err(Some(msgs::TRAILING) | None),);
+
+ // addr + bytes_len must not overflow usize
+ test!(layout(_, _).validate([usize::MAX], (1..100), _), Err(Some(msgs::OVERFLOW) | None));
+ test!(layout(_, _).validate((1..100), [usize::MAX], _), Err(Some(msgs::OVERFLOW) | None));
+ test!(
+ layout(_, _).validate(
+ [usize::MAX / 2 + 1, usize::MAX],
+ [usize::MAX / 2 + 1, usize::MAX],
+ _
+ ),
+ Err(Some(msgs::OVERFLOW) | None)
+ );
+
+ // Validates that `validate_cast_and_convert_metadata` satisfies its own
+ // documented safety postconditions, and also a few other properties
+ // that aren't documented but we want to guarantee anyway.
+ fn validate_behavior(
+ (layout, addr, bytes_len, cast_type): (DstLayout, usize, usize, CastType),
+ ) {
+ if let Ok((elems, split_at)) =
+ layout.validate_cast_and_convert_metadata(addr, bytes_len, cast_type)
+ {
+ let (size_info, align) = (layout.size_info, layout.align);
+ let debug_str = format!(
+ "layout({:?}, {}).validate_cast_and_convert_metadata({}, {}, {:?}) => ({}, {})",
+ size_info, align, addr, bytes_len, cast_type, elems, split_at
+ );
+
+ // If this is a sized type (no trailing slice), then `elems` is
+ // meaningless, but in practice we set it to 0. Callers are not
+ // allowed to rely on this, but a lot of math is nicer if
+ // they're able to, and some callers might accidentally do that.
+ let sized = matches!(layout.size_info, SizeInfo::Sized { .. });
+ assert!(!(sized && elems != 0), "{}", debug_str);
+
+ let resulting_size = match layout.size_info {
+ SizeInfo::Sized { size } => size,
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
+ let padded_size = |elems| {
+ let without_padding = offset + elems * elem_size;
+ without_padding + util::padding_needed_for(without_padding, align)
+ };
+
+ let resulting_size = padded_size(elems);
+ // Test that `validate_cast_and_convert_metadata`
+ // computed the largest possible value that fits in the
+ // given range.
+ assert!(padded_size(elems + 1) > bytes_len, "{}", debug_str);
+ resulting_size
+ }
+ };
+
+ // Test safety postconditions guaranteed by
+ // `validate_cast_and_convert_metadata`.
+ assert!(resulting_size <= bytes_len, "{}", debug_str);
+ match cast_type {
+ CastType::Prefix => {
+ assert_eq!(addr % align, 0, "{}", debug_str);
+ assert_eq!(resulting_size, split_at, "{}", debug_str);
+ }
+ CastType::Suffix => {
+ assert_eq!(split_at, bytes_len - resulting_size, "{}", debug_str);
+ assert_eq!((addr + split_at) % align, 0, "{}", debug_str);
+ }
+ }
+ } else {
+ let min_size = match layout.size_info {
+ SizeInfo::Sized { size } => size,
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, .. }) => {
+ offset + util::padding_needed_for(offset, layout.align)
+ }
+ };
+
+ // If a cast is invalid, it is either because...
+ // 1. there are insufficient bytes at the given region for type:
+ let insufficient_bytes = bytes_len < min_size;
+ // 2. performing the cast would misalign type:
+ let base = match cast_type {
+ CastType::Prefix => 0,
+ CastType::Suffix => bytes_len,
+ };
+ let misaligned = (base + addr) % layout.align != 0;
+
+ assert!(insufficient_bytes || misaligned);
+ }
+ }
+
+ let sizes = 0..8;
+ let elem_sizes = 1..8;
+ let size_infos = sizes
+ .clone()
+ .map(Into::<SizeInfo>::into)
+ .chain(itertools::iproduct!(sizes, elem_sizes).map(Into::<SizeInfo>::into));
+ let layouts = itertools::iproduct!(size_infos, [1, 2, 4, 8, 16, 32])
+ .filter(|(size_info, align)| !matches!(size_info, SizeInfo::Sized { size } if size % align != 0))
+ .map(|(size_info, align)| layout(size_info, align));
+ itertools::iproduct!(layouts, 0..8, 0..8, [CastType::Prefix, CastType::Suffix])
+ .for_each(validate_behavior);
+ }
+
+ #[test]
+ #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
+ fn test_validate_rust_layout() {
+ use core::{
+ convert::TryInto as _,
+ ptr::{self, NonNull},
+ };
+
+ use crate::util::testutil::*;
+
+ // This test synthesizes pointers with various metadata and uses Rust's
+ // built-in APIs to confirm that Rust makes decisions about type layout
+ // which are consistent with what we believe is guaranteed by the
+ // language. If this test fails, it doesn't just mean our code is wrong
+ // - it means we're misunderstanding the language's guarantees.
+
+ #[derive(Debug)]
+ struct MacroArgs {
+ offset: usize,
+ align: NonZeroUsize,
+ elem_size: Option<usize>,
+ }
+
+ /// # Safety
+ ///
+ /// `test` promises to only call `addr_of_slice_field` on a `NonNull<T>`
+ /// which points to a valid `T`.
+ ///
+ /// `with_elems` must produce a pointer which points to a valid `T`.
+ fn test<T: ?Sized, W: Fn(usize) -> NonNull<T>>(
+ args: MacroArgs,
+ with_elems: W,
+ addr_of_slice_field: Option<fn(NonNull<T>) -> NonNull<u8>>,
+ ) {
+ let dst = args.elem_size.is_some();
+ let layout = {
+ let size_info = match args.elem_size {
+ Some(elem_size) => {
+ SizeInfo::SliceDst(TrailingSliceLayout { offset: args.offset, elem_size })
+ }
+ None => SizeInfo::Sized {
+ // Rust only supports types whose sizes are a multiple
+ // of their alignment. If the macro created a type like
+ // this:
+ //
+ // #[repr(C, align(2))]
+ // struct Foo([u8; 1]);
+ //
+ // ...then Rust will automatically round the type's size
+ // up to 2.
+ size: args.offset + util::padding_needed_for(args.offset, args.align),
+ },
+ };
+ DstLayout { size_info, align: args.align, statically_shallow_unpadded: false }
+ };
+
+ for elems in 0..128 {
+ let ptr = with_elems(elems);
+
+ if let Some(addr_of_slice_field) = addr_of_slice_field {
+ let slc_field_ptr = addr_of_slice_field(ptr).as_ptr();
+ // SAFETY: Both `slc_field_ptr` and `ptr` are pointers to
+ // the same valid Rust object.
+ // Work around https://github.com/rust-lang/rust-clippy/issues/12280
+ let offset: usize =
+ unsafe { slc_field_ptr.byte_offset_from(ptr.as_ptr()).try_into().unwrap() };
+ assert_eq!(offset, args.offset);
+ }
+
+ // SAFETY: `ptr` points to a valid `T`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ let (size, align) = unsafe {
+ (mem::size_of_val_raw(ptr.as_ptr()), mem::align_of_val_raw(ptr.as_ptr()))
+ };
+
+ // Avoid expensive allocation when running under Miri.
+ let assert_msg = if !cfg!(miri) {
+ format!("\n{:?}\nsize:{}, align:{}", args, size, align)
+ } else {
+ String::new()
+ };
+
+ let without_padding =
+ args.offset + args.elem_size.map(|elem_size| elems * elem_size).unwrap_or(0);
+ assert!(size >= without_padding, "{}", assert_msg);
+ assert_eq!(align, args.align.get(), "{}", assert_msg);
+
+ // This encodes the most important part of the test: our
+ // understanding of how Rust determines the layout of repr(C)
+ // types. Sized repr(C) types are trivial, but DST types have
+ // some subtlety. Note that:
+ // - For sized types, `without_padding` is just the size of the
+ // type that we constructed for `Foo`. Since we may have
+ // requested a larger alignment, `Foo` may actually be larger
+ // than this, hence `padding_needed_for`.
+ // - For unsized types, `without_padding` is dynamically
+ // computed from the offset, the element size, and element
+ // count. We expect that the size of the object should be
+ // `offset + elem_size * elems` rounded up to the next
+ // alignment.
+ let expected_size =
+ without_padding + util::padding_needed_for(without_padding, args.align);
+ assert_eq!(expected_size, size, "{}", assert_msg);
+
+ // For zero-sized element types,
+ // `validate_cast_and_convert_metadata` just panics, so we skip
+ // testing those types.
+ if args.elem_size.map(|elem_size| elem_size > 0).unwrap_or(true) {
+ let addr = ptr.addr().get();
+ let (got_elems, got_split_at) = layout
+ .validate_cast_and_convert_metadata(addr, size, CastType::Prefix)
+ .unwrap();
+ // Avoid expensive allocation when running under Miri.
+ let assert_msg = if !cfg!(miri) {
+ format!(
+ "{}\nvalidate_cast_and_convert_metadata({}, {})",
+ assert_msg, addr, size,
+ )
+ } else {
+ String::new()
+ };
+ assert_eq!(got_split_at, size, "{}", assert_msg);
+ if dst {
+ assert!(got_elems >= elems, "{}", assert_msg);
+ if got_elems != elems {
+ // If `validate_cast_and_convert_metadata`
+ // returned more elements than `elems`, that
+ // means that `elems` is not the maximum number
+ // of elements that can fit in `size` - in other
+ // words, there is enough padding at the end of
+ // the value to fit at least one more element.
+ // If we use this metadata to synthesize a
+ // pointer, despite having a different element
+ // count, we still expect it to have the same
+ // size.
+ let got_ptr = with_elems(got_elems);
+ // SAFETY: `got_ptr` is a pointer to a valid `T`.
+ let size_of_got_ptr = unsafe { mem::size_of_val_raw(got_ptr.as_ptr()) };
+ assert_eq!(size_of_got_ptr, size, "{}", assert_msg);
+ }
+ } else {
+ // For sized casts, the returned element value is
+ // technically meaningless, and we don't guarantee any
+ // particular value. In practice, it's always zero.
+ assert_eq!(got_elems, 0, "{}", assert_msg)
+ }
+ }
+ }
+ }
+
+ macro_rules! validate_against_rust {
+ ($offset:literal, $align:literal $(, $elem_size:literal)?) => {{
+ #[repr(C, align($align))]
+ struct Foo([u8; $offset]$(, [[u8; $elem_size]])?);
+
+ let args = MacroArgs {
+ offset: $offset,
+ align: $align.try_into().unwrap(),
+ elem_size: {
+ #[allow(unused)]
+ let ret = None::<usize>;
+ $(let ret = Some($elem_size);)?
+ ret
+ }
+ };
+
+ #[repr(C, align($align))]
+ struct FooAlign;
+ // Create an aligned buffer to use in order to synthesize
+ // pointers to `Foo`. We don't ever load values from these
+ // pointers - we just do arithmetic on them - so having a "real"
+ // block of memory as opposed to a validly-aligned-but-dangling
+ // pointer is only necessary to make Miri happy since we run it
+ // with "strict provenance" checking enabled.
+ let aligned_buf = Align::<_, FooAlign>::new([0u8; 1024]);
+ let with_elems = |elems| {
+ let slc = NonNull::slice_from_raw_parts(NonNull::from(&aligned_buf.t), elems);
+ #[allow(clippy::as_conversions)]
+ NonNull::new(slc.as_ptr() as *mut Foo).unwrap()
+ };
+ let addr_of_slice_field = {
+ #[allow(unused)]
+ let f = None::<fn(NonNull<Foo>) -> NonNull<u8>>;
+ $(
+ // SAFETY: `test` promises to only call `f` with a `ptr`
+ // to a valid `Foo`.
+ let f: Option<fn(NonNull<Foo>) -> NonNull<u8>> = Some(|ptr: NonNull<Foo>| unsafe {
+ NonNull::new(ptr::addr_of_mut!((*ptr.as_ptr()).1)).unwrap().cast::<u8>()
+ });
+ let _ = $elem_size;
+ )?
+ f
+ };
+
+ test::<Foo, _>(args, with_elems, addr_of_slice_field);
+ }};
+ }
+
+ // Every permutation of:
+ // - offset in [0, 4]
+ // - align in [1, 16]
+ // - elem_size in [0, 4] (plus no elem_size)
+ validate_against_rust!(0, 1);
+ validate_against_rust!(0, 1, 0);
+ validate_against_rust!(0, 1, 1);
+ validate_against_rust!(0, 1, 2);
+ validate_against_rust!(0, 1, 3);
+ validate_against_rust!(0, 1, 4);
+ validate_against_rust!(0, 2);
+ validate_against_rust!(0, 2, 0);
+ validate_against_rust!(0, 2, 1);
+ validate_against_rust!(0, 2, 2);
+ validate_against_rust!(0, 2, 3);
+ validate_against_rust!(0, 2, 4);
+ validate_against_rust!(0, 4);
+ validate_against_rust!(0, 4, 0);
+ validate_against_rust!(0, 4, 1);
+ validate_against_rust!(0, 4, 2);
+ validate_against_rust!(0, 4, 3);
+ validate_against_rust!(0, 4, 4);
+ validate_against_rust!(0, 8);
+ validate_against_rust!(0, 8, 0);
+ validate_against_rust!(0, 8, 1);
+ validate_against_rust!(0, 8, 2);
+ validate_against_rust!(0, 8, 3);
+ validate_against_rust!(0, 8, 4);
+ validate_against_rust!(0, 16);
+ validate_against_rust!(0, 16, 0);
+ validate_against_rust!(0, 16, 1);
+ validate_against_rust!(0, 16, 2);
+ validate_against_rust!(0, 16, 3);
+ validate_against_rust!(0, 16, 4);
+ validate_against_rust!(1, 1);
+ validate_against_rust!(1, 1, 0);
+ validate_against_rust!(1, 1, 1);
+ validate_against_rust!(1, 1, 2);
+ validate_against_rust!(1, 1, 3);
+ validate_against_rust!(1, 1, 4);
+ validate_against_rust!(1, 2);
+ validate_against_rust!(1, 2, 0);
+ validate_against_rust!(1, 2, 1);
+ validate_against_rust!(1, 2, 2);
+ validate_against_rust!(1, 2, 3);
+ validate_against_rust!(1, 2, 4);
+ validate_against_rust!(1, 4);
+ validate_against_rust!(1, 4, 0);
+ validate_against_rust!(1, 4, 1);
+ validate_against_rust!(1, 4, 2);
+ validate_against_rust!(1, 4, 3);
+ validate_against_rust!(1, 4, 4);
+ validate_against_rust!(1, 8);
+ validate_against_rust!(1, 8, 0);
+ validate_against_rust!(1, 8, 1);
+ validate_against_rust!(1, 8, 2);
+ validate_against_rust!(1, 8, 3);
+ validate_against_rust!(1, 8, 4);
+ validate_against_rust!(1, 16);
+ validate_against_rust!(1, 16, 0);
+ validate_against_rust!(1, 16, 1);
+ validate_against_rust!(1, 16, 2);
+ validate_against_rust!(1, 16, 3);
+ validate_against_rust!(1, 16, 4);
+ validate_against_rust!(2, 1);
+ validate_against_rust!(2, 1, 0);
+ validate_against_rust!(2, 1, 1);
+ validate_against_rust!(2, 1, 2);
+ validate_against_rust!(2, 1, 3);
+ validate_against_rust!(2, 1, 4);
+ validate_against_rust!(2, 2);
+ validate_against_rust!(2, 2, 0);
+ validate_against_rust!(2, 2, 1);
+ validate_against_rust!(2, 2, 2);
+ validate_against_rust!(2, 2, 3);
+ validate_against_rust!(2, 2, 4);
+ validate_against_rust!(2, 4);
+ validate_against_rust!(2, 4, 0);
+ validate_against_rust!(2, 4, 1);
+ validate_against_rust!(2, 4, 2);
+ validate_against_rust!(2, 4, 3);
+ validate_against_rust!(2, 4, 4);
+ validate_against_rust!(2, 8);
+ validate_against_rust!(2, 8, 0);
+ validate_against_rust!(2, 8, 1);
+ validate_against_rust!(2, 8, 2);
+ validate_against_rust!(2, 8, 3);
+ validate_against_rust!(2, 8, 4);
+ validate_against_rust!(2, 16);
+ validate_against_rust!(2, 16, 0);
+ validate_against_rust!(2, 16, 1);
+ validate_against_rust!(2, 16, 2);
+ validate_against_rust!(2, 16, 3);
+ validate_against_rust!(2, 16, 4);
+ validate_against_rust!(3, 1);
+ validate_against_rust!(3, 1, 0);
+ validate_against_rust!(3, 1, 1);
+ validate_against_rust!(3, 1, 2);
+ validate_against_rust!(3, 1, 3);
+ validate_against_rust!(3, 1, 4);
+ validate_against_rust!(3, 2);
+ validate_against_rust!(3, 2, 0);
+ validate_against_rust!(3, 2, 1);
+ validate_against_rust!(3, 2, 2);
+ validate_against_rust!(3, 2, 3);
+ validate_against_rust!(3, 2, 4);
+ validate_against_rust!(3, 4);
+ validate_against_rust!(3, 4, 0);
+ validate_against_rust!(3, 4, 1);
+ validate_against_rust!(3, 4, 2);
+ validate_against_rust!(3, 4, 3);
+ validate_against_rust!(3, 4, 4);
+ validate_against_rust!(3, 8);
+ validate_against_rust!(3, 8, 0);
+ validate_against_rust!(3, 8, 1);
+ validate_against_rust!(3, 8, 2);
+ validate_against_rust!(3, 8, 3);
+ validate_against_rust!(3, 8, 4);
+ validate_against_rust!(3, 16);
+ validate_against_rust!(3, 16, 0);
+ validate_against_rust!(3, 16, 1);
+ validate_against_rust!(3, 16, 2);
+ validate_against_rust!(3, 16, 3);
+ validate_against_rust!(3, 16, 4);
+ validate_against_rust!(4, 1);
+ validate_against_rust!(4, 1, 0);
+ validate_against_rust!(4, 1, 1);
+ validate_against_rust!(4, 1, 2);
+ validate_against_rust!(4, 1, 3);
+ validate_against_rust!(4, 1, 4);
+ validate_against_rust!(4, 2);
+ validate_against_rust!(4, 2, 0);
+ validate_against_rust!(4, 2, 1);
+ validate_against_rust!(4, 2, 2);
+ validate_against_rust!(4, 2, 3);
+ validate_against_rust!(4, 2, 4);
+ validate_against_rust!(4, 4);
+ validate_against_rust!(4, 4, 0);
+ validate_against_rust!(4, 4, 1);
+ validate_against_rust!(4, 4, 2);
+ validate_against_rust!(4, 4, 3);
+ validate_against_rust!(4, 4, 4);
+ validate_against_rust!(4, 8);
+ validate_against_rust!(4, 8, 0);
+ validate_against_rust!(4, 8, 1);
+ validate_against_rust!(4, 8, 2);
+ validate_against_rust!(4, 8, 3);
+ validate_against_rust!(4, 8, 4);
+ validate_against_rust!(4, 16);
+ validate_against_rust!(4, 16, 0);
+ validate_against_rust!(4, 16, 1);
+ validate_against_rust!(4, 16, 2);
+ validate_against_rust!(4, 16, 3);
+ validate_against_rust!(4, 16, 4);
+ }
+}
+
+#[cfg(kani)]
+mod proofs {
+ use core::alloc::Layout;
+
+ use super::*;
+
+ impl kani::Arbitrary for DstLayout {
+ fn any() -> Self {
+ let align: NonZeroUsize = kani::any();
+ let size_info: SizeInfo = kani::any();
+
+ kani::assume(align.is_power_of_two());
+ kani::assume(align < DstLayout::THEORETICAL_MAX_ALIGN);
+
+ // For testing purposes, we most care about instantiations of
+ // `DstLayout` that can correspond to actual Rust types. We use
+ // `Layout` to verify that our `DstLayout` satisfies the validity
+ // conditions of Rust layouts.
+ kani::assume(
+ match size_info {
+ SizeInfo::Sized { size } => Layout::from_size_align(size, align.get()),
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size: _ }) => {
+ // `SliceDst` cannot encode an exact size, but we know
+ // it is at least `offset` bytes.
+ Layout::from_size_align(offset, align.get())
+ }
+ }
+ .is_ok(),
+ );
+
+ Self { align: align, size_info: size_info, statically_shallow_unpadded: kani::any() }
+ }
+ }
+
+ impl kani::Arbitrary for SizeInfo {
+ fn any() -> Self {
+ let is_sized: bool = kani::any();
+
+ match is_sized {
+ true => {
+ let size: usize = kani::any();
+
+ kani::assume(size <= DstLayout::MAX_SIZE);
+
+ SizeInfo::Sized { size }
+ }
+ false => SizeInfo::SliceDst(kani::any()),
+ }
+ }
+ }
+
+ impl kani::Arbitrary for TrailingSliceLayout {
+ fn any() -> Self {
+ let elem_size: usize = kani::any();
+ let offset: usize = kani::any();
+
+ kani::assume(elem_size < DstLayout::MAX_SIZE);
+ kani::assume(offset < DstLayout::MAX_SIZE);
+
+ TrailingSliceLayout { elem_size, offset }
+ }
+ }
+
+ #[kani::proof]
+ fn prove_requires_dynamic_padding() {
+ let layout: DstLayout = kani::any();
+
+ let SizeInfo::SliceDst(size_info) = layout.size_info else {
+ kani::assume(false);
+ loop {}
+ };
+
+ let meta: usize = kani::any();
+
+ let Some(trailing_slice_size) = size_info.elem_size.checked_mul(meta) else {
+ // The `trailing_slice_size` exceeds `usize::MAX`; `meta` is invalid.
+ kani::assume(false);
+ loop {}
+ };
+
+ let Some(unpadded_size) = size_info.offset.checked_add(trailing_slice_size) else {
+ // The `unpadded_size` exceeds `usize::MAX`; `meta`` is invalid.
+ kani::assume(false);
+ loop {}
+ };
+
+ if unpadded_size >= DstLayout::MAX_SIZE {
+ // The `unpadded_size` exceeds `isize::MAX`; `meta` is invalid.
+ kani::assume(false);
+ loop {}
+ }
+
+ let trailing_padding = util::padding_needed_for(unpadded_size, layout.align);
+
+ if !layout.requires_dynamic_padding() {
+ assert!(trailing_padding == 0);
+ }
+ }
+
+ #[kani::proof]
+ fn prove_dst_layout_extend() {
+ use crate::util::{max, min, padding_needed_for};
+
+ let base: DstLayout = kani::any();
+ let field: DstLayout = kani::any();
+ let packed: Option<NonZeroUsize> = kani::any();
+
+ if let Some(max_align) = packed {
+ kani::assume(max_align.is_power_of_two());
+ kani::assume(base.align <= max_align);
+ }
+
+ // The base can only be extended if it's sized.
+ kani::assume(matches!(base.size_info, SizeInfo::Sized { .. }));
+ let base_size = if let SizeInfo::Sized { size } = base.size_info {
+ size
+ } else {
+ unreachable!();
+ };
+
+ // Under the above conditions, `DstLayout::extend` will not panic.
+ let composite = base.extend(field, packed);
+
+ // The field's alignment is clamped by `max_align` (i.e., the
+ // `packed` attribute, if any) [1].
+ //
+ // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
+ //
+ // The alignments of each field, for the purpose of positioning
+ // fields, is the smaller of the specified alignment and the
+ // alignment of the field's type.
+ let field_align = min(field.align, packed.unwrap_or(DstLayout::THEORETICAL_MAX_ALIGN));
+
+ // The struct's alignment is the maximum of its previous alignment and
+ // `field_align`.
+ assert_eq!(composite.align, max(base.align, field_align));
+
+ // Compute the minimum amount of inter-field padding needed to
+ // satisfy the field's alignment, and offset of the trailing field.
+ // [1]
+ //
+ // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
+ //
+ // Inter-field padding is guaranteed to be the minimum required in
+ // order to satisfy each field's (possibly altered) alignment.
+ let padding = padding_needed_for(base_size, field_align);
+ let offset = base_size + padding;
+
+ // For testing purposes, we'll also construct `alloc::Layout`
+ // stand-ins for `DstLayout`, and show that `extend` behaves
+ // comparably on both types.
+ let base_analog = Layout::from_size_align(base_size, base.align.get()).unwrap();
+
+ match field.size_info {
+ SizeInfo::Sized { size: field_size } => {
+ if let SizeInfo::Sized { size: composite_size } = composite.size_info {
+ // If the trailing field is sized, the resulting layout will
+ // be sized. Its size will be the sum of the preceding
+ // layout, the size of the new field, and the size of
+ // inter-field padding between the two.
+ assert_eq!(composite_size, offset + field_size);
+
+ let field_analog =
+ Layout::from_size_align(field_size, field_align.get()).unwrap();
+
+ if let Ok((actual_composite, actual_offset)) = base_analog.extend(field_analog)
+ {
+ assert_eq!(actual_offset, offset);
+ assert_eq!(actual_composite.size(), composite_size);
+ assert_eq!(actual_composite.align(), composite.align.get());
+ } else {
+ // An error here reflects that composite of `base`
+ // and `field` cannot correspond to a real Rust type
+ // fragment, because such a fragment would violate
+ // the basic invariants of a valid Rust layout. At
+ // the time of writing, `DstLayout` is a little more
+ // permissive than `Layout`, so we don't assert
+ // anything in this branch (e.g., unreachability).
+ }
+ } else {
+ panic!("The composite of two sized layouts must be sized.")
+ }
+ }
+ SizeInfo::SliceDst(TrailingSliceLayout {
+ offset: field_offset,
+ elem_size: field_elem_size,
+ }) => {
+ if let SizeInfo::SliceDst(TrailingSliceLayout {
+ offset: composite_offset,
+ elem_size: composite_elem_size,
+ }) = composite.size_info
+ {
+ // The offset of the trailing slice component is the sum
+ // of the offset of the trailing field and the trailing
+ // slice offset within that field.
+ assert_eq!(composite_offset, offset + field_offset);
+ // The elem size is unchanged.
+ assert_eq!(composite_elem_size, field_elem_size);
+
+ let field_analog =
+ Layout::from_size_align(field_offset, field_align.get()).unwrap();
+
+ if let Ok((actual_composite, actual_offset)) = base_analog.extend(field_analog)
+ {
+ assert_eq!(actual_offset, offset);
+ assert_eq!(actual_composite.size(), composite_offset);
+ assert_eq!(actual_composite.align(), composite.align.get());
+ } else {
+ // An error here reflects that composite of `base`
+ // and `field` cannot correspond to a real Rust type
+ // fragment, because such a fragment would violate
+ // the basic invariants of a valid Rust layout. At
+ // the time of writing, `DstLayout` is a little more
+ // permissive than `Layout`, so we don't assert
+ // anything in this branch (e.g., unreachability).
+ }
+ } else {
+ panic!("The extension of a layout with a DST must result in a DST.")
+ }
+ }
+ }
+ }
+
+ #[kani::proof]
+ #[kani::should_panic]
+ fn prove_dst_layout_extend_dst_panics() {
+ let base: DstLayout = kani::any();
+ let field: DstLayout = kani::any();
+ let packed: Option<NonZeroUsize> = kani::any();
+
+ if let Some(max_align) = packed {
+ kani::assume(max_align.is_power_of_two());
+ kani::assume(base.align <= max_align);
+ }
+
+ kani::assume(matches!(base.size_info, SizeInfo::SliceDst(..)));
+
+ let _ = base.extend(field, packed);
+ }
+
+ #[kani::proof]
+ fn prove_dst_layout_pad_to_align() {
+ use crate::util::padding_needed_for;
+
+ let layout: DstLayout = kani::any();
+
+ let padded = layout.pad_to_align();
+
+ // Calling `pad_to_align` does not alter the `DstLayout`'s alignment.
+ assert_eq!(padded.align, layout.align);
+
+ if let SizeInfo::Sized { size: unpadded_size } = layout.size_info {
+ if let SizeInfo::Sized { size: padded_size } = padded.size_info {
+ // If the layout is sized, it will remain sized after padding is
+ // added. Its sum will be its unpadded size and the size of the
+ // trailing padding needed to satisfy its alignment
+ // requirements.
+ let padding = padding_needed_for(unpadded_size, layout.align);
+ assert_eq!(padded_size, unpadded_size + padding);
+
+ // Prove that calling `DstLayout::pad_to_align` behaves
+ // identically to `Layout::pad_to_align`.
+ let layout_analog =
+ Layout::from_size_align(unpadded_size, layout.align.get()).unwrap();
+ let padded_analog = layout_analog.pad_to_align();
+ assert_eq!(padded_analog.align(), layout.align.get());
+ assert_eq!(padded_analog.size(), padded_size);
+ } else {
+ panic!("The padding of a sized layout must result in a sized layout.")
+ }
+ } else {
+ // If the layout is a DST, padding cannot be statically added.
+ assert_eq!(padded.size_info, layout.size_info);
+ }
+ }
+}
diff --git a/rust/zerocopy/src/lib.rs b/rust/zerocopy/src/lib.rs
new file mode 100644
index 000000000000..89a696f732b8
--- /dev/null
+++ b/rust/zerocopy/src/lib.rs
@@ -0,0 +1,7610 @@
+// Copyright 2018 The Fuchsia Authors
+//
+// Licensed under the 2-Clause BSD License <LICENSE-BSD or
+// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+// After updating the following doc comment, make sure to run the following
+// command to update `README.md` based on its contents:
+//
+// cargo -q run --manifest-path tools/Cargo.toml -p generate-readme > README.md
+
+//! ***<span style="font-size: 140%">Fast, safe, <span
+//! style="color:red;">compile error</span>. Pick two.</span>***
+//!
+//! Zerocopy makes zero-cost memory manipulation effortless. We write `unsafe`
+//! so you don't have to.
+//!
+//! *For an overview of what's changed from zerocopy 0.7, check out our [release
+//! notes][release-notes], which include a step-by-step upgrading guide.*
+//!
+//! *Have questions? Need more out of zerocopy? Submit a [customer request
+//! issue][customer-request-issue] or ask the maintainers on
+//! [GitHub][github-q-a] or [Discord][discord]!*
+//!
+//! [customer-request-issue]: https://github.com/google/zerocopy/issues/new/choose
+//! [release-notes]: https://github.com/google/zerocopy/discussions/1680
+//! [github-q-a]: https://github.com/google/zerocopy/discussions/categories/q-a
+//! [discord]: https://discord.gg/MAvWH2R6zk
+//!
+//! # Overview
+//!
+//! ##### Conversion Traits
+//!
+//! Zerocopy provides four derivable traits for zero-cost conversions:
+//! - [`TryFromBytes`] indicates that a type may safely be converted from
+//! certain byte sequences (conditional on runtime checks)
+//! - [`FromZeros`] indicates that a sequence of zero bytes represents a valid
+//! instance of a type
+//! - [`FromBytes`] indicates that a type may safely be converted from an
+//! arbitrary byte sequence
+//! - [`IntoBytes`] indicates that a type may safely be converted *to* a byte
+//! sequence
+//!
+//! These traits support sized types, slices, and [slice DSTs][slice-dsts].
+//!
+//! [slice-dsts]: KnownLayout#dynamically-sized-types
+//!
+//! ##### Marker Traits
+//!
+//! Zerocopy provides three derivable marker traits that do not provide any
+//! functionality themselves, but are required to call certain methods provided
+//! by the conversion traits:
+//! - [`KnownLayout`] indicates that zerocopy can reason about certain layout
+//! qualities of a type
+//! - [`Immutable`] indicates that a type is free from interior mutability,
+//! except by ownership or an exclusive (`&mut`) borrow
+//! - [`Unaligned`] indicates that a type's alignment requirement is 1
+//!
+//! You should generally derive these marker traits whenever possible.
+//!
+//! ##### Conversion Macros
+//!
+//! Zerocopy provides six macros for safe casting between types:
+//!
+//! - ([`try_`][try_transmute])[`transmute`] (conditionally) converts a value of
+//! one type to a value of another type of the same size
+//! - ([`try_`][try_transmute_mut])[`transmute_mut`] (conditionally) converts a
+//! mutable reference of one type to a mutable reference of another type of
+//! the same size
+//! - ([`try_`][try_transmute_ref])[`transmute_ref`] (conditionally) converts a
+//! mutable or immutable reference of one type to an immutable reference of
+//! another type of the same size
+//!
+//! These macros perform *compile-time* size and alignment checks, meaning that
+//! unconditional casts have zero cost at runtime. Conditional casts do not need
+//! to validate size or alignment runtime, but do need to validate contents.
+//!
+//! These macros cannot be used in generic contexts. For generic conversions,
+//! use the methods defined by the [conversion traits](#conversion-traits).
+//!
+//! ##### Byteorder-Aware Numerics
+//!
+//! Zerocopy provides byte-order aware integer types that support these
+//! conversions; see the [`byteorder`] module. These types are especially useful
+//! for network parsing.
+//!
+//! # Cargo Features
+//!
+//! - **`alloc`**
+//! By default, `zerocopy` is `no_std`. When the `alloc` feature is enabled,
+//! the `alloc` crate is added as a dependency, and some allocation-related
+//! functionality is added.
+//!
+//! - **`std`**
+//! By default, `zerocopy` is `no_std`. When the `std` feature is enabled, the
+//! `std` crate is added as a dependency (ie, `no_std` is disabled), and
+//! support for some `std` types is added. `std` implies `alloc`.
+//!
+//! - **`derive`**
+//! Provides derives for the core marker traits via the `zerocopy-derive`
+//! crate. These derives are re-exported from `zerocopy`, so it is not
+//! necessary to depend on `zerocopy-derive` directly.
+//!
+//! However, you may experience better compile times if you instead directly
+//! depend on both `zerocopy` and `zerocopy-derive` in your `Cargo.toml`,
+//! since doing so will allow Rust to compile these crates in parallel. To do
+//! so, do *not* enable the `derive` feature, and list both dependencies in
+//! your `Cargo.toml` with the same leading non-zero version number; e.g:
+//!
+//! ```toml
+//! [dependencies]
+//! zerocopy = "0.X"
+//! zerocopy-derive = "0.X"
+//! ```
+//!
+//! To avoid the risk of [duplicate import errors][duplicate-import-errors] if
+//! one of your dependencies enables zerocopy's `derive` feature, import
+//! derives as `use zerocopy_derive::*` rather than by name (e.g., `use
+//! zerocopy_derive::FromBytes`).
+//!
+//! - **`simd`**
+//! When the `simd` feature is enabled, `FromZeros`, `FromBytes`, and
+//! `IntoBytes` impls are emitted for all stable SIMD types which exist on the
+//! target platform. Note that the layout of SIMD types is not yet stabilized,
+//! so these impls may be removed in the future if layout changes make them
+//! invalid. For more information, see the Unsafe Code Guidelines Reference
+//! page on the [layout of packed SIMD vectors][simd-layout].
+//!
+//! - **`simd-nightly`**
+//! Enables the `simd` feature and adds support for SIMD types which are only
+//! available on nightly. Since these types are unstable, support for any type
+//! may be removed at any point in the future.
+//!
+//! - **`float-nightly`**
+//! Adds support for the unstable `f16` and `f128` types. These types are
+//! not yet fully implemented and may not be supported on all platforms.
+//!
+//! [duplicate-import-errors]: https://github.com/google/zerocopy/issues/1587
+//! [simd-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html
+//!
+//! # Build Tuning
+//!
+//! ## `--cfg zerocopy_inline_always`
+//!
+//! Upgrades `#[inline]` to `#[inline(always)]` on many of zerocopy's public
+//! functions and methods. This provides a narrowly-scoped alternative that
+//! *may* improve the optimization of hot paths using zerocopy without the broad
+//! compile-time penalties of configuring `codegen-units=1`.
+//!
+//! # Security Ethos
+//!
+//! Zerocopy is expressly designed for use in security-critical contexts. We
+//! strive to ensure that that zerocopy code is sound under Rust's current
+//! memory model, and *any future memory model*. We ensure this by:
+//! - **...not 'guessing' about Rust's semantics.**
+//! We annotate `unsafe` code with a precise rationale for its soundness that
+//! cites a relevant section of Rust's official documentation. When Rust's
+//! documented semantics are unclear, we work with the Rust Operational
+//! Semantics Team to clarify Rust's documentation.
+//! - **...rigorously testing our implementation.**
+//! We run tests using [Miri], ensuring that zerocopy is sound across a wide
+//! array of supported target platforms of varying endianness and pointer
+//! width, and across both current and experimental memory models of Rust.
+//! - **...formally proving the correctness of our implementation.**
+//! We apply formal verification tools like [Kani][kani] to prove zerocopy's
+//! correctness.
+//!
+//! For more information, see our full [soundness policy].
+//!
+//! [Miri]: https://github.com/rust-lang/miri
+//! [Kani]: https://github.com/model-checking/kani
+//! [soundness policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#soundness
+//!
+//! # Relationship to Project Safe Transmute
+//!
+//! [Project Safe Transmute] is an official initiative of the Rust Project to
+//! develop language-level support for safer transmutation. The Project consults
+//! with crates like zerocopy to identify aspects of safer transmutation that
+//! would benefit from compiler support, and has developed an [experimental,
+//! compiler-supported analysis][mcp-transmutability] which determines whether,
+//! for a given type, any value of that type may be soundly transmuted into
+//! another type. Once this functionality is sufficiently mature, zerocopy
+//! intends to replace its internal transmutability analysis (implemented by our
+//! custom derives) with the compiler-supported one. This change will likely be
+//! an implementation detail that is invisible to zerocopy's users.
+//!
+//! Project Safe Transmute will not replace the need for most of zerocopy's
+//! higher-level abstractions. The experimental compiler analysis is a tool for
+//! checking the soundness of `unsafe` code, not a tool to avoid writing
+//! `unsafe` code altogether. For the foreseeable future, crates like zerocopy
+//! will still be required in order to provide higher-level abstractions on top
+//! of the building block provided by Project Safe Transmute.
+//!
+//! [Project Safe Transmute]: https://rust-lang.github.io/rfcs/2835-project-safe-transmute.html
+//! [mcp-transmutability]: https://github.com/rust-lang/compiler-team/issues/411
+//!
+//! # MSRV
+//!
+//! See our [MSRV policy].
+//!
+//! [MSRV policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#msrv
+//!
+//! # Changelog
+//!
+//! Zerocopy uses [GitHub Releases].
+//!
+//! [GitHub Releases]: https://github.com/google/zerocopy/releases
+//!
+//! # Thanks
+//!
+//! Zerocopy is maintained by engineers at Google with help from [many wonderful
+//! contributors][contributors]. Thank you to everyone who has lent a hand in
+//! making Rust a little more secure!
+//!
+//! [contributors]: https://github.com/google/zerocopy/graphs/contributors
+
+// Sometimes we want to use lints which were added after our MSRV.
+// `unknown_lints` is `warn` by default and we deny warnings in CI, so without
+// this attribute, any unknown lint would cause a CI failure when testing with
+// our MSRV.
+#![allow(unknown_lints, non_local_definitions, unreachable_patterns)]
+#![deny(renamed_and_removed_lints)]
+#![deny(
+ anonymous_parameters,
+ deprecated_in_future,
+ late_bound_lifetime_arguments,
+ missing_copy_implementations,
+ missing_debug_implementations,
+ missing_docs,
+ path_statements,
+ patterns_in_fns_without_body,
+ rust_2018_idioms,
+ trivial_numeric_casts,
+ unreachable_pub,
+ unsafe_op_in_unsafe_fn,
+ unused_extern_crates,
+ // We intentionally choose not to deny `unused_qualifications`. When items
+ // are added to the prelude (e.g., `core::mem::size_of`), this has the
+ // consequence of making some uses trigger this lint on the latest toolchain
+ // (e.g., `mem::size_of`), but fixing it (e.g. by replacing with `size_of`)
+ // does not work on older toolchains.
+ //
+ // We tested a more complicated fix in #1413, but ultimately decided that,
+ // since this lint is just a minor style lint, the complexity isn't worth it
+ // - it's fine to occasionally have unused qualifications slip through,
+ // especially since these do not affect our user-facing API in any way.
+ variant_size_differences
+)]
+#![cfg_attr(
+ __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS,
+ deny(fuzzy_provenance_casts, lossy_provenance_casts)
+)]
+#![deny(
+ clippy::all,
+ clippy::alloc_instead_of_core,
+ clippy::arithmetic_side_effects,
+ clippy::as_underscore,
+ clippy::assertions_on_result_states,
+ clippy::as_conversions,
+ clippy::correctness,
+ clippy::dbg_macro,
+ clippy::decimal_literal_representation,
+ clippy::double_must_use,
+ clippy::get_unwrap,
+ clippy::indexing_slicing,
+ clippy::missing_inline_in_public_items,
+ clippy::missing_safety_doc,
+ clippy::multiple_unsafe_ops_per_block,
+ clippy::must_use_candidate,
+ clippy::must_use_unit,
+ clippy::obfuscated_if_else,
+ clippy::perf,
+ clippy::print_stdout,
+ clippy::return_self_not_must_use,
+ clippy::std_instead_of_core,
+ clippy::style,
+ clippy::suspicious,
+ clippy::todo,
+ clippy::undocumented_unsafe_blocks,
+ clippy::unimplemented,
+ clippy::unnested_or_patterns,
+ clippy::unwrap_used,
+ clippy::use_debug
+)]
+// `clippy::incompatible_msrv` (implied by `clippy::suspicious`): This sometimes
+// has false positives, and we test on our MSRV in CI, so it doesn't help us
+// anyway.
+#![allow(clippy::needless_lifetimes, clippy::type_complexity, clippy::incompatible_msrv)]
+#![deny(
+ rustdoc::bare_urls,
+ rustdoc::broken_intra_doc_links,
+ rustdoc::invalid_codeblock_attributes,
+ rustdoc::invalid_html_tags,
+ rustdoc::invalid_rust_codeblocks,
+ rustdoc::missing_crate_level_docs,
+ rustdoc::private_intra_doc_links
+)]
+// In test code, it makes sense to weight more heavily towards concise, readable
+// code over correct or debuggable code.
+#![cfg_attr(any(test, kani), allow(
+ // In tests, you get line numbers and have access to source code, so panic
+ // messages are less important. You also often unwrap a lot, which would
+ // make expect'ing instead very verbose.
+ clippy::unwrap_used,
+ // In tests, there's no harm to "panic risks" - the worst that can happen is
+ // that your test will fail, and you'll fix it. By contrast, panic risks in
+ // production code introduce the possibly of code panicking unexpectedly "in
+ // the field".
+ clippy::arithmetic_side_effects,
+ clippy::indexing_slicing,
+))]
+#![cfg_attr(not(any(test, kani, feature = "std")), no_std)]
+#![cfg_attr(
+ all(feature = "simd-nightly", target_arch = "arm"),
+ feature(stdarch_arm_neon_intrinsics)
+)]
+#![cfg_attr(
+ all(feature = "simd-nightly", any(target_arch = "powerpc", target_arch = "powerpc64")),
+ feature(stdarch_powerpc)
+)]
+#![cfg_attr(feature = "float-nightly", feature(f16, f128))]
+#![cfg_attr(doc_cfg, feature(doc_cfg))]
+#![cfg_attr(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, feature(coverage_attribute))]
+#![cfg_attr(
+ any(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, miri),
+ feature(layout_for_ptr)
+)]
+#![cfg_attr(all(test, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), feature(test))]
+
+// This is a hack to allow zerocopy-derive derives to work in this crate. They
+// assume that zerocopy is linked as an extern crate, so they access items from
+// it as `zerocopy::Xxx`. This makes that still work.
+#[cfg(any(feature = "derive", test))]
+extern crate self as zerocopy;
+
+#[cfg(all(test, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS))]
+extern crate test;
+
+#[doc(hidden)]
+#[macro_use]
+pub mod util;
+
+pub mod byte_slice;
+pub mod byteorder;
+mod deprecated;
+
+#[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_DEV_MODE)]
+pub mod doctests;
+
+// This module is `pub` so that zerocopy's error types and error handling
+// documentation is grouped together in a cohesive module. In practice, we
+// expect most users to use the re-export of `error`'s items to avoid identifier
+// stuttering.
+pub mod error;
+mod impls;
+#[doc(hidden)]
+pub mod layout;
+mod macros;
+#[cfg_attr(not(zerocopy_unstable_ptr), doc(hidden))]
+#[cfg_attr(doc_cfg, doc(cfg(zerocopy_unstable_ptr)))]
+pub mod pointer;
+mod r#ref;
+mod split_at;
+// FIXME(#252): If we make this pub, come up with a better name.
+mod wrappers;
+
+use core::{
+ cell::{Cell, UnsafeCell},
+ cmp::Ordering,
+ fmt::{self, Debug, Display, Formatter},
+ hash::Hasher,
+ marker::PhantomData,
+ mem::{self, ManuallyDrop, MaybeUninit as CoreMaybeUninit},
+ num::{
+ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
+ NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
+ },
+ ops::{Deref, DerefMut},
+ ptr::{self, NonNull},
+ slice,
+};
+#[cfg(feature = "std")]
+use std::io;
+
+#[doc(hidden)]
+pub use crate::pointer::{
+ invariant::{self, BecauseExclusive},
+ PtrInner,
+};
+pub use crate::{
+ byte_slice::*,
+ byteorder::*,
+ error::*,
+ r#ref::*,
+ split_at::{Split, SplitAt},
+ wrappers::*,
+};
+
+#[cfg(any(feature = "alloc", test, kani))]
+extern crate alloc;
+#[cfg(any(feature = "alloc", test))]
+use alloc::{boxed::Box, vec::Vec};
+#[cfg(any(feature = "alloc", test))]
+use core::alloc::Layout;
+
+// Used by `KnownLayout`.
+#[doc(hidden)]
+pub use crate::layout::*;
+// Used by `TryFromBytes::is_bit_valid`.
+#[doc(hidden)]
+pub use crate::pointer::{invariant::BecauseImmutable, Maybe, Ptr};
+// For each trait polyfill, as soon as the corresponding feature is stable, the
+// polyfill import will be unused because method/function resolution will prefer
+// the inherent method/function over a trait method/function. Thus, we suppress
+// the `unused_imports` warning.
+//
+// See the documentation on `util::polyfills` for more information.
+#[allow(unused_imports)]
+use crate::util::polyfills::{self, NonNullExt as _, NumExt as _};
+#[cfg_attr(not(zerocopy_unstable_ptr), doc(hidden))]
+#[cfg_attr(doc_cfg, doc(cfg(zerocopy_unstable_ptr)))]
+pub use crate::util::MetadataOf;
+
+#[cfg(all(test, not(__ZEROCOPY_INTERNAL_USE_ONLY_DEV_MODE)))]
+const _: () = {
+ #[deprecated = "Development of zerocopy using cargo is not supported. Please use `cargo.sh` or `win-cargo.bat` instead."]
+ #[allow(unused)]
+ const WARNING: () = ();
+ #[warn(deprecated)]
+ WARNING
+};
+
+/// Implements [`KnownLayout`].
+///
+/// This derive analyzes various aspects of a type's layout that are needed for
+/// some of zerocopy's APIs. It can be applied to structs, enums, and unions;
+/// e.g.:
+///
+/// ```
+/// # use zerocopy_derive::KnownLayout;
+/// #[derive(KnownLayout)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(KnownLayout)]
+/// enum MyEnum {
+/// # V00,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(KnownLayout)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// # Limitations
+///
+/// This derive cannot currently be applied to unsized structs without an
+/// explicit `repr` attribute.
+///
+/// Some invocations of this derive run afoul of a [known bug] in Rust's type
+/// privacy checker. For example, this code:
+///
+/// ```compile_fail,E0446
+/// use zerocopy::*;
+/// # use zerocopy_derive::*;
+///
+/// #[derive(KnownLayout)]
+/// #[repr(C)]
+/// pub struct PublicType {
+/// leading: Foo,
+/// trailing: Bar,
+/// }
+///
+/// #[derive(KnownLayout)]
+/// struct Foo;
+///
+/// #[derive(KnownLayout)]
+/// struct Bar;
+/// ```
+///
+/// ...results in a compilation error:
+///
+/// ```text
+/// error[E0446]: private type `Bar` in public interface
+/// --> examples/bug.rs:3:10
+/// |
+/// 3 | #[derive(KnownLayout)]
+/// | ^^^^^^^^^^^ can't leak private type
+/// ...
+/// 14 | struct Bar;
+/// | ---------- `Bar` declared as private
+/// |
+/// = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info)
+/// ```
+///
+/// This issue arises when `#[derive(KnownLayout)]` is applied to `repr(C)`
+/// structs whose trailing field type is less public than the enclosing struct.
+///
+/// To work around this, mark the trailing field type `pub` and annotate it with
+/// `#[doc(hidden)]`; e.g.:
+///
+/// ```no_run
+/// use zerocopy::*;
+/// # use zerocopy_derive::*;
+///
+/// #[derive(KnownLayout)]
+/// #[repr(C)]
+/// pub struct PublicType {
+/// leading: Foo,
+/// trailing: Bar,
+/// }
+///
+/// #[derive(KnownLayout)]
+/// struct Foo;
+///
+/// #[doc(hidden)]
+/// #[derive(KnownLayout)]
+/// pub struct Bar; // <- `Bar` is now also `pub`
+/// ```
+///
+/// [known bug]: https://github.com/rust-lang/rust/issues/45713
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::KnownLayout;
+// These exist so that code which was written against the old names will get
+// less confusing error messages when they upgrade to a more recent version of
+// zerocopy. On our MSRV toolchain, the error messages read, for example:
+//
+// error[E0603]: trait `FromZeroes` is private
+// --> examples/deprecated.rs:1:15
+// |
+// 1 | use zerocopy::FromZeroes;
+// | ^^^^^^^^^^ private trait
+// |
+// note: the trait `FromZeroes` is defined here
+// --> /Users/josh/workspace/zerocopy/src/lib.rs:1845:5
+// |
+// 1845 | use FromZeros as FromZeroes;
+// | ^^^^^^^^^^^^^^^^^^^^^^^
+//
+// The "note" provides enough context to make it easy to figure out how to fix
+// the error.
+#[allow(unused)]
+use {FromZeros as FromZeroes, IntoBytes as AsBytes, Ref as LayoutVerified};
+
+/// Indicates that zerocopy can reason about certain aspects of a type's layout.
+///
+/// This trait is required by many of zerocopy's APIs. It supports sized types,
+/// slices, and [slice DSTs](#dynamically-sized-types).
+///
+/// # Implementation
+///
+/// **Do not implement this trait yourself!** Instead, use
+/// [`#[derive(KnownLayout)]`][derive]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::KnownLayout;
+/// #[derive(KnownLayout)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(KnownLayout)]
+/// enum MyEnum {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(KnownLayout)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// This derive performs a sophisticated analysis to deduce the layout
+/// characteristics of types. You **must** implement this trait via the derive.
+///
+/// # Dynamically-sized types
+///
+/// `KnownLayout` supports slice-based dynamically sized types ("slice DSTs").
+///
+/// A slice DST is a type whose trailing field is either a slice or another
+/// slice DST, rather than a type with fixed size. For example:
+///
+/// ```
+/// #[repr(C)]
+/// struct PacketHeader {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[repr(C)]
+/// struct Packet {
+/// header: PacketHeader,
+/// body: [u8],
+/// }
+/// ```
+///
+/// It can be useful to think of slice DSTs as a generalization of slices - in
+/// other words, a normal slice is just the special case of a slice DST with
+/// zero leading fields. In particular:
+/// - Like slices, slice DSTs can have different lengths at runtime
+/// - Like slices, slice DSTs cannot be passed by-value, but only by reference
+/// or via other indirection such as `Box`
+/// - Like slices, a reference (or `Box`, or other pointer type) to a slice DST
+/// encodes the number of elements in the trailing slice field
+///
+/// ## Slice DST layout
+///
+/// Just like other composite Rust types, the layout of a slice DST is not
+/// well-defined unless it is specified using an explicit `#[repr(...)]`
+/// attribute such as `#[repr(C)]`. [Other representations are
+/// supported][reprs], but in this section, we'll use `#[repr(C)]` as our
+/// example.
+///
+/// A `#[repr(C)]` slice DST is laid out [just like sized `#[repr(C)]`
+/// types][repr-c-structs], but the presence of a variable-length field
+/// introduces the possibility of *dynamic padding*. In particular, it may be
+/// necessary to add trailing padding *after* the trailing slice field in order
+/// to satisfy the outer type's alignment, and the amount of padding required
+/// may be a function of the length of the trailing slice field. This is just a
+/// natural consequence of the normal `#[repr(C)]` rules applied to slice DSTs,
+/// but it can result in surprising behavior. For example, consider the
+/// following type:
+///
+/// ```
+/// #[repr(C)]
+/// struct Foo {
+/// a: u32,
+/// b: u8,
+/// z: [u16],
+/// }
+/// ```
+///
+/// Assuming that `u32` has alignment 4 (this is not true on all platforms),
+/// then `Foo` has alignment 4 as well. Here is the smallest possible value for
+/// `Foo`:
+///
+/// ```text
+/// byte offset | 01234567
+/// field | aaaab---
+/// ><
+/// ```
+///
+/// In this value, `z` has length 0. Abiding by `#[repr(C)]`, the lowest offset
+/// that we can place `z` at is 5, but since `z` has alignment 2, we need to
+/// round up to offset 6. This means that there is one byte of padding between
+/// `b` and `z`, then 0 bytes of `z` itself (denoted `><` in this diagram), and
+/// then two bytes of padding after `z` in order to satisfy the overall
+/// alignment of `Foo`. The size of this instance is 8 bytes.
+///
+/// What about if `z` has length 1?
+///
+/// ```text
+/// byte offset | 01234567
+/// field | aaaab-zz
+/// ```
+///
+/// In this instance, `z` has length 1, and thus takes up 2 bytes. That means
+/// that we no longer need padding after `z` in order to satisfy `Foo`'s
+/// alignment. We've now seen two different values of `Foo` with two different
+/// lengths of `z`, but they both have the same size - 8 bytes.
+///
+/// What about if `z` has length 2?
+///
+/// ```text
+/// byte offset | 012345678901
+/// field | aaaab-zzzz--
+/// ```
+///
+/// Now `z` has length 2, and thus takes up 4 bytes. This brings our un-padded
+/// size to 10, and so we now need another 2 bytes of padding after `z` to
+/// satisfy `Foo`'s alignment.
+///
+/// Again, all of this is just a logical consequence of the `#[repr(C)]` rules
+/// applied to slice DSTs, but it can be surprising that the amount of trailing
+/// padding becomes a function of the trailing slice field's length, and thus
+/// can only be computed at runtime.
+///
+/// [reprs]: https://doc.rust-lang.org/reference/type-layout.html#representations
+/// [repr-c-structs]: https://doc.rust-lang.org/reference/type-layout.html#reprc-structs
+///
+/// ## What is a valid size?
+///
+/// There are two places in zerocopy's API that we refer to "a valid size" of a
+/// type. In normal casts or conversions, where the source is a byte slice, we
+/// need to know whether the source byte slice is a valid size of the
+/// destination type. In prefix or suffix casts, we need to know whether *there
+/// exists* a valid size of the destination type which fits in the source byte
+/// slice and, if so, what the largest such size is.
+///
+/// As outlined above, a slice DST's size is defined by the number of elements
+/// in its trailing slice field. However, there is not necessarily a 1-to-1
+/// mapping between trailing slice field length and overall size. As we saw in
+/// the previous section with the type `Foo`, instances with both 0 and 1
+/// elements in the trailing `z` field result in a `Foo` whose size is 8 bytes.
+///
+/// When we say "x is a valid size of `T`", we mean one of two things:
+/// - If `T: Sized`, then we mean that `x == size_of::<T>()`
+/// - If `T` is a slice DST, then we mean that there exists a `len` such that the instance of
+/// `T` with `len` trailing slice elements has size `x`
+///
+/// When we say "largest possible size of `T` that fits in a byte slice", we
+/// mean one of two things:
+/// - If `T: Sized`, then we mean `size_of::<T>()` if the byte slice is at least
+/// `size_of::<T>()` bytes long
+/// - If `T` is a slice DST, then we mean to consider all values, `len`, such
+/// that the instance of `T` with `len` trailing slice elements fits in the
+/// byte slice, and to choose the largest such `len`, if any
+///
+///
+/// # Safety
+///
+/// This trait does not convey any safety guarantees to code outside this crate.
+///
+/// You must not rely on the `#[doc(hidden)]` internals of `KnownLayout`. Future
+/// releases of zerocopy may make backwards-breaking changes to these items,
+/// including changes that only affect soundness, which may cause code which
+/// uses those items to silently become unsound.
+///
+#[cfg_attr(feature = "derive", doc = "[derive]: zerocopy_derive::KnownLayout")]
+#[cfg_attr(
+ not(feature = "derive"),
+ doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.KnownLayout.html"),
+)]
+#[cfg_attr(
+ not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
+ diagnostic::on_unimplemented(note = "Consider adding `#[derive(KnownLayout)]` to `{Self}`")
+)]
+pub unsafe trait KnownLayout {
+ // The `Self: Sized` bound makes it so that `KnownLayout` can still be
+ // object safe. It's not currently object safe thanks to `const LAYOUT`, and
+ // it likely won't be in the future, but there's no reason not to be
+ // forwards-compatible with object safety.
+ #[doc(hidden)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// The type of metadata stored in a pointer to `Self`.
+ ///
+ /// This is `()` for sized types and [`usize`] for slice DSTs.
+ type PointerMetadata: PointerMetadata;
+
+ /// A maybe-uninitialized analog of `Self`
+ ///
+ /// # Safety
+ ///
+ /// `Self::LAYOUT` and `Self::MaybeUninit::LAYOUT` are identical.
+ /// `Self::MaybeUninit` admits uninitialized bytes in all positions.
+ #[doc(hidden)]
+ type MaybeUninit: ?Sized + KnownLayout<PointerMetadata = Self::PointerMetadata>;
+
+ /// The layout of `Self`.
+ ///
+ /// # Safety
+ ///
+ /// Callers may assume that `LAYOUT` accurately reflects the layout of
+ /// `Self`. In particular:
+ /// - `LAYOUT.align` is equal to `Self`'s alignment
+ /// - If `Self: Sized`, then `LAYOUT.size_info == SizeInfo::Sized { size }`
+ /// where `size == size_of::<Self>()`
+ /// - If `Self` is a slice DST, then `LAYOUT.size_info ==
+ /// SizeInfo::SliceDst(slice_layout)` where:
+ /// - The size, `size`, of an instance of `Self` with `elems` trailing
+ /// slice elements is equal to `slice_layout.offset +
+ /// slice_layout.elem_size * elems` rounded up to the nearest multiple
+ /// of `LAYOUT.align`
+ /// - For such an instance, any bytes in the range `[slice_layout.offset +
+ /// slice_layout.elem_size * elems, size)` are padding and must not be
+ /// assumed to be initialized
+ #[doc(hidden)]
+ const LAYOUT: DstLayout;
+
+ /// SAFETY: The returned pointer has the same address and provenance as
+ /// `bytes`. If `Self` is a DST, the returned pointer's referent has `elems`
+ /// elements in its trailing slice.
+ #[doc(hidden)]
+ fn raw_from_ptr_len(bytes: NonNull<u8>, meta: Self::PointerMetadata) -> NonNull<Self>;
+
+ /// Extracts the metadata from a pointer to `Self`.
+ ///
+ /// # Safety
+ ///
+ /// `pointer_to_metadata` always returns the correct metadata stored in
+ /// `ptr`.
+ #[doc(hidden)]
+ fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata;
+
+ /// Computes the length of the byte range addressed by `ptr`.
+ ///
+ /// Returns `None` if the resulting length would not fit in an `usize`.
+ ///
+ /// # Safety
+ ///
+ /// Callers may assume that `size_of_val_raw` always returns the correct
+ /// size.
+ ///
+ /// Callers may assume that, if `ptr` addresses a byte range whose length
+ /// fits in an `usize`, this will return `Some`.
+ #[doc(hidden)]
+ #[must_use]
+ #[inline(always)]
+ fn size_of_val_raw(ptr: NonNull<Self>) -> Option<usize> {
+ let meta = Self::pointer_to_metadata(ptr.as_ptr());
+ // SAFETY: `size_for_metadata` promises to only return `None` if the
+ // resulting size would not fit in a `usize`.
+ Self::size_for_metadata(meta)
+ }
+
+ #[doc(hidden)]
+ #[must_use]
+ #[inline(always)]
+ fn raw_dangling() -> NonNull<Self> {
+ let meta = Self::PointerMetadata::from_elem_count(0);
+ Self::raw_from_ptr_len(NonNull::dangling(), meta)
+ }
+
+ /// Computes the size of an object of type `Self` with the given pointer
+ /// metadata.
+ ///
+ /// # Safety
+ ///
+ /// `size_for_metadata` promises to return `None` if and only if the
+ /// resulting size would not fit in a [`usize`]. Note that the returned size
+ /// could exceed the actual maximum valid size of an allocated object,
+ /// [`isize::MAX`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::KnownLayout;
+ ///
+ /// assert_eq!(u8::size_for_metadata(()), Some(1));
+ /// assert_eq!(u16::size_for_metadata(()), Some(2));
+ /// assert_eq!(<[u8]>::size_for_metadata(42), Some(42));
+ /// assert_eq!(<[u16]>::size_for_metadata(42), Some(84));
+ ///
+ /// // This size exceeds the maximum valid object size (`isize::MAX`):
+ /// assert_eq!(<[u8]>::size_for_metadata(usize::MAX), Some(usize::MAX));
+ ///
+ /// // This size, if computed, would exceed `usize::MAX`:
+ /// assert_eq!(<[u16]>::size_for_metadata(usize::MAX), None);
+ /// ```
+ #[inline(always)]
+ fn size_for_metadata(meta: Self::PointerMetadata) -> Option<usize> {
+ meta.size_for_metadata(Self::LAYOUT)
+ }
+
+ /// Computes whether `meta` can describe a valid allocation of `Self`.
+ ///
+ /// # Safety
+ ///
+ /// `is_valid_metadata` promises to return `true` if and only if the size of
+ /// an allocation of `Self` with `meta` would not overflow an
+ /// [`isize::MAX`].
+ #[doc(hidden)]
+ #[inline(always)]
+ fn is_valid_metadata(meta: Self::PointerMetadata) -> bool {
+ meta.to_elem_count() <= maximum_trailing_slice_len::<Self>().to_elem_count()
+ }
+}
+
+/// Efficiently produces the [`TrailingSliceLayout`] of `T`.
+#[inline(always)]
+pub(crate) fn trailing_slice_layout<T>() -> TrailingSliceLayout
+where
+ T: ?Sized + KnownLayout<PointerMetadata = usize>,
+{
+ trait LayoutFacts {
+ const SIZE_INFO: TrailingSliceLayout;
+ }
+
+ impl<T: ?Sized> LayoutFacts for T
+ where
+ T: KnownLayout<PointerMetadata = usize>,
+ {
+ const SIZE_INFO: TrailingSliceLayout = match T::LAYOUT.size_info {
+ crate::SizeInfo::Sized { .. } => const_panic!("unreachable"),
+ crate::SizeInfo::SliceDst(info) => info,
+ };
+ }
+
+ T::SIZE_INFO
+}
+
+/// Efficiently produces the maximum trailing slice length `T`.
+#[inline(always)]
+pub(crate) fn maximum_trailing_slice_len<T>() -> usize
+where
+ T: ?Sized + KnownLayout,
+{
+ trait LayoutFacts {
+ const MAX_LEN: usize;
+ }
+
+ impl<T: ?Sized> LayoutFacts for T
+ where
+ T: KnownLayout,
+ {
+ const MAX_LEN: usize = match T::LAYOUT.size_info {
+ SizeInfo::SliceDst(TrailingSliceLayout { elem_size: 0, .. }) => usize::MAX,
+ _ => match T::LAYOUT.validate_cast_and_convert_metadata(
+ T::LAYOUT.align.get(),
+ DstLayout::MAX_SIZE,
+ CastType::Prefix,
+ ) {
+ Ok((elems, _)) => elems,
+ Err(_) => const_panic!("unreachable"),
+ },
+ };
+ }
+
+ T::MAX_LEN
+}
+
+/// The metadata associated with a [`KnownLayout`] type.
+#[doc(hidden)]
+pub trait PointerMetadata: Copy + Eq + Debug + Ord {
+ /// Constructs a `Self` from an element count.
+ ///
+ /// If `Self = ()`, this returns `()`. If `Self = usize`, this returns
+ /// `elems`. No other types are currently supported.
+ fn from_elem_count(elems: usize) -> Self;
+
+ /// Converts `self` to an element count.
+ ///
+ /// If `Self = ()`, this returns `0`. If `Self = usize`, this returns
+ /// `self`. No other types are currently supported.
+ fn to_elem_count(self) -> usize;
+
+ /// Computes the size of the object with the given layout and pointer
+ /// metadata.
+ ///
+ /// # Panics
+ ///
+ /// If `Self = ()`, `layout` must describe a sized type. If `Self = usize`,
+ /// `layout` must describe a slice DST. Otherwise, `size_for_metadata` may
+ /// panic.
+ ///
+ /// # Safety
+ ///
+ /// `size_for_metadata` promises to only return `None` if the resulting size
+ /// would not fit in a `usize`.
+ fn size_for_metadata(self, layout: DstLayout) -> Option<usize>;
+}
+
+impl PointerMetadata for () {
+ #[inline]
+ #[allow(clippy::unused_unit)]
+ fn from_elem_count(_elems: usize) -> () {}
+
+ #[inline]
+ fn to_elem_count(self) -> usize {
+ 0
+ }
+
+ #[inline]
+ fn size_for_metadata(self, layout: DstLayout) -> Option<usize> {
+ match layout.size_info {
+ SizeInfo::Sized { size } => Some(size),
+ // NOTE: This branch is unreachable, but we return `None` rather
+ // than `unreachable!()` to avoid generating panic paths.
+ SizeInfo::SliceDst(_) => None,
+ }
+ }
+}
+
+impl PointerMetadata for usize {
+ #[inline]
+ fn from_elem_count(elems: usize) -> usize {
+ elems
+ }
+
+ #[inline]
+ fn to_elem_count(self) -> usize {
+ self
+ }
+
+ #[inline]
+ fn size_for_metadata(self, layout: DstLayout) -> Option<usize> {
+ match layout.size_info {
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
+ let slice_len = elem_size.checked_mul(self)?;
+ let without_padding = offset.checked_add(slice_len)?;
+ without_padding.checked_add(util::padding_needed_for(without_padding, layout.align))
+ }
+ // NOTE: This branch is unreachable, but we return `None` rather
+ // than `unreachable!()` to avoid generating panic paths.
+ SizeInfo::Sized { .. } => None,
+ }
+ }
+}
+
+// SAFETY: Delegates safety to `DstLayout::for_slice`.
+unsafe impl<T> KnownLayout for [T] {
+ #[allow(clippy::missing_inline_in_public_items, dead_code)]
+ #[cfg_attr(
+ all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS),
+ coverage(off)
+ )]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
+
+ type PointerMetadata = usize;
+
+ // SAFETY: `CoreMaybeUninit<T>::LAYOUT` and `T::LAYOUT` are identical
+ // because `CoreMaybeUninit<T>` has the same size and alignment as `T` [1].
+ // Consequently, `[CoreMaybeUninit<T>]::LAYOUT` and `[T]::LAYOUT` are
+ // identical, because they both lack a fixed-sized prefix and because they
+ // inherit the alignments of their inner element type (which are identical)
+ // [2][3].
+ //
+ // `[CoreMaybeUninit<T>]` admits uninitialized bytes at all positions
+ // because `CoreMaybeUninit<T>` admits uninitialized bytes at all positions
+ // and because the inner elements of `[CoreMaybeUninit<T>]` are laid out
+ // back-to-back [2][3].
+ //
+ // [1] Per https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#layout-1:
+ //
+ // `MaybeUninit<T>` is guaranteed to have the same size, alignment, and ABI as
+ // `T`
+ //
+ // [2] Per https://doc.rust-lang.org/1.82.0/reference/type-layout.html#slice-layout:
+ //
+ // Slices have the same layout as the section of the array they slice.
+ //
+ // [3] Per https://doc.rust-lang.org/1.82.0/reference/type-layout.html#array-layout:
+ //
+ // An array of `[T; N]` has a size of `size_of::<T>() * N` and the same
+ // alignment of `T`. Arrays are laid out so that the zero-based `nth`
+ // element of the array is offset from the start of the array by `n *
+ // size_of::<T>()` bytes.
+ type MaybeUninit = [CoreMaybeUninit<T>];
+
+ const LAYOUT: DstLayout = DstLayout::for_slice::<T>();
+
+ // SAFETY: `.cast` preserves address and provenance. The returned pointer
+ // refers to an object with `elems` elements by construction.
+ #[inline(always)]
+ fn raw_from_ptr_len(data: NonNull<u8>, elems: usize) -> NonNull<Self> {
+ // FIXME(#67): Remove this allow. See NonNullExt for more details.
+ #[allow(unstable_name_collisions)]
+ NonNull::slice_from_raw_parts(data.cast::<T>(), elems)
+ }
+
+ #[inline(always)]
+ fn pointer_to_metadata(ptr: *mut [T]) -> usize {
+ #[allow(clippy::as_conversions)]
+ let slc = ptr as *const [()];
+
+ // SAFETY:
+ // - `()` has alignment 1, so `slc` is trivially aligned.
+ // - `slc` was derived from a non-null pointer.
+ // - The size is 0 regardless of the length, so it is sound to
+ // materialize a reference regardless of location.
+ // - By invariant, `self.ptr` has valid provenance.
+ let slc = unsafe { &*slc };
+
+ // This is correct because the preceding `as` cast preserves the number
+ // of slice elements. [1]
+ //
+ // [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
+ //
+ // For slice types like `[T]` and `[U]`, the raw pointer types `*const
+ // [T]`, `*mut [T]`, `*const [U]`, and `*mut [U]` encode the number of
+ // elements in this slice. Casts between these raw pointer types
+ // preserve the number of elements. ... The same holds for `str` and
+ // any compound type whose unsized tail is a slice type, such as
+ // struct `Foo(i32, [u8])` or `(u64, Foo)`.
+ slc.len()
+ }
+}
+
+#[rustfmt::skip]
+impl_known_layout!(
+ (),
+ u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
+ bool, char,
+ NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
+ NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize
+);
+#[rustfmt::skip]
+#[cfg(feature = "float-nightly")]
+impl_known_layout!(
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "float-nightly")))]
+ f16,
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "float-nightly")))]
+ f128
+);
+#[rustfmt::skip]
+impl_known_layout!(
+ T => Option<T>,
+ T: ?Sized => PhantomData<T>,
+ T => Wrapping<T>,
+ T => CoreMaybeUninit<T>,
+ T: ?Sized => *const T,
+ T: ?Sized => *mut T,
+ T: ?Sized => &'_ T,
+ T: ?Sized => &'_ mut T,
+);
+impl_known_layout!(const N: usize, T => [T; N]);
+
+// SAFETY: `str` has the same representation as `[u8]`. `ManuallyDrop<T>` [1],
+// `UnsafeCell<T>` [2], and `Cell<T>` [3] have the same representation as `T`.
+//
+// [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
+//
+// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
+// `T`
+//
+// [2] Per https://doc.rust-lang.org/1.85.0/core/cell/struct.UnsafeCell.html#memory-layout:
+//
+// `UnsafeCell<T>` has the same in-memory representation as its inner type
+// `T`.
+//
+// [3] Per https://doc.rust-lang.org/1.85.0/core/cell/struct.Cell.html#memory-layout:
+//
+// `Cell<T>` has the same in-memory representation as `T`.
+#[allow(clippy::multiple_unsafe_ops_per_block)]
+const _: () = unsafe {
+ unsafe_impl_known_layout!(
+ #[repr([u8])]
+ str
+ );
+ unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ManuallyDrop<T>);
+ unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] UnsafeCell<T>);
+ unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] Cell<T>);
+};
+
+// SAFETY:
+// - By consequence of the invariant on `T::MaybeUninit` that `T::LAYOUT` and
+// `T::MaybeUninit::LAYOUT` are equal, `T` and `T::MaybeUninit` have the same:
+// - Fixed prefix size
+// - Alignment
+// - (For DSTs) trailing slice element size
+// - By consequence of the above, referents `T::MaybeUninit` and `T` have the
+// require the same kind of pointer metadata, and thus it is valid to perform
+// an `as` cast from `*mut T` and `*mut T::MaybeUninit`, and this operation
+// preserves referent size (ie, `size_of_val_raw`).
+const _: () = unsafe {
+ unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T::MaybeUninit)] MaybeUninit<T>)
+};
+
+// FIXME(#196, #2856): Eventually, we'll want to support enums variants and
+// union fields being treated uniformly since they behave similarly to each
+// other in terms of projecting validity – specifically, for a type `T` with
+// validity `V`, if `T` is a struct type, then its fields straightforwardly also
+// have validity `V`. By contrast, if `T` is an enum or union type, then
+// validity is not straightforwardly recursive in this way.
+#[doc(hidden)]
+pub const STRUCT_VARIANT_ID: i128 = -1;
+#[doc(hidden)]
+pub const UNION_VARIANT_ID: i128 = -2;
+#[doc(hidden)]
+pub const REPR_C_UNION_VARIANT_ID: i128 = -3;
+
+/// # Safety
+///
+/// `Self::ProjectToTag` must satisfy its safety invariant.
+#[doc(hidden)]
+pub unsafe trait HasTag {
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// The type's enum tag, or `()` for non-enum types.
+ type Tag: Immutable;
+
+ /// A pointer projection from `Self` to its tag.
+ ///
+ /// # Safety
+ ///
+ /// It must be the case that, for all `slf: Ptr<'_, Self, I>`, it is sound
+ /// to project from `slf` to `Ptr<'_, Self::Tag, I>` using this projection.
+ type ProjectToTag: pointer::cast::Project<Self, Self::Tag>;
+}
+
+/// Projects a given field from `Self`.
+///
+/// All implementations of `HasField` for a particular field `f` in `Self`
+/// should use the same `Field` type; this ensures that `Field` is inferable
+/// given an explicit `VARIANT_ID` and `FIELD_ID`.
+///
+/// # Safety
+///
+/// A field `f` is `HasField` for `Self` if and only if:
+///
+/// - If `Self` has the layout of a struct or union type, then `VARIANT_ID` is
+/// `STRUCT_VARIANT_ID` or `UNION_VARIANT_ID` respectively; otherwise, if
+/// `Self` has the layout of an enum type, `VARIANT_ID` is the numerical index
+/// of the enum variant in which `f` appears. Note that `Self` does not need
+/// to actually *be* such a type – it just needs to have the same layout as
+/// such a type. For example, a `#[repr(transparent)]` wrapper around an enum
+/// has the same layout as that enum.
+/// - If `f` has name `n`, `FIELD_ID` is `zerocopy::ident_id!(n)`; otherwise,
+/// if `f` is at index `i`, `FIELD_ID` is `zerocopy::ident_id!(i)`.
+/// - `Field` is a type with the same visibility as `f`.
+/// - `Type` has the same type as `f`.
+///
+/// The caller must **not** assume that a pointer's referent being aligned
+/// implies that calling `project` on that pointer will result in a pointer to
+/// an aligned referent. For example, `HasField` may be implemented for
+/// `#[repr(packed)]` structs.
+///
+/// The implementation of `project` must satisfy its safety post-condition.
+#[doc(hidden)]
+pub unsafe trait HasField<Field, const VARIANT_ID: i128, const FIELD_ID: i128>:
+ HasTag
+{
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// The type of the field.
+ type Type: ?Sized;
+
+ /// Projects from `slf` to the field.
+ ///
+ /// Users should generally not call `project` directly, and instead should
+ /// use high-level APIs like [`PtrInner::project`] or [`Ptr::project`].
+ ///
+ /// # Safety
+ ///
+ /// The returned pointer refers to a non-strict subset of the bytes of
+ /// `slf`'s referent, and has the same provenance as `slf`.
+ #[must_use]
+ fn project(slf: PtrInner<'_, Self>) -> *mut Self::Type;
+}
+
+/// Projects a given field from `Self`.
+///
+/// Implementations of this trait encode the conditions under which a field can
+/// be projected from a `Ptr<'_, Self, I>`, and how the invariants of that
+/// [`Ptr`] (`I`) determine the invariants of pointers projected from it. In
+/// other words, it is a type-level function over invariants; `I` goes in,
+/// `Self::Invariants` comes out.
+///
+/// # Safety
+///
+/// `T: ProjectField<Field, I, VARIANT_ID, FIELD_ID>` if, for a
+/// `ptr: Ptr<'_, T, I>` such that `T::is_projectable(ptr).is_ok()`,
+/// `<T as HasField<Field, VARIANT_ID, FIELD_ID>>::project(ptr.as_inner())`
+/// conforms to `T::Invariants`.
+#[doc(hidden)]
+pub unsafe trait ProjectField<Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>:
+ HasField<Field, VARIANT_ID, FIELD_ID>
+where
+ I: invariant::Invariants,
+{
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// The invariants of the projected field pointer, with respect to the
+ /// invariants, `I`, of the containing pointer. The aliasing dimension of
+ /// the invariants is guaranteed to remain unchanged.
+ type Invariants: invariant::Invariants<Aliasing = I::Aliasing>;
+
+ /// The failure mode of projection. `()` if the projection is fallible,
+ /// otherwise [`core::convert::Infallible`].
+ type Error;
+
+ /// Is the given field projectable from `ptr`?
+ ///
+ /// If a field with [`Self::Invariants`] is projectable from the referent,
+ /// this function produces an `Ok(ptr)` from which the projection can be
+ /// made; otherwise `Err`.
+ ///
+ /// This method must be overriden if the field's projectability depends on
+ /// the value of the bytes in `ptr`.
+ #[inline(always)]
+ fn is_projectable<'a>(_ptr: Ptr<'a, Self::Tag, I>) -> Result<(), Self::Error> {
+ trait IsInfallible {
+ const IS_INFALLIBLE: bool;
+ }
+
+ struct Projection<T, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>(
+ PhantomData<(Field, I, T)>,
+ )
+ where
+ T: ?Sized + HasField<Field, VARIANT_ID, FIELD_ID>,
+ I: invariant::Invariants;
+
+ impl<T, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128> IsInfallible
+ for Projection<T, Field, I, VARIANT_ID, FIELD_ID>
+ where
+ T: ?Sized + HasField<Field, VARIANT_ID, FIELD_ID>,
+ I: invariant::Invariants,
+ {
+ const IS_INFALLIBLE: bool = {
+ let is_infallible = match VARIANT_ID {
+ // For nondestructive projections of struct and union
+ // fields, the projected field's satisfaction of
+ // `Invariants` does not depend on the value of the
+ // referent. This default implementation of `is_projectable`
+ // is non-destructive, as it does not overwrite any part of
+ // the referent.
+ crate::STRUCT_VARIANT_ID | crate::UNION_VARIANT_ID => true,
+ _enum_variant => {
+ use crate::invariant::{Validity, ValidityKind};
+ match I::Validity::KIND {
+ // The `Uninit` and `Initialized` validity
+ // invariants do not depend on the enum's tag. In
+ // particular, we don't actually care about what
+ // variant is present – we can treat *any* range of
+ // uninitialized or initialized memory as containing
+ // an uninitialized or initialized instance of *any*
+ // type – the type itself is irrelevant.
+ ValidityKind::Uninit | ValidityKind::Initialized => true,
+ // The projectability of an enum field from an
+ // `AsInitialized` or `Valid` state is a dynamic
+ // property of its tag.
+ ValidityKind::AsInitialized | ValidityKind::Valid => false,
+ }
+ }
+ };
+ const_assert!(is_infallible);
+ is_infallible
+ };
+ }
+
+ const_assert!(
+ <Projection<Self, Field, I, VARIANT_ID, FIELD_ID> as IsInfallible>::IS_INFALLIBLE
+ );
+
+ Ok(())
+ }
+}
+
+/// Analyzes whether a type is [`FromZeros`].
+///
+/// This derive analyzes, at compile time, whether the annotated type satisfies
+/// the [safety conditions] of `FromZeros` and implements `FromZeros` and its
+/// supertraits if it is sound to do so. This derive can be applied to structs,
+/// enums, and unions; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{FromZeros, Immutable};
+/// #[derive(FromZeros)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromZeros)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant0,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromZeros, Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// [safety conditions]: trait@FromZeros#safety
+///
+/// # Analysis
+///
+/// *This section describes, roughly, the analysis performed by this derive to
+/// determine whether it is sound to implement `FromZeros` for a given type.
+/// Unless you are modifying the implementation of this derive, or attempting to
+/// manually implement `FromZeros` for a type yourself, you don't need to read
+/// this section.*
+///
+/// If a type has the following properties, then this derive can implement
+/// `FromZeros` for that type:
+///
+/// - If the type is a struct, all of its fields must be `FromZeros`.
+/// - If the type is an enum:
+/// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`,
+/// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`).
+/// - It must have a variant with a discriminant/tag of `0`, and its fields
+/// must be `FromZeros`. See [the reference] for a description of
+/// discriminant values are specified.
+/// - The fields of that variant must be `FromZeros`.
+///
+/// This analysis is subject to change. Unsafe code may *only* rely on the
+/// documented [safety conditions] of `FromZeros`, and must *not* rely on the
+/// implementation details of this derive.
+///
+/// [the reference]: https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
+///
+/// ## Why isn't an explicit representation required for structs?
+///
+/// Neither this derive, nor the [safety conditions] of `FromZeros`, requires
+/// that structs are marked with `#[repr(C)]`.
+///
+/// Per the [Rust reference](reference),
+///
+/// > The representation of a type can change the padding between fields, but
+/// > does not change the layout of the fields themselves.
+///
+/// [reference]: https://doc.rust-lang.org/reference/type-layout.html#representations
+///
+/// Since the layout of structs only consists of padding bytes and field bytes,
+/// a struct is soundly `FromZeros` if:
+/// 1. its padding is soundly `FromZeros`, and
+/// 2. its fields are soundly `FromZeros`.
+///
+/// The answer to the first question is always yes: padding bytes do not have
+/// any validity constraints. A [discussion] of this question in the Unsafe Code
+/// Guidelines Working Group concluded that it would be virtually unimaginable
+/// for future versions of rustc to add validity constraints to padding bytes.
+///
+/// [discussion]: https://github.com/rust-lang/unsafe-code-guidelines/issues/174
+///
+/// Whether a struct is soundly `FromZeros` therefore solely depends on whether
+/// its fields are `FromZeros`.
+// FIXME(#146): Document why we don't require an enum to have an explicit `repr`
+// attribute.
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::FromZeros;
+/// Analyzes whether a type is [`Immutable`].
+///
+/// This derive analyzes, at compile time, whether the annotated type satisfies
+/// the [safety conditions] of `Immutable` and implements `Immutable` if it is
+/// sound to do so. This derive can be applied to structs, enums, and unions;
+/// e.g.:
+///
+/// ```
+/// # use zerocopy_derive::Immutable;
+/// #[derive(Immutable)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Immutable)]
+/// enum MyEnum {
+/// # Variant0,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// # Analysis
+///
+/// *This section describes, roughly, the analysis performed by this derive to
+/// determine whether it is sound to implement `Immutable` for a given type.
+/// Unless you are modifying the implementation of this derive, you don't need
+/// to read this section.*
+///
+/// If a type has the following properties, then this derive can implement
+/// `Immutable` for that type:
+///
+/// - All fields must be `Immutable`.
+///
+/// This analysis is subject to change. Unsafe code may *only* rely on the
+/// documented [safety conditions] of `Immutable`, and must *not* rely on the
+/// implementation details of this derive.
+///
+/// [safety conditions]: trait@Immutable#safety
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::Immutable;
+
+/// Types which are free from interior mutability.
+///
+/// `T: Immutable` indicates that `T` does not permit interior mutation, except
+/// by ownership or an exclusive (`&mut`) borrow.
+///
+/// # Implementation
+///
+/// **Do not implement this trait yourself!** Instead, use
+/// [`#[derive(Immutable)]`][derive] (requires the `derive` Cargo feature);
+/// e.g.:
+///
+/// ```
+/// # use zerocopy_derive::Immutable;
+/// #[derive(Immutable)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Immutable)]
+/// enum MyEnum {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// This derive performs a sophisticated, compile-time safety analysis to
+/// determine whether a type is `Immutable`.
+///
+/// # Safety
+///
+/// Unsafe code outside of this crate must not make any assumptions about `T`
+/// based on `T: Immutable`. We reserve the right to relax the requirements for
+/// `Immutable` in the future, and if unsafe code outside of this crate makes
+/// assumptions based on `T: Immutable`, future relaxations may cause that code
+/// to become unsound.
+///
+// # Safety (Internal)
+//
+// If `T: Immutable`, unsafe code *inside of this crate* may assume that, given
+// `t: &T`, `t` does not permit interior mutation of its referent. Because
+// [`UnsafeCell`] is the only type which permits interior mutation, it is
+// sufficient (though not necessary) to guarantee that `T` contains no
+// `UnsafeCell`s.
+//
+// [`UnsafeCell`]: core::cell::UnsafeCell
+#[cfg_attr(
+ feature = "derive",
+ doc = "[derive]: zerocopy_derive::Immutable",
+ doc = "[derive-analysis]: zerocopy_derive::Immutable#analysis"
+)]
+#[cfg_attr(
+ not(feature = "derive"),
+ doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.Immutable.html"),
+ doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.Immutable.html#analysis"),
+)]
+#[cfg_attr(
+ not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
+ diagnostic::on_unimplemented(note = "Consider adding `#[derive(Immutable)]` to `{Self}`")
+)]
+pub unsafe trait Immutable {
+ // The `Self: Sized` bound makes it so that `Immutable` is still object
+ // safe.
+ #[doc(hidden)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+}
+
+/// Implements [`TryFromBytes`].
+///
+/// This derive synthesizes the runtime checks required to check whether a
+/// sequence of initialized bytes corresponds to a valid instance of a type.
+/// This derive can be applied to structs, enums, and unions; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{TryFromBytes, Immutable};
+/// #[derive(TryFromBytes)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(TryFromBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # V00,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(TryFromBytes, Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// # Portability
+///
+/// To ensure consistent endianness for enums with multi-byte representations,
+/// explicitly specify and convert each discriminant using `.to_le()` or
+/// `.to_be()`; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::TryFromBytes;
+/// // `DataStoreVersion` is encoded in little-endian.
+/// #[derive(TryFromBytes)]
+/// #[repr(u32)]
+/// pub enum DataStoreVersion {
+/// /// Version 1 of the data store.
+/// V1 = 9u32.to_le(),
+///
+/// /// Version 2 of the data store.
+/// V2 = 10u32.to_le(),
+/// }
+/// ```
+///
+/// [safety conditions]: trait@TryFromBytes#safety
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::TryFromBytes;
+
+/// Types for which some bit patterns are valid.
+///
+/// A memory region of the appropriate length which contains initialized bytes
+/// can be viewed as a `TryFromBytes` type so long as the runtime value of those
+/// bytes corresponds to a [*valid instance*] of that type. For example,
+/// [`bool`] is `TryFromBytes`, so zerocopy can transmute a [`u8`] into a
+/// [`bool`] so long as it first checks that the value of the [`u8`] is `0` or
+/// `1`.
+///
+/// # Implementation
+///
+/// **Do not implement this trait yourself!** Instead, use
+/// [`#[derive(TryFromBytes)]`][derive]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{TryFromBytes, Immutable};
+/// #[derive(TryFromBytes)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(TryFromBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # V00,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(TryFromBytes, Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// This derive ensures that the runtime check of whether bytes correspond to a
+/// valid instance is sound. You **must** implement this trait via the derive.
+///
+/// # What is a "valid instance"?
+///
+/// In Rust, each type has *bit validity*, which refers to the set of bit
+/// patterns which may appear in an instance of that type. It is impossible for
+/// safe Rust code to produce values which violate bit validity (ie, values
+/// outside of the "valid" set of bit patterns). If `unsafe` code produces an
+/// invalid value, this is considered [undefined behavior].
+///
+/// Rust's bit validity rules are currently being decided, which means that some
+/// types have three classes of bit patterns: those which are definitely valid,
+/// and whose validity is documented in the language; those which may or may not
+/// be considered valid at some point in the future; and those which are
+/// definitely invalid.
+///
+/// Zerocopy takes a conservative approach, and only considers a bit pattern to
+/// be valid if its validity is a documented guarantee provided by the
+/// language.
+///
+/// For most use cases, Rust's current guarantees align with programmers'
+/// intuitions about what ought to be valid. As a result, zerocopy's
+/// conservatism should not affect most users.
+///
+/// If you are negatively affected by lack of support for a particular type,
+/// we encourage you to let us know by [filing an issue][github-repo].
+///
+/// # `TryFromBytes` is not symmetrical with [`IntoBytes`]
+///
+/// There are some types which implement both `TryFromBytes` and [`IntoBytes`],
+/// but for which `TryFromBytes` is not guaranteed to accept all byte sequences
+/// produced by `IntoBytes`. In other words, for some `T: TryFromBytes +
+/// IntoBytes`, there exist values of `t: T` such that
+/// `TryFromBytes::try_ref_from_bytes(t.as_bytes()) == None`. Code should not
+/// generally assume that values produced by `IntoBytes` will necessarily be
+/// accepted as valid by `TryFromBytes`.
+///
+/// # Safety
+///
+/// On its own, `T: TryFromBytes` does not make any guarantees about the layout
+/// or representation of `T`. It merely provides the ability to perform a
+/// validity check at runtime via methods like [`try_ref_from_bytes`].
+///
+/// You must not rely on the `#[doc(hidden)]` internals of `TryFromBytes`.
+/// Future releases of zerocopy may make backwards-breaking changes to these
+/// items, including changes that only affect soundness, which may cause code
+/// which uses those items to silently become unsound.
+///
+/// [undefined behavior]: https://raphlinus.github.io/programming/rust/2018/08/17/undefined-behavior.html
+/// [github-repo]: https://github.com/google/zerocopy
+/// [`try_ref_from_bytes`]: TryFromBytes::try_ref_from_bytes
+/// [*valid instance*]: #what-is-a-valid-instance
+#[cfg_attr(feature = "derive", doc = "[derive]: zerocopy_derive::TryFromBytes")]
+#[cfg_attr(
+ not(feature = "derive"),
+ doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.TryFromBytes.html"),
+)]
+#[cfg_attr(
+ not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
+ diagnostic::on_unimplemented(note = "Consider adding `#[derive(TryFromBytes)]` to `{Self}`")
+)]
+pub unsafe trait TryFromBytes {
+ // The `Self: Sized` bound makes it so that `TryFromBytes` is still object
+ // safe.
+ #[doc(hidden)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// Does a given memory range contain a valid instance of `Self`?
+ ///
+ /// # Safety
+ ///
+ /// Unsafe code may assume that, if `is_bit_valid(candidate)` returns true,
+ /// `*candidate` contains a valid `Self`.
+ ///
+ /// # Panics
+ ///
+ /// `is_bit_valid` may panic. Callers are responsible for ensuring that any
+ /// `unsafe` code remains sound even in the face of `is_bit_valid`
+ /// panicking. (We support user-defined validation routines; so long as
+ /// these routines are not required to be `unsafe`, there is no way to
+ /// ensure that these do not generate panics.)
+ ///
+ /// Besides user-defined validation routines panicking, `is_bit_valid` will
+ /// either panic or fail to compile if called on a pointer with [`Shared`]
+ /// aliasing when `Self: !Immutable`.
+ ///
+ /// [`UnsafeCell`]: core::cell::UnsafeCell
+ /// [`Shared`]: invariant::Shared
+ #[doc(hidden)]
+ fn is_bit_valid<A>(candidate: Maybe<'_, Self, A>) -> bool
+ where
+ A: invariant::Alignment;
+
+ /// Attempts to interpret the given `source` as a `&Self`.
+ ///
+ /// If the bytes of `source` are a valid instance of `Self`, this method
+ /// returns a reference to those bytes interpreted as a `Self`. If the
+ /// length of `source` is not a [valid size of `Self`][valid-size], or if
+ /// `source` is not appropriately aligned, or if `source` is not a valid
+ /// instance of `Self`, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][ConvertError::from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: u16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let _ = ZSTy::try_ref_from_bytes(0u16.as_bytes()); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the byte sequence `0xC0C0`.
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
+ ///
+ /// let packet = Packet::try_ref_from_bytes(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &[0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
+ /// assert!(Packet::try_ref_from_bytes(bytes).is_err());
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_ref_from_bytes",
+ format = "coco",
+ arity = 3,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 3
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_ref_from_bytes(source: &[u8]) -> Result<&Self, TryCastError<&[u8], Self>>
+ where
+ Self: KnownLayout + Immutable,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ match Ptr::from_ref(source).try_cast_into_no_leftover::<Self, BecauseImmutable>(None) {
+ Ok(source) => {
+ // This call may panic. If that happens, it doesn't cause any soundness
+ // issues, as we have not generated any invalid state which we need to
+ // fix before returning.
+ match source.try_into_valid() {
+ Ok(valid) => Ok(valid.as_ref()),
+ Err(e) => {
+ Err(e.map_src(|src| src.as_bytes::<BecauseImmutable>().as_ref()).into())
+ }
+ }
+ }
+ Err(e) => Err(e.map_src(Ptr::as_ref).into()),
+ }
+ }
+
+ /// Attempts to interpret the prefix of the given `source` as a `&Self`.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the leading bytes of `source`. If that prefix is a valid
+ /// instance of `Self`, this method returns a reference to those bytes
+ /// interpreted as `Self`, and a reference to the remaining bytes. If there
+ /// are insufficient bytes, or if `source` is not appropriately aligned, or
+ /// if those bytes are not a valid instance of `Self`, this returns `Err`.
+ /// If [`Self: Unaligned`][self-unaligned], you can [infallibly discard the
+ /// alignment error][ConvertError::from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: u16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let _ = ZSTy::try_ref_from_prefix(0u16.as_bytes()); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `Packet`.
+ /// let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
+ ///
+ /// let (packet, suffix) = Packet::try_ref_from_prefix(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
+ /// assert_eq!(suffix, &[6u8][..]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &[0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
+ /// assert!(Packet::try_ref_from_prefix(bytes).is_err());
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_ref_from_prefix",
+ format = "coco",
+ arity = 3,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 3
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_ref_from_prefix(source: &[u8]) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>
+ where
+ Self: KnownLayout + Immutable,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ try_ref_from_prefix_suffix(source, CastType::Prefix, None)
+ }
+
+ /// Attempts to interpret the suffix of the given `source` as a `&Self`.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the trailing bytes of `source`. If that suffix is a
+ /// valid instance of `Self`, this method returns a reference to those bytes
+ /// interpreted as `Self`, and a reference to the preceding bytes. If there
+ /// are insufficient bytes, or if the suffix of `source` would not be
+ /// appropriately aligned, or if the suffix is not a valid instance of
+ /// `Self`, this returns `Err`. If [`Self: Unaligned`][self-unaligned], you
+ /// can [infallibly discard the alignment error][ConvertError::from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: u16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let _ = ZSTy::try_ref_from_suffix(0u16.as_bytes()); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `Packet`.
+ /// let bytes = &[0, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let (prefix, packet) = Packet::try_ref_from_suffix(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ /// assert_eq!(prefix, &[0u8][..]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0x10][..];
+ /// assert!(Packet::try_ref_from_suffix(bytes).is_err());
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_ref_from_suffix",
+ format = "coco",
+ arity = 3,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 3
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_ref_from_suffix(source: &[u8]) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>
+ where
+ Self: KnownLayout + Immutable,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ try_ref_from_prefix_suffix(source, CastType::Suffix, None).map(swap)
+ }
+
+ /// Attempts to interpret the given `source` as a `&mut Self` without
+ /// copying.
+ ///
+ /// If the bytes of `source` are a valid instance of `Self`, this method
+ /// returns a reference to those bytes interpreted as a `Self`. If the
+ /// length of `source` is not a [valid size of `Self`][valid-size], or if
+ /// `source` is not appropriately aligned, or if `source` is not a valid
+ /// instance of `Self`, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][ConvertError::from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut source = [85, 85];
+ /// let _ = ZSTy::try_mut_from_bytes(&mut source[..]); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &mut [0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
+ ///
+ /// let packet = Packet::try_mut_from_bytes(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
+ ///
+ /// packet.temperature = 111;
+ ///
+ /// assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 0, 1, 2, 3, 4, 5]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
+ /// assert!(Packet::try_mut_from_bytes(bytes).is_err());
+ /// ```
+ ///
+ #[doc = codegen_header!("h5", "try_mut_from_bytes")]
+ ///
+ /// See [`TryFromBytes::try_ref_from_bytes`](#method.try_ref_from_bytes.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_mut_from_bytes(bytes: &mut [u8]) -> Result<&mut Self, TryCastError<&mut [u8], Self>>
+ where
+ Self: KnownLayout + IntoBytes,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ match Ptr::from_mut(bytes).try_cast_into_no_leftover::<Self, BecauseExclusive>(None) {
+ Ok(source) => {
+ // This call may panic. If that happens, it doesn't cause any soundness
+ // issues, as we have not generated any invalid state which we need to
+ // fix before returning.
+ match source.try_into_valid() {
+ Ok(source) => Ok(source.as_mut()),
+ Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()),
+ }
+ }
+ Err(e) => Err(e.map_src(Ptr::as_mut).into()),
+ }
+ }
+
+ /// Attempts to interpret the prefix of the given `source` as a `&mut
+ /// Self`.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the leading bytes of `source`. If that prefix is a valid
+ /// instance of `Self`, this method returns a reference to those bytes
+ /// interpreted as `Self`, and a reference to the remaining bytes. If there
+ /// are insufficient bytes, or if `source` is not appropriately aligned, or
+ /// if the bytes are not a valid instance of `Self`, this returns `Err`. If
+ /// [`Self: Unaligned`][self-unaligned], you can [infallibly discard the
+ /// alignment error][ConvertError::from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut source = [85, 85];
+ /// let _ = ZSTy::try_mut_from_prefix(&mut source[..]); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `Packet`.
+ /// let bytes = &mut [0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
+ ///
+ /// let (packet, suffix) = Packet::try_mut_from_prefix(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
+ /// assert_eq!(suffix, &[6u8][..]);
+ ///
+ /// packet.temperature = 111;
+ /// suffix[0] = 222;
+ ///
+ /// assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 0, 1, 2, 3, 4, 5, 222]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
+ /// assert!(Packet::try_mut_from_prefix(bytes).is_err());
+ /// ```
+ ///
+ #[doc = codegen_header!("h5", "try_mut_from_prefix")]
+ ///
+ /// See [`TryFromBytes::try_ref_from_prefix`](#method.try_ref_from_prefix.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_mut_from_prefix(
+ source: &mut [u8],
+ ) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>
+ where
+ Self: KnownLayout + IntoBytes,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ try_mut_from_prefix_suffix(source, CastType::Prefix, None)
+ }
+
+ /// Attempts to interpret the suffix of the given `source` as a `&mut
+ /// Self`.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the trailing bytes of `source`. If that suffix is a
+ /// valid instance of `Self`, this method returns a reference to those bytes
+ /// interpreted as `Self`, and a reference to the preceding bytes. If there
+ /// are insufficient bytes, or if the suffix of `source` would not be
+ /// appropriately aligned, or if the suffix is not a valid instance of
+ /// `Self`, this returns `Err`. If [`Self: Unaligned`][self-unaligned], you
+ /// can [infallibly discard the alignment error][ConvertError::from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: u16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut source = [85, 85];
+ /// let _ = ZSTy::try_mut_from_suffix(&mut source[..]); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `Packet`.
+ /// let bytes = &mut [0, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let (prefix, packet) = Packet::try_mut_from_suffix(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ /// assert_eq!(prefix, &[0u8][..]);
+ ///
+ /// prefix[0] = 111;
+ /// packet.temperature = 222;
+ ///
+ /// assert_eq!(bytes, [111, 0xC0, 0xC0, 240, 222, 2, 3, 4, 5, 6, 7]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0x10][..];
+ /// assert!(Packet::try_mut_from_suffix(bytes).is_err());
+ /// ```
+ ///
+ #[doc = codegen_header!("h5", "try_mut_from_suffix")]
+ ///
+ /// See [`TryFromBytes::try_ref_from_suffix`](#method.try_ref_from_suffix.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_mut_from_suffix(
+ source: &mut [u8],
+ ) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>
+ where
+ Self: KnownLayout + IntoBytes,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ try_mut_from_prefix_suffix(source, CastType::Suffix, None).map(swap)
+ }
+
+ /// Attempts to interpret the given `source` as a `&Self` with a DST length
+ /// equal to `count`.
+ ///
+ /// This method attempts to return a reference to `source` interpreted as a
+ /// `Self` with `count` trailing elements. If the length of `source` is not
+ /// equal to the size of `Self` with `count` elements, if `source` is not
+ /// appropriately aligned, or if `source` does not contain a valid instance
+ /// of `Self`, this returns `Err`. If [`Self: Unaligned`][self-unaligned],
+ /// you can [infallibly discard the alignment error][ConvertError::from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![allow(non_camel_case_types)] // For C0::xC0
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &[0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let packet = Packet::try_ref_from_bytes_with_elems(bytes, 3).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0xC0][..];
+ /// assert!(Packet::try_ref_from_bytes_with_elems(bytes, 3).is_err());
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`try_ref_from_bytes`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use core::num::NonZeroU16;
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: NonZeroU16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = 0xCAFEu16.as_bytes();
+ /// let zsty = ZSTy::try_ref_from_bytes_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`try_ref_from_bytes`]: TryFromBytes::try_ref_from_bytes
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_ref_from_bytes_with_elems",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 2
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_ref_from_bytes_with_elems(
+ source: &[u8],
+ count: usize,
+ ) -> Result<&Self, TryCastError<&[u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + Immutable,
+ {
+ match Ptr::from_ref(source).try_cast_into_no_leftover::<Self, BecauseImmutable>(Some(count))
+ {
+ Ok(source) => {
+ // This call may panic. If that happens, it doesn't cause any soundness
+ // issues, as we have not generated any invalid state which we need to
+ // fix before returning.
+ match source.try_into_valid() {
+ Ok(source) => Ok(source.as_ref()),
+ Err(e) => {
+ Err(e.map_src(|src| src.as_bytes::<BecauseImmutable>().as_ref()).into())
+ }
+ }
+ }
+ Err(e) => Err(e.map_src(Ptr::as_ref).into()),
+ }
+ }
+
+ /// Attempts to interpret the prefix of the given `source` as a `&Self` with
+ /// a DST length equal to `count`.
+ ///
+ /// This method attempts to return a reference to the prefix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the remaining bytes. If the length of `source` is less than the size
+ /// of `Self` with `count` elements, if `source` is not appropriately
+ /// aligned, or if the prefix of `source` does not contain a valid instance
+ /// of `Self`, this returns `Err`. If [`Self: Unaligned`][self-unaligned],
+ /// you can [infallibly discard the alignment error][ConvertError::from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![allow(non_camel_case_types)] // For C0::xC0
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &[0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7, 8][..];
+ ///
+ /// let (packet, suffix) = Packet::try_ref_from_prefix_with_elems(bytes, 3).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ /// assert_eq!(suffix, &[8u8][..]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
+ /// assert!(Packet::try_ref_from_prefix_with_elems(bytes, 3).is_err());
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`try_ref_from_prefix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use core::num::NonZeroU16;
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: NonZeroU16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = 0xCAFEu16.as_bytes();
+ /// let (zsty, _) = ZSTy::try_ref_from_prefix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`try_ref_from_prefix`]: TryFromBytes::try_ref_from_prefix
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_ref_from_prefix_with_elems",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 2
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_ref_from_prefix_with_elems(
+ source: &[u8],
+ count: usize,
+ ) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + Immutable,
+ {
+ try_ref_from_prefix_suffix(source, CastType::Prefix, Some(count))
+ }
+
+ /// Attempts to interpret the suffix of the given `source` as a `&Self` with
+ /// a DST length equal to `count`.
+ ///
+ /// This method attempts to return a reference to the suffix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the preceding bytes. If the length of `source` is less than the size
+ /// of `Self` with `count` elements, if the suffix of `source` is not
+ /// appropriately aligned, or if the suffix of `source` does not contain a
+ /// valid instance of `Self`, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][ConvertError::from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![allow(non_camel_case_types)] // For C0::xC0
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &[123, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let (prefix, packet) = Packet::try_ref_from_suffix_with_elems(bytes, 3).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ /// assert_eq!(prefix, &[123u8][..]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
+ /// assert!(Packet::try_ref_from_suffix_with_elems(bytes, 3).is_err());
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`try_ref_from_prefix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use core::num::NonZeroU16;
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: NonZeroU16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = 0xCAFEu16.as_bytes();
+ /// let (_, zsty) = ZSTy::try_ref_from_suffix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`try_ref_from_prefix`]: TryFromBytes::try_ref_from_prefix
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_ref_from_suffix_with_elems",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 2
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_ref_from_suffix_with_elems(
+ source: &[u8],
+ count: usize,
+ ) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + Immutable,
+ {
+ try_ref_from_prefix_suffix(source, CastType::Suffix, Some(count)).map(swap)
+ }
+
+ /// Attempts to interpret the given `source` as a `&mut Self` with a DST
+ /// length equal to `count`.
+ ///
+ /// This method attempts to return a reference to `source` interpreted as a
+ /// `Self` with `count` trailing elements. If the length of `source` is not
+ /// equal to the size of `Self` with `count` elements, if `source` is not
+ /// appropriately aligned, or if `source` does not contain a valid instance
+ /// of `Self`, this returns `Err`. If [`Self: Unaligned`][self-unaligned],
+ /// you can [infallibly discard the alignment error][ConvertError::from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![allow(non_camel_case_types)] // For C0::xC0
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &mut [0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let packet = Packet::try_mut_from_bytes_with_elems(bytes, 3).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ ///
+ /// packet.temperature = 111;
+ ///
+ /// assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 2, 3, 4, 5, 6, 7]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0xC0][..];
+ /// assert!(Packet::try_mut_from_bytes_with_elems(bytes, 3).is_err());
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`try_mut_from_bytes`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use core::num::NonZeroU16;
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: NonZeroU16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut src = 0xCAFEu16;
+ /// let src = src.as_mut_bytes();
+ /// let zsty = ZSTy::try_mut_from_bytes_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`try_mut_from_bytes`]: TryFromBytes::try_mut_from_bytes
+ ///
+ #[doc = codegen_header!("h5", "try_mut_from_bytes_with_elems")]
+ ///
+ /// See [`TryFromBytes::try_ref_from_bytes_with_elems`](#method.try_ref_from_bytes_with_elems.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_mut_from_bytes_with_elems(
+ source: &mut [u8],
+ count: usize,
+ ) -> Result<&mut Self, TryCastError<&mut [u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + IntoBytes,
+ {
+ match Ptr::from_mut(source).try_cast_into_no_leftover::<Self, BecauseExclusive>(Some(count))
+ {
+ Ok(source) => {
+ // This call may panic. If that happens, it doesn't cause any soundness
+ // issues, as we have not generated any invalid state which we need to
+ // fix before returning.
+ match source.try_into_valid() {
+ Ok(source) => Ok(source.as_mut()),
+ Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()),
+ }
+ }
+ Err(e) => Err(e.map_src(Ptr::as_mut).into()),
+ }
+ }
+
+ /// Attempts to interpret the prefix of the given `source` as a `&mut Self`
+ /// with a DST length equal to `count`.
+ ///
+ /// This method attempts to return a reference to the prefix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the remaining bytes. If the length of `source` is less than the size
+ /// of `Self` with `count` elements, if `source` is not appropriately
+ /// aligned, or if the prefix of `source` does not contain a valid instance
+ /// of `Self`, this returns `Err`. If [`Self: Unaligned`][self-unaligned],
+ /// you can [infallibly discard the alignment error][ConvertError::from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![allow(non_camel_case_types)] // For C0::xC0
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &mut [0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7, 8][..];
+ ///
+ /// let (packet, suffix) = Packet::try_mut_from_prefix_with_elems(bytes, 3).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ /// assert_eq!(suffix, &[8u8][..]);
+ ///
+ /// packet.temperature = 111;
+ /// suffix[0] = 222;
+ ///
+ /// assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 2, 3, 4, 5, 6, 7, 222]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
+ /// assert!(Packet::try_mut_from_prefix_with_elems(bytes, 3).is_err());
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`try_mut_from_prefix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use core::num::NonZeroU16;
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: NonZeroU16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut src = 0xCAFEu16;
+ /// let src = src.as_mut_bytes();
+ /// let (zsty, _) = ZSTy::try_mut_from_prefix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`try_mut_from_prefix`]: TryFromBytes::try_mut_from_prefix
+ ///
+ #[doc = codegen_header!("h5", "try_mut_from_prefix_with_elems")]
+ ///
+ /// See [`TryFromBytes::try_ref_from_prefix_with_elems`](#method.try_ref_from_prefix_with_elems.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_mut_from_prefix_with_elems(
+ source: &mut [u8],
+ count: usize,
+ ) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + IntoBytes,
+ {
+ try_mut_from_prefix_suffix(source, CastType::Prefix, Some(count))
+ }
+
+ /// Attempts to interpret the suffix of the given `source` as a `&mut Self`
+ /// with a DST length equal to `count`.
+ ///
+ /// This method attempts to return a reference to the suffix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the preceding bytes. If the length of `source` is less than the size
+ /// of `Self` with `count` elements, if the suffix of `source` is not
+ /// appropriately aligned, or if the suffix of `source` does not contain a
+ /// valid instance of `Self`, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][ConvertError::from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![allow(non_camel_case_types)] // For C0::xC0
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// marshmallows: [[u8; 2]],
+ /// }
+ ///
+ /// let bytes = &mut [123, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let (prefix, packet) = Packet::try_mut_from_suffix_with_elems(bytes, 3).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
+ /// assert_eq!(prefix, &[123u8][..]);
+ ///
+ /// prefix[0] = 111;
+ /// packet.temperature = 222;
+ ///
+ /// assert_eq!(bytes, [111, 0xC0, 0xC0, 240, 222, 2, 3, 4, 5, 6, 7]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
+ /// assert!(Packet::try_mut_from_suffix_with_elems(bytes, 3).is_err());
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`try_mut_from_prefix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use core::num::NonZeroU16;
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(TryFromBytes, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: NonZeroU16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut src = 0xCAFEu16;
+ /// let src = src.as_mut_bytes();
+ /// let (_, zsty) = ZSTy::try_mut_from_suffix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`try_mut_from_prefix`]: TryFromBytes::try_mut_from_prefix
+ ///
+ #[doc = codegen_header!("h5", "try_mut_from_suffix_with_elems")]
+ ///
+ /// See [`TryFromBytes::try_ref_from_suffix_with_elems`](#method.try_ref_from_suffix_with_elems.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_mut_from_suffix_with_elems(
+ source: &mut [u8],
+ count: usize,
+ ) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + IntoBytes,
+ {
+ try_mut_from_prefix_suffix(source, CastType::Suffix, Some(count)).map(swap)
+ }
+
+ /// Attempts to read the given `source` as a `Self`.
+ ///
+ /// If `source.len() != size_of::<Self>()` or the bytes are not a valid
+ /// instance of `Self`, this returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// }
+ ///
+ /// let bytes = &[0xC0, 0xC0, 240, 77][..];
+ ///
+ /// let packet = Packet::try_read_from_bytes(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &mut [0x10, 0xC0, 240, 77][..];
+ /// assert!(Packet::try_read_from_bytes(bytes).is_err());
+ /// ```
+ ///
+ /// # Performance Considerations
+ ///
+ /// In this version of zerocopy, this method reads the `source` into a
+ /// well-aligned stack allocation and *then* validates that the allocation
+ /// is a valid `Self`. This ensures that validation can be performed using
+ /// aligned reads (which carry a performance advantage over unaligned reads
+ /// on many platforms) at the cost of an unconditional copy.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_read_from_bytes",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_read_from_bytes(source: &[u8]) -> Result<Self, TryReadError<&[u8], Self>>
+ where
+ Self: Sized,
+ {
+ // FIXME(#2981): If `align_of::<Self>() == 1`, validate `source` in-place.
+
+ let candidate = match CoreMaybeUninit::<Self>::read_from_bytes(source) {
+ Ok(candidate) => candidate,
+ Err(e) => {
+ return Err(TryReadError::Size(e.with_dst()));
+ }
+ };
+ // SAFETY: `candidate` was copied from from `source: &[u8]`, so all of
+ // its bytes are initialized.
+ unsafe { try_read_from(source, candidate) }
+ }
+
+ /// Attempts to read a `Self` from the prefix of the given `source`.
+ ///
+ /// This attempts to read a `Self` from the first `size_of::<Self>()` bytes
+ /// of `source`, returning that `Self` and any remaining bytes. If
+ /// `source.len() < size_of::<Self>()` or the bytes are not a valid instance
+ /// of `Self`, it returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `Packet`.
+ /// let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
+ ///
+ /// let (packet, suffix) = Packet::try_read_from_prefix(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(suffix, &[0u8, 1, 2, 3, 4, 5, 6][..]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &[0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
+ /// assert!(Packet::try_read_from_prefix(bytes).is_err());
+ /// ```
+ ///
+ /// # Performance Considerations
+ ///
+ /// In this version of zerocopy, this method reads the `source` into a
+ /// well-aligned stack allocation and *then* validates that the allocation
+ /// is a valid `Self`. This ensures that validation can be performed using
+ /// aligned reads (which carry a performance advantage over unaligned reads
+ /// on many platforms) at the cost of an unconditional copy.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_read_from_prefix",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_read_from_prefix(source: &[u8]) -> Result<(Self, &[u8]), TryReadError<&[u8], Self>>
+ where
+ Self: Sized,
+ {
+ // FIXME(#2981): If `align_of::<Self>() == 1`, validate `source` in-place.
+
+ let (candidate, suffix) = match CoreMaybeUninit::<Self>::read_from_prefix(source) {
+ Ok(candidate) => candidate,
+ Err(e) => {
+ return Err(TryReadError::Size(e.with_dst()));
+ }
+ };
+ // SAFETY: `candidate` was copied from from `source: &[u8]`, so all of
+ // its bytes are initialized.
+ unsafe { try_read_from(source, candidate).map(|slf| (slf, suffix)) }
+ }
+
+ /// Attempts to read a `Self` from the suffix of the given `source`.
+ ///
+ /// This attempts to read a `Self` from the last `size_of::<Self>()` bytes
+ /// of `source`, returning that `Self` and any preceding bytes. If
+ /// `source.len() < size_of::<Self>()` or the bytes are not a valid instance
+ /// of `Self`, it returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![allow(non_camel_case_types)] // For C0::xC0
+ /// use zerocopy::TryFromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// // The only valid value of this type is the byte `0xC0`
+ /// #[derive(TryFromBytes)]
+ /// #[repr(u8)]
+ /// enum C0 { xC0 = 0xC0 }
+ ///
+ /// // The only valid value of this type is the bytes `0xC0C0`.
+ /// #[derive(TryFromBytes)]
+ /// #[repr(C)]
+ /// struct C0C0(C0, C0);
+ ///
+ /// #[derive(TryFromBytes)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// magic_number: C0C0,
+ /// mug_size: u8,
+ /// temperature: u8,
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `Packet`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 0xC0, 0xC0, 240, 77][..];
+ ///
+ /// let (prefix, packet) = Packet::try_read_from_suffix(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.mug_size, 240);
+ /// assert_eq!(packet.temperature, 77);
+ /// assert_eq!(prefix, &[0u8, 1, 2, 3, 4, 5][..]);
+ ///
+ /// // These bytes are not valid instance of `Packet`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 0x10, 0xC0, 240, 77][..];
+ /// assert!(Packet::try_read_from_suffix(bytes).is_err());
+ /// ```
+ ///
+ /// # Performance Considerations
+ ///
+ /// In this version of zerocopy, this method reads the `source` into a
+ /// well-aligned stack allocation and *then* validates that the allocation
+ /// is a valid `Self`. This ensures that validation can be performed using
+ /// aligned reads (which carry a performance advantage over unaligned reads
+ /// on many platforms) at the cost of an unconditional copy.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "try_read_from_suffix",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn try_read_from_suffix(source: &[u8]) -> Result<(&[u8], Self), TryReadError<&[u8], Self>>
+ where
+ Self: Sized,
+ {
+ // FIXME(#2981): If `align_of::<Self>() == 1`, validate `source` in-place.
+
+ let (prefix, candidate) = match CoreMaybeUninit::<Self>::read_from_suffix(source) {
+ Ok(candidate) => candidate,
+ Err(e) => {
+ return Err(TryReadError::Size(e.with_dst()));
+ }
+ };
+ // SAFETY: `candidate` was copied from from `source: &[u8]`, so all of
+ // its bytes are initialized.
+ unsafe { try_read_from(source, candidate).map(|slf| (prefix, slf)) }
+ }
+}
+
+#[inline(always)]
+fn try_ref_from_prefix_suffix<T: TryFromBytes + KnownLayout + Immutable + ?Sized>(
+ source: &[u8],
+ cast_type: CastType,
+ meta: Option<T::PointerMetadata>,
+) -> Result<(&T, &[u8]), TryCastError<&[u8], T>> {
+ match Ptr::from_ref(source).try_cast_into::<T, BecauseImmutable>(cast_type, meta) {
+ Ok((source, prefix_suffix)) => {
+ // This call may panic. If that happens, it doesn't cause any soundness
+ // issues, as we have not generated any invalid state which we need to
+ // fix before returning.
+ match source.try_into_valid() {
+ Ok(valid) => Ok((valid.as_ref(), prefix_suffix.as_ref())),
+ Err(e) => Err(e.map_src(|src| src.as_bytes::<BecauseImmutable>().as_ref()).into()),
+ }
+ }
+ Err(e) => Err(e.map_src(Ptr::as_ref).into()),
+ }
+}
+
+#[inline(always)]
+fn try_mut_from_prefix_suffix<T: IntoBytes + TryFromBytes + KnownLayout + ?Sized>(
+ candidate: &mut [u8],
+ cast_type: CastType,
+ meta: Option<T::PointerMetadata>,
+) -> Result<(&mut T, &mut [u8]), TryCastError<&mut [u8], T>> {
+ match Ptr::from_mut(candidate).try_cast_into::<T, BecauseExclusive>(cast_type, meta) {
+ Ok((candidate, prefix_suffix)) => {
+ // This call may panic. If that happens, it doesn't cause any soundness
+ // issues, as we have not generated any invalid state which we need to
+ // fix before returning.
+ match candidate.try_into_valid() {
+ Ok(valid) => Ok((valid.as_mut(), prefix_suffix.as_mut())),
+ Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()),
+ }
+ }
+ Err(e) => Err(e.map_src(Ptr::as_mut).into()),
+ }
+}
+
+#[inline(always)]
+fn swap<T, U>((t, u): (T, U)) -> (U, T) {
+ (u, t)
+}
+
+/// # Safety
+///
+/// All bytes of `candidate` must be initialized.
+#[inline(always)]
+unsafe fn try_read_from<S, T: TryFromBytes>(
+ source: S,
+ mut candidate: CoreMaybeUninit<T>,
+) -> Result<T, TryReadError<S, T>> {
+ // We use `from_mut` despite not mutating via `c_ptr` so that we don't need
+ // to add a `T: Immutable` bound.
+ let c_ptr = Ptr::from_mut(&mut candidate);
+ // SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived from
+ // `candidate`, which the caller promises is entirely initialized. Since
+ // `candidate` is a `MaybeUninit`, it has no validity requirements, and so
+ // no values written to an `Initialized` `c_ptr` can violate its validity.
+ // Since `c_ptr` has `Exclusive` aliasing, no mutations may happen except
+ // via `c_ptr` so long as it is live, so we don't need to worry about the
+ // fact that `c_ptr` may have more restricted validity than `candidate`.
+ let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
+ let mut c_ptr = c_ptr.cast::<_, crate::pointer::cast::CastSized, _>();
+
+ // Since we don't have `T: KnownLayout`, we hack around that by using
+ // `Wrapping<T>`, which implements `KnownLayout` even if `T` doesn't.
+ //
+ // This call may panic. If that happens, it doesn't cause any soundness
+ // issues, as we have not generated any invalid state which we need to fix
+ // before returning.
+ if !Wrapping::<T>::is_bit_valid(c_ptr.reborrow_shared().forget_aligned()) {
+ return Err(ValidityError::new(source).into());
+ }
+
+ fn _assert_same_size_and_validity<T>()
+ where
+ Wrapping<T>: pointer::TransmuteFrom<T, invariant::Valid, invariant::Valid>,
+ T: pointer::TransmuteFrom<Wrapping<T>, invariant::Valid, invariant::Valid>,
+ {
+ }
+
+ _assert_same_size_and_validity::<T>();
+
+ // SAFETY: We just validated that `candidate` contains a valid
+ // `Wrapping<T>`, which has the same size and bit validity as `T`, as
+ // guaranteed by the preceding type assertion.
+ Ok(unsafe { candidate.assume_init() })
+}
+
+/// Types for which a sequence of `0` bytes is a valid instance.
+///
+/// Any memory region of the appropriate length which is guaranteed to contain
+/// only zero bytes can be viewed as any `FromZeros` type with no runtime
+/// overhead. This is useful whenever memory is known to be in a zeroed state,
+/// such memory returned from some allocation routines.
+///
+/// # Warning: Padding bytes
+///
+/// Note that, when a value is moved or copied, only the non-padding bytes of
+/// that value are guaranteed to be preserved. It is unsound to assume that
+/// values written to padding bytes are preserved after a move or copy. For more
+/// details, see the [`FromBytes` docs][frombytes-warning-padding-bytes].
+///
+/// [frombytes-warning-padding-bytes]: FromBytes#warning-padding-bytes
+///
+/// # Implementation
+///
+/// **Do not implement this trait yourself!** Instead, use
+/// [`#[derive(FromZeros)]`][derive]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{FromZeros, Immutable};
+/// #[derive(FromZeros)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromZeros)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant0,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromZeros, Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// This derive performs a sophisticated, compile-time safety analysis to
+/// determine whether a type is `FromZeros`.
+///
+/// # Safety
+///
+/// *This section describes what is required in order for `T: FromZeros`, and
+/// what unsafe code may assume of such types. If you don't plan on implementing
+/// `FromZeros` manually, and you don't plan on writing unsafe code that
+/// operates on `FromZeros` types, then you don't need to read this section.*
+///
+/// If `T: FromZeros`, then unsafe code may assume that it is sound to produce a
+/// `T` whose bytes are all initialized to zero. If a type is marked as
+/// `FromZeros` which violates this contract, it may cause undefined behavior.
+///
+/// `#[derive(FromZeros)]` only permits [types which satisfy these
+/// requirements][derive-analysis].
+///
+#[cfg_attr(
+ feature = "derive",
+ doc = "[derive]: zerocopy_derive::FromZeros",
+ doc = "[derive-analysis]: zerocopy_derive::FromZeros#analysis"
+)]
+#[cfg_attr(
+ not(feature = "derive"),
+ doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.FromZeros.html"),
+ doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.FromZeros.html#analysis"),
+)]
+#[cfg_attr(
+ not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
+ diagnostic::on_unimplemented(note = "Consider adding `#[derive(FromZeros)]` to `{Self}`")
+)]
+pub unsafe trait FromZeros: TryFromBytes {
+ // The `Self: Sized` bound makes it so that `FromZeros` is still object
+ // safe.
+ #[doc(hidden)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// Overwrites `self` with zeros.
+ ///
+ /// Sets every byte in `self` to 0. While this is similar to doing `*self =
+ /// Self::new_zeroed()`, it differs in that `zero` does not semantically
+ /// drop the current value and replace it with a new one — it simply
+ /// modifies the bytes of the existing value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use zerocopy::FromZeros;
+ /// # use zerocopy_derive::*;
+ /// #
+ /// #[derive(FromZeros)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// let mut header = PacketHeader {
+ /// src_port: 100u16.to_be_bytes(),
+ /// dst_port: 200u16.to_be_bytes(),
+ /// length: 300u16.to_be_bytes(),
+ /// checksum: 400u16.to_be_bytes(),
+ /// };
+ ///
+ /// header.zero();
+ ///
+ /// assert_eq!(header.src_port, [0, 0]);
+ /// assert_eq!(header.dst_port, [0, 0]);
+ /// assert_eq!(header.length, [0, 0]);
+ /// assert_eq!(header.checksum, [0, 0]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "zero",
+ format = "coco",
+ arity = 3,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 3
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[inline(always)]
+ fn zero(&mut self) {
+ let slf: *mut Self = self;
+ let len = mem::size_of_val(self);
+ // SAFETY:
+ // - `self` is guaranteed by the type system to be valid for writes of
+ // size `size_of_val(self)`.
+ // - `u8`'s alignment is 1, and thus `self` is guaranteed to be aligned
+ // as required by `u8`.
+ // - Since `Self: FromZeros`, the all-zeros instance is a valid instance
+ // of `Self.`
+ //
+ // FIXME(#429): Add references to docs and quotes.
+ unsafe { ptr::write_bytes(slf.cast::<u8>(), 0, len) };
+ }
+
+ /// Creates an instance of `Self` from zeroed bytes.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use zerocopy::FromZeros;
+ /// # use zerocopy_derive::*;
+ /// #
+ /// #[derive(FromZeros)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// let header: PacketHeader = FromZeros::new_zeroed();
+ ///
+ /// assert_eq!(header.src_port, [0, 0]);
+ /// assert_eq!(header.dst_port, [0, 0]);
+ /// assert_eq!(header.length, [0, 0]);
+ /// assert_eq!(header.checksum, [0, 0]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "new_zeroed",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn new_zeroed() -> Self
+ where
+ Self: Sized,
+ {
+ // SAFETY: `FromZeros` says that the all-zeros bit pattern is legal.
+ unsafe { mem::zeroed() }
+ }
+
+ /// Creates a `Box<Self>` from zeroed bytes.
+ ///
+ /// This function is useful for allocating large values on the heap and
+ /// zero-initializing them, without ever creating a temporary instance of
+ /// `Self` on the stack. For example, `<[u8; 1048576]>::new_box_zeroed()`
+ /// will allocate `[u8; 1048576]` directly on the heap; it does not require
+ /// storing `[u8; 1048576]` in a temporary variable on the stack.
+ ///
+ /// On systems that use a heap implementation that supports allocating from
+ /// pre-zeroed memory, using `new_box_zeroed` (or related functions) may
+ /// have performance benefits.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error on allocation failure. Allocation failure is guaranteed
+ /// never to cause a panic or an abort.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "new_box_zeroed",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects (other than allocation)"]
+ #[cfg(any(feature = "alloc", test))]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+ #[inline]
+ fn new_box_zeroed() -> Result<Box<Self>, AllocError>
+ where
+ Self: Sized,
+ {
+ // If `T` is a ZST, then return a proper boxed instance of it. There is
+ // no allocation, but `Box` does require a correct dangling pointer.
+ let layout = Layout::new::<Self>();
+ if layout.size() == 0 {
+ // Construct the `Box` from a dangling pointer to avoid calling
+ // `Self::new_zeroed`. This ensures that stack space is never
+ // allocated for `Self` even on lower opt-levels where this branch
+ // might not get optimized out.
+
+ // SAFETY: Per [1], when `T` is a ZST, `Box<T>`'s only validity
+ // requirements are that the pointer is non-null and sufficiently
+ // aligned. Per [2], `NonNull::dangling` produces a pointer which
+ // is sufficiently aligned. Since the produced pointer is a
+ // `NonNull`, it is non-null.
+ //
+ // [1] Per https://doc.rust-lang.org/1.81.0/std/boxed/index.html#memory-layout:
+ //
+ // For zero-sized values, the `Box` pointer has to be non-null and sufficiently aligned.
+ //
+ // [2] Per https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.dangling:
+ //
+ // Creates a new `NonNull` that is dangling, but well-aligned.
+ return Ok(unsafe { Box::from_raw(NonNull::dangling().as_ptr()) });
+ }
+
+ // FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ let ptr = unsafe { alloc::alloc::alloc_zeroed(layout).cast::<Self>() };
+ if ptr.is_null() {
+ return Err(AllocError);
+ }
+ // FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ Ok(unsafe { Box::from_raw(ptr) })
+ }
+
+ /// Creates a `Box<[Self]>` (a boxed slice) from zeroed bytes.
+ ///
+ /// This function is useful for allocating large values of `[Self]` on the
+ /// heap and zero-initializing them, without ever creating a temporary
+ /// instance of `[Self; _]` on the stack. For example,
+ /// `u8::new_box_slice_zeroed(1048576)` will allocate the slice directly on
+ /// the heap; it does not require storing the slice on the stack.
+ ///
+ /// On systems that use a heap implementation that supports allocating from
+ /// pre-zeroed memory, using `new_box_slice_zeroed` may have performance
+ /// benefits.
+ ///
+ /// If `Self` is a zero-sized type, then this function will return a
+ /// `Box<[Self]>` that has the correct `len`. Such a box cannot contain any
+ /// actual information, but its `len()` property will report the correct
+ /// value.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error on allocation failure. Allocation failure is
+ /// guaranteed never to cause a panic or an abort.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "new_box_zeroed_with_elems",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 2
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects (other than allocation)"]
+ #[cfg(feature = "alloc")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+ #[inline]
+ fn new_box_zeroed_with_elems(count: usize) -> Result<Box<Self>, AllocError>
+ where
+ Self: KnownLayout<PointerMetadata = usize>,
+ {
+ // SAFETY: `alloc::alloc::alloc_zeroed` is a valid argument of
+ // `new_box`. The referent of the pointer returned by `alloc_zeroed`
+ // (and, consequently, the `Box` derived from it) is a valid instance of
+ // `Self`, because `Self` is `FromZeros`.
+ unsafe { crate::util::new_box(count, alloc::alloc::alloc_zeroed) }
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromZeros::new_box_zeroed_with_elems`")]
+ #[doc(hidden)]
+ #[cfg(feature = "alloc")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+ #[must_use = "has no side effects (other than allocation)"]
+ #[inline(always)]
+ fn new_box_slice_zeroed(len: usize) -> Result<Box<[Self]>, AllocError>
+ where
+ Self: Sized,
+ {
+ <[Self]>::new_box_zeroed_with_elems(len)
+ }
+
+ /// Creates a `Vec<Self>` from zeroed bytes.
+ ///
+ /// This function is useful for allocating large values of `Vec`s and
+ /// zero-initializing them, without ever creating a temporary instance of
+ /// `[Self; _]` (or many temporary instances of `Self`) on the stack. For
+ /// example, `u8::new_vec_zeroed(1048576)` will allocate directly on the
+ /// heap; it does not require storing intermediate values on the stack.
+ ///
+ /// On systems that use a heap implementation that supports allocating from
+ /// pre-zeroed memory, using `new_vec_zeroed` may have performance benefits.
+ ///
+ /// If `Self` is a zero-sized type, then this function will return a
+ /// `Vec<Self>` that has the correct `len`. Such a `Vec` cannot contain any
+ /// actual information, but its `len()` property will report the correct
+ /// value.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error on allocation failure. Allocation failure is
+ /// guaranteed never to cause a panic or an abort.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "new_vec_zeroed",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects (other than allocation)"]
+ #[cfg(feature = "alloc")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+ #[inline(always)]
+ fn new_vec_zeroed(len: usize) -> Result<Vec<Self>, AllocError>
+ where
+ Self: Sized,
+ {
+ <[Self]>::new_box_zeroed_with_elems(len).map(Into::into)
+ }
+
+ /// Extends a `Vec<Self>` by pushing `additional` new items onto the end of
+ /// the vector. The new items are initialized with zeros.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "extend_vec_zeroed",
+ format = "coco_static_size",
+ )]
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[cfg(feature = "alloc")]
+ #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.57.0", feature = "alloc"))))]
+ #[inline(always)]
+ fn extend_vec_zeroed(v: &mut Vec<Self>, additional: usize) -> Result<(), AllocError>
+ where
+ Self: Sized,
+ {
+ // PANICS: We pass `v.len()` for `position`, so the `position > v.len()`
+ // panic condition is not satisfied.
+ <Self as FromZeros>::insert_vec_zeroed(v, v.len(), additional)
+ }
+
+ /// Inserts `additional` new items into `Vec<Self>` at `position`. The new
+ /// items are initialized with zeros.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `position > v.len()`.
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "insert_vec_zeroed",
+ format = "coco_static_size",
+ )]
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[cfg(feature = "alloc")]
+ #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.57.0", feature = "alloc"))))]
+ #[inline]
+ fn insert_vec_zeroed(
+ v: &mut Vec<Self>,
+ position: usize,
+ additional: usize,
+ ) -> Result<(), AllocError>
+ where
+ Self: Sized,
+ {
+ assert!(position <= v.len());
+ // We only conditionally compile on versions on which `try_reserve` is
+ // stable; the Clippy lint is a false positive.
+ v.try_reserve(additional).map_err(|_| AllocError)?;
+ // SAFETY: The `try_reserve` call guarantees that these cannot overflow:
+ // * `ptr.add(position)`
+ // * `position + additional`
+ // * `v.len() + additional`
+ //
+ // `v.len() - position` cannot overflow because we asserted that
+ // `position <= v.len()`.
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ unsafe {
+ // This is a potentially overlapping copy.
+ let ptr = v.as_mut_ptr();
+ #[allow(clippy::arithmetic_side_effects)]
+ ptr.add(position).copy_to(ptr.add(position + additional), v.len() - position);
+ ptr.add(position).write_bytes(0, additional);
+ #[allow(clippy::arithmetic_side_effects)]
+ v.set_len(v.len() + additional);
+ }
+
+ Ok(())
+ }
+}
+
+/// Analyzes whether a type is [`FromBytes`].
+///
+/// This derive analyzes, at compile time, whether the annotated type satisfies
+/// the [safety conditions] of `FromBytes` and implements `FromBytes` and its
+/// supertraits if it is sound to do so. This derive can be applied to structs,
+/// enums, and unions;
+/// e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{FromBytes, FromZeros, Immutable};
+/// #[derive(FromBytes)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # V00, V01, V02, V03, V04, V05, V06, V07, V08, V09, V0A, V0B, V0C, V0D, V0E,
+/// # V0F, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V1A, V1B, V1C, V1D,
+/// # V1E, V1F, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V2A, V2B, V2C,
+/// # V2D, V2E, V2F, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V3A, V3B,
+/// # V3C, V3D, V3E, V3F, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V4A,
+/// # V4B, V4C, V4D, V4E, V4F, V50, V51, V52, V53, V54, V55, V56, V57, V58, V59,
+/// # V5A, V5B, V5C, V5D, V5E, V5F, V60, V61, V62, V63, V64, V65, V66, V67, V68,
+/// # V69, V6A, V6B, V6C, V6D, V6E, V6F, V70, V71, V72, V73, V74, V75, V76, V77,
+/// # V78, V79, V7A, V7B, V7C, V7D, V7E, V7F, V80, V81, V82, V83, V84, V85, V86,
+/// # V87, V88, V89, V8A, V8B, V8C, V8D, V8E, V8F, V90, V91, V92, V93, V94, V95,
+/// # V96, V97, V98, V99, V9A, V9B, V9C, V9D, V9E, V9F, VA0, VA1, VA2, VA3, VA4,
+/// # VA5, VA6, VA7, VA8, VA9, VAA, VAB, VAC, VAD, VAE, VAF, VB0, VB1, VB2, VB3,
+/// # VB4, VB5, VB6, VB7, VB8, VB9, VBA, VBB, VBC, VBD, VBE, VBF, VC0, VC1, VC2,
+/// # VC3, VC4, VC5, VC6, VC7, VC8, VC9, VCA, VCB, VCC, VCD, VCE, VCF, VD0, VD1,
+/// # VD2, VD3, VD4, VD5, VD6, VD7, VD8, VD9, VDA, VDB, VDC, VDD, VDE, VDF, VE0,
+/// # VE1, VE2, VE3, VE4, VE5, VE6, VE7, VE8, VE9, VEA, VEB, VEC, VED, VEE, VEF,
+/// # VF0, VF1, VF2, VF3, VF4, VF5, VF6, VF7, VF8, VF9, VFA, VFB, VFC, VFD, VFE,
+/// # VFF,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromBytes, Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// [safety conditions]: trait@FromBytes#safety
+///
+/// # Analysis
+///
+/// *This section describes, roughly, the analysis performed by this derive to
+/// determine whether it is sound to implement `FromBytes` for a given type.
+/// Unless you are modifying the implementation of this derive, or attempting to
+/// manually implement `FromBytes` for a type yourself, you don't need to read
+/// this section.*
+///
+/// If a type has the following properties, then this derive can implement
+/// `FromBytes` for that type:
+///
+/// - If the type is a struct, all of its fields must be `FromBytes`.
+/// - If the type is an enum:
+/// - It must have a defined representation which is one of `u8`, `u16`, `i8`,
+/// or `i16`.
+/// - The maximum number of discriminants must be used (so that every possible
+/// bit pattern is a valid one).
+/// - Its fields must be `FromBytes`.
+///
+/// This analysis is subject to change. Unsafe code may *only* rely on the
+/// documented [safety conditions] of `FromBytes`, and must *not* rely on the
+/// implementation details of this derive.
+///
+/// ## Why isn't an explicit representation required for structs?
+///
+/// Neither this derive, nor the [safety conditions] of `FromBytes`, requires
+/// that structs are marked with `#[repr(C)]`.
+///
+/// Per the [Rust reference](reference),
+///
+/// > The representation of a type can change the padding between fields, but
+/// > does not change the layout of the fields themselves.
+///
+/// [reference]: https://doc.rust-lang.org/reference/type-layout.html#representations
+///
+/// Since the layout of structs only consists of padding bytes and field bytes,
+/// a struct is soundly `FromBytes` if:
+/// 1. its padding is soundly `FromBytes`, and
+/// 2. its fields are soundly `FromBytes`.
+///
+/// The answer to the first question is always yes: padding bytes do not have
+/// any validity constraints. A [discussion] of this question in the Unsafe Code
+/// Guidelines Working Group concluded that it would be virtually unimaginable
+/// for future versions of rustc to add validity constraints to padding bytes.
+///
+/// [discussion]: https://github.com/rust-lang/unsafe-code-guidelines/issues/174
+///
+/// Whether a struct is soundly `FromBytes` therefore solely depends on whether
+/// its fields are `FromBytes`.
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::FromBytes;
+
+/// Types for which any bit pattern is valid.
+///
+/// Any memory region of the appropriate length which contains initialized bytes
+/// can be viewed as any `FromBytes` type with no runtime overhead. This is
+/// useful for efficiently parsing bytes as structured data.
+///
+/// # Warning: Padding bytes
+///
+/// Note that, when a value is moved or copied, only the non-padding bytes of
+/// that value are guaranteed to be preserved. It is unsound to assume that
+/// values written to padding bytes are preserved after a move or copy. For
+/// example, the following is unsound:
+///
+/// ```rust,no_run
+/// use core::mem::{size_of, transmute};
+/// use zerocopy::FromZeros;
+/// # use zerocopy_derive::*;
+///
+/// // Assume `Foo` is a type with padding bytes.
+/// #[derive(FromZeros, Default)]
+/// struct Foo {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// let mut foo: Foo = Foo::default();
+/// FromZeros::zero(&mut foo);
+/// // UNSOUND: Although `FromZeros::zero` writes zeros to all bytes of `foo`,
+/// // those writes are not guaranteed to be preserved in padding bytes when
+/// // `foo` is moved, so this may expose padding bytes as `u8`s.
+/// let foo_bytes: [u8; size_of::<Foo>()] = unsafe { transmute(foo) };
+/// ```
+///
+/// # Implementation
+///
+/// **Do not implement this trait yourself!** Instead, use
+/// [`#[derive(FromBytes)]`][derive]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{FromBytes, Immutable};
+/// #[derive(FromBytes)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # V00, V01, V02, V03, V04, V05, V06, V07, V08, V09, V0A, V0B, V0C, V0D, V0E,
+/// # V0F, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V1A, V1B, V1C, V1D,
+/// # V1E, V1F, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V2A, V2B, V2C,
+/// # V2D, V2E, V2F, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V3A, V3B,
+/// # V3C, V3D, V3E, V3F, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V4A,
+/// # V4B, V4C, V4D, V4E, V4F, V50, V51, V52, V53, V54, V55, V56, V57, V58, V59,
+/// # V5A, V5B, V5C, V5D, V5E, V5F, V60, V61, V62, V63, V64, V65, V66, V67, V68,
+/// # V69, V6A, V6B, V6C, V6D, V6E, V6F, V70, V71, V72, V73, V74, V75, V76, V77,
+/// # V78, V79, V7A, V7B, V7C, V7D, V7E, V7F, V80, V81, V82, V83, V84, V85, V86,
+/// # V87, V88, V89, V8A, V8B, V8C, V8D, V8E, V8F, V90, V91, V92, V93, V94, V95,
+/// # V96, V97, V98, V99, V9A, V9B, V9C, V9D, V9E, V9F, VA0, VA1, VA2, VA3, VA4,
+/// # VA5, VA6, VA7, VA8, VA9, VAA, VAB, VAC, VAD, VAE, VAF, VB0, VB1, VB2, VB3,
+/// # VB4, VB5, VB6, VB7, VB8, VB9, VBA, VBB, VBC, VBD, VBE, VBF, VC0, VC1, VC2,
+/// # VC3, VC4, VC5, VC6, VC7, VC8, VC9, VCA, VCB, VCC, VCD, VCE, VCF, VD0, VD1,
+/// # VD2, VD3, VD4, VD5, VD6, VD7, VD8, VD9, VDA, VDB, VDC, VDD, VDE, VDF, VE0,
+/// # VE1, VE2, VE3, VE4, VE5, VE6, VE7, VE8, VE9, VEA, VEB, VEC, VED, VEE, VEF,
+/// # VF0, VF1, VF2, VF3, VF4, VF5, VF6, VF7, VF8, VF9, VFA, VFB, VFC, VFD, VFE,
+/// # VFF,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(FromBytes, Immutable)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// This derive performs a sophisticated, compile-time safety analysis to
+/// determine whether a type is `FromBytes`.
+///
+/// # Safety
+///
+/// *This section describes what is required in order for `T: FromBytes`, and
+/// what unsafe code may assume of such types. If you don't plan on implementing
+/// `FromBytes` manually, and you don't plan on writing unsafe code that
+/// operates on `FromBytes` types, then you don't need to read this section.*
+///
+/// If `T: FromBytes`, then unsafe code may assume that it is sound to produce a
+/// `T` whose bytes are initialized to any sequence of valid `u8`s (in other
+/// words, any byte value which is not uninitialized). If a type is marked as
+/// `FromBytes` which violates this contract, it may cause undefined behavior.
+///
+/// `#[derive(FromBytes)]` only permits [types which satisfy these
+/// requirements][derive-analysis].
+///
+#[cfg_attr(
+ feature = "derive",
+ doc = "[derive]: zerocopy_derive::FromBytes",
+ doc = "[derive-analysis]: zerocopy_derive::FromBytes#analysis"
+)]
+#[cfg_attr(
+ not(feature = "derive"),
+ doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.FromBytes.html"),
+ doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.FromBytes.html#analysis"),
+)]
+#[cfg_attr(
+ not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
+ diagnostic::on_unimplemented(note = "Consider adding `#[derive(FromBytes)]` to `{Self}`")
+)]
+pub unsafe trait FromBytes: FromZeros {
+ // The `Self: Sized` bound makes it so that `FromBytes` is still object
+ // safe.
+ #[doc(hidden)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// Interprets the given `source` as a `&Self`.
+ ///
+ /// This method attempts to return a reference to `source` interpreted as a
+ /// `Self`. If the length of `source` is not a [valid size of
+ /// `Self`][valid-size], or if `source` is not appropriately aligned, this
+ /// returns `Err`. If [`Self: Unaligned`][self-unaligned], you can
+ /// [infallibly discard the alignment error][size-error-from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: u16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let _ = ZSTy::ref_from_bytes(0u16.as_bytes()); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// #[derive(FromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// header: PacketHeader,
+ /// body: [u8],
+ /// }
+ ///
+ /// // These bytes encode a `Packet`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11][..];
+ ///
+ /// let packet = Packet::ref_from_bytes(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.header.src_port, [0, 1]);
+ /// assert_eq!(packet.header.dst_port, [2, 3]);
+ /// assert_eq!(packet.header.length, [4, 5]);
+ /// assert_eq!(packet.header.checksum, [6, 7]);
+ /// assert_eq!(packet.body, [8, 9, 10, 11]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "ref_from_bytes",
+ format = "coco",
+ arity = 3,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 3
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn ref_from_bytes(source: &[u8]) -> Result<&Self, CastError<&[u8], Self>>
+ where
+ Self: KnownLayout + Immutable,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ match Ptr::from_ref(source).try_cast_into_no_leftover::<_, BecauseImmutable>(None) {
+ Ok(ptr) => Ok(ptr.recall_validity().as_ref()),
+ Err(err) => Err(err.map_src(|src| src.as_ref())),
+ }
+ }
+
+ /// Interprets the prefix of the given `source` as a `&Self` without
+ /// copying.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the leading bytes of `source`, then attempts to return
+ /// both a reference to those bytes interpreted as a `Self`, and a reference
+ /// to the remaining bytes. If there are insufficient bytes, or if `source`
+ /// is not appropriately aligned, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][size-error-from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. See [`ref_from_prefix_with_elems`], which does
+ /// support such types. Attempting to use this method on such types results
+ /// in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: u16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let _ = ZSTy::ref_from_prefix(0u16.as_bytes()); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// [`ref_from_prefix_with_elems`]: FromBytes::ref_from_prefix_with_elems
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// #[derive(FromBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct Packet {
+ /// header: PacketHeader,
+ /// body: [[u8; 2]],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `Packet`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14][..];
+ ///
+ /// let (packet, suffix) = Packet::ref_from_prefix(bytes).unwrap();
+ ///
+ /// assert_eq!(packet.header.src_port, [0, 1]);
+ /// assert_eq!(packet.header.dst_port, [2, 3]);
+ /// assert_eq!(packet.header.length, [4, 5]);
+ /// assert_eq!(packet.header.checksum, [6, 7]);
+ /// assert_eq!(packet.body, [[8, 9], [10, 11], [12, 13]]);
+ /// assert_eq!(suffix, &[14u8][..]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "ref_from_prefix",
+ format = "coco",
+ arity = 3,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 3
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn ref_from_prefix(source: &[u8]) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>
+ where
+ Self: KnownLayout + Immutable,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ ref_from_prefix_suffix(source, None, CastType::Prefix)
+ }
+
+ /// Interprets the suffix of the given bytes as a `&Self`.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the trailing bytes of `source`, then attempts to return
+ /// both a reference to those bytes interpreted as a `Self`, and a reference
+ /// to the preceding bytes. If there are insufficient bytes, or if that
+ /// suffix of `source` is not appropriately aligned, this returns `Err`. If
+ /// [`Self: Unaligned`][self-unaligned], you can [infallibly discard the
+ /// alignment error][size-error-from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. See [`ref_from_suffix_with_elems`], which does
+ /// support such types. Attempting to use this method on such types results
+ /// in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: u16,
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let _ = ZSTy::ref_from_suffix(0u16.as_bytes()); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// [`ref_from_suffix_with_elems`]: FromBytes::ref_from_suffix_with_elems
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct PacketTrailer {
+ /// frame_check_sequence: [u8; 4],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `PacketTrailer`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (prefix, trailer) = PacketTrailer::ref_from_suffix(bytes).unwrap();
+ ///
+ /// assert_eq!(prefix, &[0, 1, 2, 3, 4, 5][..]);
+ /// assert_eq!(trailer.frame_check_sequence, [6, 7, 8, 9]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "ref_from_suffix",
+ format = "coco",
+ arity = 3,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 3
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn ref_from_suffix(source: &[u8]) -> Result<(&[u8], &Self), CastError<&[u8], Self>>
+ where
+ Self: Immutable + KnownLayout,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ ref_from_prefix_suffix(source, None, CastType::Suffix).map(swap)
+ }
+
+ /// Interprets the given `source` as a `&mut Self`.
+ ///
+ /// This method attempts to return a reference to `source` interpreted as a
+ /// `Self`. If the length of `source` is not a [valid size of
+ /// `Self`][valid-size], or if `source` is not appropriately aligned, this
+ /// returns `Err`. If [`Self: Unaligned`][self-unaligned], you can
+ /// [infallibly discard the alignment error][size-error-from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. See [`mut_from_prefix_with_elems`], which does
+ /// support such types. Attempting to use this method on such types results
+ /// in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut source = [85, 85];
+ /// let _ = ZSTy::mut_from_bytes(&mut source[..]); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// [`mut_from_prefix_with_elems`]: FromBytes::mut_from_prefix_with_elems
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// // These bytes encode a `PacketHeader`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let header = PacketHeader::mut_from_bytes(bytes).unwrap();
+ ///
+ /// assert_eq!(header.src_port, [0, 1]);
+ /// assert_eq!(header.dst_port, [2, 3]);
+ /// assert_eq!(header.length, [4, 5]);
+ /// assert_eq!(header.checksum, [6, 7]);
+ ///
+ /// header.checksum = [0, 0];
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0]);
+ ///
+ /// ```
+ ///
+ #[doc = codegen_header!("h5", "mut_from_bytes")]
+ ///
+ /// See [`FromBytes::ref_from_bytes`](#method.ref_from_bytes.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn mut_from_bytes(source: &mut [u8]) -> Result<&mut Self, CastError<&mut [u8], Self>>
+ where
+ Self: IntoBytes + KnownLayout,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ match Ptr::from_mut(source).try_cast_into_no_leftover::<_, BecauseExclusive>(None) {
+ Ok(ptr) => Ok(ptr.recall_validity::<_, (_, (_, _))>().as_mut()),
+ Err(err) => Err(err.map_src(|src| src.as_mut())),
+ }
+ }
+
+ /// Interprets the prefix of the given `source` as a `&mut Self` without
+ /// copying.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the leading bytes of `source`, then attempts to return
+ /// both a reference to those bytes interpreted as a `Self`, and a reference
+ /// to the remaining bytes. If there are insufficient bytes, or if `source`
+ /// is not appropriately aligned, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][size-error-from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. See [`mut_from_suffix_with_elems`], which does
+ /// support such types. Attempting to use this method on such types results
+ /// in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut source = [85, 85];
+ /// let _ = ZSTy::mut_from_prefix(&mut source[..]); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// [`mut_from_suffix_with_elems`]: FromBytes::mut_from_suffix_with_elems
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `PacketHeader`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (header, body) = PacketHeader::mut_from_prefix(bytes).unwrap();
+ ///
+ /// assert_eq!(header.src_port, [0, 1]);
+ /// assert_eq!(header.dst_port, [2, 3]);
+ /// assert_eq!(header.length, [4, 5]);
+ /// assert_eq!(header.checksum, [6, 7]);
+ /// assert_eq!(body, &[8, 9][..]);
+ ///
+ /// header.checksum = [0, 0];
+ /// body.fill(1);
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0, 1, 1]);
+ /// ```
+ ///
+ #[doc = codegen_header!("h5", "mut_from_prefix")]
+ ///
+ /// See [`FromBytes::ref_from_prefix`](#method.ref_from_prefix.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn mut_from_prefix(
+ source: &mut [u8],
+ ) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>
+ where
+ Self: IntoBytes + KnownLayout,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ mut_from_prefix_suffix(source, None, CastType::Prefix)
+ }
+
+ /// Interprets the suffix of the given `source` as a `&mut Self` without
+ /// copying.
+ ///
+ /// This method computes the [largest possible size of `Self`][valid-size]
+ /// that can fit in the trailing bytes of `source`, then attempts to return
+ /// both a reference to those bytes interpreted as a `Self`, and a reference
+ /// to the preceding bytes. If there are insufficient bytes, or if that
+ /// suffix of `source` is not appropriately aligned, this returns `Err`. If
+ /// [`Self: Unaligned`][self-unaligned], you can [infallibly discard the
+ /// alignment error][size-error-from].
+ ///
+ /// `Self` may be a sized type, a slice, or a [slice DST][slice-dst].
+ ///
+ /// [valid-size]: crate::KnownLayout#what-is-a-valid-size
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ /// [slice-dst]: KnownLayout#dynamically-sized-types
+ ///
+ /// # Compile-Time Assertions
+ ///
+ /// This method cannot yet be used on unsized types whose dynamically-sized
+ /// component is zero-sized. Attempting to use this method on such types
+ /// results in a compile-time assertion error; e.g.:
+ ///
+ /// ```compile_fail,E0080
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let mut source = [85, 85];
+ /// let _ = ZSTy::mut_from_suffix(&mut source[..]); // ⚠ Compile Error!
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketTrailer {
+ /// frame_check_sequence: [u8; 4],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `PacketTrailer`.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (prefix, trailer) = PacketTrailer::mut_from_suffix(bytes).unwrap();
+ ///
+ /// assert_eq!(prefix, &[0u8, 1, 2, 3, 4, 5][..]);
+ /// assert_eq!(trailer.frame_check_sequence, [6, 7, 8, 9]);
+ ///
+ /// prefix.fill(0);
+ /// trailer.frame_check_sequence.fill(1);
+ ///
+ /// assert_eq!(bytes, [0, 0, 0, 0, 0, 0, 1, 1, 1, 1]);
+ /// ```
+ ///
+ #[doc = codegen_header!("h5", "mut_from_suffix")]
+ ///
+ /// See [`FromBytes::ref_from_suffix`](#method.ref_from_suffix.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn mut_from_suffix(
+ source: &mut [u8],
+ ) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>
+ where
+ Self: IntoBytes + KnownLayout,
+ {
+ static_assert_dst_is_not_zst!(Self);
+ mut_from_prefix_suffix(source, None, CastType::Suffix).map(swap)
+ }
+
+ /// Interprets the given `source` as a `&Self` with a DST length equal to
+ /// `count`.
+ ///
+ /// This method attempts to return a reference to `source` interpreted as a
+ /// `Self` with `count` trailing elements. If the length of `source` is not
+ /// equal to the size of `Self` with `count` elements, or if `source` is not
+ /// appropriately aligned, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][size-error-from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// # #[derive(Debug, PartialEq, Eq)]
+ /// #[derive(FromBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct Pixel {
+ /// r: u8,
+ /// g: u8,
+ /// b: u8,
+ /// a: u8,
+ /// }
+ ///
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let pixels = <[Pixel]>::ref_from_bytes_with_elems(bytes, 2).unwrap();
+ ///
+ /// assert_eq!(pixels, &[
+ /// Pixel { r: 0, g: 1, b: 2, a: 3 },
+ /// Pixel { r: 4, g: 5, b: 6, a: 7 },
+ /// ]);
+ ///
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`ref_from_bytes`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = &[85, 85][..];
+ /// let zsty = ZSTy::ref_from_bytes_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`ref_from_bytes`]: FromBytes::ref_from_bytes
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "ref_from_bytes_with_elems",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 2
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn ref_from_bytes_with_elems(
+ source: &[u8],
+ count: usize,
+ ) -> Result<&Self, CastError<&[u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + Immutable,
+ {
+ let source = Ptr::from_ref(source);
+ let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
+ match maybe_slf {
+ Ok(slf) => Ok(slf.recall_validity().as_ref()),
+ Err(err) => Err(err.map_src(|s| s.as_ref())),
+ }
+ }
+
+ /// Interprets the prefix of the given `source` as a DST `&Self` with length
+ /// equal to `count`.
+ ///
+ /// This method attempts to return a reference to the prefix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the remaining bytes. If there are insufficient bytes, or if `source`
+ /// is not appropriately aligned, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][size-error-from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// # #[derive(Debug, PartialEq, Eq)]
+ /// #[derive(FromBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct Pixel {
+ /// r: u8,
+ /// g: u8,
+ /// b: u8,
+ /// a: u8,
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode two `Pixel`s.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (pixels, suffix) = <[Pixel]>::ref_from_prefix_with_elems(bytes, 2).unwrap();
+ ///
+ /// assert_eq!(pixels, &[
+ /// Pixel { r: 0, g: 1, b: 2, a: 3 },
+ /// Pixel { r: 4, g: 5, b: 6, a: 7 },
+ /// ]);
+ ///
+ /// assert_eq!(suffix, &[8, 9]);
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`ref_from_prefix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = &[85, 85][..];
+ /// let (zsty, _) = ZSTy::ref_from_prefix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`ref_from_prefix`]: FromBytes::ref_from_prefix
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "ref_from_prefix_with_elems",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 2
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn ref_from_prefix_with_elems(
+ source: &[u8],
+ count: usize,
+ ) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + Immutable,
+ {
+ ref_from_prefix_suffix(source, Some(count), CastType::Prefix)
+ }
+
+ /// Interprets the suffix of the given `source` as a DST `&Self` with length
+ /// equal to `count`.
+ ///
+ /// This method attempts to return a reference to the suffix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the preceding bytes. If there are insufficient bytes, or if that
+ /// suffix of `source` is not appropriately aligned, this returns `Err`. If
+ /// [`Self: Unaligned`][self-unaligned], you can [infallibly discard the
+ /// alignment error][size-error-from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// # #[derive(Debug, PartialEq, Eq)]
+ /// #[derive(FromBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct Pixel {
+ /// r: u8,
+ /// g: u8,
+ /// b: u8,
+ /// a: u8,
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode two `Pixel`s.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (prefix, pixels) = <[Pixel]>::ref_from_suffix_with_elems(bytes, 2).unwrap();
+ ///
+ /// assert_eq!(prefix, &[0, 1]);
+ ///
+ /// assert_eq!(pixels, &[
+ /// Pixel { r: 2, g: 3, b: 4, a: 5 },
+ /// Pixel { r: 6, g: 7, b: 8, a: 9 },
+ /// ]);
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`ref_from_suffix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, Immutable, KnownLayout)]
+ /// #[repr(C)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = &[85, 85][..];
+ /// let (_, zsty) = ZSTy::ref_from_suffix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`ref_from_suffix`]: FromBytes::ref_from_suffix
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "ref_from_suffix_with_elems",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Unsized"
+ @variant "dynamic_size"
+ ],
+ [
+ @index 2
+ @title "Dynamically Padded"
+ @variant "dynamic_padding"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn ref_from_suffix_with_elems(
+ source: &[u8],
+ count: usize,
+ ) -> Result<(&[u8], &Self), CastError<&[u8], Self>>
+ where
+ Self: KnownLayout<PointerMetadata = usize> + Immutable,
+ {
+ ref_from_prefix_suffix(source, Some(count), CastType::Suffix).map(swap)
+ }
+
+ /// Interprets the given `source` as a `&mut Self` with a DST length equal
+ /// to `count`.
+ ///
+ /// This method attempts to return a reference to `source` interpreted as a
+ /// `Self` with `count` trailing elements. If the length of `source` is not
+ /// equal to the size of `Self` with `count` elements, or if `source` is not
+ /// appropriately aligned, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][size-error-from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// # #[derive(Debug, PartialEq, Eq)]
+ /// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct Pixel {
+ /// r: u8,
+ /// g: u8,
+ /// b: u8,
+ /// a: u8,
+ /// }
+ ///
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let pixels = <[Pixel]>::mut_from_bytes_with_elems(bytes, 2).unwrap();
+ ///
+ /// assert_eq!(pixels, &[
+ /// Pixel { r: 0, g: 1, b: 2, a: 3 },
+ /// Pixel { r: 4, g: 5, b: 6, a: 7 },
+ /// ]);
+ ///
+ /// pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 };
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 0, 0, 0, 0]);
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`mut_from_bytes`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = &mut [85, 85][..];
+ /// let zsty = ZSTy::mut_from_bytes_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`mut_from_bytes`]: FromBytes::mut_from_bytes
+ ///
+ #[doc = codegen_header!("h5", "mut_from_bytes_with_elems")]
+ ///
+ /// See [`TryFromBytes::ref_from_bytes_with_elems`](#method.ref_from_bytes_with_elems.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn mut_from_bytes_with_elems(
+ source: &mut [u8],
+ count: usize,
+ ) -> Result<&mut Self, CastError<&mut [u8], Self>>
+ where
+ Self: IntoBytes + KnownLayout<PointerMetadata = usize> + Immutable,
+ {
+ let source = Ptr::from_mut(source);
+ let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
+ match maybe_slf {
+ Ok(slf) => Ok(slf.recall_validity::<_, (_, (_, BecauseExclusive))>().as_mut()),
+ Err(err) => Err(err.map_src(|s| s.as_mut())),
+ }
+ }
+
+ /// Interprets the prefix of the given `source` as a `&mut Self` with DST
+ /// length equal to `count`.
+ ///
+ /// This method attempts to return a reference to the prefix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the preceding bytes. If there are insufficient bytes, or if `source`
+ /// is not appropriately aligned, this returns `Err`. If [`Self:
+ /// Unaligned`][self-unaligned], you can [infallibly discard the alignment
+ /// error][size-error-from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// # #[derive(Debug, PartialEq, Eq)]
+ /// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct Pixel {
+ /// r: u8,
+ /// g: u8,
+ /// b: u8,
+ /// a: u8,
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode two `Pixel`s.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (pixels, suffix) = <[Pixel]>::mut_from_prefix_with_elems(bytes, 2).unwrap();
+ ///
+ /// assert_eq!(pixels, &[
+ /// Pixel { r: 0, g: 1, b: 2, a: 3 },
+ /// Pixel { r: 4, g: 5, b: 6, a: 7 },
+ /// ]);
+ ///
+ /// assert_eq!(suffix, &[8, 9]);
+ ///
+ /// pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 };
+ /// suffix.fill(1);
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 0, 0, 0, 0, 1, 1]);
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`mut_from_prefix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = &mut [85, 85][..];
+ /// let (zsty, _) = ZSTy::mut_from_prefix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`mut_from_prefix`]: FromBytes::mut_from_prefix
+ ///
+ #[doc = codegen_header!("h5", "mut_from_prefix_with_elems")]
+ ///
+ /// See [`TryFromBytes::ref_from_prefix_with_elems`](#method.ref_from_prefix_with_elems.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn mut_from_prefix_with_elems(
+ source: &mut [u8],
+ count: usize,
+ ) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>
+ where
+ Self: IntoBytes + KnownLayout<PointerMetadata = usize>,
+ {
+ mut_from_prefix_suffix(source, Some(count), CastType::Prefix)
+ }
+
+ /// Interprets the suffix of the given `source` as a `&mut Self` with DST
+ /// length equal to `count`.
+ ///
+ /// This method attempts to return a reference to the suffix of `source`
+ /// interpreted as a `Self` with `count` trailing elements, and a reference
+ /// to the remaining bytes. If there are insufficient bytes, or if that
+ /// suffix of `source` is not appropriately aligned, this returns `Err`. If
+ /// [`Self: Unaligned`][self-unaligned], you can [infallibly discard the
+ /// alignment error][size-error-from].
+ ///
+ /// [self-unaligned]: Unaligned
+ /// [size-error-from]: error/struct.SizeError.html#method.from-1
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// # #[derive(Debug, PartialEq, Eq)]
+ /// #[derive(FromBytes, IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct Pixel {
+ /// r: u8,
+ /// g: u8,
+ /// b: u8,
+ /// a: u8,
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode two `Pixel`s.
+ /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (prefix, pixels) = <[Pixel]>::mut_from_suffix_with_elems(bytes, 2).unwrap();
+ ///
+ /// assert_eq!(prefix, &[0, 1]);
+ ///
+ /// assert_eq!(pixels, &[
+ /// Pixel { r: 2, g: 3, b: 4, a: 5 },
+ /// Pixel { r: 6, g: 7, b: 8, a: 9 },
+ /// ]);
+ ///
+ /// prefix.fill(9);
+ /// pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 };
+ ///
+ /// assert_eq!(bytes, [9, 9, 2, 3, 4, 5, 0, 0, 0, 0]);
+ /// ```
+ ///
+ /// Since an explicit `count` is provided, this method supports types with
+ /// zero-sized trailing slice elements. Methods such as [`mut_from_suffix`]
+ /// which do not take an explicit count do not support such types.
+ ///
+ /// ```
+ /// use zerocopy::*;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct ZSTy {
+ /// leading_sized: [u8; 2],
+ /// trailing_dst: [()],
+ /// }
+ ///
+ /// let src = &mut [85, 85][..];
+ /// let (_, zsty) = ZSTy::mut_from_suffix_with_elems(src, 42).unwrap();
+ /// assert_eq!(zsty.trailing_dst.len(), 42);
+ /// ```
+ ///
+ /// [`mut_from_suffix`]: FromBytes::mut_from_suffix
+ ///
+ #[doc = codegen_header!("h5", "mut_from_suffix_with_elems")]
+ ///
+ /// See [`TryFromBytes::ref_from_suffix_with_elems`](#method.ref_from_suffix_with_elems.codegen).
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn mut_from_suffix_with_elems(
+ source: &mut [u8],
+ count: usize,
+ ) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>
+ where
+ Self: IntoBytes + KnownLayout<PointerMetadata = usize>,
+ {
+ mut_from_prefix_suffix(source, Some(count), CastType::Suffix).map(swap)
+ }
+
+ /// Reads a copy of `Self` from the given `source`.
+ ///
+ /// If `source.len() != size_of::<Self>()`, `read_from_bytes` returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// // These bytes encode a `PacketHeader`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..];
+ ///
+ /// let header = PacketHeader::read_from_bytes(bytes).unwrap();
+ ///
+ /// assert_eq!(header.src_port, [0, 1]);
+ /// assert_eq!(header.dst_port, [2, 3]);
+ /// assert_eq!(header.length, [4, 5]);
+ /// assert_eq!(header.checksum, [6, 7]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "read_from_bytes",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn read_from_bytes(source: &[u8]) -> Result<Self, SizeError<&[u8], Self>>
+ where
+ Self: Sized,
+ {
+ match Ref::<_, Unalign<Self>>::sized_from(source) {
+ Ok(r) => Ok(Ref::read(&r).into_inner()),
+ Err(CastError::Size(e)) => Err(e.with_dst()),
+ Err(CastError::Alignment(_)) => {
+ // SAFETY: `Unalign<Self>` is trivially aligned, so
+ // `Ref::sized_from` cannot fail due to unmet alignment
+ // requirements.
+ unsafe { core::hint::unreachable_unchecked() }
+ }
+ Err(CastError::Validity(i)) => match i {},
+ }
+ }
+
+ /// Reads a copy of `Self` from the prefix of the given `source`.
+ ///
+ /// This attempts to read a `Self` from the first `size_of::<Self>()` bytes
+ /// of `source`, returning that `Self` and any remaining bytes. If
+ /// `source.len() < size_of::<Self>()`, it returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `PacketHeader`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (header, body) = PacketHeader::read_from_prefix(bytes).unwrap();
+ ///
+ /// assert_eq!(header.src_port, [0, 1]);
+ /// assert_eq!(header.dst_port, [2, 3]);
+ /// assert_eq!(header.length, [4, 5]);
+ /// assert_eq!(header.checksum, [6, 7]);
+ /// assert_eq!(body, [8, 9]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "read_from_prefix",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn read_from_prefix(source: &[u8]) -> Result<(Self, &[u8]), SizeError<&[u8], Self>>
+ where
+ Self: Sized,
+ {
+ match Ref::<_, Unalign<Self>>::sized_from_prefix(source) {
+ Ok((r, suffix)) => Ok((Ref::read(&r).into_inner(), suffix)),
+ Err(CastError::Size(e)) => Err(e.with_dst()),
+ Err(CastError::Alignment(_)) => {
+ // SAFETY: `Unalign<Self>` is trivially aligned, so
+ // `Ref::sized_from_prefix` cannot fail due to unmet alignment
+ // requirements.
+ unsafe { core::hint::unreachable_unchecked() }
+ }
+ Err(CastError::Validity(i)) => match i {},
+ }
+ }
+
+ /// Reads a copy of `Self` from the suffix of the given `source`.
+ ///
+ /// This attempts to read a `Self` from the last `size_of::<Self>()` bytes
+ /// of `source`, returning that `Self` and any preceding bytes. If
+ /// `source.len() < size_of::<Self>()`, it returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::FromBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes)]
+ /// #[repr(C)]
+ /// struct PacketTrailer {
+ /// frame_check_sequence: [u8; 4],
+ /// }
+ ///
+ /// // These are more bytes than are needed to encode a `PacketTrailer`.
+ /// let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
+ ///
+ /// let (prefix, trailer) = PacketTrailer::read_from_suffix(bytes).unwrap();
+ ///
+ /// assert_eq!(prefix, [0, 1, 2, 3, 4, 5]);
+ /// assert_eq!(trailer.frame_check_sequence, [6, 7, 8, 9]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "read_from_suffix",
+ format = "coco_static_size",
+ )]
+ #[must_use = "has no side effects"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ fn read_from_suffix(source: &[u8]) -> Result<(&[u8], Self), SizeError<&[u8], Self>>
+ where
+ Self: Sized,
+ {
+ match Ref::<_, Unalign<Self>>::sized_from_suffix(source) {
+ Ok((prefix, r)) => Ok((prefix, Ref::read(&r).into_inner())),
+ Err(CastError::Size(e)) => Err(e.with_dst()),
+ Err(CastError::Alignment(_)) => {
+ // SAFETY: `Unalign<Self>` is trivially aligned, so
+ // `Ref::sized_from_suffix` cannot fail due to unmet alignment
+ // requirements.
+ unsafe { core::hint::unreachable_unchecked() }
+ }
+ Err(CastError::Validity(i)) => match i {},
+ }
+ }
+
+ /// Reads a copy of `self` from an `io::Read`.
+ ///
+ /// This is useful for interfacing with operating system byte sinks (files,
+ /// sockets, etc.).
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use zerocopy::{byteorder::big_endian::*, FromBytes};
+ /// use std::fs::File;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes)]
+ /// #[repr(C)]
+ /// struct BitmapFileHeader {
+ /// signature: [u8; 2],
+ /// size: U32,
+ /// reserved: U64,
+ /// offset: U64,
+ /// }
+ ///
+ /// let mut file = File::open("image.bin").unwrap();
+ /// let header = BitmapFileHeader::read_from_io(&mut file).unwrap();
+ /// ```
+ #[cfg(feature = "std")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
+ #[inline(always)]
+ fn read_from_io<R>(mut src: R) -> io::Result<Self>
+ where
+ Self: Sized,
+ R: io::Read,
+ {
+ // NOTE(#2319, #2320): We do `buf.zero()` separately rather than
+ // constructing `let buf = CoreMaybeUninit::zeroed()` because, if `Self`
+ // contains padding bytes, then a typed copy of `CoreMaybeUninit<Self>`
+ // will not necessarily preserve zeros written to those padding byte
+ // locations, and so `buf` could contain uninitialized bytes.
+ let mut buf = CoreMaybeUninit::<Self>::uninit();
+ buf.zero();
+
+ let ptr = Ptr::from_mut(&mut buf);
+ // SAFETY: After `buf.zero()`, `buf` consists entirely of initialized,
+ // zeroed bytes. Since `MaybeUninit` has no validity requirements, `ptr`
+ // cannot be used to write values which will violate `buf`'s bit
+ // validity. Since `ptr` has `Exclusive` aliasing, nothing other than
+ // `ptr` may be used to mutate `ptr`'s referent, and so its bit validity
+ // cannot be violated even though `buf` may have more permissive bit
+ // validity than `ptr`.
+ let ptr = unsafe { ptr.assume_validity::<invariant::Initialized>() };
+ let ptr = ptr.as_bytes();
+ src.read_exact(ptr.as_mut())?;
+ // SAFETY: `buf` entirely consists of initialized bytes, and `Self` is
+ // `FromBytes`.
+ Ok(unsafe { buf.assume_init() })
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::ref_from_bytes`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn ref_from(source: &[u8]) -> Option<&Self>
+ where
+ Self: KnownLayout + Immutable,
+ {
+ Self::ref_from_bytes(source).ok()
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::mut_from_bytes`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn mut_from(source: &mut [u8]) -> Option<&mut Self>
+ where
+ Self: KnownLayout + IntoBytes,
+ {
+ Self::mut_from_bytes(source).ok()
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::ref_from_prefix_with_elems`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn slice_from_prefix(source: &[u8], count: usize) -> Option<(&[Self], &[u8])>
+ where
+ Self: Sized + Immutable,
+ {
+ <[Self]>::ref_from_prefix_with_elems(source, count).ok()
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::ref_from_suffix_with_elems`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn slice_from_suffix(source: &[u8], count: usize) -> Option<(&[u8], &[Self])>
+ where
+ Self: Sized + Immutable,
+ {
+ <[Self]>::ref_from_suffix_with_elems(source, count).ok()
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::mut_from_prefix_with_elems`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn mut_slice_from_prefix(source: &mut [u8], count: usize) -> Option<(&mut [Self], &mut [u8])>
+ where
+ Self: Sized + IntoBytes,
+ {
+ <[Self]>::mut_from_prefix_with_elems(source, count).ok()
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::mut_from_suffix_with_elems`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn mut_slice_from_suffix(source: &mut [u8], count: usize) -> Option<(&mut [u8], &mut [Self])>
+ where
+ Self: Sized + IntoBytes,
+ {
+ <[Self]>::mut_from_suffix_with_elems(source, count).ok()
+ }
+
+ #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::read_from_bytes`")]
+ #[doc(hidden)]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn read_from(source: &[u8]) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ Self::read_from_bytes(source).ok()
+ }
+}
+
+/// Interprets the given affix of the given bytes as a `&Self`.
+///
+/// This method computes the largest possible size of `Self` that can fit in the
+/// prefix or suffix bytes of `source`, then attempts to return both a reference
+/// to those bytes interpreted as a `Self`, and a reference to the excess bytes.
+/// If there are insufficient bytes, or if that affix of `source` is not
+/// appropriately aligned, this returns `Err`.
+#[inline(always)]
+fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
+ source: &[u8],
+ meta: Option<T::PointerMetadata>,
+ cast_type: CastType,
+) -> Result<(&T, &[u8]), CastError<&[u8], T>> {
+ let (slf, prefix_suffix) = Ptr::from_ref(source)
+ .try_cast_into::<_, BecauseImmutable>(cast_type, meta)
+ .map_err(|err| err.map_src(|s| s.as_ref()))?;
+ Ok((slf.recall_validity().as_ref(), prefix_suffix.as_ref()))
+}
+
+/// Interprets the given affix of the given bytes as a `&mut Self` without
+/// copying.
+///
+/// This method computes the largest possible size of `Self` that can fit in the
+/// prefix or suffix bytes of `source`, then attempts to return both a reference
+/// to those bytes interpreted as a `Self`, and a reference to the excess bytes.
+/// If there are insufficient bytes, or if that affix of `source` is not
+/// appropriately aligned, this returns `Err`.
+#[inline(always)]
+fn mut_from_prefix_suffix<T: FromBytes + IntoBytes + KnownLayout + ?Sized>(
+ source: &mut [u8],
+ meta: Option<T::PointerMetadata>,
+ cast_type: CastType,
+) -> Result<(&mut T, &mut [u8]), CastError<&mut [u8], T>> {
+ let (slf, prefix_suffix) = Ptr::from_mut(source)
+ .try_cast_into::<_, BecauseExclusive>(cast_type, meta)
+ .map_err(|err| err.map_src(|s| s.as_mut()))?;
+ Ok((slf.recall_validity::<_, (_, (_, _))>().as_mut(), prefix_suffix.as_mut()))
+}
+
+/// Analyzes whether a type is [`IntoBytes`].
+///
+/// This derive analyzes, at compile time, whether the annotated type satisfies
+/// the [safety conditions] of `IntoBytes` and implements `IntoBytes` if it is
+/// sound to do so. This derive can be applied to structs and enums (see below
+/// for union support); e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{IntoBytes};
+/// #[derive(IntoBytes)]
+/// #[repr(C)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(IntoBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// [safety conditions]: trait@IntoBytes#safety
+///
+/// # Error Messages
+///
+/// On Rust toolchains prior to 1.78.0, due to the way that the custom derive
+/// for `IntoBytes` is implemented, you may get an error like this:
+///
+/// ```text
+/// error[E0277]: the trait bound `(): PaddingFree<Foo, true>` is not satisfied
+/// --> lib.rs:23:10
+/// |
+/// 1 | #[derive(IntoBytes)]
+/// | ^^^^^^^^^ the trait `PaddingFree<Foo, true>` is not implemented for `()`
+/// |
+/// = help: the following implementations were found:
+/// <() as PaddingFree<T, false>>
+/// ```
+///
+/// This error indicates that the type being annotated has padding bytes, which
+/// is illegal for `IntoBytes` types. Consider reducing the alignment of some
+/// fields by using types in the [`byteorder`] module, wrapping field types in
+/// [`Unalign`], adding explicit struct fields where those padding bytes would
+/// be, or using `#[repr(packed)]`. See the Rust Reference's page on [type
+/// layout] for more information about type layout and padding.
+///
+/// [type layout]: https://doc.rust-lang.org/reference/type-layout.html
+///
+/// # Unions
+///
+/// Currently, union bit validity is [up in the air][union-validity], and so
+/// zerocopy does not support `#[derive(IntoBytes)]` on unions by default.
+/// However, implementing `IntoBytes` on a union type is likely sound on all
+/// existing Rust toolchains - it's just that it may become unsound in the
+/// future. You can opt-in to `#[derive(IntoBytes)]` support on unions by
+/// passing the unstable `zerocopy_derive_union_into_bytes` cfg:
+///
+/// ```shell
+/// $ RUSTFLAGS='--cfg zerocopy_derive_union_into_bytes' cargo build
+/// ```
+///
+/// However, it is your responsibility to ensure that this derive is sound on
+/// the specific versions of the Rust toolchain you are using! We make no
+/// stability or soundness guarantees regarding this cfg, and may remove it at
+/// any point.
+///
+/// We are actively working with Rust to stabilize the necessary language
+/// guarantees to support this in a forwards-compatible way, which will enable
+/// us to remove the cfg gate. As part of this effort, we need to know how much
+/// demand there is for this feature. If you would like to use `IntoBytes` on
+/// unions, [please let us know][discussion].
+///
+/// [union-validity]: https://github.com/rust-lang/unsafe-code-guidelines/issues/438
+/// [discussion]: https://github.com/google/zerocopy/discussions/1802
+///
+/// # Analysis
+///
+/// *This section describes, roughly, the analysis performed by this derive to
+/// determine whether it is sound to implement `IntoBytes` for a given type.
+/// Unless you are modifying the implementation of this derive, or attempting to
+/// manually implement `IntoBytes` for a type yourself, you don't need to read
+/// this section.*
+///
+/// If a type has the following properties, then this derive can implement
+/// `IntoBytes` for that type:
+///
+/// - If the type is a struct, its fields must be [`IntoBytes`]. Additionally:
+/// - if the type is `repr(transparent)` or `repr(packed)`, it is
+/// [`IntoBytes`] if its fields are [`IntoBytes`]; else,
+/// - if the type is `repr(C)` with at most one field, it is [`IntoBytes`]
+/// if its field is [`IntoBytes`]; else,
+/// - if the type has no generic parameters, it is [`IntoBytes`] if the type
+/// is sized and has no padding bytes; else,
+/// - if the type is `repr(C)`, its fields must be [`Unaligned`].
+/// - If the type is an enum:
+/// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`,
+/// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`).
+/// - It must have no padding bytes.
+/// - Its fields must be [`IntoBytes`].
+///
+/// This analysis is subject to change. Unsafe code may *only* rely on the
+/// documented [safety conditions] of `FromBytes`, and must *not* rely on the
+/// implementation details of this derive.
+///
+/// [Rust Reference]: https://doc.rust-lang.org/reference/type-layout.html
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::IntoBytes;
+
+/// Types that can be converted to an immutable slice of initialized bytes.
+///
+/// Any `IntoBytes` type can be converted to a slice of initialized bytes of the
+/// same size. This is useful for efficiently serializing structured data as raw
+/// bytes.
+///
+/// # Implementation
+///
+/// **Do not implement this trait yourself!** Instead, use
+/// [`#[derive(IntoBytes)]`][derive]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::IntoBytes;
+/// #[derive(IntoBytes)]
+/// #[repr(C)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(IntoBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant0,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// This derive performs a sophisticated, compile-time safety analysis to
+/// determine whether a type is `IntoBytes`. See the [derive
+/// documentation][derive] for guidance on how to interpret error messages
+/// produced by the derive's analysis.
+///
+/// # Safety
+///
+/// *This section describes what is required in order for `T: IntoBytes`, and
+/// what unsafe code may assume of such types. If you don't plan on implementing
+/// `IntoBytes` manually, and you don't plan on writing unsafe code that
+/// operates on `IntoBytes` types, then you don't need to read this section.*
+///
+/// If `T: IntoBytes`, then unsafe code may assume that it is sound to treat any
+/// `t: T` as an immutable `[u8]` of length `size_of_val(t)`. If a type is
+/// marked as `IntoBytes` which violates this contract, it may cause undefined
+/// behavior.
+///
+/// `#[derive(IntoBytes)]` only permits [types which satisfy these
+/// requirements][derive-analysis].
+///
+#[cfg_attr(
+ feature = "derive",
+ doc = "[derive]: zerocopy_derive::IntoBytes",
+ doc = "[derive-analysis]: zerocopy_derive::IntoBytes#analysis"
+)]
+#[cfg_attr(
+ not(feature = "derive"),
+ doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.IntoBytes.html"),
+ doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.IntoBytes.html#analysis"),
+)]
+#[cfg_attr(
+ not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
+ diagnostic::on_unimplemented(note = "Consider adding `#[derive(IntoBytes)]` to `{Self}`")
+)]
+pub unsafe trait IntoBytes {
+ // The `Self: Sized` bound makes it so that this function doesn't prevent
+ // `IntoBytes` from being object safe. Note that other `IntoBytes` methods
+ // prevent object safety, but those provide a benefit in exchange for object
+ // safety. If at some point we remove those methods, change their type
+ // signatures, or move them out of this trait so that `IntoBytes` is object
+ // safe again, it's important that this function not prevent object safety.
+ #[doc(hidden)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+
+ /// Gets the bytes of this value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::IntoBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// let header = PacketHeader {
+ /// src_port: [0, 1],
+ /// dst_port: [2, 3],
+ /// length: [4, 5],
+ /// checksum: [6, 7],
+ /// };
+ ///
+ /// let bytes = header.as_bytes();
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "as_bytes",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+ )]
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn as_bytes(&self) -> &[u8]
+ where
+ Self: Immutable,
+ {
+ // Note that this method does not have a `Self: Sized` bound;
+ // `size_of_val` works for unsized values too.
+ let len = mem::size_of_val(self);
+ let slf: *const Self = self;
+
+ // SAFETY:
+ // - `slf.cast::<u8>()` is valid for reads for `len * size_of::<u8>()`
+ // many bytes because...
+ // - `slf` is the same pointer as `self`, and `self` is a reference
+ // which points to an object whose size is `len`. Thus...
+ // - The entire region of `len` bytes starting at `slf` is contained
+ // within a single allocation.
+ // - `slf` is non-null.
+ // - `slf` is trivially aligned to `align_of::<u8>() == 1`.
+ // - `Self: IntoBytes` ensures that all of the bytes of `slf` are
+ // initialized.
+ // - Since `slf` is derived from `self`, and `self` is an immutable
+ // reference, the only other references to this memory region that
+ // could exist are other immutable references, which by `Self:
+ // Immutable` don't permit mutation.
+ // - The total size of the resulting slice is no larger than
+ // `isize::MAX` because no allocation produced by safe code can be
+ // larger than `isize::MAX`.
+ //
+ // FIXME(#429): Add references to docs and quotes.
+ unsafe { slice::from_raw_parts(slf.cast::<u8>(), len) }
+ }
+
+ /// Gets the bytes of this value mutably.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::IntoBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// # #[derive(Eq, PartialEq, Debug)]
+ /// #[derive(FromBytes, IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// let mut header = PacketHeader {
+ /// src_port: [0, 1],
+ /// dst_port: [2, 3],
+ /// length: [4, 5],
+ /// checksum: [6, 7],
+ /// };
+ ///
+ /// let bytes = header.as_mut_bytes();
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]);
+ ///
+ /// bytes.reverse();
+ ///
+ /// assert_eq!(header, PacketHeader {
+ /// src_port: [7, 6],
+ /// dst_port: [5, 4],
+ /// length: [3, 2],
+ /// checksum: [1, 0],
+ /// });
+ /// ```
+ ///
+ #[doc = codegen_header!("h5", "as_mut_bytes")]
+ ///
+ /// See [`IntoBytes::as_bytes`](#method.as_bytes.codegen).
+ #[must_use = "has no side effects"]
+ #[inline(always)]
+ fn as_mut_bytes(&mut self) -> &mut [u8]
+ where
+ Self: FromBytes,
+ {
+ // Note that this method does not have a `Self: Sized` bound;
+ // `size_of_val` works for unsized values too.
+ let len = mem::size_of_val(self);
+ let slf: *mut Self = self;
+
+ // SAFETY:
+ // - `slf.cast::<u8>()` is valid for reads and writes for `len *
+ // size_of::<u8>()` many bytes because...
+ // - `slf` is the same pointer as `self`, and `self` is a reference
+ // which points to an object whose size is `len`. Thus...
+ // - The entire region of `len` bytes starting at `slf` is contained
+ // within a single allocation.
+ // - `slf` is non-null.
+ // - `slf` is trivially aligned to `align_of::<u8>() == 1`.
+ // - `Self: IntoBytes` ensures that all of the bytes of `slf` are
+ // initialized.
+ // - `Self: FromBytes` ensures that no write to this memory region
+ // could result in it containing an invalid `Self`.
+ // - Since `slf` is derived from `self`, and `self` is a mutable
+ // reference, no other references to this memory region can exist.
+ // - The total size of the resulting slice is no larger than
+ // `isize::MAX` because no allocation produced by safe code can be
+ // larger than `isize::MAX`.
+ //
+ // FIXME(#429): Add references to docs and quotes.
+ unsafe { slice::from_raw_parts_mut(slf.cast::<u8>(), len) }
+ }
+
+ /// Writes a copy of `self` to `dst`.
+ ///
+ /// If `dst.len() != size_of_val(self)`, `write_to` returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::IntoBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// let header = PacketHeader {
+ /// src_port: [0, 1],
+ /// dst_port: [2, 3],
+ /// length: [4, 5],
+ /// checksum: [6, 7],
+ /// };
+ ///
+ /// let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0];
+ ///
+ /// header.write_to(&mut bytes[..]);
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]);
+ /// ```
+ ///
+ /// If too many or too few target bytes are provided, `write_to` returns
+ /// `Err` and leaves the target bytes unmodified:
+ ///
+ /// ```
+ /// # use zerocopy::IntoBytes;
+ /// # let header = u128::MAX;
+ /// let mut excessive_bytes = &mut [0u8; 128][..];
+ ///
+ /// let write_result = header.write_to(excessive_bytes);
+ ///
+ /// assert!(write_result.is_err());
+ /// assert_eq!(excessive_bytes, [0u8; 128]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "write_to",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+ )]
+ #[must_use = "callers should check the return value to see if the operation succeeded"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ #[allow(clippy::mut_from_ref)] // False positive: `&self -> &mut [u8]`
+ fn write_to(&self, dst: &mut [u8]) -> Result<(), SizeError<&Self, &mut [u8]>>
+ where
+ Self: Immutable,
+ {
+ let src = self.as_bytes();
+ if dst.len() == src.len() {
+ // SAFETY: Within this branch of the conditional, we have ensured
+ // that `dst.len()` is equal to `src.len()`. Neither the size of the
+ // source nor the size of the destination change between the above
+ // size check and the invocation of `copy_unchecked`.
+ unsafe { util::copy_unchecked(src, dst) }
+ Ok(())
+ } else {
+ Err(SizeError::new(self))
+ }
+ }
+
+ /// Writes a copy of `self` to the prefix of `dst`.
+ ///
+ /// `write_to_prefix` writes `self` to the first `size_of_val(self)` bytes
+ /// of `dst`. If `dst.len() < size_of_val(self)`, it returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::IntoBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// let header = PacketHeader {
+ /// src_port: [0, 1],
+ /// dst_port: [2, 3],
+ /// length: [4, 5],
+ /// checksum: [6, 7],
+ /// };
+ ///
+ /// let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ ///
+ /// header.write_to_prefix(&mut bytes[..]);
+ ///
+ /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7, 0, 0]);
+ /// ```
+ ///
+ /// If insufficient target bytes are provided, `write_to_prefix` returns
+ /// `Err` and leaves the target bytes unmodified:
+ ///
+ /// ```
+ /// # use zerocopy::IntoBytes;
+ /// # let header = u128::MAX;
+ /// let mut insufficient_bytes = &mut [0, 0][..];
+ ///
+ /// let write_result = header.write_to_suffix(insufficient_bytes);
+ ///
+ /// assert!(write_result.is_err());
+ /// assert_eq!(insufficient_bytes, [0, 0]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "write_to_prefix",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+ )]
+ #[must_use = "callers should check the return value to see if the operation succeeded"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ #[allow(clippy::mut_from_ref)] // False positive: `&self -> &mut [u8]`
+ fn write_to_prefix(&self, dst: &mut [u8]) -> Result<(), SizeError<&Self, &mut [u8]>>
+ where
+ Self: Immutable,
+ {
+ let src = self.as_bytes();
+ match dst.get_mut(..src.len()) {
+ Some(dst) => {
+ // SAFETY: Within this branch of the `match`, we have ensured
+ // through fallible subslicing that `dst.len()` is equal to
+ // `src.len()`. Neither the size of the source nor the size of
+ // the destination change between the above subslicing operation
+ // and the invocation of `copy_unchecked`.
+ unsafe { util::copy_unchecked(src, dst) }
+ Ok(())
+ }
+ None => Err(SizeError::new(self)),
+ }
+ }
+
+ /// Writes a copy of `self` to the suffix of `dst`.
+ ///
+ /// `write_to_suffix` writes `self` to the last `size_of_val(self)` bytes of
+ /// `dst`. If `dst.len() < size_of_val(self)`, it returns `Err`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use zerocopy::IntoBytes;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(IntoBytes, Immutable)]
+ /// #[repr(C)]
+ /// struct PacketHeader {
+ /// src_port: [u8; 2],
+ /// dst_port: [u8; 2],
+ /// length: [u8; 2],
+ /// checksum: [u8; 2],
+ /// }
+ ///
+ /// let header = PacketHeader {
+ /// src_port: [0, 1],
+ /// dst_port: [2, 3],
+ /// length: [4, 5],
+ /// checksum: [6, 7],
+ /// };
+ ///
+ /// let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ ///
+ /// header.write_to_suffix(&mut bytes[..]);
+ ///
+ /// assert_eq!(bytes, [0, 0, 0, 1, 2, 3, 4, 5, 6, 7]);
+ ///
+ /// let mut insufficient_bytes = &mut [0, 0][..];
+ ///
+ /// let write_result = header.write_to_suffix(insufficient_bytes);
+ ///
+ /// assert!(write_result.is_err());
+ /// assert_eq!(insufficient_bytes, [0, 0]);
+ /// ```
+ ///
+ /// If insufficient target bytes are provided, `write_to_suffix` returns
+ /// `Err` and leaves the target bytes unmodified:
+ ///
+ /// ```
+ /// # use zerocopy::IntoBytes;
+ /// # let header = u128::MAX;
+ /// let mut insufficient_bytes = &mut [0, 0][..];
+ ///
+ /// let write_result = header.write_to_suffix(insufficient_bytes);
+ ///
+ /// assert!(write_result.is_err());
+ /// assert_eq!(insufficient_bytes, [0, 0]);
+ /// ```
+ ///
+ #[doc = codegen_section!(
+ header = "h5",
+ bench = "write_to_suffix",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+ )]
+ #[must_use = "callers should check the return value to see if the operation succeeded"]
+ #[cfg_attr(zerocopy_inline_always, inline(always))]
+ #[cfg_attr(not(zerocopy_inline_always), inline)]
+ #[allow(clippy::mut_from_ref)] // False positive: `&self -> &mut [u8]`
+ fn write_to_suffix(&self, dst: &mut [u8]) -> Result<(), SizeError<&Self, &mut [u8]>>
+ where
+ Self: Immutable,
+ {
+ let src = self.as_bytes();
+ let start = if let Some(start) = dst.len().checked_sub(src.len()) {
+ start
+ } else {
+ return Err(SizeError::new(self));
+ };
+ let dst = if let Some(dst) = dst.get_mut(start..) {
+ dst
+ } else {
+ // get_mut() should never return None here. We return a `SizeError`
+ // rather than .unwrap() because in the event the branch is not
+ // optimized away, returning a value is generally lighter-weight
+ // than panicking.
+ return Err(SizeError::new(self));
+ };
+ // SAFETY: Through fallible subslicing of `dst`, we have ensured that
+ // `dst.len()` is equal to `src.len()`. Neither the size of the source
+ // nor the size of the destination change between the above subslicing
+ // operation and the invocation of `copy_unchecked`.
+ unsafe {
+ util::copy_unchecked(src, dst);
+ }
+ Ok(())
+ }
+
+ /// Writes a copy of `self` to an `io::Write`.
+ ///
+ /// This is a shorthand for `dst.write_all(self.as_bytes())`, and is useful
+ /// for interfacing with operating system byte sinks (files, sockets, etc.).
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use zerocopy::{byteorder::big_endian::U16, FromBytes, IntoBytes};
+ /// use std::fs::File;
+ /// # use zerocopy_derive::*;
+ ///
+ /// #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
+ /// #[repr(C, packed)]
+ /// struct GrayscaleImage {
+ /// height: U16,
+ /// width: U16,
+ /// pixels: [U16],
+ /// }
+ ///
+ /// let image = GrayscaleImage::ref_from_bytes(&[0, 0, 0, 0][..]).unwrap();
+ /// let mut file = File::create("image.bin").unwrap();
+ /// image.write_to_io(&mut file).unwrap();
+ /// ```
+ ///
+ /// If the write fails, `write_to_io` returns `Err` and a partial write may
+ /// have occurred; e.g.:
+ ///
+ /// ```
+ /// # use zerocopy::IntoBytes;
+ ///
+ /// let src = u128::MAX;
+ /// let mut dst = [0u8; 2];
+ ///
+ /// let write_result = src.write_to_io(&mut dst[..]);
+ ///
+ /// assert!(write_result.is_err());
+ /// assert_eq!(dst, [255, 255]);
+ /// ```
+ #[cfg(feature = "std")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
+ #[inline(always)]
+ fn write_to_io<W>(&self, mut dst: W) -> io::Result<()>
+ where
+ Self: Immutable,
+ W: io::Write,
+ {
+ dst.write_all(self.as_bytes())
+ }
+
+ #[deprecated(since = "0.8.0", note = "`IntoBytes::as_bytes_mut` was renamed to `as_mut_bytes`")]
+ #[doc(hidden)]
+ #[inline]
+ fn as_bytes_mut(&mut self) -> &mut [u8]
+ where
+ Self: FromBytes,
+ {
+ self.as_mut_bytes()
+ }
+}
+
+/// Analyzes whether a type is [`Unaligned`].
+///
+/// This derive analyzes, at compile time, whether the annotated type satisfies
+/// the [safety conditions] of `Unaligned` and implements `Unaligned` if it is
+/// sound to do so. This derive can be applied to structs, enums, and unions;
+/// e.g.:
+///
+/// ```
+/// # use zerocopy_derive::Unaligned;
+/// #[derive(Unaligned)]
+/// #[repr(C)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Unaligned)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant0,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Unaligned)]
+/// #[repr(packed)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// # Analysis
+///
+/// *This section describes, roughly, the analysis performed by this derive to
+/// determine whether it is sound to implement `Unaligned` for a given type.
+/// Unless you are modifying the implementation of this derive, or attempting to
+/// manually implement `Unaligned` for a type yourself, you don't need to read
+/// this section.*
+///
+/// If a type has the following properties, then this derive can implement
+/// `Unaligned` for that type:
+///
+/// - If the type is a struct or union:
+/// - If `repr(align(N))` is provided, `N` must equal 1.
+/// - If the type is `repr(C)` or `repr(transparent)`, all fields must be
+/// [`Unaligned`].
+/// - If the type is not `repr(C)` or `repr(transparent)`, it must be
+/// `repr(packed)` or `repr(packed(1))`.
+/// - If the type is an enum:
+/// - If `repr(align(N))` is provided, `N` must equal 1.
+/// - It must be a field-less enum (meaning that all variants have no fields).
+/// - It must be `repr(i8)` or `repr(u8)`.
+///
+/// [safety conditions]: trait@Unaligned#safety
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::Unaligned;
+
+/// Types with no alignment requirement.
+///
+/// If `T: Unaligned`, then `align_of::<T>() == 1`.
+///
+/// # Implementation
+///
+/// **Do not implement this trait yourself!** Instead, use
+/// [`#[derive(Unaligned)]`][derive]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::Unaligned;
+/// #[derive(Unaligned)]
+/// #[repr(C)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Unaligned)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant0,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Unaligned)]
+/// #[repr(packed)]
+/// union MyUnion {
+/// # variant: u8,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// This derive performs a sophisticated, compile-time safety analysis to
+/// determine whether a type is `Unaligned`.
+///
+/// # Safety
+///
+/// *This section describes what is required in order for `T: Unaligned`, and
+/// what unsafe code may assume of such types. If you don't plan on implementing
+/// `Unaligned` manually, and you don't plan on writing unsafe code that
+/// operates on `Unaligned` types, then you don't need to read this section.*
+///
+/// If `T: Unaligned`, then unsafe code may assume that it is sound to produce a
+/// reference to `T` at any memory location regardless of alignment. If a type
+/// is marked as `Unaligned` which violates this contract, it may cause
+/// undefined behavior.
+///
+/// `#[derive(Unaligned)]` only permits [types which satisfy these
+/// requirements][derive-analysis].
+///
+#[cfg_attr(
+ feature = "derive",
+ doc = "[derive]: zerocopy_derive::Unaligned",
+ doc = "[derive-analysis]: zerocopy_derive::Unaligned#analysis"
+)]
+#[cfg_attr(
+ not(feature = "derive"),
+ doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.Unaligned.html"),
+ doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.Unaligned.html#analysis"),
+)]
+#[cfg_attr(
+ not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
+ diagnostic::on_unimplemented(note = "Consider adding `#[derive(Unaligned)]` to `{Self}`")
+)]
+pub unsafe trait Unaligned {
+ // The `Self: Sized` bound makes it so that `Unaligned` is still object
+ // safe.
+ #[doc(hidden)]
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized;
+}
+
+/// Derives optimized [`PartialEq`] and [`Eq`] implementations.
+///
+/// This derive can be applied to structs and enums implementing both
+/// [`Immutable`] and [`IntoBytes`]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{ByteEq, Immutable, IntoBytes};
+/// #[derive(ByteEq, Immutable, IntoBytes)]
+/// #[repr(C)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(ByteEq, Immutable, IntoBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The standard library's [`derive(Eq, PartialEq)`][derive@PartialEq] computes
+/// equality by individually comparing each field. Instead, the implementation
+/// of [`PartialEq::eq`] emitted by `derive(ByteHash)` converts the entirety of
+/// `self` and `other` to byte slices and compares those slices for equality.
+/// This may have performance advantages.
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::ByteEq;
+/// Derives an optimized [`Hash`] implementation.
+///
+/// This derive can be applied to structs and enums implementing both
+/// [`Immutable`] and [`IntoBytes`]; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{ByteHash, Immutable, IntoBytes};
+/// #[derive(ByteHash, Immutable, IntoBytes)]
+/// #[repr(C)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(ByteHash, Immutable, IntoBytes)]
+/// #[repr(u8)]
+/// enum MyEnum {
+/// # Variant,
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The standard library's [`derive(Hash)`][derive@Hash] produces hashes by
+/// individually hashing each field and combining the results. Instead, the
+/// implementations of [`Hash::hash()`] and [`Hash::hash_slice()`] generated by
+/// `derive(ByteHash)` convert the entirety of `self` to a byte slice and hashes
+/// it in a single call to [`Hasher::write()`]. This may have performance
+/// advantages.
+///
+/// [`Hash`]: core::hash::Hash
+/// [`Hash::hash()`]: core::hash::Hash::hash()
+/// [`Hash::hash_slice()`]: core::hash::Hash::hash_slice()
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::ByteHash;
+/// Implements [`SplitAt`].
+///
+/// This derive can be applied to structs; e.g.:
+///
+/// ```
+/// # use zerocopy_derive::{ByteEq, Immutable, IntoBytes};
+/// #[derive(ByteEq, Immutable, IntoBytes)]
+/// #[repr(C)]
+/// struct MyStruct {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+#[cfg(any(feature = "derive", test))]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
+pub use zerocopy_derive::SplitAt;
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
+#[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+mod alloc_support {
+ use super::*;
+
+ /// Extends a `Vec<T>` by pushing `additional` new items onto the end of the
+ /// vector. The new items are initialized with zeros.
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[doc(hidden)]
+ #[deprecated(since = "0.8.0", note = "moved to `FromZeros`")]
+ #[inline(always)]
+ pub fn extend_vec_zeroed<T: FromZeros>(
+ v: &mut Vec<T>,
+ additional: usize,
+ ) -> Result<(), AllocError> {
+ <T as FromZeros>::extend_vec_zeroed(v, additional)
+ }
+
+ /// Inserts `additional` new items into `Vec<T>` at `position`. The new
+ /// items are initialized with zeros.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `position > v.len()`.
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[doc(hidden)]
+ #[deprecated(since = "0.8.0", note = "moved to `FromZeros`")]
+ #[inline(always)]
+ pub fn insert_vec_zeroed<T: FromZeros>(
+ v: &mut Vec<T>,
+ position: usize,
+ additional: usize,
+ ) -> Result<(), AllocError> {
+ <T as FromZeros>::insert_vec_zeroed(v, position, additional)
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+#[doc(hidden)]
+pub use alloc_support::*;
+
+#[cfg(test)]
+#[allow(clippy::assertions_on_result_states, clippy::unreadable_literal)]
+mod tests {
+ use static_assertions::assert_impl_all;
+
+ use super::*;
+ use crate::util::testutil::*;
+
+ // An unsized type.
+ //
+ // This is used to test the custom derives of our traits. The `[u8]` type
+ // gets a hand-rolled impl, so it doesn't exercise our custom derives.
+ #[derive(Debug, Eq, PartialEq, FromBytes, IntoBytes, Unaligned, Immutable)]
+ #[repr(transparent)]
+ struct Unsized([u8]);
+
+ impl Unsized {
+ fn from_mut_slice(slc: &mut [u8]) -> &mut Unsized {
+ // SAFETY: This *probably* sound - since the layouts of `[u8]` and
+ // `Unsized` are the same, so are the layouts of `&mut [u8]` and
+ // `&mut Unsized`. [1] Even if it turns out that this isn't actually
+ // guaranteed by the language spec, we can just change this since
+ // it's in test code.
+ //
+ // [1] https://github.com/rust-lang/unsafe-code-guidelines/issues/375
+ unsafe { mem::transmute(slc) }
+ }
+ }
+
+ #[test]
+ fn test_known_layout() {
+ // Test that `$ty` and `ManuallyDrop<$ty>` have the expected layout.
+ // Test that `PhantomData<$ty>` has the same layout as `()` regardless
+ // of `$ty`.
+ macro_rules! test {
+ ($ty:ty, $expect:expr) => {
+ let expect = $expect;
+ assert_eq!(<$ty as KnownLayout>::LAYOUT, expect);
+ assert_eq!(<ManuallyDrop<$ty> as KnownLayout>::LAYOUT, expect);
+ assert_eq!(<PhantomData<$ty> as KnownLayout>::LAYOUT, <() as KnownLayout>::LAYOUT);
+ };
+ }
+
+ let layout =
+ |offset, align, trailing_slice_elem_size, statically_shallow_unpadded| DstLayout {
+ align: NonZeroUsize::new(align).unwrap(),
+ size_info: match trailing_slice_elem_size {
+ None => SizeInfo::Sized { size: offset },
+ Some(elem_size) => {
+ SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
+ }
+ },
+ statically_shallow_unpadded,
+ };
+
+ test!((), layout(0, 1, None, false));
+ test!(u8, layout(1, 1, None, false));
+ // Use `align_of` because `u64` alignment may be smaller than 8 on some
+ // platforms.
+ test!(u64, layout(8, mem::align_of::<u64>(), None, false));
+ test!(AU64, layout(8, 8, None, false));
+
+ test!(Option<&'static ()>, usize::LAYOUT);
+
+ test!([()], layout(0, 1, Some(0), true));
+ test!([u8], layout(0, 1, Some(1), true));
+ test!(str, layout(0, 1, Some(1), true));
+ }
+
+ #[cfg(feature = "derive")]
+ #[test]
+ fn test_known_layout_derive() {
+ // In this and other files (`late_compile_pass.rs`,
+ // `mid_compile_pass.rs`, and `struct.rs`), we test success and failure
+ // modes of `derive(KnownLayout)` for the following combination of
+ // properties:
+ //
+ // +------------+--------------------------------------+-----------+
+ // | | trailing field properties | |
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // |------------+----------+----------------+----------+-----------|
+ // | N | N | N | N | KL00 |
+ // | N | N | N | Y | KL01 |
+ // | N | N | Y | N | KL02 |
+ // | N | N | Y | Y | KL03 |
+ // | N | Y | N | N | KL04 |
+ // | N | Y | N | Y | KL05 |
+ // | N | Y | Y | N | KL06 |
+ // | N | Y | Y | Y | KL07 |
+ // | Y | N | N | N | KL08 |
+ // | Y | N | N | Y | KL09 |
+ // | Y | N | Y | N | KL10 |
+ // | Y | N | Y | Y | KL11 |
+ // | Y | Y | N | N | KL12 |
+ // | Y | Y | N | Y | KL13 |
+ // | Y | Y | Y | N | KL14 |
+ // | Y | Y | Y | Y | KL15 |
+ // +------------+----------+----------------+----------+-----------+
+
+ struct NotKnownLayout<T = ()> {
+ _t: T,
+ }
+
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct AlignSize<const ALIGN: usize, const SIZE: usize>
+ where
+ elain::Align<ALIGN>: elain::Alignment,
+ {
+ _align: elain::Align<ALIGN>,
+ size: [u8; SIZE],
+ }
+
+ type AU16 = AlignSize<2, 2>;
+ type AU32 = AlignSize<4, 4>;
+
+ fn _assert_kl<T: ?Sized + KnownLayout>(_: &T) {}
+
+ let sized_layout = |align, size| DstLayout {
+ align: NonZeroUsize::new(align).unwrap(),
+ size_info: SizeInfo::Sized { size },
+ statically_shallow_unpadded: false,
+ };
+
+ let unsized_layout = |align, elem_size, offset, statically_shallow_unpadded| DstLayout {
+ align: NonZeroUsize::new(align).unwrap(),
+ size_info: SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }),
+ statically_shallow_unpadded,
+ };
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | N | N | N | Y | KL01 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ struct KL01(NotKnownLayout<AU32>, NotKnownLayout<AU16>);
+
+ let expected = DstLayout::for_type::<KL01>();
+
+ assert_eq!(<KL01 as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL01 as KnownLayout>::LAYOUT, sized_layout(4, 8));
+
+ // ...with `align(N)`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(align(64))]
+ struct KL01Align(NotKnownLayout<AU32>, NotKnownLayout<AU16>);
+
+ let expected = DstLayout::for_type::<KL01Align>();
+
+ assert_eq!(<KL01Align as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL01Align as KnownLayout>::LAYOUT, sized_layout(64, 64));
+
+ // ...with `packed`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(packed)]
+ struct KL01Packed(NotKnownLayout<AU32>, NotKnownLayout<AU16>);
+
+ let expected = DstLayout::for_type::<KL01Packed>();
+
+ assert_eq!(<KL01Packed as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL01Packed as KnownLayout>::LAYOUT, sized_layout(1, 6));
+
+ // ...with `packed(N)`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(packed(2))]
+ struct KL01PackedN(NotKnownLayout<AU32>, NotKnownLayout<AU16>);
+
+ assert_impl_all!(KL01PackedN: KnownLayout);
+
+ let expected = DstLayout::for_type::<KL01PackedN>();
+
+ assert_eq!(<KL01PackedN as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL01PackedN as KnownLayout>::LAYOUT, sized_layout(2, 6));
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | N | N | Y | Y | KL03 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ struct KL03(NotKnownLayout, u8);
+
+ let expected = DstLayout::for_type::<KL03>();
+
+ assert_eq!(<KL03 as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL03 as KnownLayout>::LAYOUT, sized_layout(1, 1));
+
+ // ... with `align(N)`
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(align(64))]
+ struct KL03Align(NotKnownLayout<AU32>, u8);
+
+ let expected = DstLayout::for_type::<KL03Align>();
+
+ assert_eq!(<KL03Align as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL03Align as KnownLayout>::LAYOUT, sized_layout(64, 64));
+
+ // ... with `packed`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(packed)]
+ struct KL03Packed(NotKnownLayout<AU32>, u8);
+
+ let expected = DstLayout::for_type::<KL03Packed>();
+
+ assert_eq!(<KL03Packed as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL03Packed as KnownLayout>::LAYOUT, sized_layout(1, 5));
+
+ // ... with `packed(N)`
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(packed(2))]
+ struct KL03PackedN(NotKnownLayout<AU32>, u8);
+
+ assert_impl_all!(KL03PackedN: KnownLayout);
+
+ let expected = DstLayout::for_type::<KL03PackedN>();
+
+ assert_eq!(<KL03PackedN as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL03PackedN as KnownLayout>::LAYOUT, sized_layout(2, 6));
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | N | Y | N | Y | KL05 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ struct KL05<T>(u8, T);
+
+ fn _test_kl05<T>(t: T) -> impl KnownLayout {
+ KL05(0u8, t)
+ }
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | N | Y | Y | Y | KL07 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ struct KL07<T: KnownLayout>(u8, T);
+
+ fn _test_kl07<T: KnownLayout>(t: T) -> impl KnownLayout {
+ let _ = KL07(0u8, t);
+ }
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | Y | N | Y | N | KL10 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KL10(NotKnownLayout<AU32>, [u8]);
+
+ let expected = DstLayout::new_zst(None)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), None)
+ .extend(<[u8] as KnownLayout>::LAYOUT, None)
+ .pad_to_align();
+
+ assert_eq!(<KL10 as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL10 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 4, false));
+
+ // ...with `align(N)`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C, align(64))]
+ struct KL10Align(NotKnownLayout<AU32>, [u8]);
+
+ let repr_align = NonZeroUsize::new(64);
+
+ let expected = DstLayout::new_zst(repr_align)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), None)
+ .extend(<[u8] as KnownLayout>::LAYOUT, None)
+ .pad_to_align();
+
+ assert_eq!(<KL10Align as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL10Align as KnownLayout>::LAYOUT, unsized_layout(64, 1, 4, false));
+
+ // ...with `packed`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C, packed)]
+ struct KL10Packed(NotKnownLayout<AU32>, [u8]);
+
+ let repr_packed = NonZeroUsize::new(1);
+
+ let expected = DstLayout::new_zst(None)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), repr_packed)
+ .extend(<[u8] as KnownLayout>::LAYOUT, repr_packed)
+ .pad_to_align();
+
+ assert_eq!(<KL10Packed as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL10Packed as KnownLayout>::LAYOUT, unsized_layout(1, 1, 4, false));
+
+ // ...with `packed(N)`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C, packed(2))]
+ struct KL10PackedN(NotKnownLayout<AU32>, [u8]);
+
+ let repr_packed = NonZeroUsize::new(2);
+
+ let expected = DstLayout::new_zst(None)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), repr_packed)
+ .extend(<[u8] as KnownLayout>::LAYOUT, repr_packed)
+ .pad_to_align();
+
+ assert_eq!(<KL10PackedN as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL10PackedN as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4, false));
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | Y | N | Y | Y | KL11 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KL11(NotKnownLayout<AU64>, u8);
+
+ let expected = DstLayout::new_zst(None)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), None)
+ .extend(<u8 as KnownLayout>::LAYOUT, None)
+ .pad_to_align();
+
+ assert_eq!(<KL11 as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL11 as KnownLayout>::LAYOUT, sized_layout(8, 16));
+
+ // ...with `align(N)`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C, align(64))]
+ struct KL11Align(NotKnownLayout<AU64>, u8);
+
+ let repr_align = NonZeroUsize::new(64);
+
+ let expected = DstLayout::new_zst(repr_align)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), None)
+ .extend(<u8 as KnownLayout>::LAYOUT, None)
+ .pad_to_align();
+
+ assert_eq!(<KL11Align as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL11Align as KnownLayout>::LAYOUT, sized_layout(64, 64));
+
+ // ...with `packed`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C, packed)]
+ struct KL11Packed(NotKnownLayout<AU64>, u8);
+
+ let repr_packed = NonZeroUsize::new(1);
+
+ let expected = DstLayout::new_zst(None)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), repr_packed)
+ .extend(<u8 as KnownLayout>::LAYOUT, repr_packed)
+ .pad_to_align();
+
+ assert_eq!(<KL11Packed as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL11Packed as KnownLayout>::LAYOUT, sized_layout(1, 9));
+
+ // ...with `packed(N)`:
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C, packed(2))]
+ struct KL11PackedN(NotKnownLayout<AU64>, u8);
+
+ let repr_packed = NonZeroUsize::new(2);
+
+ let expected = DstLayout::new_zst(None)
+ .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), repr_packed)
+ .extend(<u8 as KnownLayout>::LAYOUT, repr_packed)
+ .pad_to_align();
+
+ assert_eq!(<KL11PackedN as KnownLayout>::LAYOUT, expected);
+ assert_eq!(<KL11PackedN as KnownLayout>::LAYOUT, sized_layout(2, 10));
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | Y | Y | Y | N | KL14 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KL14<T: ?Sized + KnownLayout>(u8, T);
+
+ fn _test_kl14<T: ?Sized + KnownLayout>(kl: &KL14<T>) {
+ _assert_kl(kl)
+ }
+
+ // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
+ // | Y | Y | Y | Y | KL15 |
+ #[allow(dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KL15<T: KnownLayout>(u8, T);
+
+ fn _test_kl15<T: KnownLayout>(t: T) -> impl KnownLayout {
+ let _ = KL15(0u8, t);
+ }
+
+ // Test a variety of combinations of field types:
+ // - ()
+ // - u8
+ // - AU16
+ // - [()]
+ // - [u8]
+ // - [AU16]
+
+ #[allow(clippy::upper_case_acronyms, dead_code)]
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KLTU<T, U: ?Sized>(T, U);
+
+ assert_eq!(<KLTU<(), ()> as KnownLayout>::LAYOUT, sized_layout(1, 0));
+
+ assert_eq!(<KLTU<(), u8> as KnownLayout>::LAYOUT, sized_layout(1, 1));
+
+ assert_eq!(<KLTU<(), AU16> as KnownLayout>::LAYOUT, sized_layout(2, 2));
+
+ assert_eq!(<KLTU<(), [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 0, false));
+
+ assert_eq!(<KLTU<(), [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0, false));
+
+ assert_eq!(<KLTU<(), [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 0, false));
+
+ assert_eq!(<KLTU<u8, ()> as KnownLayout>::LAYOUT, sized_layout(1, 1));
+
+ assert_eq!(<KLTU<u8, u8> as KnownLayout>::LAYOUT, sized_layout(1, 2));
+
+ assert_eq!(<KLTU<u8, AU16> as KnownLayout>::LAYOUT, sized_layout(2, 4));
+
+ assert_eq!(<KLTU<u8, [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 1, false));
+
+ assert_eq!(<KLTU<u8, [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1, false));
+
+ assert_eq!(<KLTU<u8, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2, false));
+
+ assert_eq!(<KLTU<AU16, ()> as KnownLayout>::LAYOUT, sized_layout(2, 2));
+
+ assert_eq!(<KLTU<AU16, u8> as KnownLayout>::LAYOUT, sized_layout(2, 4));
+
+ assert_eq!(<KLTU<AU16, AU16> as KnownLayout>::LAYOUT, sized_layout(2, 4));
+
+ assert_eq!(<KLTU<AU16, [()]> as KnownLayout>::LAYOUT, unsized_layout(2, 0, 2, false));
+
+ assert_eq!(<KLTU<AU16, [u8]> as KnownLayout>::LAYOUT, unsized_layout(2, 1, 2, false));
+
+ assert_eq!(<KLTU<AU16, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2, false));
+
+ // Test a variety of field counts.
+
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KLF0;
+
+ assert_eq!(<KLF0 as KnownLayout>::LAYOUT, sized_layout(1, 0));
+
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KLF1([u8]);
+
+ assert_eq!(<KLF1 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0, true));
+
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KLF2(NotKnownLayout<u8>, [u8]);
+
+ assert_eq!(<KLF2 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1, false));
+
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KLF3(NotKnownLayout<u8>, NotKnownLayout<AU16>, [u8]);
+
+ assert_eq!(<KLF3 as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4, false));
+
+ #[derive(KnownLayout)]
+ #[repr(C)]
+ struct KLF4(NotKnownLayout<u8>, NotKnownLayout<AU16>, NotKnownLayout<AU32>, [u8]);
+
+ assert_eq!(<KLF4 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 8, false));
+ }
+
+ #[test]
+ fn test_object_safety() {
+ fn _takes_immutable(_: &dyn Immutable) {}
+ fn _takes_unaligned(_: &dyn Unaligned) {}
+ }
+
+ #[test]
+ fn test_from_zeros_only() {
+ // Test types that implement `FromZeros` but not `FromBytes`.
+
+ assert!(!bool::new_zeroed());
+ assert_eq!(char::new_zeroed(), '\0');
+
+ #[cfg(feature = "alloc")]
+ {
+ assert_eq!(bool::new_box_zeroed(), Ok(Box::new(false)));
+ assert_eq!(char::new_box_zeroed(), Ok(Box::new('\0')));
+
+ assert_eq!(
+ <[bool]>::new_box_zeroed_with_elems(3).unwrap().as_ref(),
+ [false, false, false]
+ );
+ assert_eq!(
+ <[char]>::new_box_zeroed_with_elems(3).unwrap().as_ref(),
+ ['\0', '\0', '\0']
+ );
+
+ assert_eq!(bool::new_vec_zeroed(3).unwrap().as_ref(), [false, false, false]);
+ assert_eq!(char::new_vec_zeroed(3).unwrap().as_ref(), ['\0', '\0', '\0']);
+ }
+
+ let mut string = "hello".to_string();
+ let s: &mut str = string.as_mut();
+ assert_eq!(s, "hello");
+ s.zero();
+ assert_eq!(s, "\0\0\0\0\0");
+ }
+
+ #[test]
+ fn test_zst_count_preserved() {
+ // Test that, when an explicit count is provided to for a type with a
+ // ZST trailing slice element, that count is preserved. This is
+ // important since, for such types, all element counts result in objects
+ // of the same size, and so the correct behavior is ambiguous. However,
+ // preserving the count as requested by the user is the behavior that we
+ // document publicly.
+
+ // FromZeros methods
+ #[cfg(feature = "alloc")]
+ assert_eq!(<[()]>::new_box_zeroed_with_elems(3).unwrap().len(), 3);
+ #[cfg(feature = "alloc")]
+ assert_eq!(<()>::new_vec_zeroed(3).unwrap().len(), 3);
+
+ // FromBytes methods
+ assert_eq!(<[()]>::ref_from_bytes_with_elems(&[][..], 3).unwrap().len(), 3);
+ assert_eq!(<[()]>::ref_from_prefix_with_elems(&[][..], 3).unwrap().0.len(), 3);
+ assert_eq!(<[()]>::ref_from_suffix_with_elems(&[][..], 3).unwrap().1.len(), 3);
+ assert_eq!(<[()]>::mut_from_bytes_with_elems(&mut [][..], 3).unwrap().len(), 3);
+ assert_eq!(<[()]>::mut_from_prefix_with_elems(&mut [][..], 3).unwrap().0.len(), 3);
+ assert_eq!(<[()]>::mut_from_suffix_with_elems(&mut [][..], 3).unwrap().1.len(), 3);
+ }
+
+ #[test]
+ fn test_read_write() {
+ const VAL: u64 = 0x12345678;
+ #[cfg(target_endian = "big")]
+ const VAL_BYTES: [u8; 8] = VAL.to_be_bytes();
+ #[cfg(target_endian = "little")]
+ const VAL_BYTES: [u8; 8] = VAL.to_le_bytes();
+ const ZEROS: [u8; 8] = [0u8; 8];
+
+ // Test `FromBytes::{read_from, read_from_prefix, read_from_suffix}`.
+
+ assert_eq!(u64::read_from_bytes(&VAL_BYTES[..]), Ok(VAL));
+ // The first 8 bytes are from `VAL_BYTES` and the second 8 bytes are all
+ // zeros.
+ let bytes_with_prefix: [u8; 16] = transmute!([VAL_BYTES, [0; 8]]);
+ assert_eq!(u64::read_from_prefix(&bytes_with_prefix[..]), Ok((VAL, &ZEROS[..])));
+ assert_eq!(u64::read_from_suffix(&bytes_with_prefix[..]), Ok((&VAL_BYTES[..], 0)));
+ // The first 8 bytes are all zeros and the second 8 bytes are from
+ // `VAL_BYTES`
+ let bytes_with_suffix: [u8; 16] = transmute!([[0; 8], VAL_BYTES]);
+ assert_eq!(u64::read_from_prefix(&bytes_with_suffix[..]), Ok((0, &VAL_BYTES[..])));
+ assert_eq!(u64::read_from_suffix(&bytes_with_suffix[..]), Ok((&ZEROS[..], VAL)));
+
+ // Test `IntoBytes::{write_to, write_to_prefix, write_to_suffix}`.
+
+ let mut bytes = [0u8; 8];
+ assert_eq!(VAL.write_to(&mut bytes[..]), Ok(()));
+ assert_eq!(bytes, VAL_BYTES);
+ let mut bytes = [0u8; 16];
+ assert_eq!(VAL.write_to_prefix(&mut bytes[..]), Ok(()));
+ let want: [u8; 16] = transmute!([VAL_BYTES, [0; 8]]);
+ assert_eq!(bytes, want);
+ let mut bytes = [0u8; 16];
+ assert_eq!(VAL.write_to_suffix(&mut bytes[..]), Ok(()));
+ let want: [u8; 16] = transmute!([[0; 8], VAL_BYTES]);
+ assert_eq!(bytes, want);
+ }
+
+ #[test]
+ #[cfg(feature = "std")]
+ fn test_read_io_with_padding_soundness() {
+ // This test is designed to exhibit potential UB in
+ // `FromBytes::read_from_io`. (see #2319, #2320).
+
+ // On most platforms (where `align_of::<u16>() == 2`), `WithPadding`
+ // will have inter-field padding between `x` and `y`.
+ #[derive(FromBytes)]
+ #[repr(C)]
+ struct WithPadding {
+ x: u8,
+ y: u16,
+ }
+ struct ReadsInRead;
+ impl std::io::Read for ReadsInRead {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ // This body branches on every byte of `buf`, ensuring that it
+ // exhibits UB if any byte of `buf` is uninitialized.
+ if buf.iter().all(|&x| x == 0) {
+ Ok(buf.len())
+ } else {
+ buf.iter_mut().for_each(|x| *x = 0);
+ Ok(buf.len())
+ }
+ }
+ }
+ assert!(matches!(WithPadding::read_from_io(ReadsInRead), Ok(WithPadding { x: 0, y: 0 })));
+ }
+
+ #[test]
+ #[cfg(feature = "std")]
+ fn test_read_write_io() {
+ let mut long_buffer = [0, 0, 0, 0];
+ assert!(matches!(u16::MAX.write_to_io(&mut long_buffer[..]), Ok(())));
+ assert_eq!(long_buffer, [255, 255, 0, 0]);
+ assert!(matches!(u16::read_from_io(&long_buffer[..]), Ok(u16::MAX)));
+
+ let mut short_buffer = [0, 0];
+ assert!(u32::MAX.write_to_io(&mut short_buffer[..]).is_err());
+ assert_eq!(short_buffer, [255, 255]);
+ assert!(u32::read_from_io(&short_buffer[..]).is_err());
+ }
+
+ #[test]
+ fn test_try_from_bytes_try_read_from() {
+ assert_eq!(<bool as TryFromBytes>::try_read_from_bytes(&[0]), Ok(false));
+ assert_eq!(<bool as TryFromBytes>::try_read_from_bytes(&[1]), Ok(true));
+
+ assert_eq!(<bool as TryFromBytes>::try_read_from_prefix(&[0, 2]), Ok((false, &[2][..])));
+ assert_eq!(<bool as TryFromBytes>::try_read_from_prefix(&[1, 2]), Ok((true, &[2][..])));
+
+ assert_eq!(<bool as TryFromBytes>::try_read_from_suffix(&[2, 0]), Ok((&[2][..], false)));
+ assert_eq!(<bool as TryFromBytes>::try_read_from_suffix(&[2, 1]), Ok((&[2][..], true)));
+
+ // If we don't pass enough bytes, it fails.
+ assert!(matches!(
+ <u8 as TryFromBytes>::try_read_from_bytes(&[]),
+ Err(TryReadError::Size(_))
+ ));
+ assert!(matches!(
+ <u8 as TryFromBytes>::try_read_from_prefix(&[]),
+ Err(TryReadError::Size(_))
+ ));
+ assert!(matches!(
+ <u8 as TryFromBytes>::try_read_from_suffix(&[]),
+ Err(TryReadError::Size(_))
+ ));
+
+ // If we pass too many bytes, it fails.
+ assert!(matches!(
+ <u8 as TryFromBytes>::try_read_from_bytes(&[0, 0]),
+ Err(TryReadError::Size(_))
+ ));
+
+ // If we pass an invalid value, it fails.
+ assert!(matches!(
+ <bool as TryFromBytes>::try_read_from_bytes(&[2]),
+ Err(TryReadError::Validity(_))
+ ));
+ assert!(matches!(
+ <bool as TryFromBytes>::try_read_from_prefix(&[2, 0]),
+ Err(TryReadError::Validity(_))
+ ));
+ assert!(matches!(
+ <bool as TryFromBytes>::try_read_from_suffix(&[0, 2]),
+ Err(TryReadError::Validity(_))
+ ));
+
+ // Reading from a misaligned buffer should still succeed. Since `AU64`'s
+ // alignment is 8, and since we read from two adjacent addresses one
+ // byte apart, it is guaranteed that at least one of them (though
+ // possibly both) will be misaligned.
+ let bytes: [u8; 9] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
+ assert_eq!(<AU64 as TryFromBytes>::try_read_from_bytes(&bytes[..8]), Ok(AU64(0)));
+ assert_eq!(<AU64 as TryFromBytes>::try_read_from_bytes(&bytes[1..9]), Ok(AU64(0)));
+
+ assert_eq!(
+ <AU64 as TryFromBytes>::try_read_from_prefix(&bytes[..8]),
+ Ok((AU64(0), &[][..]))
+ );
+ assert_eq!(
+ <AU64 as TryFromBytes>::try_read_from_prefix(&bytes[1..9]),
+ Ok((AU64(0), &[][..]))
+ );
+
+ assert_eq!(
+ <AU64 as TryFromBytes>::try_read_from_suffix(&bytes[..8]),
+ Ok((&[][..], AU64(0)))
+ );
+ assert_eq!(
+ <AU64 as TryFromBytes>::try_read_from_suffix(&bytes[1..9]),
+ Ok((&[][..], AU64(0)))
+ );
+ }
+
+ #[test]
+ fn test_ref_from_mut_from_bytes() {
+ // Test `FromBytes::{ref_from_bytes, mut_from_bytes}{,_prefix,Suffix}`
+ // success cases. Exhaustive coverage for these methods is covered by
+ // the `Ref` tests above, which these helper methods defer to.
+
+ let mut buf =
+ Align::<[u8; 16], AU64>::new([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
+
+ assert_eq!(
+ AU64::ref_from_bytes(&buf.t[8..]).unwrap().0.to_ne_bytes(),
+ [8, 9, 10, 11, 12, 13, 14, 15]
+ );
+ let suffix = AU64::mut_from_bytes(&mut buf.t[8..]).unwrap();
+ suffix.0 = 0x0101010101010101;
+ // The `[u8:9]` is a non-half size of the full buffer, which would catch
+ // `from_prefix` having the same implementation as `from_suffix` (issues #506, #511).
+ assert_eq!(
+ <[u8; 9]>::ref_from_suffix(&buf.t[..]).unwrap(),
+ (&[0, 1, 2, 3, 4, 5, 6][..], &[7u8, 1, 1, 1, 1, 1, 1, 1, 1])
+ );
+ let (prefix, suffix) = AU64::mut_from_suffix(&mut buf.t[1..]).unwrap();
+ assert_eq!(prefix, &mut [1u8, 2, 3, 4, 5, 6, 7][..]);
+ suffix.0 = 0x0202020202020202;
+ let (prefix, suffix) = <[u8; 10]>::mut_from_suffix(&mut buf.t[..]).unwrap();
+ assert_eq!(prefix, &mut [0u8, 1, 2, 3, 4, 5][..]);
+ suffix[0] = 42;
+ assert_eq!(
+ <[u8; 9]>::ref_from_prefix(&buf.t[..]).unwrap(),
+ (&[0u8, 1, 2, 3, 4, 5, 42, 7, 2], &[2u8, 2, 2, 2, 2, 2, 2][..])
+ );
+ <[u8; 2]>::mut_from_prefix(&mut buf.t[..]).unwrap().0[1] = 30;
+ assert_eq!(buf.t, [0, 30, 2, 3, 4, 5, 42, 7, 2, 2, 2, 2, 2, 2, 2, 2]);
+ }
+
+ #[test]
+ fn test_ref_from_mut_from_bytes_error() {
+ // Test `FromBytes::{ref_from_bytes, mut_from_bytes}{,_prefix,Suffix}`
+ // error cases.
+
+ // Fail because the buffer is too large.
+ let mut buf = Align::<[u8; 16], AU64>::default();
+ // `buf.t` should be aligned to 8, so only the length check should fail.
+ assert!(AU64::ref_from_bytes(&buf.t[..]).is_err());
+ assert!(AU64::mut_from_bytes(&mut buf.t[..]).is_err());
+ assert!(<[u8; 8]>::ref_from_bytes(&buf.t[..]).is_err());
+ assert!(<[u8; 8]>::mut_from_bytes(&mut buf.t[..]).is_err());
+
+ // Fail because the buffer is too small.
+ let mut buf = Align::<[u8; 4], AU64>::default();
+ assert!(AU64::ref_from_bytes(&buf.t[..]).is_err());
+ assert!(AU64::mut_from_bytes(&mut buf.t[..]).is_err());
+ assert!(<[u8; 8]>::ref_from_bytes(&buf.t[..]).is_err());
+ assert!(<[u8; 8]>::mut_from_bytes(&mut buf.t[..]).is_err());
+ assert!(AU64::ref_from_prefix(&buf.t[..]).is_err());
+ assert!(AU64::mut_from_prefix(&mut buf.t[..]).is_err());
+ assert!(AU64::ref_from_suffix(&buf.t[..]).is_err());
+ assert!(AU64::mut_from_suffix(&mut buf.t[..]).is_err());
+ assert!(<[u8; 8]>::ref_from_prefix(&buf.t[..]).is_err());
+ assert!(<[u8; 8]>::mut_from_prefix(&mut buf.t[..]).is_err());
+ assert!(<[u8; 8]>::ref_from_suffix(&buf.t[..]).is_err());
+ assert!(<[u8; 8]>::mut_from_suffix(&mut buf.t[..]).is_err());
+
+ // Fail because the alignment is insufficient.
+ let mut buf = Align::<[u8; 13], AU64>::default();
+ assert!(AU64::ref_from_bytes(&buf.t[1..]).is_err());
+ assert!(AU64::mut_from_bytes(&mut buf.t[1..]).is_err());
+ assert!(AU64::ref_from_bytes(&buf.t[1..]).is_err());
+ assert!(AU64::mut_from_bytes(&mut buf.t[1..]).is_err());
+ assert!(AU64::ref_from_prefix(&buf.t[1..]).is_err());
+ assert!(AU64::mut_from_prefix(&mut buf.t[1..]).is_err());
+ assert!(AU64::ref_from_suffix(&buf.t[..]).is_err());
+ assert!(AU64::mut_from_suffix(&mut buf.t[..]).is_err());
+ }
+
+ #[test]
+ fn test_to_methods() {
+ /// Run a series of tests by calling `IntoBytes` methods on `t`.
+ ///
+ /// `bytes` is the expected byte sequence returned from `t.as_bytes()`
+ /// before `t` has been modified. `post_mutation` is the expected
+ /// sequence returned from `t.as_bytes()` after `t.as_mut_bytes()[0]`
+ /// has had its bits flipped (by applying `^= 0xFF`).
+ ///
+ /// `N` is the size of `t` in bytes.
+ fn test<T: FromBytes + IntoBytes + Immutable + Debug + Eq + ?Sized, const N: usize>(
+ t: &mut T,
+ bytes: &[u8],
+ post_mutation: &T,
+ ) {
+ // Test that we can access the underlying bytes, and that we get the
+ // right bytes and the right number of bytes.
+ assert_eq!(t.as_bytes(), bytes);
+
+ // Test that changes to the underlying byte slices are reflected in
+ // the original object.
+ t.as_mut_bytes()[0] ^= 0xFF;
+ assert_eq!(t, post_mutation);
+ t.as_mut_bytes()[0] ^= 0xFF;
+
+ // `write_to` rejects slices that are too small or too large.
+ assert!(t.write_to(&mut vec![0; N - 1][..]).is_err());
+ assert!(t.write_to(&mut vec![0; N + 1][..]).is_err());
+
+ // `write_to` works as expected.
+ let mut bytes = [0; N];
+ assert_eq!(t.write_to(&mut bytes[..]), Ok(()));
+ assert_eq!(bytes, t.as_bytes());
+
+ // `write_to_prefix` rejects slices that are too small.
+ assert!(t.write_to_prefix(&mut vec![0; N - 1][..]).is_err());
+
+ // `write_to_prefix` works with exact-sized slices.
+ let mut bytes = [0; N];
+ assert_eq!(t.write_to_prefix(&mut bytes[..]), Ok(()));
+ assert_eq!(bytes, t.as_bytes());
+
+ // `write_to_prefix` works with too-large slices, and any bytes past
+ // the prefix aren't modified.
+ let mut too_many_bytes = vec![0; N + 1];
+ too_many_bytes[N] = 123;
+ assert_eq!(t.write_to_prefix(&mut too_many_bytes[..]), Ok(()));
+ assert_eq!(&too_many_bytes[..N], t.as_bytes());
+ assert_eq!(too_many_bytes[N], 123);
+
+ // `write_to_suffix` rejects slices that are too small.
+ assert!(t.write_to_suffix(&mut vec![0; N - 1][..]).is_err());
+
+ // `write_to_suffix` works with exact-sized slices.
+ let mut bytes = [0; N];
+ assert_eq!(t.write_to_suffix(&mut bytes[..]), Ok(()));
+ assert_eq!(bytes, t.as_bytes());
+
+ // `write_to_suffix` works with too-large slices, and any bytes
+ // before the suffix aren't modified.
+ let mut too_many_bytes = vec![0; N + 1];
+ too_many_bytes[0] = 123;
+ assert_eq!(t.write_to_suffix(&mut too_many_bytes[..]), Ok(()));
+ assert_eq!(&too_many_bytes[1..], t.as_bytes());
+ assert_eq!(too_many_bytes[0], 123);
+ }
+
+ #[derive(Debug, Eq, PartialEq, FromBytes, IntoBytes, Immutable)]
+ #[repr(C)]
+ struct Foo {
+ a: u32,
+ b: Wrapping<u32>,
+ c: Option<NonZeroU32>,
+ }
+
+ let expected_bytes: Vec<u8> = if cfg!(target_endian = "little") {
+ vec![1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
+ } else {
+ vec![0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0]
+ };
+ let post_mutation_expected_a =
+ if cfg!(target_endian = "little") { 0x00_00_00_FE } else { 0xFF_00_00_01 };
+ test::<_, 12>(
+ &mut Foo { a: 1, b: Wrapping(2), c: None },
+ expected_bytes.as_bytes(),
+ &Foo { a: post_mutation_expected_a, b: Wrapping(2), c: None },
+ );
+ test::<_, 3>(
+ Unsized::from_mut_slice(&mut [1, 2, 3]),
+ &[1, 2, 3],
+ Unsized::from_mut_slice(&mut [0xFE, 2, 3]),
+ );
+ }
+
+ #[test]
+ fn test_array() {
+ #[derive(FromBytes, IntoBytes, Immutable)]
+ #[repr(C)]
+ struct Foo {
+ a: [u16; 33],
+ }
+
+ let foo = Foo { a: [0xFFFF; 33] };
+ let expected = [0xFFu8; 66];
+ assert_eq!(foo.as_bytes(), &expected[..]);
+ }
+
+ #[test]
+ fn test_new_zeroed() {
+ assert!(!bool::new_zeroed());
+ assert_eq!(u64::new_zeroed(), 0);
+ // This test exists in order to exercise unsafe code, especially when
+ // running under Miri.
+ #[allow(clippy::unit_cmp)]
+ {
+ assert_eq!(<()>::new_zeroed(), ());
+ }
+ }
+
+ #[test]
+ fn test_transparent_packed_generic_struct() {
+ #[derive(IntoBytes, FromBytes, Unaligned)]
+ #[repr(transparent)]
+ #[allow(dead_code)] // We never construct this type
+ struct Foo<T> {
+ _t: T,
+ _phantom: PhantomData<()>,
+ }
+
+ assert_impl_all!(Foo<u32>: FromZeros, FromBytes, IntoBytes);
+ assert_impl_all!(Foo<u8>: Unaligned);
+
+ #[derive(IntoBytes, FromBytes, Unaligned)]
+ #[repr(C, packed)]
+ #[allow(dead_code)] // We never construct this type
+ struct Bar<T, U> {
+ _t: T,
+ _u: U,
+ }
+
+ assert_impl_all!(Bar<u8, AU64>: FromZeros, FromBytes, IntoBytes, Unaligned);
+ }
+
+ #[cfg(feature = "alloc")]
+ mod alloc {
+ use super::*;
+
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[test]
+ fn test_extend_vec_zeroed() {
+ // Test extending when there is an existing allocation.
+ let mut v = vec![100u16, 200, 300];
+ FromZeros::extend_vec_zeroed(&mut v, 3).unwrap();
+ assert_eq!(v.len(), 6);
+ assert_eq!(&*v, &[100, 200, 300, 0, 0, 0]);
+ drop(v);
+
+ // Test extending when there is no existing allocation.
+ let mut v: Vec<u64> = Vec::new();
+ FromZeros::extend_vec_zeroed(&mut v, 3).unwrap();
+ assert_eq!(v.len(), 3);
+ assert_eq!(&*v, &[0, 0, 0]);
+ drop(v);
+ }
+
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[test]
+ fn test_extend_vec_zeroed_zst() {
+ // Test extending when there is an existing (fake) allocation.
+ let mut v = vec![(), (), ()];
+ <()>::extend_vec_zeroed(&mut v, 3).unwrap();
+ assert_eq!(v.len(), 6);
+ assert_eq!(&*v, &[(), (), (), (), (), ()]);
+ drop(v);
+
+ // Test extending when there is no existing (fake) allocation.
+ let mut v: Vec<()> = Vec::new();
+ <()>::extend_vec_zeroed(&mut v, 3).unwrap();
+ assert_eq!(&*v, &[(), (), ()]);
+ drop(v);
+ }
+
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[test]
+ fn test_insert_vec_zeroed() {
+ // Insert at start (no existing allocation).
+ let mut v: Vec<u64> = Vec::new();
+ u64::insert_vec_zeroed(&mut v, 0, 2).unwrap();
+ assert_eq!(v.len(), 2);
+ assert_eq!(&*v, &[0, 0]);
+ drop(v);
+
+ // Insert at start.
+ let mut v = vec![100u64, 200, 300];
+ u64::insert_vec_zeroed(&mut v, 0, 2).unwrap();
+ assert_eq!(v.len(), 5);
+ assert_eq!(&*v, &[0, 0, 100, 200, 300]);
+ drop(v);
+
+ // Insert at middle.
+ let mut v = vec![100u64, 200, 300];
+ u64::insert_vec_zeroed(&mut v, 1, 1).unwrap();
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[100, 0, 200, 300]);
+ drop(v);
+
+ // Insert at end.
+ let mut v = vec![100u64, 200, 300];
+ u64::insert_vec_zeroed(&mut v, 3, 1).unwrap();
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[100, 200, 300, 0]);
+ drop(v);
+ }
+
+ #[cfg(not(no_zerocopy_panic_in_const_and_vec_try_reserve_1_57_0))]
+ #[test]
+ fn test_insert_vec_zeroed_zst() {
+ // Insert at start (no existing fake allocation).
+ let mut v: Vec<()> = Vec::new();
+ <()>::insert_vec_zeroed(&mut v, 0, 2).unwrap();
+ assert_eq!(v.len(), 2);
+ assert_eq!(&*v, &[(), ()]);
+ drop(v);
+
+ // Insert at start.
+ let mut v = vec![(), (), ()];
+ <()>::insert_vec_zeroed(&mut v, 0, 2).unwrap();
+ assert_eq!(v.len(), 5);
+ assert_eq!(&*v, &[(), (), (), (), ()]);
+ drop(v);
+
+ // Insert at middle.
+ let mut v = vec![(), (), ()];
+ <()>::insert_vec_zeroed(&mut v, 1, 1).unwrap();
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[(), (), (), ()]);
+ drop(v);
+
+ // Insert at end.
+ let mut v = vec![(), (), ()];
+ <()>::insert_vec_zeroed(&mut v, 3, 1).unwrap();
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[(), (), (), ()]);
+ drop(v);
+ }
+
+ #[test]
+ fn test_new_box_zeroed() {
+ assert_eq!(u64::new_box_zeroed(), Ok(Box::new(0)));
+ }
+
+ #[test]
+ fn test_new_box_zeroed_array() {
+ drop(<[u32; 0x1000]>::new_box_zeroed());
+ }
+
+ #[test]
+ fn test_new_box_zeroed_zst() {
+ // This test exists in order to exercise unsafe code, especially
+ // when running under Miri.
+ #[allow(clippy::unit_cmp)]
+ {
+ assert_eq!(<()>::new_box_zeroed(), Ok(Box::new(())));
+ }
+ }
+
+ #[test]
+ fn test_new_box_zeroed_with_elems() {
+ let mut s: Box<[u64]> = <[u64]>::new_box_zeroed_with_elems(3).unwrap();
+ assert_eq!(s.len(), 3);
+ assert_eq!(&*s, &[0, 0, 0]);
+ s[1] = 3;
+ assert_eq!(&*s, &[0, 3, 0]);
+ }
+
+ #[test]
+ fn test_new_box_zeroed_with_elems_empty() {
+ let s: Box<[u64]> = <[u64]>::new_box_zeroed_with_elems(0).unwrap();
+ assert_eq!(s.len(), 0);
+ }
+
+ #[test]
+ fn test_new_box_zeroed_with_elems_zst() {
+ let mut s: Box<[()]> = <[()]>::new_box_zeroed_with_elems(3).unwrap();
+ assert_eq!(s.len(), 3);
+ assert!(s.get(10).is_none());
+ // This test exists in order to exercise unsafe code, especially
+ // when running under Miri.
+ #[allow(clippy::unit_cmp)]
+ {
+ assert_eq!(s[1], ());
+ }
+ s[2] = ();
+ }
+
+ #[test]
+ fn test_new_box_zeroed_with_elems_zst_empty() {
+ let s: Box<[()]> = <[()]>::new_box_zeroed_with_elems(0).unwrap();
+ assert_eq!(s.len(), 0);
+ }
+
+ #[test]
+ fn new_box_zeroed_with_elems_errors() {
+ assert_eq!(<[u16]>::new_box_zeroed_with_elems(usize::MAX), Err(AllocError));
+
+ let max = <usize as core::convert::TryFrom<_>>::try_from(isize::MAX).unwrap();
+ assert_eq!(
+ <[u16]>::new_box_zeroed_with_elems((max / mem::size_of::<u16>()) + 1),
+ Err(AllocError)
+ );
+ }
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn test_deprecated_from_bytes() {
+ let val = 0u32;
+ let bytes = val.as_bytes();
+
+ assert!(u32::ref_from(bytes).is_some());
+ // mut_from needs mut bytes
+ let mut val = 0u32;
+ let mut_bytes = val.as_mut_bytes();
+ assert!(u32::mut_from(mut_bytes).is_some());
+
+ assert!(u32::read_from(bytes).is_some());
+
+ let (slc, rest) = <u32>::slice_from_prefix(bytes, 0).unwrap();
+ assert!(slc.is_empty());
+ assert_eq!(rest.len(), 4);
+
+ let (rest, slc) = <u32>::slice_from_suffix(bytes, 0).unwrap();
+ assert!(slc.is_empty());
+ assert_eq!(rest.len(), 4);
+
+ let (slc, rest) = <u32>::mut_slice_from_prefix(mut_bytes, 0).unwrap();
+ assert!(slc.is_empty());
+ assert_eq!(rest.len(), 4);
+
+ let (rest, slc) = <u32>::mut_slice_from_suffix(mut_bytes, 0).unwrap();
+ assert!(slc.is_empty());
+ assert_eq!(rest.len(), 4);
+ }
+
+ #[test]
+ fn test_try_ref_from_prefix_suffix() {
+ use crate::util::testutil::Align;
+ let bytes = &Align::<[u8; 4], u32>::new([0u8; 4]).t[..];
+ let (r, rest): (&u32, &[u8]) = u32::try_ref_from_prefix(bytes).unwrap();
+ assert_eq!(*r, 0);
+ assert_eq!(rest.len(), 0);
+
+ let (rest, r): (&[u8], &u32) = u32::try_ref_from_suffix(bytes).unwrap();
+ assert_eq!(*r, 0);
+ assert_eq!(rest.len(), 0);
+ }
+
+ #[test]
+ fn test_raw_dangling() {
+ use crate::util::AsAddress;
+ let ptr: NonNull<u32> = u32::raw_dangling();
+ assert_eq!(AsAddress::addr(ptr), 1);
+
+ let ptr: NonNull<[u32]> = <[u32]>::raw_dangling();
+ assert_eq!(AsAddress::addr(ptr), 1);
+ }
+
+ #[test]
+ fn test_try_ref_from_prefix_with_elems() {
+ use crate::util::testutil::Align;
+ let bytes = &Align::<[u8; 8], u32>::new([0u8; 8]).t[..];
+ let (r, rest): (&[u32], &[u8]) = <[u32]>::try_ref_from_prefix_with_elems(bytes, 2).unwrap();
+ assert_eq!(r.len(), 2);
+ assert_eq!(rest.len(), 0);
+ }
+
+ #[test]
+ fn test_try_ref_from_suffix_with_elems() {
+ use crate::util::testutil::Align;
+ let bytes = &Align::<[u8; 8], u32>::new([0u8; 8]).t[..];
+ let (rest, r): (&[u8], &[u32]) = <[u32]>::try_ref_from_suffix_with_elems(bytes, 2).unwrap();
+ assert_eq!(r.len(), 2);
+ assert_eq!(rest.len(), 0);
+ }
+}
diff --git a/rust/zerocopy/src/macros.rs b/rust/zerocopy/src/macros.rs
new file mode 100644
index 000000000000..948f8f912bb7
--- /dev/null
+++ b/rust/zerocopy/src/macros.rs
@@ -0,0 +1,1823 @@
+// Copyright 2024 The Fuchsia Authors
+//
+// Licensed under the 2-Clause BSD License <LICENSE-BSD or
+// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+/// Safely transmutes a value of one type to a value of another type of the same
+/// size.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// const fn transmute<Src, Dst>(src: Src) -> Dst
+/// where
+/// Src: IntoBytes,
+/// Dst: FromBytes,
+/// size_of::<Src>() == size_of::<Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// However, unlike a function, this macro can only be invoked when the types of
+/// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
+/// inferred from the calling context; they cannot be explicitly specified in
+/// the macro invocation.
+///
+/// Note that the `Src` produced by the expression `$e` will *not* be dropped.
+/// Semantically, its bits will be copied into a new value of type `Dst`, the
+/// original `Src` will be forgotten, and the value of type `Dst` will be
+/// returned.
+///
+/// # `#![allow(shrink)]`
+///
+/// If `#![allow(shrink)]` is provided, `transmute!` additionally supports
+/// transmutations that shrink the size of the value; e.g.:
+///
+/// ```
+/// # use zerocopy::transmute;
+/// let u: u32 = transmute!(#![allow(shrink)] 0u64);
+/// assert_eq!(u, 0u32);
+/// ```
+///
+/// # Examples
+///
+/// ```
+/// # use zerocopy::transmute;
+/// let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+///
+/// let two_dimensional: [[u8; 4]; 2] = transmute!(one_dimensional);
+///
+/// assert_eq!(two_dimensional, [[0, 1, 2, 3], [4, 5, 6, 7]]);
+/// ```
+///
+/// # Use in `const` contexts
+///
+/// This macro can be invoked in `const` contexts.
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "transmute",
+ format = "coco_static_size",
+)]
+#[macro_export]
+macro_rules! transmute {
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because there's no way, in a generic context, to enforce that two types
+ // have the same size. `core::mem::transmute` uses compiler magic to enforce
+ // this so long as the types are concrete.
+ (#![allow(shrink)] $e:expr) => {{
+ let mut e = $e;
+ if false {
+ // This branch, though never taken, ensures that the type of `e` is
+ // `IntoBytes` and that the type of the outer macro invocation
+ // expression is `FromBytes`.
+
+ fn transmute<Src, Dst>(src: Src) -> Dst
+ where
+ Src: $crate::IntoBytes,
+ Dst: $crate::FromBytes,
+ {
+ let _ = src;
+ loop {}
+ }
+ loop {}
+ #[allow(unreachable_code)]
+ transmute(e)
+ } else {
+ use $crate::util::macro_util::core_reexport::mem::ManuallyDrop;
+
+ // NOTE: `repr(packed)` is important! It ensures that the size of
+ // `Transmute` won't be rounded up to accommodate `Src`'s or `Dst`'s
+ // alignment, which would break the size comparison logic below.
+ //
+ // As an example of why this is problematic, consider `Src = [u8;
+ // 5]`, `Dst = u32`. The total size of `Transmute<Src, Dst>` would
+ // be 8, and so we would reject a `[u8; 5]` to `u32` transmute as
+ // being size-increasing, which it isn't.
+ #[repr(C, packed)]
+ union Transmute<Src, Dst> {
+ src: ManuallyDrop<Src>,
+ dst: ManuallyDrop<Dst>,
+ }
+
+ // SAFETY: `Transmute` is a `repr(C)` union whose `src` field has
+ // type `ManuallyDrop<Src>`. Thus, the `src` field starts at byte
+ // offset 0 within `Transmute` [1]. `ManuallyDrop<T>` has the same
+ // layout and bit validity as `T`, so it is sound to transmute `Src`
+ // to `Transmute`.
+ //
+ // [1] https://doc.rust-lang.org/1.85.0/reference/type-layout.html#reprc-unions
+ //
+ // [2] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
+ //
+ // `ManuallyDrop<T>` is guaranteed to have the same layout and bit
+ // validity as `T`
+ let u: Transmute<_, _> = unsafe {
+ // Clippy: We can't annotate the types; this macro is designed
+ // to infer the types from the calling context.
+ #[allow(clippy::missing_transmute_annotations)]
+ $crate::util::macro_util::core_reexport::mem::transmute(e)
+ };
+
+ if false {
+ // SAFETY: This code is never executed.
+ e = ManuallyDrop::into_inner(unsafe { u.src });
+ // Suppress the `unused_assignments` lint on the previous line.
+ let _ = e;
+ loop {}
+ } else {
+ // SAFETY: Per the safety comment on `let u` above, the `dst`
+ // field in `Transmute` starts at byte offset 0, and has the
+ // same layout and bit validity as `Dst`.
+ //
+ // Transmuting `Src` to `Transmute<Src, Dst>` above using
+ // `core::mem::transmute` ensures that `size_of::<Src>() ==
+ // size_of::<Transmute<Src, Dst>>()`. A `#[repr(C, packed)]`
+ // union has the maximum size of all of its fields [1], so this
+ // is equivalent to `size_of::<Src>() >= size_of::<Dst>()`.
+ //
+ // The outer `if`'s `false` branch ensures that `Src: IntoBytes`
+ // and `Dst: FromBytes`. This, combined with the size bound,
+ // ensures that this transmute is sound.
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/reference/type-layout.html#reprc-unions:
+ //
+ // The union will have a size of the maximum size of all of
+ // its fields rounded to its alignment
+ let dst = unsafe { u.dst };
+ $crate::util::macro_util::must_use(ManuallyDrop::into_inner(dst))
+ }
+ }
+ }};
+ ($e:expr) => {{
+ let e = $e;
+ if false {
+ // This branch, though never taken, ensures that the type of `e` is
+ // `IntoBytes` and that the type of the outer macro invocation
+ // expression is `FromBytes`.
+
+ fn transmute<Src, Dst>(src: Src) -> Dst
+ where
+ Src: $crate::IntoBytes,
+ Dst: $crate::FromBytes,
+ {
+ let _ = src;
+ loop {}
+ }
+ loop {}
+ #[allow(unreachable_code)]
+ transmute(e)
+ } else {
+ // SAFETY: `core::mem::transmute` ensures that the type of `e` and
+ // the type of this macro invocation expression have the same size.
+ // We know this transmute is safe thanks to the `IntoBytes` and
+ // `FromBytes` bounds enforced by the `false` branch.
+ let u = unsafe {
+ // Clippy: We can't annotate the types; this macro is designed
+ // to infer the types from the calling context.
+ #[allow(clippy::missing_transmute_annotations, unnecessary_transmutes)]
+ $crate::util::macro_util::core_reexport::mem::transmute(e)
+ };
+ $crate::util::macro_util::must_use(u)
+ }
+ }};
+}
+
+/// Safely transmutes a mutable or immutable reference of one type to an
+/// immutable reference of another type of the same size and compatible
+/// alignment.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn transmute_ref<'src, 'dst, Src, Dst>(src: &'src Src) -> &'dst Dst
+/// where
+/// 'src: 'dst,
+/// Src: IntoBytes + Immutable + ?Sized,
+/// Dst: FromBytes + Immutable + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// # Size compatibility
+///
+/// `transmute_ref!` supports transmuting between `Sized` types, between unsized
+/// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
+/// supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::transmute_ref;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &[[u8; 2]] = &[[0, 1], [2, 3]][..];
+/// let dst: &[u8] = transmute_ref!(src);
+///
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// assert_eq!(size_of_val(src), size_of_val(dst));
+/// ```
+///
+/// # Errors
+///
+/// Violations of the alignment and size compatibility checks are detected
+/// *after* the compiler performs monomorphization. This has two important
+/// consequences.
+///
+/// First, it means that generic code will *never* fail these conditions:
+///
+/// ```
+/// # use zerocopy::{transmute_ref, FromBytes, IntoBytes, Immutable};
+/// fn transmute_ref<Src, Dst>(src: &Src) -> &Dst
+/// where
+/// Src: IntoBytes + Immutable,
+/// Dst: FromBytes + Immutable,
+/// {
+/// transmute_ref!(src)
+/// }
+/// ```
+///
+/// Instead, failures will only be detected once generic code is instantiated
+/// with concrete types:
+///
+/// ```compile_fail,E0080
+/// # use zerocopy::{transmute_ref, FromBytes, IntoBytes, Immutable};
+/// #
+/// # fn transmute_ref<Src, Dst>(src: &Src) -> &Dst
+/// # where
+/// # Src: IntoBytes + Immutable,
+/// # Dst: FromBytes + Immutable,
+/// # {
+/// # transmute_ref!(src)
+/// # }
+/// let src: &u16 = &0;
+/// let dst: &u8 = transmute_ref(src);
+/// ```
+///
+/// Second, the fact that violations are detected after monomorphization means
+/// that `cargo check` will usually not detect errors, even when types are
+/// concrete. Instead, `cargo build` must be used to detect such errors.
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::transmute_ref;
+/// let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+///
+/// let two_dimensional: &[[u8; 4]; 2] = transmute_ref!(&one_dimensional);
+///
+/// assert_eq!(two_dimensional, &[[0, 1, 2, 3], [4, 5, 6, 7]]);
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, u8>;
+///
+/// let src = Src::ref_from_bytes(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
+/// let dst: &Dst = transmute_ref!(src);
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 2, 3]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [4, 5, 6, 7]);
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [2, 3, 4, 5, 6, 7]);
+/// ```
+///
+/// # Use in `const` contexts
+///
+/// This macro can be invoked in `const` contexts only when `Src: Sized` and
+/// `Dst: Sized`.
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "transmute_ref",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+)]
+#[macro_export]
+macro_rules! transmute_ref {
+ ($e:expr) => {{
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because there's no way, in a generic context, to enforce that two
+ // types have the same size or alignment.
+
+ // Ensure that the source type is a reference or a mutable reference
+ // (note that mutable references are implicitly reborrowed here).
+ let e: &_ = $e;
+
+ #[allow(unused, clippy::diverging_sub_expression)]
+ if false {
+ // This branch, though never taken, ensures that the type of `e` is
+ // `&T` where `T: IntoBytes + Immutable`, and that the type of this
+ // macro expression is `&U` where `U: FromBytes + Immutable`.
+
+ struct AssertSrcIsIntoBytes<'a, T: ?::core::marker::Sized + $crate::IntoBytes>(&'a T);
+ struct AssertSrcIsImmutable<'a, T: ?::core::marker::Sized + $crate::Immutable>(&'a T);
+ struct AssertDstIsFromBytes<'a, U: ?::core::marker::Sized + $crate::FromBytes>(&'a U);
+ struct AssertDstIsImmutable<'a, T: ?::core::marker::Sized + $crate::Immutable>(&'a T);
+
+ let _ = AssertSrcIsIntoBytes(e);
+ let _ = AssertSrcIsImmutable(e);
+
+ if true {
+ #[allow(unused, unreachable_code)]
+ let u = AssertDstIsFromBytes(loop {});
+ u.0
+ } else {
+ #[allow(unused, unreachable_code)]
+ let u = AssertDstIsImmutable(loop {});
+ u.0
+ }
+ } else {
+ use $crate::util::macro_util::TransmuteRefDst;
+ let t = $crate::util::macro_util::Wrap::new(e);
+
+ if false {
+ // This branch exists solely to force the compiler to infer the
+ // type of `Dst` *before* it attempts to resolve the method call
+ // to `transmute_ref` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of
+ // `transmute_ref` (which requires `Dst: Sized`) because inherent
+ // methods take priority over trait methods. It does this before
+ // it realizes `Dst` is `!Sized`, leading to a compile error when
+ // it checks the bounds later.
+ //
+ // By calling this helper (which returns `&Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else`
+ // branch, the compiler knows `Dst` is `!Sized`, properly
+ // disqualifies the inherent method, and falls back to the trait
+ // implementation.
+ t.transmute_ref_inference_helper()
+ } else {
+ // SAFETY: The outer `if false` branch ensures that:
+ // - `Src: IntoBytes + Immutable`
+ // - `Dst: FromBytes + Immutable`
+ unsafe {
+ t.transmute_ref()
+ }
+ }
+ }
+ }}
+}
+
+/// Safely transmutes a mutable reference of one type to a mutable reference of
+/// another type of the same size and compatible alignment.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// const fn transmute_mut<'src, 'dst, Src, Dst>(src: &'src mut Src) -> &'dst mut Dst
+/// where
+/// 'src: 'dst,
+/// Src: FromBytes + IntoBytes + ?Sized,
+/// Dst: FromBytes + IntoBytes + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// # Size compatibility
+///
+/// `transmute_mut!` supports transmuting between `Sized` types, between unsized
+/// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
+/// supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::transmute_mut;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &mut [[u8; 2]] = &mut [[0, 1], [2, 3]][..];
+/// let dst: &mut [u8] = transmute_mut!(src);
+///
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// let dst_size = size_of_val(dst);
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(size_of_val(src), dst_size);
+/// ```
+///
+/// # Errors
+///
+/// Violations of the alignment and size compatibility checks are detected
+/// *after* the compiler performs monomorphization. This has two important
+/// consequences.
+///
+/// First, it means that generic code will *never* fail these conditions:
+///
+/// ```
+/// # use zerocopy::{transmute_mut, FromBytes, IntoBytes, Immutable};
+/// fn transmute_mut<Src, Dst>(src: &mut Src) -> &mut Dst
+/// where
+/// Src: FromBytes + IntoBytes,
+/// Dst: FromBytes + IntoBytes,
+/// {
+/// transmute_mut!(src)
+/// }
+/// ```
+///
+/// Instead, failures will only be detected once generic code is instantiated
+/// with concrete types:
+///
+/// ```compile_fail,E0080
+/// # use zerocopy::{transmute_mut, FromBytes, IntoBytes, Immutable};
+/// #
+/// # fn transmute_mut<Src, Dst>(src: &mut Src) -> &mut Dst
+/// # where
+/// # Src: FromBytes + IntoBytes,
+/// # Dst: FromBytes + IntoBytes,
+/// # {
+/// # transmute_mut!(src)
+/// # }
+/// let src: &mut u16 = &mut 0;
+/// let dst: &mut u8 = transmute_mut(src);
+/// ```
+///
+/// Second, the fact that violations are detected after monomorphization means
+/// that `cargo check` will usually not detect errors, even when types are
+/// concrete. Instead, `cargo build` must be used to detect such errors.
+///
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::transmute_mut;
+/// let mut one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+///
+/// let two_dimensional: &mut [[u8; 4]; 2] = transmute_mut!(&mut one_dimensional);
+///
+/// assert_eq!(two_dimensional, &[[0, 1, 2, 3], [4, 5, 6, 7]]);
+///
+/// two_dimensional.reverse();
+///
+/// assert_eq!(one_dimensional, [4, 5, 6, 7, 0, 1, 2, 3]);
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, u8>;
+///
+/// let mut bytes = [0, 1, 2, 3, 4, 5, 6, 7];
+/// let src = Src::mut_from_bytes(&mut bytes[..]).unwrap();
+/// let dst: &mut Dst = transmute_mut!(src);
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [2, 3, 4, 5, 6, 7]);
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 2, 3]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [4, 5, 6, 7]);
+/// ```
+#[macro_export]
+macro_rules! transmute_mut {
+ ($e:expr) => {{
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because, for backwards-compatibility on v0.8.x, we use the autoref
+ // specialization trick to dispatch to different `transmute_mut`
+ // implementations: one which doesn't require `Src: KnownLayout + Dst:
+ // KnownLayout` when `Src: Sized + Dst: Sized`, and one which requires
+ // `KnownLayout` bounds otherwise.
+
+ // Ensure that the source type is a mutable reference.
+ let e: &mut _ = $e;
+
+ #[allow(unused)]
+ use $crate::util::macro_util::TransmuteMutDst as _;
+ let t = $crate::util::macro_util::Wrap::new(e);
+ if false {
+ // This branch exists solely to force the compiler to infer the type
+ // of `Dst` *before* it attempts to resolve the method call to
+ // `transmute_mut` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of `transmute_mut`
+ // (which requires `Dst: Sized`) because inherent methods take
+ // priority over trait methods. It does this before it realizes
+ // `Dst` is `!Sized`, leading to a compile error when it checks the
+ // bounds later.
+ //
+ // By calling this helper (which returns `&mut Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else` branch,
+ // the compiler knows `Dst` is `!Sized`, properly disqualifies the
+ // inherent method, and falls back to the trait implementation.
+ t.transmute_mut_inference_helper()
+ } else {
+ t.transmute_mut()
+ }
+ }}
+}
+
+/// Conditionally transmutes a value of one type to a value of another type of
+/// the same size.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>>
+/// where
+/// Src: IntoBytes,
+/// Dst: TryFromBytes,
+/// size_of::<Src>() == size_of::<Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// However, unlike a function, this macro can only be invoked when the types of
+/// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
+/// inferred from the calling context; they cannot be explicitly specified in
+/// the macro invocation.
+///
+/// Note that the `Src` produced by the expression `$e` will *not* be dropped.
+/// Semantically, its bits will be copied into a new value of type `Dst`, the
+/// original `Src` will be forgotten, and the value of type `Dst` will be
+/// returned.
+///
+/// # Examples
+///
+/// ```
+/// # use zerocopy::*;
+/// // 0u8 → bool = false
+/// assert_eq!(try_transmute!(0u8), Ok(false));
+///
+/// // 1u8 → bool = true
+/// assert_eq!(try_transmute!(1u8), Ok(true));
+///
+/// // 2u8 → bool = error
+/// assert!(matches!(
+/// try_transmute!(2u8),
+/// Result::<bool, _>::Err(ValidityError { .. })
+/// ));
+/// ```
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "try_transmute",
+ format = "coco_static_size",
+)]
+#[macro_export]
+macro_rules! try_transmute {
+ ($e:expr) => {{
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because there's no way, in a generic context, to enforce that two
+ // types have the same size. `core::mem::transmute` uses compiler magic
+ // to enforce this so long as the types are concrete.
+
+ let e = $e;
+ if false {
+ // Check that the sizes of the source and destination types are
+ // equal.
+
+ // SAFETY: This code is never executed.
+ Ok(unsafe {
+ // Clippy: We can't annotate the types; this macro is designed
+ // to infer the types from the calling context.
+ #[allow(clippy::missing_transmute_annotations)]
+ $crate::util::macro_util::core_reexport::mem::transmute(e)
+ })
+ } else {
+ $crate::util::macro_util::try_transmute::<_, _>(e)
+ }
+ }}
+}
+
+/// Conditionally transmutes a mutable or immutable reference of one type to an
+/// immutable reference of another type of the same size and compatible
+/// alignment.
+///
+/// *Note that while the **value** of the referent is checked for validity at
+/// runtime, the **size** and **alignment** are checked at compile time. For
+/// conversions which are fallible with respect to size and alignment, see the
+/// methods on [`TryFromBytes`].*
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn try_transmute_ref<Src, Dst>(src: &Src) -> Result<&Dst, ValidityError<&Src, Dst>>
+/// where
+/// Src: IntoBytes + Immutable + ?Sized,
+/// Dst: TryFromBytes + Immutable + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// [`TryFromBytes`]: crate::TryFromBytes
+///
+/// # Size compatibility
+///
+/// `try_transmute_ref!` supports transmuting between `Sized` types, between
+/// unsized (i.e., `?Sized`) types, and from a `Sized` type to an unsized type.
+/// It supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::try_transmute_ref;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &[[u8; 2]] = &[[0, 1], [2, 3]][..];
+/// let dst: &[u8] = try_transmute_ref!(src).unwrap();
+///
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// assert_eq!(size_of_val(src), size_of_val(dst));
+/// ```
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::*;
+/// // 0u8 → bool = false
+/// assert_eq!(try_transmute_ref!(&0u8), Ok(&false));
+///
+/// // 1u8 → bool = true
+/// assert_eq!(try_transmute_ref!(&1u8), Ok(&true));
+///
+/// // 2u8 → bool = error
+/// assert!(matches!(
+/// try_transmute_ref!(&2u8),
+/// Result::<&bool, _>::Err(ValidityError { .. })
+/// ));
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, bool>;
+///
+/// let src = Src::ref_from_bytes(&[0, 1, 0, 1, 0, 1, 0, 1]).unwrap();
+/// let dst: &Dst = try_transmute_ref!(src).unwrap();
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 0, 1]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [0, 1, 0, 1]);
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [false, true, false, true, false, true]);
+/// ```
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "try_transmute_ref",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+)]
+#[macro_export]
+macro_rules! try_transmute_ref {
+ ($e:expr) => {{
+ // Ensure that the source type is a reference or a mutable reference
+ // (note that mutable references are implicitly reborrowed here).
+ let e: &_ = $e;
+
+ #[allow(unused_imports)]
+ use $crate::util::macro_util::TryTransmuteRefDst as _;
+ let t = $crate::util::macro_util::Wrap::new(e);
+ if false {
+ // This branch exists solely to force the compiler to infer the type
+ // of `Dst` *before* it attempts to resolve the method call to
+ // `try_transmute_ref` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of
+ // `try_transmute_ref` (which requires `Dst: Sized`) because
+ // inherent methods take priority over trait methods. It does this
+ // before it realizes `Dst` is `!Sized`, leading to a compile error
+ // when it checks the bounds later.
+ //
+ // By calling this helper (which returns `&Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else`
+ // branch, the compiler knows `Dst` is `!Sized`, properly
+ // disqualifies the inherent method, and falls back to the trait
+ // implementation.
+ Ok(t.transmute_ref_inference_helper())
+ } else {
+ t.try_transmute_ref()
+ }
+ }}
+}
+
+/// Conditionally transmutes a mutable reference of one type to a mutable
+/// reference of another type of the same size and compatible alignment.
+///
+/// *Note that while the **value** of the referent is checked for validity at
+/// runtime, the **size** and **alignment** are checked at compile time. For
+/// conversions which are fallible with respect to size and alignment, see the
+/// methods on [`TryFromBytes`].*
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn try_transmute_mut<Src, Dst>(src: &mut Src) -> Result<&mut Dst, ValidityError<&mut Src, Dst>>
+/// where
+/// Src: FromBytes + IntoBytes + ?Sized,
+/// Dst: TryFromBytes + IntoBytes + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// [`TryFromBytes`]: crate::TryFromBytes
+///
+/// # Size compatibility
+///
+/// `try_transmute_mut!` supports transmuting between `Sized` types, between
+/// unsized (i.e., `?Sized`) types, and from a `Sized` type to an unsized type.
+/// It supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::try_transmute_mut;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &mut [[u8; 2]] = &mut [[0, 1], [2, 3]][..];
+/// let dst: &mut [u8] = try_transmute_mut!(src).unwrap();
+///
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// let dst_size = size_of_val(dst);
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(size_of_val(src), dst_size);
+/// ```
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::*;
+/// // 0u8 → bool = false
+/// let src = &mut 0u8;
+/// assert_eq!(try_transmute_mut!(src), Ok(&mut false));
+///
+/// // 1u8 → bool = true
+/// let src = &mut 1u8;
+/// assert_eq!(try_transmute_mut!(src), Ok(&mut true));
+///
+/// // 2u8 → bool = error
+/// let src = &mut 2u8;
+/// assert!(matches!(
+/// try_transmute_mut!(src),
+/// Result::<&mut bool, _>::Err(ValidityError { .. })
+/// ));
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, bool>;
+///
+/// let mut bytes = [0, 1, 0, 1, 0, 1, 0, 1];
+/// let src = Src::mut_from_bytes(&mut bytes).unwrap();
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 0, 1]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [0, 1, 0, 1]);
+///
+/// let dst: &Dst = try_transmute_mut!(src).unwrap();
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [false, true, false, true, false, true]);
+/// ```
+#[macro_export]
+macro_rules! try_transmute_mut {
+ ($e:expr) => {{
+ // Ensure that the source type is a mutable reference.
+ let e: &mut _ = $e;
+
+ #[allow(unused_imports)]
+ use $crate::util::macro_util::TryTransmuteMutDst as _;
+ let t = $crate::util::macro_util::Wrap::new(e);
+ if false {
+ // This branch exists solely to force the compiler to infer the type
+ // of `Dst` *before* it attempts to resolve the method call to
+ // `try_transmute_mut` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of
+ // `try_transmute_mut` (which requires `Dst: Sized`) because
+ // inherent methods take priority over trait methods. It does this
+ // before it realizes `Dst` is `!Sized`, leading to a compile error
+ // when it checks the bounds later.
+ //
+ // By calling this helper (which returns `&Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else`
+ // branch, the compiler knows `Dst` is `!Sized`, properly
+ // disqualifies the inherent method, and falls back to the trait
+ // implementation.
+ Ok(t.transmute_mut_inference_helper())
+ } else {
+ t.try_transmute_mut()
+ }
+ }}
+}
+
+/// Includes a file and safely transmutes it to a value of an arbitrary type.
+///
+/// The file will be included as a byte array, `[u8; N]`, which will be
+/// transmuted to another type, `T`. `T` is inferred from the calling context,
+/// and must implement [`FromBytes`].
+///
+/// The file is located relative to the current file (similarly to how modules
+/// are found). The provided path is interpreted in a platform-specific way at
+/// compile time. So, for instance, an invocation with a Windows path containing
+/// backslashes `\` would not compile correctly on Unix.
+///
+/// `include_value!` is ignorant of byte order. For byte order-aware types, see
+/// the [`byteorder`] module.
+///
+/// [`FromBytes`]: crate::FromBytes
+/// [`byteorder`]: crate::byteorder
+///
+/// # Examples
+///
+/// Assume there are two files in the same directory with the following
+/// contents:
+///
+/// File `data` (no trailing newline):
+///
+/// ```text
+/// abcd
+/// ```
+///
+/// File `main.rs`:
+///
+/// ```rust
+/// use zerocopy::include_value;
+/// # macro_rules! include_value {
+/// # ($file:expr) => { zerocopy::include_value!(concat!("../testdata/include_value/", $file)) };
+/// # }
+///
+/// fn main() {
+/// let as_u32: u32 = include_value!("data");
+/// assert_eq!(as_u32, u32::from_ne_bytes([b'a', b'b', b'c', b'd']));
+/// let as_i32: i32 = include_value!("data");
+/// assert_eq!(as_i32, i32::from_ne_bytes([b'a', b'b', b'c', b'd']));
+/// }
+/// ```
+///
+/// # Use in `const` contexts
+///
+/// This macro can be invoked in `const` contexts.
+#[doc(alias("include_bytes", "include_data", "include_type"))]
+#[macro_export]
+macro_rules! include_value {
+ ($file:expr $(,)?) => {
+ $crate::transmute!(*::core::include_bytes!($file))
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! cryptocorrosion_derive_traits {
+ (
+ #[repr($repr:ident)]
+ $(#[$attr:meta])*
+ $vis:vis struct $name:ident $(<$($tyvar:ident),*>)?
+ $(
+ (
+ $($tuple_field_vis:vis $tuple_field_ty:ty),*
+ );
+ )?
+
+ $(
+ {
+ $($field_vis:vis $field_name:ident: $field_ty:ty,)*
+ }
+ )?
+ ) => {
+ $crate::cryptocorrosion_derive_traits!(@assert_allowed_struct_repr #[repr($repr)]);
+
+ $(#[$attr])*
+ #[repr($repr)]
+ $vis struct $name $(<$($tyvar),*>)?
+ $(
+ (
+ $($tuple_field_vis $tuple_field_ty),*
+ );
+ )?
+
+ $(
+ {
+ $($field_vis $field_name: $field_ty,)*
+ }
+ )?
+
+ // SAFETY: See inline.
+ unsafe impl $(<$($tyvar),*>)? $crate::TryFromBytes for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::FromBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::FromBytes,)*
+ )?
+ {
+ #[inline(always)]
+ fn is_bit_valid<A>(_: $crate::Maybe<'_, Self, A>) -> bool
+ where
+ A: $crate::invariant::Alignment,
+ {
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block
+ // requires all field types to be `FromBytes`. Thus, all
+ // initialized byte sequences constitutes valid instances of
+ // `Self`.
+ true
+ }
+
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block requires all
+ // field types to be `FromBytes`, which is a sub-trait of `FromZeros`.
+ unsafe impl $(<$($tyvar),*>)? $crate::FromZeros for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::FromBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::FromBytes,)*
+ )?
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block requires all
+ // field types to be `FromBytes`.
+ unsafe impl $(<$($tyvar),*>)? $crate::FromBytes for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::FromBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::FromBytes,)*
+ )?
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, this `impl` block requires all field
+ // types to be `IntoBytes`, and a padding check is used to ensures that
+ // there are no padding bytes.
+ unsafe impl $(<$($tyvar),*>)? $crate::IntoBytes for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::IntoBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::IntoBytes,)*
+ )?
+
+ (): $crate::util::macro_util::PaddingFree<
+ Self,
+ {
+ $crate::cryptocorrosion_derive_traits!(
+ @struct_padding_check #[repr($repr)]
+ $(($($tuple_field_ty),*))?
+ $({$($field_ty),*})?
+ )
+ },
+ >,
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block requires all
+ // field types to be `Immutable`.
+ unsafe impl $(<$($tyvar),*>)? $crate::Immutable for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::Immutable,)*
+ )?
+
+ $(
+ $($field_ty: $crate::Immutable,)*
+ )?
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+ };
+ (@assert_allowed_struct_repr #[repr(transparent)]) => {};
+ (@assert_allowed_struct_repr #[repr(C)]) => {};
+ (@assert_allowed_struct_repr #[$_attr:meta]) => {
+ compile_error!("repr must be `#[repr(transparent)]` or `#[repr(C)]`");
+ };
+ (
+ @struct_padding_check #[repr(transparent)]
+ $(($($tuple_field_ty:ty),*))?
+ $({$($field_ty:ty),*})?
+ ) => {
+ // SAFETY: `#[repr(transparent)]` structs cannot have the same layout as
+ // their single non-zero-sized field, and so cannot have any padding
+ // outside of that field.
+ 0
+ };
+ (
+ @struct_padding_check #[repr(C)]
+ $(($($tuple_field_ty:ty),*))?
+ $({$($field_ty:ty),*})?
+ ) => {
+ $crate::struct_padding!(
+ Self,
+ None,
+ None,
+ [
+ $($($tuple_field_ty),*)?
+ $($($field_ty),*)?
+ ]
+ )
+ };
+ (
+ #[repr(C)]
+ $(#[$attr:meta])*
+ $vis:vis union $name:ident {
+ $(
+ $field_name:ident: $field_ty:ty,
+ )*
+ }
+ ) => {
+ $(#[$attr])*
+ #[repr(C)]
+ $vis union $name {
+ $(
+ $field_name: $field_ty,
+ )*
+ }
+
+ // SAFETY: See inline.
+ unsafe impl $crate::TryFromBytes for $name
+ where
+ $(
+ $field_ty: $crate::FromBytes,
+ )*
+ {
+ #[inline(always)]
+ fn is_bit_valid<A>(_: $crate::Maybe<'_, Self, A>) -> bool
+ where
+ A: $crate::invariant::Alignment,
+ {
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this
+ // `impl` block requires all field types to be `FromBytes`.
+ // Thus, all initialized byte sequences constitutes valid
+ // instances of `Self`.
+ true
+ }
+
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
+ // block requires all field types to be `FromBytes`, which is a
+ // sub-trait of `FromZeros`.
+ unsafe impl $crate::FromZeros for $name
+ where
+ $(
+ $field_ty: $crate::FromBytes,
+ )*
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
+ // block requires all field types to be `FromBytes`.
+ unsafe impl $crate::FromBytes for $name
+ where
+ $(
+ $field_ty: $crate::FromBytes,
+ )*
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, this `impl`
+ // block requires all field types to be `IntoBytes`, and a padding check
+ // is used to ensures that there are no padding bytes before or after
+ // any field.
+ unsafe impl $crate::IntoBytes for $name
+ where
+ $(
+ $field_ty: $crate::IntoBytes,
+ )*
+ (): $crate::util::macro_util::PaddingFree<
+ Self,
+ {
+ $crate::union_padding!(
+ Self,
+ None::<usize>,
+ None::<usize>,
+ [$($field_ty),*]
+ )
+ },
+ >,
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
+ // block requires all field types to be `Immutable`.
+ unsafe impl $crate::Immutable for $name
+ where
+ $(
+ $field_ty: $crate::Immutable,
+ )*
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ byteorder::native_endian::{U16, U32},
+ util::testutil::*,
+ *,
+ };
+
+ #[derive(KnownLayout, Immutable, FromBytes, IntoBytes, PartialEq, Debug)]
+ #[repr(C)]
+ struct SliceDst<T, U> {
+ a: T,
+ b: [U],
+ }
+
+ #[test]
+ fn test_transmute() {
+ // Test that memory is transmuted as expected.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let x: [[u8; 2]; 4] = transmute!(array_of_u8s);
+ assert_eq!(x, array_of_arrays);
+ let x: [u8; 8] = transmute!(array_of_arrays);
+ assert_eq!(x, array_of_u8s);
+
+ // Test that memory is transmuted as expected when shrinking.
+ let x: [[u8; 2]; 3] = transmute!(#![allow(shrink)] array_of_u8s);
+ assert_eq!(x, [[0u8, 1], [2, 3], [4, 5]]);
+
+ // Test that the source expression's value is forgotten rather than
+ // dropped.
+ #[derive(IntoBytes)]
+ #[repr(transparent)]
+ struct PanicOnDrop(());
+ impl Drop for PanicOnDrop {
+ fn drop(&mut self) {
+ panic!("PanicOnDrop::drop");
+ }
+ }
+ #[allow(clippy::let_unit_value)]
+ let _: () = transmute!(PanicOnDrop(()));
+ #[allow(clippy::let_unit_value)]
+ let _: () = transmute!(#![allow(shrink)] PanicOnDrop(()));
+
+ // Test that `transmute!` is legal in a const context.
+ const ARRAY_OF_U8S: [u8; 8] = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ const ARRAY_OF_ARRAYS: [[u8; 2]; 4] = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ const X: [[u8; 2]; 4] = transmute!(ARRAY_OF_U8S);
+ assert_eq!(X, ARRAY_OF_ARRAYS);
+ const X_SHRINK: [[u8; 2]; 3] = transmute!(#![allow(shrink)] ARRAY_OF_U8S);
+ assert_eq!(X_SHRINK, [[0u8, 1], [2, 3], [4, 5]]);
+
+ // Test that `transmute!` works with `!Immutable` types.
+ let x: usize = transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x, 1);
+ let x: UnsafeCell<usize> = transmute!(1usize);
+ assert_eq!(x.into_inner(), 1);
+ let x: UnsafeCell<isize> = transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x.into_inner(), 1);
+ }
+
+ // A `Sized` type which doesn't implement `KnownLayout` (it is "not
+ // `KnownLayout`", or `Nkl`).
+ //
+ // This permits us to test that `transmute_ref!` and `transmute_mut!` work
+ // for types which are `Sized + !KnownLayout`. When we added support for
+ // slice DSTs in #1924, this new support relied on `KnownLayout`, but we
+ // need to make sure to remain backwards-compatible with code which uses
+ // these macros with types which are `!KnownLayout`.
+ #[derive(FromBytes, IntoBytes, Immutable, PartialEq, Eq, Debug)]
+ #[repr(transparent)]
+ struct Nkl<T>(T);
+
+ #[test]
+ fn test_transmute_ref() {
+ // Test that memory is transmuted as expected.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let x: &[[u8; 2]; 4] = transmute_ref!(&array_of_u8s);
+ assert_eq!(*x, array_of_arrays);
+ let x: &[u8; 8] = transmute_ref!(&array_of_arrays);
+ assert_eq!(*x, array_of_u8s);
+
+ // Test that `transmute_ref!` is legal in a const context.
+ const ARRAY_OF_U8S: [u8; 8] = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ const ARRAY_OF_ARRAYS: [[u8; 2]; 4] = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ #[allow(clippy::redundant_static_lifetimes)]
+ const X: &'static [[u8; 2]; 4] = transmute_ref!(&ARRAY_OF_U8S);
+ assert_eq!(*X, ARRAY_OF_ARRAYS);
+
+ // Test sized -> unsized transmutation.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let slice_of_arrays = &array_of_arrays[..];
+ let x: &[[u8; 2]] = transmute_ref!(&array_of_u8s);
+ assert_eq!(x, slice_of_arrays);
+
+ // Before 1.61.0, we can't define the `const fn transmute_ref` function
+ // that we do on and after 1.61.0.
+ #[cfg(no_zerocopy_generic_bounds_in_const_fn_1_61_0)]
+ {
+ // Test that `transmute_ref!` supports non-`KnownLayout` `Sized`
+ // types.
+ const ARRAY_OF_NKL_U8S: Nkl<[u8; 8]> = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ const ARRAY_OF_NKL_ARRAYS: Nkl<[[u8; 2]; 4]> = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ const X_NKL: &Nkl<[[u8; 2]; 4]> = transmute_ref!(&ARRAY_OF_NKL_U8S);
+ assert_eq!(*X_NKL, ARRAY_OF_NKL_ARRAYS);
+ }
+
+ #[cfg(not(no_zerocopy_generic_bounds_in_const_fn_1_61_0))]
+ {
+ // Call through a generic function to make sure our autoref
+ // specialization trick works even when types are generic.
+ const fn transmute_ref<T, U>(t: &T) -> &U
+ where
+ T: IntoBytes + Immutable,
+ U: FromBytes + Immutable,
+ {
+ transmute_ref!(t)
+ }
+
+ // Test that `transmute_ref!` supports non-`KnownLayout` `Sized`
+ // types.
+ const ARRAY_OF_NKL_U8S: Nkl<[u8; 8]> = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ const ARRAY_OF_NKL_ARRAYS: Nkl<[[u8; 2]; 4]> = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ const X_NKL: &Nkl<[[u8; 2]; 4]> = transmute_ref(&ARRAY_OF_NKL_U8S);
+ assert_eq!(*X_NKL, ARRAY_OF_NKL_ARRAYS);
+ }
+
+ // Test that `transmute_ref!` works on slice DSTs in and that memory is
+ // transmuted as expected.
+ let slice_dst_of_u8s =
+ SliceDst::<U16, [u8; 2]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let slice_dst_of_u16s =
+ SliceDst::<U16, U16>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let x: &SliceDst<U16, U16> = transmute_ref!(slice_dst_of_u8s);
+ assert_eq!(x, slice_dst_of_u16s);
+
+ let slice_dst_of_u8s =
+ SliceDst::<U16, u8>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let x: &[u8] = transmute_ref!(slice_dst_of_u8s);
+ assert_eq!(x, [0, 1, 2, 3, 4, 5]);
+
+ let x: &[u8] = transmute_ref!(slice_dst_of_u16s);
+ assert_eq!(x, [0, 1, 2, 3, 4, 5]);
+
+ let x: &[U16] = transmute_ref!(slice_dst_of_u16s);
+ let slice_of_u16s: &[U16] = <[U16]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ assert_eq!(x, slice_of_u16s);
+
+ // Test that transmuting from a type with larger trailing slice offset
+ // and larger trailing slice element works.
+ let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..];
+ let slice_dst_big = SliceDst::<U32, U16>::ref_from_bytes(bytes).unwrap();
+ let slice_dst_small = SliceDst::<U16, u8>::ref_from_bytes(bytes).unwrap();
+ let x: &SliceDst<U16, u8> = transmute_ref!(slice_dst_big);
+ assert_eq!(x, slice_dst_small);
+
+ // Test that it's legal to transmute a reference while shrinking the
+ // lifetime (note that `X` has the lifetime `'static`).
+ let x: &[u8; 8] = transmute_ref!(X);
+ assert_eq!(*x, ARRAY_OF_U8S);
+
+ // Test that `transmute_ref!` supports decreasing alignment.
+ let u = AU64(0);
+ let array = [0, 0, 0, 0, 0, 0, 0, 0];
+ let x: &[u8; 8] = transmute_ref!(&u);
+ assert_eq!(*x, array);
+
+ // Test that a mutable reference can be turned into an immutable one.
+ let mut x = 0u8;
+ #[allow(clippy::useless_transmute)]
+ let y: &u8 = transmute_ref!(&mut x);
+ assert_eq!(*y, 0);
+ }
+
+ #[test]
+ fn test_try_transmute() {
+ // Test that memory is transmuted with `try_transmute` as expected.
+ let array_of_bools = [false, true, false, true, false, true, false, true];
+ let array_of_arrays = [[0, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<[[u8; 2]; 4], _> = try_transmute!(array_of_bools);
+ assert_eq!(x, Ok(array_of_arrays));
+ let x: Result<[bool; 8], _> = try_transmute!(array_of_arrays);
+ assert_eq!(x, Ok(array_of_bools));
+
+ // Test that `try_transmute!` works with `!Immutable` types.
+ let x: Result<usize, _> = try_transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x.unwrap(), 1);
+ let x: Result<UnsafeCell<usize>, _> = try_transmute!(1usize);
+ assert_eq!(x.unwrap().into_inner(), 1);
+ let x: Result<UnsafeCell<isize>, _> = try_transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x.unwrap().into_inner(), 1);
+
+ #[derive(FromBytes, IntoBytes, Debug, PartialEq)]
+ #[repr(transparent)]
+ struct PanicOnDrop<T>(T);
+
+ impl<T> Drop for PanicOnDrop<T> {
+ fn drop(&mut self) {
+ panic!("PanicOnDrop dropped");
+ }
+ }
+
+ // Since `try_transmute!` semantically moves its argument on failure,
+ // the `PanicOnDrop` is not dropped, and thus this shouldn't panic.
+ let x: Result<usize, _> = try_transmute!(PanicOnDrop(1usize));
+ assert_eq!(x, Ok(1));
+
+ // Since `try_transmute!` semantically returns ownership of its argument
+ // on failure, the `PanicOnDrop` is returned rather than dropped, and
+ // thus this shouldn't panic.
+ let y: Result<bool, _> = try_transmute!(PanicOnDrop(2u8));
+ // We have to use `map_err` instead of comparing against
+ // `Err(PanicOnDrop(2u8))` because the latter would create and then drop
+ // its `PanicOnDrop` temporary, which would cause a panic.
+ assert_eq!(y.as_ref().map_err(|p| &p.src.0), Err::<&bool, _>(&2u8));
+ mem::forget(y);
+ }
+
+ #[test]
+ fn test_try_transmute_ref() {
+ // Test that memory is transmuted with `try_transmute_ref` as expected.
+ let array_of_bools = &[false, true, false, true, false, true, false, true];
+ let array_of_arrays = &[[0, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<&[[u8; 2]; 4], _> = try_transmute_ref!(array_of_bools);
+ assert_eq!(x, Ok(array_of_arrays));
+ let x: Result<&[bool; 8], _> = try_transmute_ref!(array_of_arrays);
+ assert_eq!(x, Ok(array_of_bools));
+
+ // Test that it's legal to transmute a reference while shrinking the
+ // lifetime.
+ {
+ let x: Result<&[[u8; 2]; 4], _> = try_transmute_ref!(array_of_bools);
+ assert_eq!(x, Ok(array_of_arrays));
+ }
+
+ // Test that `try_transmute_ref!` supports decreasing alignment.
+ let u = AU64(0);
+ let array = [0u8, 0, 0, 0, 0, 0, 0, 0];
+ let x: Result<&[u8; 8], _> = try_transmute_ref!(&u);
+ assert_eq!(x, Ok(&array));
+
+ // Test that a mutable reference can be turned into an immutable one.
+ let mut x = 0u8;
+ #[allow(clippy::useless_transmute)]
+ let y: Result<&u8, _> = try_transmute_ref!(&mut x);
+ assert_eq!(y, Ok(&0));
+
+ // Test that sized types work which don't implement `KnownLayout`.
+ let array_of_nkl_u8s = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ let array_of_nkl_arrays = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ let x: Result<&Nkl<[[u8; 2]; 4]>, _> = try_transmute_ref!(&array_of_nkl_u8s);
+ assert_eq!(x, Ok(&array_of_nkl_arrays));
+
+ // Test sized -> unsized transmutation.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let slice_of_arrays = &array_of_arrays[..];
+ let x: Result<&[[u8; 2]], _> = try_transmute_ref!(&array_of_u8s);
+ assert_eq!(x, Ok(slice_of_arrays));
+
+ // Test unsized -> unsized transmutation.
+ let slice_dst_of_u8s =
+ SliceDst::<U16, [u8; 2]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let slice_dst_of_u16s =
+ SliceDst::<U16, U16>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let x: Result<&SliceDst<U16, U16>, _> = try_transmute_ref!(slice_dst_of_u8s);
+ assert_eq!(x, Ok(slice_dst_of_u16s));
+ }
+
+ #[test]
+ fn test_try_transmute_mut() {
+ // Test that memory is transmuted with `try_transmute_mut` as expected.
+ let array_of_u8s = &mut [0u8, 1, 0, 1, 0, 1, 0, 1];
+ let array_of_arrays = &mut [[0u8, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<&mut [[u8; 2]; 4], _> = try_transmute_mut!(array_of_u8s);
+ assert_eq!(x, Ok(array_of_arrays));
+
+ let array_of_bools = &mut [false, true, false, true, false, true, false, true];
+ let array_of_arrays = &mut [[0u8, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<&mut [bool; 8], _> = try_transmute_mut!(array_of_arrays);
+ assert_eq!(x, Ok(array_of_bools));
+
+ // Test that it's legal to transmute a reference while shrinking the
+ // lifetime.
+ let array_of_bools = &mut [false, true, false, true, false, true, false, true];
+ let array_of_arrays = &mut [[0u8, 1], [0, 1], [0, 1], [0, 1]];