aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Mayle <fmayle@google.com>2023-10-15 18:49:40 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-10-15 18:49:40 +0000
commitcd02ae7fa97153c0a66f1743380b157163e2af5b (patch)
tree9c881ffa0fa0a9bea64d18626ae369cb96d607f8
parent1c8f7af2f98bcfdcfdd6c77dc3d44c5a25d5eee4 (diff)
parente3276cc65032c7f6af6b5eb2f8ca61267f4af236 (diff)
downloadzerocopy-cd02ae7fa97153c0a66f1743380b157163e2af5b.tar.gz
Revert "Upgrade zerocopy to 0.7.5" am: e3276cc650
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/zerocopy/+/2786845 Change-Id: I8547162c6eafbec6dbd525ad9d17789004b96033 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp49
-rw-r--r--CONTRIBUTING.md211
-rw-r--r--Cargo.toml54
-rw-r--r--Cargo.toml.orig51
-rw-r--r--INTERNAL.md33
-rw-r--r--METADATA14
-rw-r--r--README.md91
-rwxr-xr-xcargo.sh82
-rw-r--r--cargo2android.json6
-rwxr-xr-xgenerate-readme.sh42
-rw-r--r--rustfmt.toml15
-rw-r--r--src/byteorder.rs420
-rw-r--r--src/derive_util.rs127
-rw-r--r--src/lib.rs3883
-rw-r--r--src/macros.rs250
-rw-r--r--src/util.rs105
-rw-r--r--src/wrappers.rs497
-rw-r--r--tests/trybuild.rs45
-rw-r--r--tests/ui-msrv/invalid-impls/invalid-impls.rs25
-rw-r--r--tests/ui-msrv/invalid-impls/invalid-impls.stderr127
-rw-r--r--tests/ui-msrv/transmute-illegal.rs10
-rw-r--r--tests/ui-msrv/transmute-illegal.stderr18
-rw-r--r--tests/ui-nightly/invalid-impls/invalid-impls.rs25
-rw-r--r--tests/ui-nightly/invalid-impls/invalid-impls.stderr107
-rw-r--r--tests/ui-nightly/transmute-illegal.rs10
-rw-r--r--tests/ui-nightly/transmute-illegal.stderr16
-rw-r--r--tests/ui-stable/invalid-impls/invalid-impls.rs25
-rw-r--r--tests/ui-stable/invalid-impls/invalid-impls.stderr107
-rw-r--r--tests/ui-stable/transmute-illegal.rs10
-rw-r--r--tests/ui-stable/transmute-illegal.stderr16
31 files changed, 1478 insertions, 4999 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
deleted file mode 100644
index e74a0f5..0000000
--- a/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "git": {
- "sha1": "c150d4f1b75fc21240574b6b7dbbcdc236d388b0"
- },
- "path_in_vcs": ""
-} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 63ead71..06af1ed 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,14 +23,9 @@ rust_library {
host_supported: true,
crate_name: "zerocopy",
cargo_env_compat: true,
- cargo_pkg_version: "0.7.5",
+ cargo_pkg_version: "0.6.1",
srcs: ["src/lib.rs"],
- edition: "2021",
- features: [
- "byteorder",
- "derive",
- "zerocopy-derive",
- ],
+ edition: "2018",
rustlibs: [
"libbyteorder",
],
@@ -47,15 +42,10 @@ rust_library_rlib {
name: "libzerocopy_nostd",
crate_name: "zerocopy",
cargo_env_compat: true,
- cargo_pkg_version: "0.7.5",
+ cargo_pkg_version: "0.6.1",
srcs: ["src/lib.rs"],
- edition: "2021",
- features: [
- "alloc",
- "byteorder",
- "derive",
- "zerocopy-derive",
- ],
+ edition: "2018",
+ features: ["alloc"],
rustlibs: [
"libbyteorder_nostd",
],
@@ -79,14 +69,9 @@ rust_library_rlib {
name: "libzerocopy_nostd_noalloc",
crate_name: "zerocopy",
cargo_env_compat: true,
- cargo_pkg_version: "0.7.5",
+ cargo_pkg_version: "0.6.1",
srcs: ["src/lib.rs"],
- edition: "2021",
- features: [
- "byteorder",
- "derive",
- "zerocopy-derive",
- ],
+ edition: "2018",
rustlibs: [
"libbyteorder_nostd",
],
@@ -104,3 +89,23 @@ rust_library_rlib {
product_available: true,
vendor_available: true,
}
+
+rust_test {
+ name: "zerocopy_test_src_lib",
+ host_supported: true,
+ crate_name: "zerocopy",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.6.1",
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libbyteorder",
+ "librand",
+ ],
+ proc_macros: ["libzerocopy_derive"],
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 5d79e93..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,211 +0,0 @@
-<!-- Copyright 2022 The Fuchsia Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file. -->
-
-# How to Contribute
-
-We'd love to accept your patches and contributions to zerocopy. There are just a
-few small guidelines you need to follow.
-
-Once you've read the rest of this doc, check out our [good-first-issue
-label][good-first-issue] for some good issues you can use to get your toes wet!
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution;
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to <https://cla.developers.google.com/> to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Code Reviews
-
-All submissions, including submissions by project members, require review. We
-use GitHub pull requests for this purpose. Consult [GitHub
-Help][about_pull_requests] for more information on using pull requests.
-
-## Code Guidelines
-
-### Philosophy
-
-This section is inspired by [Flutter's style guide][flutter_philosophy], which
-contains many general principles that you should apply to all your programming
-work. Read it. The below calls out specific aspects that we feel are
-particularly important.
-
-#### Dogfood Your Features
-
-In non-library code, it's often advised to only implement features you need.
-After all, it's hard to correctly design code without a concrete use case to
-guide its design. Since zerocopy is a library, this advice is not as applicable;
-we want our API surface to be featureful and complete even if not every feature
-or method has a known use case. However, the observation that unused code is
-hard to design still holds.
-
-Thus, when designing external-facing features, try to make use of them somehow.
-This could be by using them to implement other features, or it could be by
-writing prototype code which won't actually be checked in anywhere. If you're
-feeling ambitious, you could even add (and check in) a [Cargo
-example][cargo_example] that exercises the new feature.
-
-#### Go Down the Rabbit Hole
-
-You will occasionally encounter behavior that surprises you or seems wrong. It
-probably is! Invest the time to find the root cause - you will either learn
-something, or fix something, and both are worth your time. Do not work around
-behavior you don't understand.
-
-### Avoid Duplication
-
-Avoid duplicating code whenever possible. In cases where existing code is not
-exposed in a manner suitable to your needs, prefer to extract the necessary
-parts into a common dependency.
-
-### Comments
-
-When writing comments, take a moment to consider the future reader of your
-comment. Ensure that your comments are complete sentences with proper grammar
-and punctuation. Note that adding more comments or more verbose comments is not
-always better; for example, avoid comments that repeat the code they're anchored
-on.
-
-Documentation comments should be self-contained; in other words, do not assume
-that the reader is aware of documentation in adjacent files or on adjacent
-structures. Avoid documentation comments on types which describe _instances_ of
-the type; for example, `AddressSet is a set of client addresses.` is a comment
-that describes a field of type `AddressSet`, but the type may be used to hold
-any kind of `Address`, not just a client's.
-
-Phrase your comments to avoid references that might become stale; for example:
-do not mention a variable or type by name when possible (certain doc comments
-are necessary exceptions). Also avoid references to past or future versions of
-or past or future work surrounding the item being documented; explain things
-from first principles rather than making external references (including past
-revisions).
-
-When writing TODOs:
-
-1. Include an issue reference using the format `TODO(#123):`
-1. Phrase the text as an action that is to be taken; it should be possible for
- another contributor to pick up the TODO without consulting any external
- sources, including the referenced issue.
-
-### Tests
-
-Much of the code in zerocopy has the property that, if it is buggy, those bugs
-may not cause user code to fail. This makes it extra important to write thorough
-tests, but it also makes it harder to write those tests correctly. Here are some
-guidelines on how to test code in zerocopy:
-1. All code added to zerocopy must include tests that exercise it completely.
-1. Tests must be deterministic. Threaded or time-dependent code, random number
- generators (RNGs), and communication with external processes are common
- sources of nondeterminism. See [Write reproducible, deterministic
- tests][determinism] for tips.
-1. Avoid [change detector tests][change_detector_tests]; tests that are
- unnecessarily sensitive to changes, especially ones external to the code
- under test, can hamper feature development and refactoring.
-1. Since we run tests in [Miri][miri], make sure that tests exist which exercise
- any potential [undefined behavior][undefined_behavior] so that Miri can catch
- it.
-1. If there's some user code that should be impossible to compile, add a
- [trybuild test][trybuild] to ensure that it's properly rejected.
-
-### Source Control Best Practices
-
-Commits should be arranged for ease of reading; that is, incidental changes
-such as code movement or formatting changes should be committed separately from
-actual code changes.
-
-Commits should always be focused. For example, a commit could add a feature,
-fix a bug, or refactor code, but not a mixture.
-
-Commits should be thoughtfully sized; avoid overly large or complex commits
-which can be logically separated, but also avoid overly separated commits that
-require code reviews to load multiple commits into their mental working memory
-in order to properly understand how the various pieces fit together.
-
-#### Commit Messages
-
-Commit messages should be _concise_ but self-contained (avoid relying on issue
-references as explanations for changes) and written such that they are helpful
-to people reading in the future (include rationale and any necessary context).
-
-Avoid superfluous details or narrative.
-
-Commit messages should consist of a brief subject line and a separate
-explanatory paragraph in accordance with the following:
-
-1. [Separate subject from body with a blank line](https://chris.beams.io/posts/git-commit/#separate)
-1. [Limit the subject line to 50 characters](https://chris.beams.io/posts/git-commit/#limit-50)
-1. [Capitalize the subject line](https://chris.beams.io/posts/git-commit/#capitalize)
-1. [Do not end the subject line with a period](https://chris.beams.io/posts/git-commit/#end)
-1. [Use the imperative mood in the subject line](https://chris.beams.io/posts/git-commit/#imperative)
-1. [Wrap the body at 72 characters](https://chris.beams.io/posts/git-commit/#wrap-72)
-1. [Use the body to explain what and why vs. how](https://chris.beams.io/posts/git-commit/#why-not-how)
-
-If the code affects a particular subsystem, prefix the subject line with the
-name of that subsystem in square brackets, omitting any "zerocopy" prefix
-(that's implicit). For example, for a commit adding a feature to the
-zerocopy-derive crate:
-
-```text
-[derive] Support AsBytes on types with parameters
-```
-
-The body may be omitted if the subject is self-explanatory; e.g. when fixing a
-typo. The git book contains a [Commit Guidelines][commit_guidelines] section
-with much of the same advice, and the list above is part of a [blog
-post][beams_git_commit] by [Chris Beams][chris_beams].
-
-Commit messages should make use of issue integration. Including an issue
-reference like `#123` will cause the GitHub UI to link the text of that
-reference to the referenced issue, and will also make it so that the referenced
-issue back-links to the commit. Use "Closes", "Fixes", or "Resolves" on its own
-line to automatically close an issue when your commit is merged:
-
-```text
-Closes #123
-Fixes #123
-Resolves #123
-```
-
-When using issue integration, don't omit necessary context that may also be
-included in the relevant issue (see "Commit messages should be _concise_ but
-self-contained" above). Git history is more likely to be retained indefinitely
-than issue history (for example, if this repository is migrated away from GitHub
-at some point in the future).
-
-Commit messages should never contain references to any of:
-
-1. Relative moments in time
-1. Non-public URLs
-1. Individuals
-1. Hosted code reviews (such as on https://github.com/google/zerocopy/pulls)
- + Refer to commits in this repository by their SHA-1 hash
- + Refer to commits in other repositories by public web address (such as
- https://github.com/google/zerocopy/commit/789b3deb)
-1. Other entities which may not make sense to arbitrary future readers
-
-## Community Guidelines
-
-This project follows [Google's Open Source Community
-Guidelines][google_open_source_guidelines].
-
-[about_pull_requests]: https://help.github.com/articles/about-pull-requests/
-[beams_git_commit]: https://chris.beams.io/posts/git-commit/
-[cargo_example]: http://xion.io/post/code/rust-examples.html
-[change_detector_tests]: https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html
-[chris_beams]: https://chris.beams.io/
-[commit_guidelines]: https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines
-[determinism]: https://fuchsia.dev/fuchsia-src/contribute/testing/best-practices#write_reproducible_deterministic_tests
-[flutter_philosophy]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#philosophy
-[good-first-issue]: https://github.com/google/zerocopy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
-[google_open_source_guidelines]: https://opensource.google/conduct/
-[magic_number]: https://en.wikipedia.org/wiki/Magic_number_(programming)
-[miri]: https://github.com/rust-lang/miri
-[trybuild]: https://crates.io/crates/trybuild
-[undefined_behavior]: https://raphlinus.github.io/programming/rust/2018/08/17/undefined-behavior.html
diff --git a/Cargo.toml b/Cargo.toml
index 802ec0b..271d552 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,66 +10,26 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2021"
-rust-version = "1.61.0"
+edition = "2018"
name = "zerocopy"
-version = "0.7.5"
+version = "0.6.1"
authors = ["Joshua Liebow-Feeser <joshlf@google.com>"]
-exclude = [".*"]
+include = ["src/*", "Cargo.toml"]
description = "Utilities for zero-copy parsing and serialization"
-readme = "README.md"
-license = "BSD-2-Clause"
-repository = "https://github.com/google/zerocopy"
-
-[package.metadata.ci]
-pinned-nightly = "nightly-2023-05-25"
-pinned-stable = "1.69.0"
-
+license-file = "LICENSE"
+repository = "https://fuchsia.googlesource.com/fuchsia/+/HEAD/src/lib/zerocopy"
[package.metadata.docs.rs]
all-features = true
-
[dependencies.byteorder]
version = "1.3"
-optional = true
default-features = false
[dependencies.zerocopy-derive]
-version = "=0.7.5"
-optional = true
-
-[dev-dependencies.assert_matches]
-version = "1.5"
-
-[dev-dependencies.itertools]
-version = "0.11"
-
+version = "0.3.1"
[dev-dependencies.rand]
-version = "0.8.5"
-features = ["small_rng"]
-
-[dev-dependencies.rustversion]
-version = "1.0"
-
-[dev-dependencies.static_assertions]
-version = "1.1"
-
-[dev-dependencies.trybuild]
-version = "=1.0.80"
-
-[dev-dependencies.zerocopy-derive]
-version = "=0.7.5"
+version = "0.6"
[features]
-__internal_use_only_features_that_work_on_stable = [
- "alloc",
- "derive",
- "simd",
-]
alloc = []
-default = ["byteorder"]
-derive = ["zerocopy-derive"]
simd = []
simd-nightly = ["simd"]
-
-[target."cfg(any())".dependencies.zerocopy-derive]
-version = "=0.7.5"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index aa05c44..dfe576c 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -2,68 +2,33 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Put both crates in a single workspace so that `trybuild` compiler errors have
-# paths that are stable regardless of the path to the repository root. This
-# avoids issues like:
-# https://github.com/dtolnay/trybuild/issues/207#issuecomment-131227.594
-[workspace]
+# This file is used when publishing to crates.io
[package]
-edition = "2021"
+edition = "2018"
name = "zerocopy"
-version = "0.7.5"
+version = "0.6.1"
authors = ["Joshua Liebow-Feeser <joshlf@google.com>"]
description = "Utilities for zero-copy parsing and serialization"
-license = "BSD-2-Clause"
-repository = "https://github.com/google/zerocopy"
-rust-version = "1.61.0"
+license-file = "../../../LICENSE"
+repository = "https://fuchsia.googlesource.com/fuchsia/+/HEAD/src/lib/zerocopy"
-exclude = [".*"]
+include = ["src/*", "Cargo.toml"]
[package.metadata.docs.rs]
all-features = true
-[package.metadata.ci]
-# The versions of the stable and nightly compiler toolchains to use in CI.
-pinned-stable = "1.69.0"
-pinned-nightly = "nightly-2023-05-25"
-
[features]
-default = ["byteorder"]
-
alloc = []
-derive = ["zerocopy-derive"]
simd = []
simd-nightly = ["simd"]
-# This feature depends on all other features that work on the stable compiler.
-# We make no stability guarantees about this feature; it may be modified or
-# removed at any time.
-__internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd"]
[dependencies]
-zerocopy-derive = { version = "=0.7.5", path = "zerocopy-derive", optional = true }
+zerocopy-derive = "0.3.1"
[dependencies.byteorder]
version = "1.3"
default-features = false
-optional = true
-
-# The "associated proc macro pattern" ensures that the versions of zerocopy and
-# zerocopy-derive remain equal, even if the 'derive' feature isn't used.
-# See: https://github.com/matklad/macro-dep-test
-[target.'cfg(any())'.dependencies]
-zerocopy-derive = { version = "=0.7.5", path = "zerocopy-derive" }
[dev-dependencies]
-assert_matches = "1.5"
-itertools = "0.11"
-rand = { version = "0.8.5", features = ["small_rng"] }
-rustversion = "1.0"
-static_assertions = "1.1"
-# Pinned to a specific version so that the version used for local development
-# and the version used in CI are guaranteed to be the same. Future versions
-# sometimes change the output format slightly, so a version mismatch can cause
-# CI test failures.
-trybuild = "=1.0.80"
-# In tests, unlike in production, zerocopy-derive is not optional
-zerocopy-derive = { version = "=0.7.5", path = "zerocopy-derive" }
+rand = "0.6"
diff --git a/INTERNAL.md b/INTERNAL.md
deleted file mode 100644
index 4c9ed6a..0000000
--- a/INTERNAL.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Internal details
-
-This file documents various internal details of zerocopy and its infrastructure
-that consumers don't need to be concerned about. It focuses on details that
-affect multiple files, and allows each affected code location to reference this
-document rather than requiring us to repeat the same explanation in multiple
-locations.
-
-## CI and toolchain versions
-
-In CI (`.github/workflows/ci.yml`), we pin to specific versions or dates of the
-stable and nightly toolchains. The reason is twofold: First, our UI tests (see
-`tests/trybuild.rs` and `zerocopy-derive/tests/trybuild.rs`) depend on the
-format of rustc's error messages, and that format can change between toolchain
-versions (we also maintain multiple copies of our UI tests - one for each
-toolchain version pinned in CI - for this reason). Second, not all nightlies
-have a working Miri, so we need to pin to one that does (see
-https://rust-lang.github.io/rustup-components-history/).
-
-Updating the versions pinned in CI may cause the UI tests to break. In order to
-fix UI tests after a version update, set the environment variable
-`TRYBUILD=overwrite` while running `cargo test`.
-
-## Crate versions
-
-We ensure that the crate versions of zerocopy and zerocopy-derive are always the
-same in-tree, and that zerocopy depends upon zerocopy-derive using an exact
-version match to the current version in-tree. This has the result that, even
-when published on crates.io, both crates effectively constitute a single atomic
-version. So long as the code in zerocopy is compatible with the code in
-zerocopy-derive in the same Git commit, then publishing them both is fine. This
-frees us from the normal task of reasoning about compatibility with a range of
-semver-compatible versions of different crates. \ No newline at end of file
diff --git a/METADATA b/METADATA
index 2b18dc0..0094c01 100644
--- a/METADATA
+++ b/METADATA
@@ -1,7 +1,3 @@
-# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update rust/crates/zerocopy
-# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
-
name: "zerocopy"
description: "Utilities for zero-copy parsing and serialization"
third_party {
@@ -11,13 +7,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/zerocopy/zerocopy-0.7.5.crate"
+ value: "https://static.crates.io/crates/zerocopy/zerocopy-0.6.1.crate"
}
- version: "0.7.5"
+ version: "0.6.1"
license_type: NOTICE
last_upgrade_date {
- year: 2023
- month: 9
- day: 28
+ year: 2022
+ month: 11
+ day: 18
}
}
diff --git a/README.md b/README.md
deleted file mode 100644
index d9d3d90..0000000
--- a/README.md
+++ /dev/null
@@ -1,91 +0,0 @@
-<!-- Copyright 2022 The Fuchsia Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
-
-WARNING: DO NOT EDIT THIS FILE. It is generated automatically. Edits should be
-made in the doc comment on `src/lib.rs` or in `generate-readme.sh`.
--->
-
-# zerocopy
-
-*<span style="font-size: 100%; color:grey;">Want to help improve zerocopy?
-Fill out our [user survey][user-survey]!</span>*
-
-***<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.
-
-## Overview
-
-Zerocopy provides four core marker traits, each of which can be derived
-(e.g., `#[derive(FromZeroes)]`):
-- `FromZeroes` 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
-- `AsBytes` indicates that a type may safely be converted *to* a byte
- sequence
-- `Unaligned` indicates that a type's alignment requirement is 1
-
-Types which implement a subset of these traits can then be converted to/from
-byte sequences with little to no runtime overhead.
-
-Zerocopy also provides byte-order aware integer types that support these
-conversions; see the `byteorder` module. These types are especially useful
-for network parsing.
-
-[user-survey]: https://docs.google.com/forms/d/e/1FAIpQLSdzBNTN9tzwsmtyZxRFNL02K36IWCdHWW2ZBckyQS2xiO3i8Q/viewform?usp=published_options
-
-## 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.
-
-- **`byteorder`** (enabled by default)
- Adds the `byteorder` module and a dependency on the `byteorder` crate.
- The `byteorder` module provides byte order-aware equivalents of the
- multi-byte primitive numerical types. Unlike their primitive equivalents,
- the types in this module have no alignment requirement and support byte
- order conversions. This can be useful in handling file formats, network
- packet layouts, etc which don't provide alignment guarantees and which may
- use a byte order different from that of the execution platform.
-
-- **`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"
- ```
-
-- **`simd`**
- When the `simd` feature is enabled, `FromZeroes`, `FromBytes`, and
- `AsBytes` 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.
-
-[simd-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html
-
-## Disclaimer
-
-Disclaimer: Zerocopy is not an officially supported Google product.
diff --git a/cargo.sh b/cargo.sh
deleted file mode 100755
index 7dacf84..0000000
--- a/cargo.sh
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2023 The Fuchsia Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This script is a thin wrapper around Cargo that provides human-friendly
-# toolchain names which are automatically translated to the toolchain versions
-# we have pinned in CI.
-#
-# cargo.sh --version <toolchain-name> # looks up the version for the named toolchain
-# cargo.sh +<toolchain-name> [...] # runs cargo commands with the named toolchain
-# cargo.sh +all [...] # runs cargo commands with each toolchain
-#
-# The meta-toolchain "all" instructs this script to run the provided command
-# once for each toolchain (msrv, stable, nightly).
-#
-# A common task that is especially annoying to perform by hand is to update
-# trybuild's stderr files. Using this script:
-#
-# TRYBUILD=overwrite ./cargo.sh +all test --workspace
-
-set -eo pipefail
-
-function print-usage-and-exit {
- echo "Usage:" >&2
- echo " $0 --version <toolchain-name>" >&2
- echo " $0 +<toolchain-name> [...]" >&2
- echo " $0 +all [...]" >&2
- exit 1
-}
-
-[[ $# -gt 0 ]] || print-usage-and-exit
-
-function pkg-meta {
- cargo metadata --format-version 1 | jq -r ".packages[] | select(.name == \"zerocopy\").$1"
-}
-
-function lookup-version {
- VERSION="$1"
- case "$VERSION" in
- msrv)
- pkg-meta rust_version
- ;;
- stable)
- pkg-meta 'metadata.ci."pinned-stable"'
- ;;
- nightly)
- pkg-meta 'metadata.ci."pinned-nightly"'
- ;;
- *)
- echo "Unrecognized toolchain name: '$VERSION' (options are 'msrv', 'stable', 'nightly')" >&2
- return 1
- ;;
- esac
-}
-
-case "$1" in
- # cargo.sh --version <toolchain-name>
- --version)
- [[ $# -eq 2 ]] || print-usage-and-exit
- lookup-version "$2"
- ;;
- # cargo.sh +all [...]
- +all)
- echo "[cargo.sh] warning: running the same command for each toolchain (msrv, stable, nightly)" >&2
- for toolchain in msrv stable nightly; do
- echo "[cargo.sh] running with toolchain: $toolchain" >&2
- TOOLCHAIN="$(lookup-version $toolchain)"
- cargo "+$TOOLCHAIN" ${@:2}
- done
- exit 0
- ;;
- # cargo.sh +<toolchain-name> [...]
- +*)
- TOOLCHAIN="$(lookup-version ${1:1})"
- cargo "+$TOOLCHAIN" ${@:2}
- ;;
- *)
- print-usage-and-exit
- ;;
-esac
diff --git a/cargo2android.json b/cargo2android.json
index d665005..92fc80c 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -1,15 +1,14 @@
{
"device": true,
"run": true,
- "features": "derive,byteorder",
"variants": [
{
- "tests": false
+ "tests": true
},
{
"alloc": true,
"dependency-suffix": "_nostd",
- "features": "alloc,derive,byteorder",
+ "features": "alloc",
"force-rlib": true,
"no-host": true,
"suffix": "_nostd",
@@ -17,7 +16,6 @@
},
{
"dependency-suffix": "_nostd",
- "features": "derive,byteorder",
"force-rlib": true,
"no-host": true,
"suffix": "_nostd_noalloc",
diff --git a/generate-readme.sh b/generate-readme.sh
deleted file mode 100755
index b900737..0000000
--- a/generate-readme.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 The Fuchsia Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-set -eo pipefail
-
-COPYRIGHT_HEADER=$(mktemp)
-BODY=$(mktemp)
-DISCLAIMER_FOOTER=$(mktemp)
-
-cat > $COPYRIGHT_HEADER <<'EOF'
-<!-- Copyright 2022 The Fuchsia Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
-
-WARNING: DO NOT EDIT THIS FILE. It is generated automatically. Edits should be
-made in the doc comment on `src/lib.rs` or in `generate-readme.sh`.
--->
-
-EOF
-
-# This uses the `cargo readme` tool, which you can install via `cargo install
-# cargo-readme --version 3.2.0`.
-#
-# The `sed` command is used to strip code links like:
-#
-# /// Here is a link to [`Vec`].
-#
-# These links don't work in a Markdown file, and so we remove the `[` and `]`
-# characters to convert them to non-link code snippets.
-cargo readme --no-license | sed 's/\[\(`[^`]*`\)]/\1/g' > $BODY
-
-cat > $DISCLAIMER_FOOTER <<'EOF'
-
-## Disclaimer
-
-Disclaimer: Zerocopy is not an officially supported Google product.
-EOF
-
-cat $COPYRIGHT_HEADER $BODY $DISCLAIMER_FOOTER
diff --git a/rustfmt.toml b/rustfmt.toml
deleted file mode 100644
index 0b0d02c..0000000
--- a/rustfmt.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2022 The Fuchsia Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-edition = "2021"
-
-# The "Default" setting has a heuristic which splits lines too aggresively.
-# We are willing to revisit this setting in future versions of rustfmt.
-# Bugs:
-# * https://github.com/rust-lang/rustfmt/issues/3119
-# * https://github.com/rust-lang/rustfmt/issues/3120
-use_small_heuristics = "Max"
-
-# Prevent carriage returns
-newline_style = "Unix"
diff --git a/src/byteorder.rs b/src/byteorder.rs
index ecee7a0..e42d3a1 100644
--- a/src/byteorder.rs
+++ b/src/byteorder.rs
@@ -7,50 +7,46 @@
//! 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`], [`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 the [`FromBytes`], [`AsBytes`], and [`Unaligned`] traits.
+//! For each native multi-byte integer type - `u16`, `i16`, `u32`, etc - an
+//! equivalent type is defined by this module - [`U16`], [`I16`], [`U32`], 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 the [`FromBytes`], [`AsBytes`], and
+//! [`Unaligned`] 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.
+//! These two properties, taken together, make these types very 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.
//!
//! # Example
//!
//! One use of these types is for representing network packet formats, such as
//! UDP:
//!
-//! ```rust,edition2021
-//! # #[cfg(feature = "derive")] { // This example uses derives, and won't compile without them
-//! use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref, Unaligned};
-//! use zerocopy::byteorder::network_endian::U16;
+//! ```edition2018
+//! # use zerocopy::*;
+//! use ::byteorder::NetworkEndian;
//!
-//! #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
+//! #[derive(FromBytes, AsBytes, Unaligned)]
//! #[repr(C)]
//! struct UdpHeader {
-//! src_port: U16,
-//! dst_port: U16,
-//! length: U16,
-//! checksum: U16,
+//! src_port: U16<NetworkEndian>,
+//! dst_port: U16<NetworkEndian>,
+//! length: U16<NetworkEndian>,
+//! checksum: U16<NetworkEndian>,
//! }
//!
//! struct UdpPacket<B: ByteSlice> {
-//! header: Ref<B, UdpHeader>,
+//! header: LayoutVerified<B, UdpHeader>,
//! body: B,
//! }
//!
//! impl<B: ByteSlice> UdpPacket<B> {
//! fn parse(bytes: B) -> Option<UdpPacket<B>> {
-//! let (header, body) = Ref::new_from_prefix(bytes)?;
+//! let (header, body) = LayoutVerified::new_from_prefix(bytes)?;
//! Some(UdpPacket { header, body })
//! }
//!
@@ -60,27 +56,28 @@
//!
//! // more getters...
//! }
-//! # }
//! ```
-use core::{
- convert::{TryFrom, TryInto},
- fmt::{self, Binary, Debug, Display, Formatter, LowerHex, Octal, UpperHex},
- marker::PhantomData,
- num::TryFromIntError,
-};
+use core::convert::{TryFrom, TryInto};
+use core::fmt::{self, Binary, Debug, Display, Formatter, LowerHex, Octal, UpperHex};
+use core::marker::PhantomData;
+use core::num::TryFromIntError;
-// We don't reexport `WriteBytesExt` or `ReadBytesExt` because those are only
-// available with the `std` feature enabled, and zerocopy is `no_std` by
-// default.
-pub use ::byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, NetworkEndian, BE, LE};
+use zerocopy_derive::*;
-use super::*;
+use crate::AsBytes;
+// This allows the custom derives to work. See the comment on this module for an
+// explanation.
+use crate::zerocopy;
+
+// NOTE: We don't reexport `WriteBytesExt` or `ReadBytesExt` because those are
+// only available with the `std` feature enabled, and zerocopy is `no_std` by
+// default.
+pub use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, NetworkEndian, BE, LE};
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)
}
@@ -88,26 +85,6 @@ macro_rules! impl_fmt_trait {
};
}
-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_traits);
- };
- ($name:ident, $native:ident, "signed integer") => {
- impl_fmt_traits!($name, $native, @all_traits);
- };
-
- ($name:ident, $native:ident, @all_traits) => {
- 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! doc_comment {
($x:expr, $($tt:tt)*) => {
#[doc = $x]
@@ -116,7 +93,7 @@ macro_rules! doc_comment {
}
macro_rules! define_max_value_constant {
- ($name:ident, $bytes:expr, "unsigned integer") => {
+ ($name:ident, $bytes:expr, unsigned) => {
/// The maximum value.
///
/// This constant should be preferred to constructing a new value using
@@ -124,15 +101,15 @@ macro_rules! define_max_value_constant {
/// 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") => {};
+ ($name:ident, $bytes:expr, signed) => {
+ // We don't provide maximum and minimum value constants for signed
+ // values 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.
+ };
}
macro_rules! define_type {
@@ -143,14 +120,12 @@ macro_rules! define_type {
$bytes:expr,
$read_method:ident,
$write_method:ident,
- $number_kind:tt,
+ $sign:ident,
[$($larger_native:ty),*],
- [$($larger_native_try:ty),*],
- [$($larger_byteorder:ident),*],
- [$($larger_byteorder_try:ident),*]) => {
+ [$($larger_byteorder:ident),*]) => {
doc_comment! {
- concat!("A ", stringify!($bits), "-bit ", $number_kind,
- " stored in `O` byte order.
+ concat!("A ", stringify!($bits), "-bit ", stringify!($sign), " integer
+stored in `O` 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).
@@ -175,30 +150,27 @@ example of how it can be used for parsing UDP packets.
[`FromBytes`]: crate::FromBytes
[`AsBytes`]: crate::AsBytes
[`Unaligned`]: crate::Unaligned"),
- #[derive(Copy, Clone, Eq, PartialEq, Hash)]
- #[cfg_attr(any(feature = "derive", test), derive(FromZeroes, FromBytes, AsBytes, Unaligned))]
+ #[derive(FromBytes, Unaligned, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct $name<O>([u8; $bytes], PhantomData<O>);
}
- safety_comment! {
- /// 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 `FromZeroes`, `FromBytes`, `AsBytes`, and `Unaligned`.
- impl_or_verify!(O => FromZeroes for $name<O>);
- impl_or_verify!(O => FromBytes for $name<O>);
- impl_or_verify!(O => AsBytes 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
}
}
+ // TODO(joshlf): Replace this with #[derive(AsBytes)] once that derive
+ // supports type parameters.
+ unsafe impl<O: ByteOrder> AsBytes for $name<O> {
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
+ }
+
impl<O> $name<O> {
/// The value zero.
///
@@ -207,23 +179,21 @@ example of how it can be used for parsing UDP packets.
/// on the endianness and platform.
pub const ZERO: $name<O> = $name([0u8; $bytes], PhantomData);
- define_max_value_constant!($name, $bytes, $number_kind);
+ define_max_value_constant!($name, $bytes, $sign);
/// Constructs a new value from bytes which are already in the
/// endianness `O`.
- #[inline(always)]
pub const fn from_bytes(bytes: [u8; $bytes]) -> $name<O> {
$name(bytes, PhantomData)
}
}
impl<O: ByteOrder> $name<O> {
- // TODO(joshlf): Make these const fns if the `ByteOrder` methods
- // ever become const fns.
+ // TODO(joshlf): Make these const fns if the ByteOrder methods ever
+ // become const fns.
/// Constructs a new value, possibly performing an endianness swap
/// to guarantee that the returned value has endianness `O`.
- #[inline(always)]
pub fn new(n: $native) -> $name<O> {
let mut out = $name::default();
O::$write_method(&mut out.0[..], n);
@@ -233,7 +203,6 @@ example of how it can be used for parsing UDP packets.
/// 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.
- #[inline(always)]
pub fn get(self) -> $native {
O::$read_method(&self.0[..])
}
@@ -241,40 +210,35 @@ example of how it can be used for parsing UDP packets.
/// 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) {
O::$write_method(&mut self.0[..], n);
}
}
- // The reasoning behind which traits to implement here is to only
+ // NOTE: 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)
}
@@ -282,18 +246,14 @@ example of how it can be used for parsing UDP packets.
$(
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> {
+ impl<O: ByteOrder> TryFrom<$larger_native> for $name<O> {
type Error = TryFromIntError;
- #[inline(always)]
- fn try_from(x: $larger_native_try) -> Result<$name<O>, TryFromIntError> {
+ fn try_from(x: $larger_native) -> Result<$name<O>, TryFromIntError> {
$native::try_from(x).map($name::new)
}
}
@@ -301,58 +261,53 @@ example of how it can be used for parsing UDP packets.
$(
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> {
+ impl<O: ByteOrder, P: ByteOrder> TryFrom<$larger_byteorder<P>> for $name<O> {
type Error = TryFromIntError;
- #[inline(always)]
- fn try_from(x: $larger_byteorder_try<P>) -> Result<$name<O>, TryFromIntError> {
+ fn try_from(x: $larger_byteorder<P>) -> Result<$name<O>, TryFromIntError> {
x.get().try_into().map($name::new)
}
}
)*
impl<O: ByteOrder> AsRef<[u8; $bytes]> for $name<O> {
- #[inline(always)]
fn as_ref(&self) -> &[u8; $bytes] {
&self.0
}
}
impl<O: ByteOrder> AsMut<[u8; $bytes]> for $name<O> {
- #[inline(always)]
fn as_mut(&mut self) -> &mut [u8; $bytes] {
&mut self.0
}
}
impl<O: ByteOrder> PartialEq<$name<O>> for [u8; $bytes] {
- #[inline(always)]
fn eq(&self, other: &$name<O>) -> bool {
self.eq(&other.0)
}
}
impl<O: ByteOrder> PartialEq<[u8; $bytes]> for $name<O> {
- #[inline(always)]
fn eq(&self, other: &[u8; $bytes]) -> bool {
self.0.eq(other)
}
}
- impl_fmt_traits!($name, $native, $number_kind);
+ 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);
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()
+ // This results in a format like "U16(42)"
+ write!(f, concat!(stringify!($name), "({})"), self.get())
}
}
};
@@ -366,41 +321,13 @@ define_type!(
2,
read_u16,
write_u16,
- "unsigned integer",
+ unsigned,
[u32, u64, u128, usize],
- [u32, u64, u128, usize],
- [U32, U64, U128],
[U32, U64, U128]
);
-define_type!(
- A,
- U32,
- u32,
- 32,
- 4,
- read_u32,
- write_u32,
- "unsigned integer",
- [u64, u128],
- [u64, u128],
- [U64, U128],
- [U64, U128]
-);
-define_type!(
- A,
- U64,
- u64,
- 64,
- 8,
- read_u64,
- write_u64,
- "unsigned integer",
- [u128],
- [u128],
- [U128],
- [U128]
-);
-define_type!(A, U128, u128, 128, 16, read_u128, write_u128, "unsigned integer", [], [], [], []);
+define_type!(A, U32, u32, 32, 4, read_u32, write_u32, unsigned, [u64, u128], [U64, U128]);
+define_type!(A, U64, u64, 64, 8, read_u64, write_u64, unsigned, [u128], [U128]);
+define_type!(A, U128, u128, 128, 16, read_u128, write_u128, unsigned, [], []);
define_type!(
An,
I16,
@@ -409,117 +336,27 @@ define_type!(
2,
read_i16,
write_i16,
- "signed integer",
- [i32, i64, i128, isize],
+ signed,
[i32, i64, i128, isize],
- [I32, I64, I128],
[I32, I64, I128]
);
-define_type!(
- An,
- I32,
- i32,
- 32,
- 4,
- read_i32,
- write_i32,
- "signed integer",
- [i64, i128],
- [i64, i128],
- [I64, I128],
- [I64, I128]
-);
-define_type!(
- An,
- I64,
- i64,
- 64,
- 8,
- read_i64,
- write_i64,
- "signed integer",
- [i128],
- [i128],
- [I128],
- [I128]
-);
-define_type!(An, I128, i128, 128, 16, read_i128, write_i128, "signed integer", [], [], [], []);
-define_type!(
- An,
- F32,
- f32,
- 32,
- 4,
- read_f32,
- write_f32,
- "floating point number",
- [f64],
- [],
- [F64],
- []
-);
-define_type!(An, F64, f64, 64, 8, read_f64, write_f64, "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 byteorder::$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");
+define_type!(An, I32, i32, 32, 4, read_i32, write_i32, signed, [i64, i128], [I64, I128]);
+define_type!(An, I64, i64, 64, 8, read_i64, write_i64, signed, [i128], [I128]);
+define_type!(An, I128, i128, 128, 16, read_i128, write_i128, signed, [], []);
#[cfg(test)]
mod tests {
- use ::byteorder::NativeEndian;
- use rand::{
- distributions::{Distribution, Standard},
- rngs::SmallRng,
- Rng, SeedableRng,
- };
+ use byteorder::NativeEndian;
- use {
- super::*,
- crate::{AsBytes, FromBytes, Unaligned},
- };
+ use super::*;
+ use crate::{AsBytes, FromBytes, Unaligned};
- // A native integer type (u16, i32, etc).
- trait Native: FromBytes + AsBytes + Copy + PartialEq + Debug {
+ // A native integer type (u16, i32, etc)
+ trait Native: FromBytes + AsBytes + Copy + Eq + 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)
- }
+ fn rand() -> Self;
}
trait ByteArray:
@@ -574,16 +411,12 @@ mod tests {
macro_rules! impl_traits {
($name:ident, $native:ident, $bytes:expr, $sign: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;
+ const ZERO: $native = 0;
+ const MAX_VALUE: $native = ::core::$native::MAX;
+
+ fn rand() -> $native {
+ rand::random()
+ }
}
impl<O: ByteOrder> ByteOrderType for $name<O> {
@@ -625,8 +458,6 @@ mod tests {
impl_traits!(I32, i32, 4, signed);
impl_traits!(I64, i64, 8, signed);
impl_traits!(I128, i128, 16, signed);
- impl_traits!(F32, f32, 4, signed);
- impl_traits!(F64, f64, 8, signed);
macro_rules! call_for_all_types {
($fn:ident, $byteorder:ident) => {
@@ -638,8 +469,6 @@ mod tests {
$fn::<I32<$byteorder>>();
$fn::<I64<$byteorder>>();
$fn::<I128<$byteorder>>();
- $fn::<F32<$byteorder>>();
- $fn::<F64<$byteorder>>();
};
}
@@ -653,39 +482,9 @@ mod tests {
}
#[cfg(target_endian = "big")]
- type NonNativeEndian = LittleEndian;
+ type NonNativeEndian = byteorder::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!(miri) {
- // 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
- };
+ type NonNativeEndian = byteorder::BigEndian;
#[test]
fn test_zero() {
@@ -710,9 +509,8 @@ mod tests {
#[test]
fn test_native_endian() {
fn test_native_endian<T: ByteOrderType>() {
- let mut r = SmallRng::seed_from_u64(RNG_SEED);
- for _ in 0..RAND_ITERS {
- let native = T::Native::rand(&mut r);
+ for _ in 0..1024 {
+ let native = T::Native::rand();
let mut bytes = T::ByteArray::default();
bytes.as_bytes_mut().copy_from_slice(native.as_bytes());
let mut from_native = T::new(native);
@@ -723,7 +521,7 @@ mod tests {
assert_eq!(from_native.into_bytes(), bytes);
assert_eq!(from_bytes.into_bytes(), bytes);
- let updated = T::Native::rand(&mut r);
+ let updated = T::Native::rand();
from_native.set(updated);
assert_eq!(from_native.get(), updated);
}
@@ -735,9 +533,8 @@ mod tests {
#[test]
fn test_non_native_endian() {
fn test_non_native_endian<T: ByteOrderType>() {
- let mut r = SmallRng::seed_from_u64(RNG_SEED);
- for _ in 0..RAND_ITERS {
- let native = T::Native::rand(&mut r);
+ for _ in 0..1024 {
+ let native = T::Native::rand();
let mut bytes = T::ByteArray::default();
bytes.as_bytes_mut().copy_from_slice(native.as_bytes());
bytes = bytes.invert();
@@ -749,7 +546,7 @@ mod tests {
assert_eq!(from_native.into_bytes(), bytes);
assert_eq!(from_bytes.into_bytes(), bytes);
- let updated = T::Native::rand(&mut r);
+ let updated = T::Native::rand();
from_native.set(updated);
assert_eq!(from_native.get(), updated);
}
@@ -757,13 +554,4 @@ mod tests {
call_for_all_types!(test_non_native_endian, NonNativeEndian);
}
-
- #[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)");
- }
}
diff --git a/src/derive_util.rs b/src/derive_util.rs
deleted file mode 100644
index edf88e3..0000000
--- a/src/derive_util.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2022 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//! Utilities used by `zerocopy-derive`.
-//!
-//! These are defined in `zerocopy` rather than in code generated by
-//! `zerocopy-derive` so that they can be compiled once rather than recompiled
-//! for every pair of type and trait (in other words, if they were defined in
-//! generated code, then deriving `AsBytes` and `FromBytes` on three different
-//! types would result in the code in question being emitted and compiled six
-//! different times).
-
-#![allow(missing_debug_implementations)]
-
-use core::marker::PhantomData;
-
-/// A compile-time check that should be one particular value.
-pub trait ShouldBe<const VALUE: bool> {}
-
-/// A struct for checking whether `T` contains padding.
-pub struct HasPadding<T: ?Sized, const VALUE: bool>(PhantomData<T>);
-
-impl<T: ?Sized, const VALUE: bool> ShouldBe<VALUE> for HasPadding<T, VALUE> {}
-
-/// Does the struct type `$t` have padding?
-///
-/// `$ts` is the list of the type of every field in `$t`. `$t` must be a
-/// struct type, or else `struct_has_padding!`'s result may be meaningless.
-///
-/// Note that `struct_has_padding!`'s results are independent of `repr` since
-/// they only consider the size of the type and the sizes of the fields.
-/// Whatever the repr, the size of the type already takes into account any
-/// padding that the compiler has decided to add. Structs with well-defined
-/// representations (such as `repr(C)`) can use this macro to check for padding.
-/// Note that while this may yield some consistent value for some `repr(Rust)`
-/// structs, it is not guaranteed across platforms or compilations.
-#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
-#[macro_export]
-macro_rules! struct_has_padding {
- ($t:ty, $($ts:ty),*) => {
- core::mem::size_of::<$t>() > 0 $(+ core::mem::size_of::<$ts>())*
- };
-}
-
-/// Does the union type `$t` have padding?
-///
-/// `$ts` is the list of the type of every field in `$t`. `$t` must be a
-/// union type, or else `union_has_padding!`'s result may be meaningless.
-///
-/// Note that `union_has_padding!`'s results are independent of `repr` since
-/// they only consider the size of the type and the sizes of the fields.
-/// Whatever the repr, the size of the type already takes into account any
-/// padding that the compiler has decided to add. Unions with well-defined
-/// representations (such as `repr(C)`) can use this macro to check for padding.
-/// Note that while this may yield some consistent value for some `repr(Rust)`
-/// unions, it is not guaranteed across platforms or compilations.
-#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
-#[macro_export]
-macro_rules! union_has_padding {
- ($t:ty, $($ts:ty),*) => {
- false $(|| core::mem::size_of::<$t>() != core::mem::size_of::<$ts>())*
- };
-}
-
-#[cfg(test)]
-mod tests {
- use crate::util::testutil::*;
-
- #[test]
- fn test_struct_has_padding() {
- // Test that, for each provided repr, `struct_has_padding!` reports the
- // expected value.
- macro_rules! test {
- (#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{
- #[$cfg]
- struct Test($($ts),*);
- assert_eq!(struct_has_padding!(Test, $($ts),*), $expect);
- }};
- (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => {
- test!(#[$cfg] ($($ts),*) => $expect);
- test!($(#[$cfgs])* ($($ts),*) => $expect);
- };
- }
-
- test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] () => false);
- test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8) => false);
- test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8, ()) => false);
- test!(#[repr(C)] #[repr(packed)] (u8, u8) => false);
-
- test!(#[repr(C)] (u8, AU64) => true);
- // Rust won't let you put `#[repr(packed)]` on a type which contains a
- // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here.
- // It's not ideal, but it definitely has align > 1 on /some/ of our CI
- // targets, and this isn't a particularly complex macro we're testing
- // anyway.
- test!(#[repr(packed)] (u8, u64) => false);
- }
-
- #[test]
- fn test_union_has_padding() {
- // Test that, for each provided repr, `union_has_padding!` reports the
- // expected value.
- macro_rules! test {
- (#[$cfg:meta] {$($fs:ident: $ts:ty),*} => $expect:expr) => {{
- #[$cfg]
- #[allow(unused)] // fields are never read
- union Test{ $($fs: $ts),* }
- assert_eq!(union_has_padding!(Test, $($ts),*), $expect);
- }};
- (#[$cfg:meta] $(#[$cfgs:meta])* {$($fs:ident: $ts:ty),*} => $expect:expr) => {
- test!(#[$cfg] {$($fs: $ts),*} => $expect);
- test!($(#[$cfgs])* {$($fs: $ts),*} => $expect);
- };
- }
-
- test!(#[repr(C)] #[repr(packed)] {a: u8} => false);
- test!(#[repr(C)] #[repr(packed)] {a: u8, b: u8} => false);
-
- // Rust won't let you put `#[repr(packed)]` on a type which contains a
- // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here.
- // It's not ideal, but it definitely has align > 1 on /some/ of our CI
- // targets, and this isn't a particularly complex macro we're testing
- // anyway.
- test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => true);
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 3937911..8241db7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,26 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// After updating the following doc comment, make sure to run the following
-// command to update `README.md` based on its contents:
-//
-// ./generate-readme.sh > README.md
-
-//! *<span style="font-size: 100%; color:grey;">Want to help improve zerocopy?
-//! Fill out our [user survey][user-survey]!</span>*
-//!
-//! ***<span style="font-size: 140%">Fast, safe, <span
-//! style="color:red;">compile error</span>. Pick two.</span>***
+//! Utilities for safe zero-copy parsing and serialization.
//!
-//! Zerocopy makes zero-cost memory manipulation effortless. We write `unsafe`
-//! so you don't have to.
+//! This crate provides utilities which make it easy to perform zero-copy
+//! parsing and serialization by allowing zero-copy conversion to/from byte
+//! slices.
//!
-//! # Overview
-//!
-//! Zerocopy provides four core marker traits, each of which can be derived
-//! (e.g., `#[derive(FromZeroes)]`):
-//! - [`FromZeroes`] indicates that a sequence of zero bytes represents a valid
-//! instance of a type
+//! This is enabled by three core marker traits, each of which can be derived
+//! (e.g., `#[derive(FromBytes)]`):
//! - [`FromBytes`] indicates that a type may safely be converted from an
//! arbitrary byte sequence
//! - [`AsBytes`] indicates that a type may safely be converted *to* a byte
@@ -31,574 +19,156 @@
//! Types which implement a subset of these traits can then be converted to/from
//! byte sequences with little to no runtime overhead.
//!
-//! Zerocopy also provides byte-order aware integer types that support these
-//! conversions; see the `byteorder` module. These types are especially useful
-//! for network parsing.
-//!
-//! [user-survey]: https://docs.google.com/forms/d/e/1FAIpQLSdzBNTN9tzwsmtyZxRFNL02K36IWCdHWW2ZBckyQS2xiO3i8Q/viewform?usp=published_options
+//! Note that these traits are ignorant of byte order. For byte order-aware
+//! types, see the [`byteorder`] module.
//!
-//! # Cargo Features
+//! # 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.
+//! `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.
//!
-//! - **`byteorder`** (enabled by default)
-//! Adds the [`byteorder`] module and a dependency on the `byteorder` crate.
-//! The `byteorder` module provides byte order-aware equivalents of the
-//! multi-byte primitive numerical types. Unlike their primitive equivalents,
-//! the types in this module have no alignment requirement and support byte
-//! order conversions. This can be useful in handling file formats, network
-//! packet layouts, etc which don't provide alignment guarantees and which may
-//! use a byte order different from that of the execution platform.
+//! `simd`: When the `simd` feature is enabled, `FromBytes` and `AsBytes` 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].
//!
-//! - **`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"
-//! ```
-//!
-//! - **`simd`**
-//! When the `simd` feature is enabled, `FromZeroes`, `FromBytes`, and
-//! `AsBytes` 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.
+//! `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.
//!
//! [simd-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html
-// 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)]
-#![deny(renamed_and_removed_lints)]
-#![deny(
- anonymous_parameters,
- deprecated_in_future,
- illegal_floating_point_literal_pattern,
- 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,
- unused_qualifications,
- variant_size_differences
-)]
-#![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::get_unwrap,
- clippy::indexing_slicing,
- clippy::missing_inline_in_public_items,
- clippy::missing_safety_doc,
- clippy::obfuscated_if_else,
- clippy::perf,
- clippy::print_stdout,
- 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
-)]
-#![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(test, 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,
-))]
+#![deny(missing_docs)]
#![cfg_attr(not(test), no_std)]
-#![cfg_attr(feature = "simd-nightly", feature(stdsimd))]
-
-#[macro_use]
-mod macros;
+#![recursion_limit = "2048"]
-#[cfg(feature = "byteorder")]
pub mod byteorder;
-#[cfg(any(feature = "derive", test))]
-#[doc(hidden)]
-pub mod derive_util;
-// TODO(#252): If we make this pub, come up with a better name.
-mod util;
-mod wrappers;
-#[cfg(feature = "byteorder")]
pub use crate::byteorder::*;
-pub use crate::wrappers::*;
-#[cfg(any(feature = "derive", test))]
pub use zerocopy_derive::*;
-use core::{
- alloc::Layout,
- cell::{self, RefMut},
- cmp::Ordering,
- fmt::{self, Debug, Display, Formatter},
- hash::Hasher,
- marker::PhantomData,
- mem::{self, ManuallyDrop, MaybeUninit},
- num::{
- NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
- NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
- },
- ops::{Deref, DerefMut},
- ptr, slice,
-};
-
-#[cfg(feature = "alloc")]
-extern crate alloc;
-#[cfg(feature = "alloc")]
-use {
- alloc::{boxed::Box, vec::Vec},
- core::ptr::NonNull,
-};
-
-// 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))]
+use core::cell::{Ref, RefMut};
+use core::cmp::Ordering;
+use core::fmt::{self, Debug, Display, Formatter};
+use core::marker::PhantomData;
+use core::mem;
+use core::ops::{Deref, DerefMut};
+use core::ptr;
+use core::slice;
+
+// This is a hack to allow derives of FromBytes, AsBytes, and Unaligned 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.
mod zerocopy {
- pub(crate) use crate::*;
+ pub use crate::*;
}
-/// The layout of a type which might be dynamically-sized.
-///
-/// `DstLayout` describes the layout of sized types, slice types, and "custom
-/// 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).
-#[doc(hidden)]
-#[allow(missing_debug_implementations, missing_copy_implementations)]
-#[cfg_attr(test, derive(Copy, Clone, Debug, PartialEq, Eq))]
-pub struct DstLayout {
- /// The base size and the alignment of the type:
- /// - For sized types, the size encoded by this `Layout` is
- /// `size_of::<T>()`. For DSTs, the size represents the size of the type
- /// when the trailing slice field contains 0 elements.
- /// - For all types, the alignment represents the alignment of the type.
- // TODO: If we end up replacing this with separate size and alignment to
- // make Kani happy, file an issue to eventually adopt the stdlib's
- // `Alignment` type trick.
- _base_layout: Layout,
- /// For sized types, `None`. For DSTs, the size of the element type of the
- /// trailing slice.
- _trailing_slice_elem_size: Option<usize>,
-}
-
-#[cfg_attr(test, derive(Copy, Clone, Debug))]
-enum _CastType {
- _Prefix,
- _Suffix,
-}
-
-impl DstLayout {
- /// Constructs a `DstLayout` which describes `T`.
- ///
- /// # Safety
- ///
- /// Unsafe code may assume that `DstLayout` is the correct layout for `T`.
- const fn for_type<T>() -> DstLayout {
- DstLayout { _base_layout: Layout::new::<T>(), _trailing_slice_elem_size: None }
- }
-
- /// Constructs a `DstLayout` which describes `[T]`.
- ///
- /// # Safety
- ///
- /// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`.
- const fn for_slice<T>() -> DstLayout {
- DstLayout {
- // SAFETY: `[T; 0]` has the same alignment as `T`, but zero size.
- // [1] A slice of length 0 has no size, so 0 is the correct size for
- // the base of the type.
- //
- // [1] https://doc.rust-lang.org/reference/type-layout.html#array-layout
- _base_layout: Layout::new::<[T; 0]>(),
- _trailing_slice_elem_size: Some(mem::size_of::<T>()),
+// implement an unsafe trait for a range of container types
+macro_rules! impl_for_composite_types {
+ ($trait:ident) => {
+ unsafe impl<T> $trait for PhantomData<T> {
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
}
- }
-
- /// 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 the size of
- /// the trailing slice element is a multiple of the alignment, no length
- /// for the trailing slice 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
- ///
- /// 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.
- const fn _validate_cast_and_convert_metadata(
- &self,
- addr: usize,
- bytes_len: usize,
- cast_type: _CastType,
- ) -> Option<(usize, usize)> {
- // `debug_assert!`, but with `#[allow(clippy::arithmetic_side_effects)]`.
- macro_rules! __debug_assert {
- ($e:expr $(, $msg:expr)?) => {
- debug_assert!({
- #[allow(clippy::arithmetic_side_effects)]
- let e = $e;
- e
- } $(, $msg)?);
- };
+ unsafe impl<T: $trait> $trait for [T] {
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
}
-
- // Note that, in practice, `elem_size` 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).
- //
- // TODO(#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 elem_size = match self._trailing_slice_elem_size {
- Some(elem_size) => match NonZeroUsize::new(elem_size) {
- Some(elem_size) => Some(elem_size),
- None => panic!("attempted to cast to slice type with zero-sized element"),
- },
- None => None,
- };
-
- // Precondition
- __debug_assert!(addr.checked_add(bytes_len).is_some(), "`addr` + `bytes_len` > usize::MAX");
-
- // 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. 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()`
- // guarantees that its return value is non-zero.
- #[allow(clippy::arithmetic_side_effects)]
- if (addr + offset) % self._base_layout.align() != 0 {
- return None;
+ unsafe impl $trait for () {
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
}
+ unsafe impl<T: $trait, const N: usize> $trait for [T; N] {
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
+ }
+ };
+}
- let base_size = self._base_layout.size();
-
- // LEMMA 0: max_slice_bytes + base_size == bytes_len
- //
- // LEMMA 1: base_size <= bytes_len:
- // - If `base_size > bytes_len`, `bytes_len.checked_sub(base_size)`
- // returns `None`, and we return.
- //
- // TODO(#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_bytes = if let Some(max_byte_slice) = bytes_len.checked_sub(base_size) {
- max_byte_slice
- } else {
- return None;
- };
-
- // Lemma 0
- __debug_assert!(max_slice_bytes + base_size == bytes_len);
-
- // Lemma 1
- __debug_assert!(base_size <= bytes_len);
-
- let (elems, self_bytes) = if let Some(elem_size) = elem_size {
- // Guaranteed not to divide by 0 because `elem_size` is a
- // `NonZeroUsize`.
- #[allow(clippy::arithmetic_side_effects)]
- let elems = max_slice_bytes / elem_size.get();
-
- // NOTE: Another option for this step in the algorithm is to set
- // `slice_bytes = elems * elem_size`. However, using multiplication
- // causes Kani to choke. In practice, the compiler is likely to
- // generate identical machine code in both cases. Note that this
- // divide-then-mod approach is trivially optimizable into a single
- // operation that computes both the quotient and the remainder.
-
- // First line is guaranteed not to mod by 0 because `elem_size` is a
- // `NonZeroUsize`. Second line is guaranteed not to underflow
- // because `rem <= max_slice_bytes` thanks to the mod operation.
- //
- // LEMMA 2: slice_bytes <= max_slice_bytes
- #[allow(clippy::arithmetic_side_effects)]
- let rem = max_slice_bytes % elem_size.get();
- #[allow(clippy::arithmetic_side_effects)]
- let slice_bytes = max_slice_bytes - rem;
-
- // Lemma 2
- __debug_assert!(slice_bytes <= max_slice_bytes);
-
- // Guaranteed not to overflow:
- // - max_slice_bytes + base_size == bytes_len (lemma 0)
- // - slice_bytes <= max_slice_bytes (lemma 2)
- // - slice_bytes + base_size <= bytes_len (substitution) ------+
- // - bytes_len <= usize::MAX (bytes_len: usize) |
- // - slice_bytes + base_size <= usize::MAX (substitution) |
- // |
- // LEMMA 3: self_bytes <= bytes_len: |
- // - slice_bytes + base_size <= bytes_len <--------------------------+ (reused for lemma)
- // - slice_bytes <= bytes_len
- #[allow(clippy::arithmetic_side_effects)]
- let self_bytes = base_size + slice_bytes;
-
- // Lemma 3
- __debug_assert!(self_bytes <= bytes_len);
-
- (elems, self_bytes)
- } else {
- (0, base_size)
- };
-
- // LEMMA 4: self_bytes <= bytes_len:
- // - `if` branch returns `self_bytes`; lemma 3 guarantees `self_bytes <=
- // bytes_len`
- // - `else` branch returns `base_size`; lemma 1 guarantees `base_size <=
- // bytes_len`
-
- // Lemma 4
- __debug_assert!(self_bytes <= bytes_len);
-
- let split_at = match cast_type {
- _CastType::_Prefix => self_bytes,
- // Guaranteed not to underflow because `self_bytes <= bytes_len`
- // (lemma 4).
- #[allow(clippy::arithmetic_side_effects)]
- _CastType::_Suffix => bytes_len - self_bytes,
- };
+/// Implements `$trait` for one or more `$type`s.
+macro_rules! impl_for_types {
+ ($trait:ident, $type:ty) => (
+ unsafe impl $trait for $type {
+ fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized {}
+ }
+ );
+ ($trait:ident, $type:ty, $($types:ty),*) => (
+ unsafe impl $trait for $type {
+ fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized {}
+ }
+ impl_for_types!($trait, $($types),*);
+ );
+}
- Some((elems, split_at))
- }
+/// Implements `$trait` for all signed and unsigned primitive types.
+macro_rules! impl_for_primitives {
+ ($trait:ident) => {
+ impl_for_types!(
+ $trait, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64
+ );
+ };
}
-/// A trait which carries information about a type's layout that is used by the
-/// internals of this crate.
-///
-/// This trait is not meant for consumption by code outside of this crate. While
-/// the normal semver stability guarantees apply with respect to which types
-/// implement this trait and which trait implementations are implied by this
-/// trait, no semver stability guarantees are made regarding its internals; they
-/// may change at any time, and code which makes use of them may break.
-///
-/// # Safety
-///
-/// This trait does not convey any safety guarantees to code outside this crate.
-#[doc(hidden)] // TODO: Remove this once KnownLayout is used by other APIs
-pub unsafe trait KnownLayout: sealed::KnownLayoutSealed {
- #[doc(hidden)]
- const LAYOUT: DstLayout;
-}
-
-impl<T: KnownLayout> sealed::KnownLayoutSealed for [T] {}
-// SAFETY: Delegates safety to `DstLayout::for_slice`.
-unsafe impl<T: KnownLayout> KnownLayout for [T] {
- const LAYOUT: DstLayout = DstLayout::for_slice::<T>();
-}
-
-#[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]
-impl_known_layout!(
- T => Option<T>,
- T: ?Sized => PhantomData<T>,
- T => Wrapping<T>,
- T => MaybeUninit<T>,
-);
-impl_known_layout!(const N: usize, T => [T; N]);
-
-safety_comment! {
- /// SAFETY:
- /// `str` and `ManuallyDrop<[T]>` have the same representations as `[u8]`
- /// and `[T]` repsectively. `str` has different bit validity than `[u8]`,
- /// but that doesn't affect the soundness of this impl.
- unsafe_impl_known_layout!(#[repr([u8])] str);
- unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ManuallyDrop<T>);
-}
-
-/// Types for which a sequence of bytes all set to zero represents a valid
-/// instance of the type.
+/// Types for which any byte pattern is valid.
///
/// WARNING: Do not implement this trait yourself! Instead, use
-/// `#[derive(FromZeroes)]` (requires the `derive` Cargo feature).
+/// `#[derive(FromBytes)]`.
///
-/// Any memory region of the appropriate length which is guaranteed to contain
-/// only zero bytes can be viewed as any `FromZeroes` 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.
+/// `FromBytes` types can safely be deserialized from an untrusted sequence of
+/// bytes because any byte sequence corresponds to a valid instance of the type.
///
-/// `FromZeroes` is ignorant of byte order. For byte order-aware types, see the
+/// `FromBytes` is ignorant of byte order. For byte order-aware types, see the
/// [`byteorder`] module.
///
/// # Safety
///
-/// *This section describes what is required in order for `T: FromZeroes`, and
-/// what unsafe code may assume of such types. `#[derive(FromZeroes)]` only
-/// permits types which satisfy these requirements. If you don't plan on
-/// implementing `FromZeroes` manually, and you don't plan on writing unsafe
-/// code that operates on `FromZeroes` types, then you don't need to read this
-/// section.*
-///
-/// If `T: FromZeroes`, then unsafe code may assume that:
-/// - It is sound to treat any initialized sequence of zero bytes of length
-/// `size_of::<T>()` as a `T`.
-/// - Given `b: &[u8]` where `b.len() == size_of::<T>()`, `b` is aligned to
-/// `align_of::<T>()`, and `b` contains only zero bytes, it is sound to
-/// construct a `t: &T` at the same address as `b`, and it is sound for both
-/// `b` and `t` to be live at the same time.
-///
-/// If a type is marked as `FromZeroes` which violates this contract, it may
-/// cause undefined behavior.
-///
-/// If a type has the following properties, then it is sound to implement
-/// `FromZeroes` for that type:
-/// - If the type is a struct, all of its fields must satisfy the requirements
-/// to be `FromZeroes` (they do not actually have to be `FromZeroes`).
-/// - If the type is an enum, it must be C-like (meaning that all variants have
-/// no fields) and it must have a variant with a discriminant of `0`. See [the
-/// reference] for a description of how discriminant values are chosen.
-/// - The type must not contain any [`UnsafeCell`]s (this is required in order
-/// for it to be sound to construct a `&[u8]` and a `&T` to the same region of
-/// memory). The type may contain references or pointers to `UnsafeCell`s so
-/// long as those values can themselves be initialized from zeroes
-/// (`FromZeroes` is not currently implemented for, e.g.,
-/// `Option<&UnsafeCell<_>>`, but it could be one day).
+/// If `T: FromBytes`, then unsafe code may assume that it is sound to treat any
+/// initialized sequence of bytes of length `size_of::<T>()` as a `T`. If a type
+/// is marked as `FromBytes` which violates this contract, it may cause
+/// undefined behavior.
///
-/// [the reference]: https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
-/// [`UnsafeCell`]: core::cell::UnsafeCell
+/// If a type has the following properties, then it is safe to implement
+/// `FromBytes` for that type:
+/// - If the type is a struct:
+/// - All of its fields must implement `FromBytes`
+/// - If the type is an enum:
+/// - It must be a C-like enum (meaning that all variants have no fields)
+/// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`,
+/// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`).
+/// - The maximum number of discriminants must be used (so that every possible
+/// bit pattern is a valid one). Be very careful when using the `C`,
+/// `usize`, or `isize` representations, as their size is
+/// platform-dependent.
///
/// # Rationale
///
/// ## Why isn't an explicit representation required for structs?
///
/// 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 `FromZeroes` if:
-/// 1. its padding is soundly `FromZeroes`, and
-/// 2. its fields are soundly `FromZeroes`.
+/// 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
@@ -607,46 +177,63 @@ safety_comment! {
///
/// [discussion]: https://github.com/rust-lang/unsafe-code-guidelines/issues/174
///
-/// Whether a struct is soundly `FromZeroes` therefore solely depends on whether
-/// its fields are `FromZeroes`.
-// TODO(#146): Document why we don't require an enum to have an explicit `repr`
-// attribute.
-pub unsafe trait FromZeroes {
- // The `Self: Sized` bound makes it so that `FromZeroes` is still object
+/// Whether a struct is soundly `FromBytes` therefore solely depends on whether
+/// its fields are `FromBytes`.
+pub unsafe trait FromBytes {
+ // NOTE: 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;
- /// Overwrites `self` with zeroes.
- ///
- /// 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.
- #[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: FromZeroes`, the all-zeroes instance is a valid
- // instance of `Self.`
- unsafe { ptr::write_bytes(slf.cast::<u8>(), 0, len) };
+ /// Reads a copy of `Self` from `bytes`.
+ ///
+ /// If `bytes.len() != size_of::<Self>()`, `read_from` returns `None`.
+ fn read_from<B: ByteSlice>(bytes: B) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ let lv = LayoutVerified::<_, Unalign<Self>>::new_unaligned(bytes)?;
+ Some(lv.read().into_inner())
+ }
+
+ /// Reads a copy of `Self` from the prefix of `bytes`.
+ ///
+ /// `read_from_prefix` reads a `Self` from the first `size_of::<Self>()`
+ /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()`, it returns
+ /// `None`.
+ fn read_from_prefix<B: ByteSlice>(bytes: B) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ let (lv, _suffix) = LayoutVerified::<_, Unalign<Self>>::new_unaligned_from_prefix(bytes)?;
+ Some(lv.read().into_inner())
+ }
+
+ /// Reads a copy of `Self` from the suffix of `bytes`.
+ ///
+ /// `read_from_suffix` reads a `Self` from the last `size_of::<Self>()`
+ /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()`, it returns
+ /// `None`.
+ fn read_from_suffix<B: ByteSlice>(bytes: B) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ let (_prefix, lv) = LayoutVerified::<_, Unalign<Self>>::new_unaligned_from_suffix(bytes)?;
+ Some(lv.read().into_inner())
}
/// Creates an instance of `Self` from zeroed bytes.
- #[inline(always)]
fn new_zeroed() -> Self
where
Self: Sized,
{
- // SAFETY: `FromZeroes` says that the all-zeroes bit pattern is legal.
- unsafe { mem::zeroed() }
+ unsafe {
+ // Safe because FromBytes says all bit patterns (including zeroes)
+ // are legal.
+ core::mem::zeroed()
+ }
}
/// Creates a `Box<Self>` from zeroed bytes.
@@ -667,23 +254,20 @@ pub unsafe trait FromZeroes {
/// # Panics
///
/// Panics if allocation of `size_of::<Self>()` bytes fails.
- #[cfg(feature = "alloc")]
- #[inline]
+ #[cfg(any(test, feature = "alloc"))]
fn new_box_zeroed() -> Box<Self>
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.
+ // 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 {
return Box::new(Self::new_zeroed());
}
- // TODO(#61): Add a "SAFETY" comment and remove this `allow`.
- #[allow(clippy::undocumented_unsafe_blocks)]
unsafe {
- let ptr = alloc::alloc::alloc_zeroed(layout).cast::<Self>();
+ let ptr = alloc::alloc::alloc_zeroed(layout) as *mut Self;
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
@@ -712,207 +296,44 @@ pub unsafe trait FromZeroes {
///
/// * Panics if `size_of::<Self>() * len` overflows.
/// * Panics if allocation of `size_of::<Self>() * len` bytes fails.
- #[cfg(feature = "alloc")]
- #[inline]
+ #[cfg(any(test, feature = "alloc"))]
fn new_box_slice_zeroed(len: usize) -> Box<[Self]>
where
Self: Sized,
{
- let size = mem::size_of::<Self>()
- .checked_mul(len)
- .expect("mem::size_of::<Self>() * len overflows `usize`");
- let align = mem::align_of::<Self>();
- // On stable Rust versions <= 1.64.0, `Layout::from_size_align` has a
- // bug in which sufficiently-large allocations (those which, when
- // rounded up to the alignment, overflow `isize`) are not rejected,
- // which can cause undefined behavior. See #64 for details.
- //
- // TODO(#67): Once our MSRV is > 1.64.0, remove this assertion.
- #[allow(clippy::as_conversions)]
- let max_alloc = (isize::MAX as usize).saturating_sub(align);
- assert!(size <= max_alloc);
- // TODO(https://github.com/rust-lang/rust/issues/55724): Use
- // `Layout::repeat` once it's stabilized.
- let layout =
- Layout::from_size_align(size, align).expect("total allocation size overflows `isize`");
-
- // TODO(#61): Add a "SAFETY" comment and remove this `allow`.
- #[allow(clippy::undocumented_unsafe_blocks)]
+ // TODO(https://fxbug.dev/80757): Use Layout::repeat() when `alloc_layout_extra` is stabilized
+ // This will intentionally panic if it overflows.
unsafe {
+ // from_size_align_unchecked() is sound because slice_len_bytes is
+ // guaranteed to be properly aligned (we just multiplied it by
+ // size_of::<T>(), which is guaranteed to be aligned).
+ let layout = Layout::from_size_align_unchecked(
+ size_of::<Self>().checked_mul(len).unwrap(),
+ align_of::<Self>(),
+ );
if layout.size() != 0 {
- let ptr = alloc::alloc::alloc_zeroed(layout).cast::<Self>();
+ let ptr = alloc::alloc::alloc_zeroed(layout) as *mut Self;
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
- Box::from_raw(slice::from_raw_parts_mut(ptr, len))
+ Box::from_raw(core::slice::from_raw_parts_mut(ptr, len))
} else {
- // `Box<[T]>` does not allocate when `T` is zero-sized or when
- // `len` is zero, but it does require a non-null dangling
- // pointer for its allocation.
- Box::from_raw(slice::from_raw_parts_mut(NonNull::<Self>::dangling().as_ptr(), len))
+ // Box<[T]> does not allocate when T is zero-sized or when len
+ // is zero, but it does require a non-null dangling pointer for
+ // its allocation.
+ Box::from_raw(core::slice::from_raw_parts_mut(
+ NonNull::<Self>::dangling().as_ptr(),
+ 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.
- ///
- /// # Panics
- ///
- /// * Panics if `size_of::<Self>() * len` overflows.
- /// * Panics if allocation of `size_of::<Self>() * len` bytes fails.
- #[cfg(feature = "alloc")]
- #[inline(always)]
- fn new_vec_zeroed(len: usize) -> Vec<Self>
- where
- Self: Sized,
- {
- Self::new_box_slice_zeroed(len).into()
- }
-}
-
-/// Types for which any byte pattern is valid.
-///
-/// WARNING: Do not implement this trait yourself! Instead, use
-/// `#[derive(FromBytes)]` (requires the `derive` Cargo feature).
-///
-/// `FromBytes` types can safely be deserialized from an untrusted sequence of
-/// bytes because any byte sequence corresponds to a valid instance of the type.
-///
-/// `FromBytes` is ignorant of byte order. For byte order-aware types, see the
-/// [`byteorder`] module.
-///
-/// # Safety
-///
-/// *This section describes what is required in order for `T: FromBytes`, and
-/// what unsafe code may assume of such types. `#[derive(FromBytes)]` only
-/// permits types which satisfy these requirements. 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 treat any initialized sequence of bytes of length
-/// `size_of::<T>()` as a `T`.
-/// - Given `b: &[u8]` where `b.len() == size_of::<T>()` and `b` is aligned to
-/// `align_of::<T>()`, it is sound to construct a `t: &T` at the same address
-/// as `b`, and it is sound for both `b` and `t` to be live at the same time.
-///
-/// If a type is marked as `FromBytes` which violates this contract, it may
-/// cause undefined behavior.
-///
-/// If a type has the following properties, then it is sound to implement
-/// `FromBytes` for that type:
-/// - If the type is a struct, all of its fields must satisfy the requirements
-/// to be `FromBytes` (they do not actually have to be `FromBytes`)
-/// - If the type is an enum:
-/// - It must be a C-like enum (meaning that all variants have no fields).
-/// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`,
-/// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`).
-/// - The maximum number of discriminants must be used (so that every possible
-/// bit pattern is a valid one). Be very careful when using the `C`,
-/// `usize`, or `isize` representations, as their size is
-/// platform-dependent.
-/// - The type must not contain any [`UnsafeCell`]s (this is required in order
-/// for it to be sound to construct a `&[u8]` and a `&T` to the same region of
-/// memory). The type may contain references or pointers to `UnsafeCell`s so
-/// long as those values can themselves be initialized from zeroes
-/// (`FromBytes` is not currently implemented for, e.g., `Option<*const
-/// UnsafeCell<_>>`, but it could be one day).
-///
-/// [`UnsafeCell`]: core::cell::UnsafeCell
-///
-/// # Rationale
-///
-/// ## Why isn't an explicit representation required for structs?
-///
-/// 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`.
-pub unsafe trait FromBytes: FromZeroes {
- // 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;
-
- /// Reads a copy of `Self` from `bytes`.
- ///
- /// If `bytes.len() != size_of::<Self>()`, `read_from` returns `None`.
- #[inline]
- fn read_from(bytes: &[u8]) -> Option<Self>
- where
- Self: Sized,
- {
- let r = Ref::<_, Unalign<Self>>::new_unaligned(bytes)?;
- Some(r.read().into_inner())
- }
-
- /// Reads a copy of `Self` from the prefix of `bytes`.
- ///
- /// `read_from_prefix` reads a `Self` from the first `size_of::<Self>()`
- /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()`, it returns
- /// `None`.
- #[inline]
- fn read_from_prefix(bytes: &[u8]) -> Option<Self>
- where
- Self: Sized,
- {
- let (r, _suffix) = Ref::<_, Unalign<Self>>::new_unaligned_from_prefix(bytes)?;
- Some(r.read().into_inner())
- }
-
- /// Reads a copy of `Self` from the suffix of `bytes`.
- ///
- /// `read_from_suffix` reads a `Self` from the last `size_of::<Self>()`
- /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()`, it returns
- /// `None`.
- #[inline]
- fn read_from_suffix(bytes: &[u8]) -> Option<Self>
- where
- Self: Sized,
- {
- let (_prefix, r) = Ref::<_, Unalign<Self>>::new_unaligned_from_suffix(bytes)?;
- Some(r.read().into_inner())
- }
}
/// Types which are safe to treat as an immutable byte slice.
///
/// WARNING: Do not implement this trait yourself! Instead, use
-/// `#[derive(AsBytes)]` (requires the `derive` Cargo feature).
+/// `#[derive(AsBytes)]`.
///
/// `AsBytes` types can be safely viewed as a slice of bytes. In particular,
/// this means that, in any valid instance of the type, none of the bytes of the
@@ -929,71 +350,40 @@ pub unsafe trait FromBytes: FromZeroes {
/// get an error like this:
///
/// ```text
-/// error[E0277]: the trait bound `HasPadding<Foo, true>: ShouldBe<false>` is not satisfied
-/// --> lib.rs:23:10
+/// error[E0080]: evaluation of constant value failed
+/// --> lib.rs:1:10
/// |
/// 1 | #[derive(AsBytes)]
-/// | ^^^^^^^ the trait `ShouldBe<false>` is not implemented for `HasPadding<Foo, true>`
-/// |
-/// = help: the trait `ShouldBe<VALUE>` is implemented for `HasPadding<T, VALUE>`
+/// | ^^^^^^^ attempt to divide by zero
/// ```
///
-/// This error indicates that the type being annotated has padding bytes, which
-/// is illegal for `AsBytes` types. Consider reducing the alignment of some
-/// fields by using types in the [`byteorder`] module, adding explicit struct
-/// fields where those padding bytes would be, or using `#[repr(packed)]`. See
-/// the Rust Reference's [page on type layout](type-layout) for more information
-/// about type layout and padding.
+/// This error means that the type being annotated has padding bytes, which is
+/// illegal for `AsBytes` types. Consider either adding explicit struct fields
+/// where those padding bytes would be or using `#[repr(packed)]`.
///
/// # Safety
///
-/// *This section describes what is required in order for `T: AsBytes`, and what
-/// unsafe code may assume of such types. `#[derive(AsBytes)]` only permits
-/// types which satisfy these requirements. If you don't plan on implementing
-/// `AsBytes` manually, and you don't plan on writing unsafe code that operates
-/// on `AsBytes` types, then you don't need to read this section.*
-///
-/// If `T: AsBytes`, then unsafe code may assume that:
-/// - It is sound to treat any `t: T` as an immutable `[u8]` of length
-/// `size_of_val(t)`.
-/// - Given `t: &T`, it is sound to construct a `b: &[u8]` where `b.len() ==
-/// size_of_val(t)` at the same address as `t`, and it is sound for both `b`
-/// and `t` to be live at the same time.
-///
-/// If a type is marked as `AsBytes` which violates this contract, it may cause
+/// If `T: AsBytes`, then unsafe code may assume that it is sound to treat any
+/// instance of the type as an immutable `[u8]` of length `size_of::<T>()`. If a
+/// type is marked as `AsBytes` which violates this contract, it may cause
/// undefined behavior.
///
-/// If a type has the following properties, then it is sound to implement
-/// `AsBytes` for that type:
+/// If a type has the following properties, then it is safe to implement
+/// `AsBytes` for that type
/// - If the type is a struct:
/// - It must have a defined representation (`repr(C)`, `repr(transparent)`,
/// or `repr(packed)`).
-/// - All of its fields must satisfy the requirements to be `AsBytes` (they do
-/// not actually have to be `AsBytes`).
+/// - All of its fields must be `AsBytes`
/// - Its layout must have no padding. This is always true for
/// `repr(transparent)` and `repr(packed)`. For `repr(C)`, see the layout
/// algorithm described in the [Rust Reference].
/// - If the type is an enum:
-/// - It must be a C-like enum (meaning that all variants have no fields).
+/// - It must be a C-like enum (meaning that all variants have no fields)
/// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`,
/// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`).
-/// - The type must not contain any [`UnsafeCell`]s (this is required in order
-/// for it to be sound to construct a `&[u8]` and a `&T` to the same region of
-/// memory). The type may contain references or pointers to `UnsafeCell`s so
-/// long as those values can themselves be initialized from zeroes (`AsBytes`
-/// is not currently implemented for, e.g., `Option<&UnsafeCell<_>>`, but it
-/// could be one day).
///
-/// [type-layout]: https://doc.rust-lang.org/reference/type-layout.html
/// [Rust Reference]: https://doc.rust-lang.org/reference/type-layout.html
-/// [`UnsafeCell`]: core::cell::UnsafeCell
pub unsafe trait AsBytes {
- // The `Self: Sized` bound makes it so that this function doesn't prevent
- // `AsBytes` from being object safe. Note that other `AsBytes` 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 `AsBytes` 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
@@ -1003,75 +393,35 @@ pub unsafe trait AsBytes {
///
/// `as_bytes` provides access to the bytes of this value as an immutable
/// byte slice.
- #[inline(always)]
fn as_bytes(&self) -> &[u8] {
- // 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 *
- // mem::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: AsBytes` 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, and those don't allow
- // mutation. `AsBytes` prohibits types which contain `UnsafeCell`s,
- // which are the only types for which this rule wouldn't be sufficient.
- // - 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`.
- unsafe { slice::from_raw_parts(slf.cast::<u8>(), len) }
+ unsafe {
+ // NOTE: This function does not have a Self: Sized bound.
+ // size_of_val works for unsized values too.
+ let len = mem::size_of_val(self);
+ slice::from_raw_parts(self as *const Self as *const u8, len)
+ }
}
/// Gets the bytes of this value mutably.
///
/// `as_bytes_mut` provides access to the bytes of this value as a mutable
/// byte slice.
- #[inline(always)]
fn as_bytes_mut(&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 *
- // mem::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: AsBytes` 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`.
- unsafe { slice::from_raw_parts_mut(slf.cast::<u8>(), len) }
+ unsafe {
+ // NOTE: This function does not have a Self: Sized bound.
+ // size_of_val works for unsized values too.
+ let len = mem::size_of_val(self);
+ slice::from_raw_parts_mut(self as *mut Self as *mut u8, len)
+ }
}
/// Writes a copy of `self` to `bytes`.
///
/// If `bytes.len() != size_of_val(self)`, `write_to` returns `None`.
- #[inline]
- fn write_to(&self, bytes: &mut [u8]) -> Option<()> {
+ fn write_to<B: ByteSliceMut>(&self, mut bytes: B) -> Option<()> {
if bytes.len() != mem::size_of_val(self) {
return None;
}
@@ -1084,50 +434,50 @@ pub unsafe trait AsBytes {
///
/// `write_to_prefix` writes `self` to the first `size_of_val(self)` bytes
/// of `bytes`. If `bytes.len() < size_of_val(self)`, it returns `None`.
- #[inline]
- fn write_to_prefix(&self, bytes: &mut [u8]) -> Option<()> {
+ fn write_to_prefix<B: ByteSliceMut>(&self, mut bytes: B) -> Option<()> {
let size = mem::size_of_val(self);
- bytes.get_mut(..size)?.copy_from_slice(self.as_bytes());
+ if bytes.len() < size {
+ return None;
+ }
+
+ bytes[..size].copy_from_slice(self.as_bytes());
Some(())
}
/// Writes a copy of `self` to the suffix of `bytes`.
///
- /// `write_to_suffix` writes `self` to the last `size_of_val(self)` bytes of
- /// `bytes`. If `bytes.len() < size_of_val(self)`, it returns `None`.
- #[inline]
- fn write_to_suffix(&self, bytes: &mut [u8]) -> Option<()> {
+ /// `write_to_suffix` writes `self` to the last `size_of_val(self)` bytes
+ /// of `bytes`. If `bytes.len() < size_of_val(self)`, it returns `None`.
+ fn write_to_suffix<B: ByteSliceMut>(&self, mut bytes: B) -> Option<()> {
let start = bytes.len().checked_sub(mem::size_of_val(self))?;
- bytes
- .get_mut(start..)
- .expect("`start` should be in-bounds of `bytes`")
- .copy_from_slice(self.as_bytes());
+ bytes[start..].copy_from_slice(self.as_bytes());
Some(())
}
}
+// Special case for bool (it is not included in `impl_for_primitives!`).
+impl_for_types!(AsBytes, bool);
+
+impl_for_primitives!(FromBytes);
+impl_for_primitives!(AsBytes);
+impl_for_composite_types!(FromBytes);
+impl_for_composite_types!(AsBytes);
+
/// Types with no alignment requirement.
///
/// WARNING: Do not implement this trait yourself! Instead, use
-/// `#[derive(Unaligned)]` (requires the `derive` Cargo feature).
+/// `#[derive(Unaligned)]`.
///
/// If `T: Unaligned`, then `align_of::<T>() == 1`.
///
/// # Safety
///
-/// *This section describes what is required in order for `T: Unaligned`, and
-/// what unsafe code may assume of such types. `#[derive(Unaligned)]` only
-/// permits types which satisfy these requirements. 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.
pub unsafe trait Unaligned {
- // The `Self: Sized` bound makes it so that `Unaligned` is still object
+ // NOTE: The Self: Sized bound makes it so that Unaligned is still object
// safe.
#[doc(hidden)]
fn only_derive_is_allowed_to_implement_this_trait()
@@ -1135,289 +485,8 @@ pub unsafe trait Unaligned {
Self: Sized;
}
-safety_comment! {
- /// 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."
- /// - `FromZeroes`, `FromBytes`: There is only one possible sequence of 0
- /// bytes, and `()` is inhabited.
- /// - `AsBytes`: Since `()` has size 0, it contains no padding bytes.
- /// - `Unaligned`: `()` has alignment 1.
- ///
- /// [1] https://doc.rust-lang.org/reference/type-layout.html#tuple-layout
- unsafe_impl!((): FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_unaligned!(());
-}
-
-safety_comment! {
- /// SAFETY:
- /// - `FromZeroes`, `FromBytes`: all bit patterns are valid for integers [1]
- /// - `AsBytes`: integers 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
- /// - Size is an integer multiple of alignment
- /// - 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] TODO(https://github.com/rust-lang/reference/issues/1291): Once the
- /// reference explicitly guarantees these properties, cite it.
- /// [2] https://doc.rust-lang.org/reference/type-layout.html#primitive-data-layout
- unsafe_impl!(u8: FromZeroes, FromBytes, AsBytes, Unaligned);
- unsafe_impl!(i8: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_unaligned!(u8, i8);
- unsafe_impl!(u16: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(i16: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(u32: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(i32: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(u64: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(i64: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(u128: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(i128: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(usize: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(isize: FromZeroes, FromBytes, AsBytes);
-}
-
-safety_comment! {
- /// SAFETY:
- /// - `FromZeroes`, `FromBytes`: the `{f32,f64}::from_bits` constructors'
- /// documentation [1,2] states that they are currently equivalent to
- /// `transmute`. [3]
- /// - `AsBytes`: the `{f32,f64}::to_bits` methods' documentation [4,5]
- /// states that they are currently equivalent to `transmute`. [3]
- ///
- /// TODO: Make these arguments more precisely in terms of the documentation.
- ///
- /// [1] https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.from_bits
- /// [2] https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.from_bits
- /// [3] TODO(https://github.com/rust-lang/reference/issues/1291): Once the
- /// reference explicitly guarantees these properties, cite it.
- /// [4] https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.to_bits
- /// [5] https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.to_bits
- unsafe_impl!(f32: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(f64: FromZeroes, FromBytes, AsBytes);
-}
-
-safety_comment! {
- /// SAFETY:
- /// - `FromZeroes`: Per the reference [1], 0x00 is a valid bit pattern for
- /// `bool`.
- /// - `AsBytes`: Per the reference [1], `bool` always has a size of 1 with
- /// valid bit patterns 0x01 and 0x00, so 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/reference/types/boolean.html
- unsafe_impl!(bool: FromZeroes, AsBytes, Unaligned);
- assert_unaligned!(bool);
-}
-safety_comment! {
- /// SAFETY:
- /// - `FromZeroes`: Per the reference [1], 0x0000 is a valid bit pattern for
- /// `char`.
- /// - `AsBytes`: `char` is represented as a 32-bit unsigned word (`u32`)
- /// [1], which is `AsBytes`. Note that unlike `u32`, not all bit patterns
- /// are valid for `char`.
- ///
- /// [1] https://doc.rust-lang.org/reference/types/textual.html
- unsafe_impl!(char: FromZeroes, AsBytes);
-}
-safety_comment! {
- /// SAFETY:
- /// - `FromZeroes`, `AsBytes`, `Unaligned`: Per the reference [1], `str` has
- /// the same layout as `[u8]`, and `[u8]` is `FromZeroes`, `AsBytes`, and
- /// `Unaligned`.
- ///
- /// Note that we don't `assert_unaligned!(str)` because `assert_unaligned!`
- /// uses `align_of`, which only works for `Sized` types.
- ///
- /// [1] https://doc.rust-lang.org/reference/type-layout.html#str-layout
- unsafe_impl!(str: FromZeroes, AsBytes, Unaligned);
-}
-
-safety_comment! {
- // `NonZeroXxx` is `AsBytes`, but not `FromZeroes` or `FromBytes`.
- //
- /// SAFETY:
- /// - `AsBytes`: `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.
- ///
- /// [1] https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html
- /// [2] https://doc.rust-lang.org/stable/std/num/struct.NonZeroI8.html
- /// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation
- /// that layout is the same as primitive layout.
- unsafe_impl!(NonZeroU8: AsBytes, Unaligned);
- unsafe_impl!(NonZeroI8: AsBytes, Unaligned);
- assert_unaligned!(NonZeroU8, NonZeroI8);
- unsafe_impl!(NonZeroU16: AsBytes);
- unsafe_impl!(NonZeroI16: AsBytes);
- unsafe_impl!(NonZeroU32: AsBytes);
- unsafe_impl!(NonZeroI32: AsBytes);
- unsafe_impl!(NonZeroU64: AsBytes);
- unsafe_impl!(NonZeroI64: AsBytes);
- unsafe_impl!(NonZeroU128: AsBytes);
- unsafe_impl!(NonZeroI128: AsBytes);
- unsafe_impl!(NonZeroUsize: AsBytes);
- unsafe_impl!(NonZeroIsize: AsBytes);
-}
-safety_comment! {
- /// SAFETY:
- /// - `FromZeroes`, `FromBytes`, `AsBytes`: 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] https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html
- /// [2] https://doc.rust-lang.org/stable/std/num/struct.NonZeroI8.html
- ///
- /// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation
- /// for layout guarantees.
- unsafe_impl!(Option<NonZeroU8>: FromZeroes, FromBytes, AsBytes, Unaligned);
- unsafe_impl!(Option<NonZeroI8>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_unaligned!(Option<NonZeroU8>, Option<NonZeroI8>);
- unsafe_impl!(Option<NonZeroU16>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroI16>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroU32>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroI32>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroU64>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroI64>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroU128>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroI128>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroUsize>: FromZeroes, FromBytes, AsBytes);
- unsafe_impl!(Option<NonZeroIsize>: FromZeroes, FromBytes, AsBytes);
-}
-
-safety_comment! {
- /// SAFETY:
- /// For all `T`, `PhantomData<T>` has size 0 and alignment 1. [1]
- /// - `FromZeroes`, `FromBytes`: There is only one possible sequence of 0
- /// bytes, and `PhantomData` is inhabited.
- /// - `AsBytes`: 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/std/marker/struct.PhantomData.html#layout-1
- unsafe_impl!(T: ?Sized => FromZeroes for PhantomData<T>);
- unsafe_impl!(T: ?Sized => FromBytes for PhantomData<T>);
- unsafe_impl!(T: ?Sized => AsBytes for PhantomData<T>);
- unsafe_impl!(T: ?Sized => Unaligned for PhantomData<T>);
- assert_unaligned!(PhantomData<()>, PhantomData<u8>, PhantomData<u64>);
-}
-safety_comment! {
- /// SAFETY:
- /// `Wrapping<T>` is guaranteed by its docs [1] to have the same layout as
- /// `T`. Also, `Wrapping<T>` is `#[repr(transparent)]`, and has a single
- /// field, which is `pub`. Per the reference [2], this means that the
- /// `#[repr(transparent)]` attribute is "considered part of the public ABI".
- ///
- /// [1] https://doc.rust-lang.org/nightly/core/num/struct.Wrapping.html#layout-1
- /// [2] https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent
- unsafe_impl!(T: FromZeroes => FromZeroes for Wrapping<T>);
- unsafe_impl!(T: FromBytes => FromBytes for Wrapping<T>);
- unsafe_impl!(T: AsBytes => AsBytes for Wrapping<T>);
- unsafe_impl!(T: Unaligned => Unaligned for Wrapping<T>);
- assert_unaligned!(Wrapping<()>, Wrapping<u8>);
-}
-safety_comment! {
- // `MaybeUninit<T>` is `FromZeroes` and `FromBytes`, but never `AsBytes`
- // since it may contain uninitialized bytes.
- //
- /// SAFETY:
- /// - `FromZeroes`, `FromBytes`: `MaybeUninit<T>` has no restrictions on its
- /// contents. Unfortunately, in addition to bit validity, `FromZeroes` and
- /// `FromBytes` also require that implementers contain no `UnsafeCell`s.
- /// Thus, we require `T: FromZeroes` and `T: FromBytes` in order to ensure
- /// that `T` - and thus `MaybeUninit<T>` - contains to `UnsafeCell`s.
- /// Thus, requiring that `T` implement each of these traits is sufficient
- /// - `Unaligned`: `MaybeUninit<T>` is guaranteed by its documentation [1]
- /// to have the same alignment as `T`.
- ///
- /// [1]
- /// https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
- ///
- /// TODO(https://github.com/google/zerocopy/issues/251): If we split
- /// `FromBytes` and `RefFromBytes`, or if we introduce a separate
- /// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes`
- /// and `FromBytes`.
- unsafe_impl!(T: FromZeroes => FromZeroes for MaybeUninit<T>);
- unsafe_impl!(T: FromBytes => FromBytes for MaybeUninit<T>);
- unsafe_impl!(T: Unaligned => Unaligned for MaybeUninit<T>);
- assert_unaligned!(MaybeUninit<()>, MaybeUninit<u8>);
-}
-safety_comment! {
- /// SAFETY:
- /// `ManuallyDrop` has the same layout as `T`, and accessing the inner value
- /// is safe (meaning that it's unsound to leave the inner value
- /// uninitialized while exposing the `ManuallyDrop` to safe code).
- /// - `FromZeroes`, `FromBytes`: Since it has the same layout as `T`, any
- /// valid `T` is a valid `ManuallyDrop<T>`. If `T: FromZeroes`, a sequence
- /// of zero bytes is a valid `T`, and thus a valid `ManuallyDrop<T>`. If
- /// `T: FromBytes`, any sequence of bytes is a valid `T`, and thus a valid
- /// `ManuallyDrop<T>`.
- /// - `AsBytes`: Since it has the same layout as `T`, and since it's unsound
- /// to let safe code access a `ManuallyDrop` whose inner value is
- /// uninitialized, safe code can only ever access a `ManuallyDrop` whose
- /// contents are a valid `T`. Since `T: AsBytes`, this means that safe
- /// code can only ever access a `ManuallyDrop` with all initialized bytes.
- /// - `Unaligned`: `ManuallyDrop` has the same layout (and thus alignment)
- /// as `T`, and `T: Unaligned` guarantees that that alignment is 1.
- unsafe_impl!(T: ?Sized + FromZeroes => FromZeroes for ManuallyDrop<T>);
- unsafe_impl!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>);
- unsafe_impl!(T: ?Sized + AsBytes => AsBytes for ManuallyDrop<T>);
- unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<T>);
- assert_unaligned!(ManuallyDrop<()>, ManuallyDrop<u8>);
-}
-safety_comment! {
- /// 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 `FromZeroes`, `FromBytes`, and `AsBytes` 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/reference/type-layout.html#array-layout
- unsafe_impl!(const N: usize, T: FromZeroes => FromZeroes for [T; N]);
- unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]);
- unsafe_impl!(const N: usize, T: AsBytes => AsBytes 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: FromZeroes => FromZeroes for [T]);
- unsafe_impl!(T: FromBytes => FromBytes for [T]);
- unsafe_impl!(T: AsBytes => AsBytes for [T]);
- unsafe_impl!(T: Unaligned => Unaligned for [T]);
-}
+impl_for_types!(Unaligned, u8, i8);
+impl_for_composite_types!(Unaligned);
// SIMD support
//
@@ -1464,8 +533,8 @@ safety_comment! {
// 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
-// `FromZeroes`, `FromBytes`, or `AsBytes`, that SIMD type is also
-// `FromZeroes`, `FromBytes`, or `AsBytes` respectively.
+// `FromBytes`, that SIMD type is also `FromBytes`. The same holds for
+// `AsBytes`.
// - Since no upper bound is placed on the alignment, no SIMD type can be
// guaranteed to be `Unaligned`.
//
@@ -1476,36 +545,29 @@ safety_comment! {
//
// 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
-// `FromZeroes`, `FromBytes`, or `AsBytes` 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.
+// `FromBytes` or `AsBytes` 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")]
mod simd {
- /// Defines a module which implements `FromZeroes`, `FromBytes`, and
- /// `AsBytes` for a set of types from a module in `core::arch`.
+ /// Defines a module which implements `FromBytes` and `AsBytes` 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 `FromZeroes`, `FromBytes`, and `AsBytes` 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.
+ /// to implement `FromBytes` and `AsBytes` for.
macro_rules! simd_arch_mod {
($arch:ident, $($typ:ident),*) => {
mod $arch {
use core::arch::$arch::{$($typ),*};
use crate::*;
- impl_known_layout!($($typ),*);
- safety_comment! {
- /// SAFETY:
- /// See comment on module definition for justification.
- $( unsafe_impl!($typ: FromZeroes, FromBytes, AsBytes); )*
- }
+
+ impl_for_types!(FromBytes, $($typ),*);
+ impl_for_types!(AsBytes, $($typ),*);
}
};
}
@@ -1532,7 +594,7 @@ mod simd {
vector_signed_long,
vector_unsigned_long
);
- #[cfg(target_arch = "aarch64")]
+ #[cfg(all(feature = "simd-nightly", target_arch = "aarch64"))]
#[rustfmt::skip]
simd_arch_mod!(
aarch64, float32x2_t, float32x4_t, float64x1_t, float64x2_t, int8x8_t, int8x8x2_t,
@@ -1545,7 +607,111 @@ mod simd {
);
#[cfg(all(feature = "simd-nightly", target_arch = "arm"))]
#[rustfmt::skip]
- simd_arch_mod!(arm, int8x4_t, uint8x4_t);
+ simd_arch_mod!(
+ arm, float32x2_t, float32x4_t, int8x4_t, int8x8_t, int8x8x2_t, int8x8x3_t, int8x8x4_t,
+ int8x16_t, int16x2_t, int16x4_t, int16x8_t, int32x2_t, int32x4_t, int64x1_t, int64x2_t,
+ poly8x8_t, poly8x8x2_t, poly8x8x3_t, poly8x8x4_t, poly8x16_t, poly16x4_t, poly16x8_t,
+ poly64x1_t, poly64x2_t, uint8x4_t, uint8x8_t, uint8x8x2_t, uint8x8x3_t, uint8x8x4_t,
+ uint8x16_t, uint16x2_t, uint16x4_t, uint16x8_t, uint32x2_t, uint32x4_t, uint64x1_t,
+ uint64x2_t
+ );
+}
+
+/// A type with no alignment requirement.
+///
+/// A `Unalign` wraps a `T`, removing any alignment requirement. `Unalign<T>`
+/// has the same size and ABI as `T`, but not necessarily the same alignment.
+/// This is useful if a type with an alignment requirement needs to be read from
+/// a chunk of memory which provides no alignment guarantees.
+///
+/// Since `Unalign` has no alignment requirement, the inner `T` may not be
+/// properly aligned in memory, and so `Unalign` provides no way of getting a
+/// reference to the inner `T`. Instead, the `T` may only be obtained by value
+/// (see [`get`] and [`into_inner`]).
+///
+/// [`get`]: Unalign::get
+/// [`into_inner`]: Unalign::into_inner
+#[derive(FromBytes, Unaligned, Copy)]
+#[repr(C, packed)]
+pub struct Unalign<T>(T);
+
+// Note that `Unalign: Clone` only if `T: Copy`. Since the inner `T` may not be
+// aligned, there's no way to safely call `T::clone`, and so a `T: Clone` bound
+// is not sufficient to implement `Clone` for `Unalign`.
+impl<T: Copy> Clone for Unalign<T> {
+ fn clone(&self) -> Unalign<T> {
+ *self
+ }
+}
+
+impl<T> Unalign<T> {
+ /// Constructs a new `Unalign`.
+ pub fn new(val: T) -> Unalign<T> {
+ Unalign(val)
+ }
+
+ /// Consumes `self`, returning the inner `T`.
+ pub fn into_inner(self) -> T {
+ let Unalign(val) = self;
+ val
+ }
+
+ /// Gets an unaligned raw pointer to the inner `T`.
+ ///
+ /// # Safety
+ ///
+ /// The returned raw pointer is not necessarily aligned to
+ /// `align_of::<T>()`. Most functions which operate on raw pointers require
+ /// those pointers to be aligned, so calling those functions with the result
+ /// of `get_ptr` will be undefined behavior if alignment is not guaranteed
+ /// using some out-of-band mechanism. In general, the only functions which
+ /// are safe to call with this pointer are which that are explicitly
+ /// documented as being sound to use with an unaligned pointer, such as
+ /// [`read_unaligned`].
+ ///
+ /// [`read_unaligned`]: core::ptr::read_unaligned
+ pub fn get_ptr(&self) -> *const T {
+ ptr::addr_of!(self.0)
+ }
+
+ /// Gets an unaligned mutable raw pointer to the inner `T`.
+ ///
+ /// # Safety
+ ///
+ /// The returned raw pointer is not necessarily aligned to
+ /// `align_of::<T>()`. Most functions which operate on raw pointers require
+ /// those pointers to be aligned, so calling those functions with the result
+ /// of `get_ptr` will be undefined behavior if alignment is not guaranteed
+ /// using some out-of-band mechanism. In general, the only functions which
+ /// are safe to call with this pointer are those which are explicitly
+ /// documented as being sound to use with an unaligned pointer, such as
+ /// [`read_unaligned`].
+ ///
+ /// [`read_unaligned`]: core::ptr::read_unaligned
+ pub fn get_mut_ptr(&mut self) -> *mut T {
+ ptr::addr_of_mut!(self.0)
+ }
+}
+
+impl<T: Copy> Unalign<T> {
+ /// Gets a copy of the inner `T`.
+ pub fn get(&self) -> T {
+ let Unalign(val) = *self;
+ val
+ }
+}
+
+// SAFETY: Since `T: AsBytes`, we know that it's safe to construct a `&[u8]`
+// from an aligned `&T`. Since `&[u8]` itself has no alignment requirements, it
+// must also be safe to construct a `&[u8]` from a `&T` at any address. Since
+// `Unalign<T>` is `#[repr(packed)]`, everything about its layout except for its
+// alignment is the same as `T`'s layout.
+unsafe impl<T: AsBytes> AsBytes for Unalign<T> {
+ fn only_derive_is_allowed_to_implement_this_trait()
+ where
+ Self: Sized,
+ {
+ }
}
// Used in `transmute!` below.
@@ -1576,15 +742,15 @@ macro_rules! transmute {
// This branch, though never taken, ensures that the type of `e` is
// `AsBytes` and that the type of this macro invocation expression
// is `FromBytes`.
- const fn transmute<T: $crate::AsBytes, U: $crate::FromBytes>(_t: T) -> U {
+ fn transmute<T: $crate::AsBytes, U: $crate::FromBytes>(_t: T) -> U {
unreachable!()
}
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 `AsBytes` and
- // `FromBytes` bounds enforced by the `false` branch.
+ // `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 `AsBytes` and `FromBytes`
+ // bounds enforced by the `false` branch.
//
// We use `$crate::__real_transmute` because we know it will always
// be available for crates which are using the 2015 edition of Rust.
@@ -1598,23 +764,25 @@ macro_rules! transmute {
}}
}
-/// A typed reference derived from a byte slice.
+/// A length- and alignment-checked reference to a byte slice which can safely
+/// be reinterpreted as another type.
///
-/// A `Ref<B, T>` is a reference to a `T` which is stored in a byte slice, `B`.
-/// Unlike a native reference (`&T` or `&mut T`), `Ref<B, T>` has the same
-/// mutability as the byte slice it was constructed from (`B`).
+/// `LayoutVerified` is a byte slice reference (`&[u8]`, `&mut [u8]`,
+/// `Ref<[u8]>`, `RefMut<[u8]>`, etc) with the invaraint that the slice's length
+/// and alignment are each greater than or equal to the length and alignment of
+/// `T`. Using this invariant, it implements `Deref` for `T` so long as `T:
+/// FromBytes` and `DerefMut` so long as `T: FromBytes + AsBytes`.
///
/// # Examples
///
-/// `Ref` can be used to treat a sequence of bytes as a structured type, and to
-/// read and write the fields of that type as if the byte slice reference were
-/// simply a reference to that type.
+/// `LayoutVerified` can be used to treat a sequence of bytes as a structured
+/// type, and to read and write the fields of that type as if the byte slice
+/// reference were simply a reference to that type.
///
/// ```rust
-/// # #[cfg(feature = "derive")] { // This example uses derives, and won't compile without them
-/// use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, FromZeroes, Ref, Unaligned};
+/// use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, LayoutVerified, Unaligned};
///
-/// #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
+/// #[derive(FromBytes, AsBytes, Unaligned)]
/// #[repr(C)]
/// struct UdpHeader {
/// src_port: [u8; 2],
@@ -1624,13 +792,13 @@ macro_rules! transmute {
/// }
///
/// struct UdpPacket<B> {
-/// header: Ref<B, UdpHeader>,
+/// header: LayoutVerified<B, UdpHeader>,
/// body: B,
/// }
///
/// impl<B: ByteSlice> UdpPacket<B> {
/// pub fn parse(bytes: B) -> Option<UdpPacket<B>> {
-/// let (header, body) = Ref::new_unaligned_from_prefix(bytes)?;
+/// let (header, body) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
/// Some(UdpPacket { header, body })
/// }
///
@@ -1644,108 +812,104 @@ macro_rules! transmute {
/// self.header.src_port = src_port;
/// }
/// }
-/// # }
/// ```
-pub struct Ref<B, T: ?Sized>(B, PhantomData<T>);
-
-/// Deprecated: prefer [`Ref`] instead.
-#[deprecated(since = "0.7.0", note = "LayoutVerified has been renamed to Ref")]
-#[doc(hidden)]
-pub type LayoutVerified<B, T> = Ref<B, T>;
+pub struct LayoutVerified<B, T: ?Sized>(B, PhantomData<T>);
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSlice,
{
- /// Constructs a new `Ref`.
+ /// Constructs a new `LayoutVerified`.
///
/// `new` verifies that `bytes.len() == size_of::<T>()` and that `bytes` is
- /// aligned to `align_of::<T>()`, and constructs a new `Ref`. If either of
- /// these checks fail, it returns `None`.
+ /// aligned to `align_of::<T>()`, and constructs a new `LayoutVerified`. If
+ /// either of these checks fail, it returns `None`.
#[inline]
- pub fn new(bytes: B) -> Option<Ref<B, T>> {
- if bytes.len() != mem::size_of::<T>() || !util::aligned_to::<_, T>(bytes.deref()) {
+ pub fn new(bytes: B) -> Option<LayoutVerified<B, T>> {
+ if bytes.len() != mem::size_of::<T>() || !aligned_to(bytes.deref(), mem::align_of::<T>()) {
return None;
}
- Some(Ref(bytes, PhantomData))
+ Some(LayoutVerified(bytes, PhantomData))
}
- /// Constructs a new `Ref` from the prefix of a byte slice.
+ /// Constructs a new `LayoutVerified` from the prefix of a byte slice.
///
/// `new_from_prefix` verifies that `bytes.len() >= size_of::<T>()` and that
/// `bytes` is aligned to `align_of::<T>()`. It consumes the first
- /// `size_of::<T>()` bytes from `bytes` to construct a `Ref`, and returns
- /// the remaining bytes to the caller. If either the length or alignment
- /// checks fail, it returns `None`.
+ /// `size_of::<T>()` bytes from `bytes` to construct a `LayoutVerified`, and
+ /// returns the remaining bytes to the caller. If either the length or
+ /// alignment checks fail, it returns `None`.
#[inline]
- pub fn new_from_prefix(bytes: B) -> Option<(Ref<B, T>, B)> {
- if bytes.len() < mem::size_of::<T>() || !util::aligned_to::<_, T>(bytes.deref()) {
+ pub fn new_from_prefix(bytes: B) -> Option<(LayoutVerified<B, T>, B)> {
+ if bytes.len() < mem::size_of::<T>() || !aligned_to(bytes.deref(), mem::align_of::<T>()) {
return None;
}
let (bytes, suffix) = bytes.split_at(mem::size_of::<T>());
- Some((Ref(bytes, PhantomData), suffix))
+ Some((LayoutVerified(bytes, PhantomData), suffix))
}
- /// Constructs a new `Ref` from the suffix of a byte slice.
+ /// Constructs a new `LayoutVerified` from the suffix of a byte slice.
///
/// `new_from_suffix` verifies that `bytes.len() >= size_of::<T>()` and that
/// the last `size_of::<T>()` bytes of `bytes` are aligned to
/// `align_of::<T>()`. It consumes the last `size_of::<T>()` bytes from
- /// `bytes` to construct a `Ref`, and returns the preceding bytes to the
- /// caller. If either the length or alignment checks fail, it returns
+ /// `bytes` to construct a `LayoutVerified`, and returns the preceding bytes
+ /// to the caller. If either the length or alignment checks fail, it returns
/// `None`.
#[inline]
- pub fn new_from_suffix(bytes: B) -> Option<(B, Ref<B, T>)> {
+ pub fn new_from_suffix(bytes: B) -> Option<(B, LayoutVerified<B, T>)> {
let bytes_len = bytes.len();
- let split_at = bytes_len.checked_sub(mem::size_of::<T>())?;
- let (prefix, bytes) = bytes.split_at(split_at);
- if !util::aligned_to::<_, T>(bytes.deref()) {
+ if bytes_len < mem::size_of::<T>() {
return None;
}
- Some((prefix, Ref(bytes, PhantomData)))
+ let (prefix, bytes) = bytes.split_at(bytes_len - mem::size_of::<T>());
+ if !aligned_to(bytes.deref(), mem::align_of::<T>()) {
+ return None;
+ }
+ Some((prefix, LayoutVerified(bytes, PhantomData)))
}
}
-impl<B, T> Ref<B, [T]>
+impl<B, T> LayoutVerified<B, [T]>
where
B: ByteSlice,
{
- /// Constructs a new `Ref` of a slice type.
+ /// Constructs a new `LayoutVerified` of a slice type.
///
/// `new_slice` verifies that `bytes.len()` is a multiple of
/// `size_of::<T>()` and that `bytes` is aligned to `align_of::<T>()`, and
- /// constructs a new `Ref`. If either of these checks fail, it returns
- /// `None`.
+ /// constructs a new `LayoutVerified`. If either of these checks fail, it
+ /// returns `None`.
///
/// # Panics
///
/// `new_slice` panics if `T` is a zero-sized type.
#[inline]
- pub fn new_slice(bytes: B) -> Option<Ref<B, [T]>> {
- let remainder = bytes
- .len()
- .checked_rem(mem::size_of::<T>())
- .expect("Ref::new_slice called on a zero-sized type");
- if remainder != 0 || !util::aligned_to::<_, T>(bytes.deref()) {
+ pub fn new_slice(bytes: B) -> Option<LayoutVerified<B, [T]>> {
+ assert_ne!(mem::size_of::<T>(), 0);
+ if bytes.len() % mem::size_of::<T>() != 0
+ || !aligned_to(bytes.deref(), mem::align_of::<T>())
+ {
return None;
}
- Some(Ref(bytes, PhantomData))
+ Some(LayoutVerified(bytes, PhantomData))
}
- /// Constructs a new `Ref` of a slice type from the prefix of a byte slice.
+ /// Constructs a new `LayoutVerified` of a slice type from the prefix of a
+ /// byte slice.
///
/// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::<T>() *
/// count` and that `bytes` is aligned to `align_of::<T>()`. It consumes the
- /// first `size_of::<T>() * count` bytes from `bytes` to construct a `Ref`,
- /// and returns the remaining bytes to the caller. It also ensures that
- /// `sizeof::<T>() * count` does not overflow a `usize`. If any of the
- /// length, alignment, or overflow checks fail, it returns `None`.
+ /// first `size_of::<T>() * count` bytes from `bytes` to construct a
+ /// `LayoutVerified`, and returns the remaining bytes to the caller. It also
+ /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. If any
+ /// of the length, alignment, or overflow checks fail, it returns `None`.
///
/// # Panics
///
/// `new_slice_from_prefix` panics if `T` is a zero-sized type.
#[inline]
- pub fn new_slice_from_prefix(bytes: B, count: usize) -> Option<(Ref<B, [T]>, B)> {
+ pub fn new_slice_from_prefix(bytes: B, count: usize) -> Option<(LayoutVerified<B, [T]>, B)> {
let expected_len = match mem::size_of::<T>().checked_mul(count) {
Some(len) => len,
None => return None,
@@ -1757,20 +921,21 @@ where
Self::new_slice(prefix).map(move |l| (l, bytes))
}
- /// Constructs a new `Ref` of a slice type from the suffix of a byte slice.
+ /// Constructs a new `LayoutVerified` of a slice type from the suffix of a
+ /// byte slice.
///
/// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::<T>() *
/// count` and that `bytes` is aligned to `align_of::<T>()`. It consumes the
- /// last `size_of::<T>() * count` bytes from `bytes` to construct a `Ref`,
- /// and returns the preceding bytes to the caller. It also ensures that
- /// `sizeof::<T>() * count` does not overflow a `usize`. If any of the
- /// length, alignment, or overflow checks fail, it returns `None`.
+ /// last `size_of::<T>() * count` bytes from `bytes` to construct a
+ /// `LayoutVerified`, and returns the preceding bytes to the caller. It also
+ /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. If any
+ /// of the length, alignment, or overflow checks fail, it returns `None`.
///
/// # Panics
///
/// `new_slice_from_suffix` panics if `T` is a zero-sized type.
#[inline]
- pub fn new_slice_from_suffix(bytes: B, count: usize) -> Option<(B, Ref<B, [T]>)> {
+ pub fn new_slice_from_suffix(bytes: B, count: usize) -> Option<(B, LayoutVerified<B, [T]>)> {
let expected_len = match mem::size_of::<T>().checked_mul(count) {
Some(len) => len,
None => return None,
@@ -1783,98 +948,105 @@ where
}
}
-fn map_zeroed<B: ByteSliceMut, T: ?Sized>(opt: Option<Ref<B, T>>) -> Option<Ref<B, T>> {
+fn map_zeroed<B: ByteSliceMut, T: ?Sized>(
+ opt: Option<LayoutVerified<B, T>>,
+) -> Option<LayoutVerified<B, T>> {
match opt {
- Some(mut r) => {
- r.0.fill(0);
- Some(r)
+ Some(mut lv) => {
+ for b in lv.0.iter_mut() {
+ *b = 0;
+ }
+ Some(lv)
}
None => None,
}
}
fn map_prefix_tuple_zeroed<B: ByteSliceMut, T: ?Sized>(
- opt: Option<(Ref<B, T>, B)>,
-) -> Option<(Ref<B, T>, B)> {
+ opt: Option<(LayoutVerified<B, T>, B)>,
+) -> Option<(LayoutVerified<B, T>, B)> {
match opt {
- Some((mut r, rest)) => {
- r.0.fill(0);
- Some((r, rest))
+ Some((mut lv, rest)) => {
+ for b in lv.0.iter_mut() {
+ *b = 0;
+ }
+ Some((lv, rest))
}
None => None,
}
}
fn map_suffix_tuple_zeroed<B: ByteSliceMut, T: ?Sized>(
- opt: Option<(B, Ref<B, T>)>,
-) -> Option<(B, Ref<B, T>)> {
+ opt: Option<(B, LayoutVerified<B, T>)>,
+) -> Option<(B, LayoutVerified<B, T>)> {
map_prefix_tuple_zeroed(opt.map(|(a, b)| (b, a))).map(|(a, b)| (b, a))
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSliceMut,
{
- /// Constructs a new `Ref` after zeroing the bytes.
+ /// Constructs a new `LayoutVerified` after zeroing the bytes.
///
/// `new_zeroed` verifies that `bytes.len() == size_of::<T>()` and that
- /// `bytes` is aligned to `align_of::<T>()`, and constructs a new `Ref`. If
- /// either of these checks fail, it returns `None`.
+ /// `bytes` is aligned to `align_of::<T>()`, and constructs a new
+ /// `LayoutVerified`. If either of these checks fail, it returns `None`.
///
/// If the checks succeed, then `bytes` will be initialized to zero. This
/// can be useful when re-using buffers to ensure that sensitive data
/// previously stored in the buffer is not leaked.
- #[inline(always)]
- pub fn new_zeroed(bytes: B) -> Option<Ref<B, T>> {
+ #[inline]
+ pub fn new_zeroed(bytes: B) -> Option<LayoutVerified<B, T>> {
map_zeroed(Self::new(bytes))
}
- /// Constructs a new `Ref` from the prefix of a byte slice, zeroing the
- /// prefix.
+ /// Constructs a new `LayoutVerified` from the prefix of a byte slice,
+ /// zeroing the prefix.
///
/// `new_from_prefix_zeroed` verifies that `bytes.len() >= size_of::<T>()`
/// and that `bytes` is aligned to `align_of::<T>()`. It consumes the first
- /// `size_of::<T>()` bytes from `bytes` to construct a `Ref`, and returns
- /// the remaining bytes to the caller. If either the length or alignment
- /// checks fail, it returns `None`.
+ /// `size_of::<T>()` bytes from `bytes` to construct a `LayoutVerified`, and
+ /// returns the remaining bytes to the caller. If either the length or
+ /// alignment checks fail, it returns `None`.
///
/// If the checks succeed, then the prefix which is consumed will be
/// initialized to zero. This can be useful when re-using buffers to ensure
/// that sensitive data previously stored in the buffer is not leaked.
- #[inline(always)]
- pub fn new_from_prefix_zeroed(bytes: B) -> Option<(Ref<B, T>, B)> {
+ #[inline]
+ pub fn new_from_prefix_zeroed(bytes: B) -> Option<(LayoutVerified<B, T>, B)> {
map_prefix_tuple_zeroed(Self::new_from_prefix(bytes))
}
- /// Constructs a new `Ref` from the suffix of a byte slice, zeroing the
- /// suffix.
+ /// Constructs a new `LayoutVerified` from the suffix of a byte slice,
+ /// zeroing the suffix.
///
/// `new_from_suffix_zeroed` verifies that `bytes.len() >= size_of::<T>()`
/// and that the last `size_of::<T>()` bytes of `bytes` are aligned to
/// `align_of::<T>()`. It consumes the last `size_of::<T>()` bytes from
- /// `bytes` to construct a `Ref`, and returns the preceding bytes to the
- /// caller. If either the length or alignment checks fail, it returns
+ /// `bytes` to construct a `LayoutVerified`, and returns the preceding bytes
+ /// to the caller. If either the length or alignment checks fail, it returns
/// `None`.
///
/// If the checks succeed, then the suffix which is consumed will be
/// initialized to zero. This can be useful when re-using buffers to ensure
/// that sensitive data previously stored in the buffer is not leaked.
- #[inline(always)]
- pub fn new_from_suffix_zeroed(bytes: B) -> Option<(B, Ref<B, T>)> {
+ #[inline]
+ pub fn new_from_suffix_zeroed(bytes: B) -> Option<(B, LayoutVerified<B, T>)> {
map_suffix_tuple_zeroed(Self::new_from_suffix(bytes))
}
}
-impl<B, T> Ref<B, [T]>
+impl<B, T> LayoutVerified<B, [T]>
where
B: ByteSliceMut,
{
- /// Constructs a new `Ref` of a slice type after zeroing the bytes.
+ /// Constructs a new `LayoutVerified` of a slice type after zeroing the
+ /// bytes.
///
/// `new_slice_zeroed` verifies that `bytes.len()` is a multiple of
/// `size_of::<T>()` and that `bytes` is aligned to `align_of::<T>()`, and
- /// constructs a new `Ref`. If either of these checks fail, it returns
- /// `None`.
+ /// constructs a new `LayoutVerified`. If either of these checks fail, it
+ /// returns `None`.
///
/// If the checks succeed, then `bytes` will be initialized to zero. This
/// can be useful when re-using buffers to ensure that sensitive data
@@ -1883,20 +1055,20 @@ where
/// # Panics
///
/// `new_slice` panics if `T` is a zero-sized type.
- #[inline(always)]
- pub fn new_slice_zeroed(bytes: B) -> Option<Ref<B, [T]>> {
+ #[inline]
+ pub fn new_slice_zeroed(bytes: B) -> Option<LayoutVerified<B, [T]>> {
map_zeroed(Self::new_slice(bytes))
}
- /// Constructs a new `Ref` of a slice type from the prefix of a byte slice,
- /// after zeroing the bytes.
+ /// Constructs a new `LayoutVerified` of a slice type from the prefix of a
+ /// byte slice, after zeroing the bytes.
///
/// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::<T>() *
/// count` and that `bytes` is aligned to `align_of::<T>()`. It consumes the
- /// first `size_of::<T>() * count` bytes from `bytes` to construct a `Ref`,
- /// and returns the remaining bytes to the caller. It also ensures that
- /// `sizeof::<T>() * count` does not overflow a `usize`. If any of the
- /// length, alignment, or overflow checks fail, it returns `None`.
+ /// first `size_of::<T>() * count` bytes from `bytes` to construct a
+ /// `LayoutVerified`, and returns the remaining bytes to the caller. It also
+ /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. If any
+ /// of the length, alignment, or overflow checks fail, it returns `None`.
///
/// If the checks succeed, then the suffix which is consumed will be
/// initialized to zero. This can be useful when re-using buffers to ensure
@@ -1905,20 +1077,23 @@ where
/// # Panics
///
/// `new_slice_from_prefix_zeroed` panics if `T` is a zero-sized type.
- #[inline(always)]
- pub fn new_slice_from_prefix_zeroed(bytes: B, count: usize) -> Option<(Ref<B, [T]>, B)> {
+ #[inline]
+ pub fn new_slice_from_prefix_zeroed(
+ bytes: B,
+ count: usize,
+ ) -> Option<(LayoutVerified<B, [T]>, B)> {
map_prefix_tuple_zeroed(Self::new_slice_from_prefix(bytes, count))
}
- /// Constructs a new `Ref` of a slice type from the prefix of a byte slice,
- /// after zeroing the bytes.
+ /// Constructs a new `LayoutVerified` of a slice type from the prefix of a
+ /// byte slice, after zeroing the bytes.
///
/// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::<T>() *
/// count` and that `bytes` is aligned to `align_of::<T>()`. It consumes the
- /// last `size_of::<T>() * count` bytes from `bytes` to construct a `Ref`,
- /// and returns the preceding bytes to the caller. It also ensures that
- /// `sizeof::<T>() * count` does not overflow a `usize`. If any of the
- /// length, alignment, or overflow checks fail, it returns `None`.
+ /// last `size_of::<T>() * count` bytes from `bytes` to construct a
+ /// `LayoutVerified`, and returns the preceding bytes to the caller. It also
+ /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. If any
+ /// of the length, alignment, or overflow checks fail, it returns `None`.
///
/// If the checks succeed, then the consumed suffix will be initialized to
/// zero. This can be useful when re-using buffers to ensure that sensitive
@@ -1927,169 +1102,215 @@ where
/// # Panics
///
/// `new_slice_from_suffix_zeroed` panics if `T` is a zero-sized type.
- #[inline(always)]
- pub fn new_slice_from_suffix_zeroed(bytes: B, count: usize) -> Option<(B, Ref<B, [T]>)> {
+ #[inline]
+ pub fn new_slice_from_suffix_zeroed(
+ bytes: B,
+ count: usize,
+ ) -> Option<(B, LayoutVerified<B, [T]>)> {
map_suffix_tuple_zeroed(Self::new_slice_from_suffix(bytes, count))
}
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSlice,
T: Unaligned,
{
- /// Constructs a new `Ref` for a type with no alignment requirement.
+ /// Constructs a new `LayoutVerified` for a type with no alignment
+ /// requirement.
///
/// `new_unaligned` verifies that `bytes.len() == size_of::<T>()` and
- /// constructs a new `Ref`. If the check fails, it returns `None`.
- #[inline(always)]
- pub fn new_unaligned(bytes: B) -> Option<Ref<B, T>> {
- Ref::new(bytes)
+ /// constructs a new `LayoutVerified`. If the check fails, it returns
+ /// `None`.
+ #[inline]
+ pub fn new_unaligned(bytes: B) -> Option<LayoutVerified<B, T>> {
+ if bytes.len() != mem::size_of::<T>() {
+ return None;
+ }
+ Some(LayoutVerified(bytes, PhantomData))
}
- /// Constructs a new `Ref` from the prefix of a byte slice for a type with
- /// no alignment requirement.
+ /// Constructs a new `LayoutVerified` from the prefix of a byte slice for a
+ /// type with no alignment requirement.
///
/// `new_unaligned_from_prefix` verifies that `bytes.len() >=
/// size_of::<T>()`. It consumes the first `size_of::<T>()` bytes from
- /// `bytes` to construct a `Ref`, and returns the remaining bytes to the
- /// caller. If the length check fails, it returns `None`.
- #[inline(always)]
- pub fn new_unaligned_from_prefix(bytes: B) -> Option<(Ref<B, T>, B)> {
- Ref::new_from_prefix(bytes)
+ /// `bytes` to construct a `LayoutVerified`, and returns the remaining bytes
+ /// to the caller. If the length check fails, it returns `None`.
+ #[inline]
+ pub fn new_unaligned_from_prefix(bytes: B) -> Option<(LayoutVerified<B, T>, B)> {
+ if bytes.len() < mem::size_of::<T>() {
+ return None;
+ }
+ let (bytes, suffix) = bytes.split_at(mem::size_of::<T>());
+ Some((LayoutVerified(bytes, PhantomData), suffix))
}
- /// Constructs a new `Ref` from the suffix of a byte slice for a type with
- /// no alignment requirement.
+ /// Constructs a new `LayoutVerified` from the suffix of a byte slice for a
+ /// type with no alignment requirement.
///
/// `new_unaligned_from_suffix` verifies that `bytes.len() >=
/// size_of::<T>()`. It consumes the last `size_of::<T>()` bytes from
- /// `bytes` to construct a `Ref`, and returns the preceding bytes to the
- /// caller. If the length check fails, it returns `None`.
- #[inline(always)]
- pub fn new_unaligned_from_suffix(bytes: B) -> Option<(B, Ref<B, T>)> {
- Ref::new_from_suffix(bytes)
+ /// `bytes` to construct a `LayoutVerified`, and returns the preceding bytes
+ /// to the caller. If the length check fails, it returns `None`.
+ #[inline]
+ pub fn new_unaligned_from_suffix(bytes: B) -> Option<(B, LayoutVerified<B, T>)> {
+ let bytes_len = bytes.len();
+ if bytes_len < mem::size_of::<T>() {
+ return None;
+ }
+ let (prefix, bytes) = bytes.split_at(bytes_len - mem::size_of::<T>());
+ Some((prefix, LayoutVerified(bytes, PhantomData)))
}
}
-impl<B, T> Ref<B, [T]>
+impl<B, T> LayoutVerified<B, [T]>
where
B: ByteSlice,
T: Unaligned,
{
- /// Constructs a new `Ref` of a slice type with no alignment requirement.
+ /// Constructs a new `LayoutVerified` of a slice type with no alignment
+ /// requirement.
///
/// `new_slice_unaligned` verifies that `bytes.len()` is a multiple of
- /// `size_of::<T>()` and constructs a new `Ref`. If the check fails, it
- /// returns `None`.
+ /// `size_of::<T>()` and constructs a new `LayoutVerified`. If the check
+ /// fails, it returns `None`.
///
/// # Panics
///
/// `new_slice` panics if `T` is a zero-sized type.
- #[inline(always)]
- pub fn new_slice_unaligned(bytes: B) -> Option<Ref<B, [T]>> {
- Ref::new_slice(bytes)
+ #[inline]
+ pub fn new_slice_unaligned(bytes: B) -> Option<LayoutVerified<B, [T]>> {
+ assert_ne!(mem::size_of::<T>(), 0);
+ if bytes.len() % mem::size_of::<T>() != 0 {
+ return None;
+ }
+ Some(LayoutVerified(bytes, PhantomData))
}
- /// Constructs a new `Ref` of a slice type with no alignment requirement
- /// from the prefix of a byte slice.
+ /// Constructs a new `LayoutVerified` of a slice type with no alignment
+ /// requirement from the prefix of a byte slice.
///
/// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::<T>() *
/// count`. It consumes the first `size_of::<T>() * count` bytes from
- /// `bytes` to construct a `Ref`, and returns the remaining bytes to the
- /// caller. It also ensures that `sizeof::<T>() * count` does not overflow a
- /// `usize`. If either the length, or overflow checks fail, it returns
- /// `None`.
+ /// `bytes` to construct a `LayoutVerified`, and returns the remaining bytes
+ /// to the caller. It also ensures that `sizeof::<T>() * count` does not
+ /// overflow a `usize`. If either the length, or overflow checks fail, it
+ /// returns `None`.
///
/// # Panics
///
/// `new_slice_unaligned_from_prefix` panics if `T` is a zero-sized type.
- #[inline(always)]
- pub fn new_slice_unaligned_from_prefix(bytes: B, count: usize) -> Option<(Ref<B, [T]>, B)> {
- Ref::new_slice_from_prefix(bytes, count)
+ #[inline]
+ pub fn new_slice_unaligned_from_prefix(
+ bytes: B,
+ count: usize,
+ ) -> Option<(LayoutVerified<B, [T]>, B)> {
+ let expected_len = match mem::size_of::<T>().checked_mul(count) {
+ Some(len) => len,
+ None => return None,
+ };
+ if bytes.len() < expected_len {
+ return None;
+ }
+ let (prefix, bytes) = bytes.split_at(expected_len);
+ Self::new_slice_unaligned(prefix).map(move |l| (l, bytes))
}
- /// Constructs a new `Ref` of a slice type with no alignment requirement
- /// from the suffix of a byte slice.
+ /// Constructs a new `LayoutVerified` of a slice type with no alignment
+ /// requirement from the suffix of a byte slice.
///
/// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::<T>() *
/// count`. It consumes the last `size_of::<T>() * count` bytes from `bytes`
- /// to construct a `Ref`, and returns the remaining bytes to the caller. It
- /// also ensures that `sizeof::<T>() * count` does not overflow a `usize`.
- /// If either the length, or overflow checks fail, it returns `None`.
+ /// to construct a `LayoutVerified`, and returns the remaining bytes to the
+ /// caller. It also ensures that `sizeof::<T>() * count` does not overflow a
+ /// `usize`. If either the length, or overflow checks fail, it returns
+ /// `None`.
///
/// # Panics
///
/// `new_slice_unaligned_from_suffix` panics if `T` is a zero-sized type.
- #[inline(always)]
- pub fn new_slice_unaligned_from_suffix(bytes: B, count: usize) -> Option<(B, Ref<B, [T]>)> {
- Ref::new_slice_from_suffix(bytes, count)
+ #[inline]
+ pub fn new_slice_unaligned_from_suffix(
+ bytes: B,
+ count: usize,
+ ) -> Option<(B, LayoutVerified<B, [T]>)> {
+ let expected_len = match mem::size_of::<T>().checked_mul(count) {
+ Some(len) => len,
+ None => return None,
+ };
+ if bytes.len() < expected_len {
+ return None;
+ }
+ let (bytes, suffix) = bytes.split_at(expected_len);
+ Self::new_slice_unaligned(suffix).map(move |l| (bytes, l))
}
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSliceMut,
T: Unaligned,
{
- /// Constructs a new `Ref` for a type with no alignment requirement, zeroing
- /// the bytes.
+ /// Constructs a new `LayoutVerified` for a type with no alignment
+ /// requirement, zeroing the bytes.
///
/// `new_unaligned_zeroed` verifies that `bytes.len() == size_of::<T>()` and
- /// constructs a new `Ref`. If the check fails, it returns `None`.
+ /// constructs a new `LayoutVerified`. If the check fails, it returns
+ /// `None`.
///
/// If the check succeeds, then `bytes` will be initialized to zero. This
/// can be useful when re-using buffers to ensure that sensitive data
/// previously stored in the buffer is not leaked.
- #[inline(always)]
- pub fn new_unaligned_zeroed(bytes: B) -> Option<Ref<B, T>> {
+ #[inline]
+ pub fn new_unaligned_zeroed(bytes: B) -> Option<LayoutVerified<B, T>> {
map_zeroed(Self::new_unaligned(bytes))
}
- /// Constructs a new `Ref` from the prefix of a byte slice for a type with
- /// no alignment requirement, zeroing the prefix.
+ /// Constructs a new `LayoutVerified` from the prefix of a byte slice for a
+ /// type with no alignment requirement, zeroing the prefix.
///
/// `new_unaligned_from_prefix_zeroed` verifies that `bytes.len() >=
/// size_of::<T>()`. It consumes the first `size_of::<T>()` bytes from
- /// `bytes` to construct a `Ref`, and returns the remaining bytes to the
- /// caller. If the length check fails, it returns `None`.
+ /// `bytes` to construct a `LayoutVerified`, and returns the remaining bytes
+ /// to the caller. If the length check fails, it returns `None`.
///
/// If the check succeeds, then the prefix which is consumed will be
/// initialized to zero. This can be useful when re-using buffers to ensure
/// that sensitive data previously stored in the buffer is not leaked.
- #[inline(always)]
- pub fn new_unaligned_from_prefix_zeroed(bytes: B) -> Option<(Ref<B, T>, B)> {
+ #[inline]
+ pub fn new_unaligned_from_prefix_zeroed(bytes: B) -> Option<(LayoutVerified<B, T>, B)> {
map_prefix_tuple_zeroed(Self::new_unaligned_from_prefix(bytes))
}
- /// Constructs a new `Ref` from the suffix of a byte slice for a type with
- /// no alignment requirement, zeroing the suffix.
+ /// Constructs a new `LayoutVerified` from the suffix of a byte slice for a
+ /// type with no alignment requirement, zeroing the suffix.
///
/// `new_unaligned_from_suffix_zeroed` verifies that `bytes.len() >=
/// size_of::<T>()`. It consumes the last `size_of::<T>()` bytes from
- /// `bytes` to construct a `Ref`, and returns the preceding bytes to the
- /// caller. If the length check fails, it returns `None`.
+ /// `bytes` to construct a `LayoutVerified`, and returns the preceding bytes
+ /// to the caller. If the length check fails, it returns `None`.
///
/// If the check succeeds, then the suffix which is consumed will be
/// initialized to zero. This can be useful when re-using buffers to ensure
/// that sensitive data previously stored in the buffer is not leaked.
- #[inline(always)]
- pub fn new_unaligned_from_suffix_zeroed(bytes: B) -> Option<(B, Ref<B, T>)> {
+ #[inline]
+ pub fn new_unaligned_from_suffix_zeroed(bytes: B) -> Option<(B, LayoutVerified<B, T>)> {
map_suffix_tuple_zeroed(Self::new_unaligned_from_suffix(bytes))
}
}
-impl<B, T> Ref<B, [T]>
+impl<B, T> LayoutVerified<B, [T]>
where
B: ByteSliceMut,
T: Unaligned,
{
- /// Constructs a new `Ref` for a slice type with no alignment requirement,
- /// zeroing the bytes.
+ /// Constructs a new `LayoutVerified` for a slice type with no alignment
+ /// requirement, zeroing the bytes.
///
/// `new_slice_unaligned_zeroed` verifies that `bytes.len()` is a multiple
- /// of `size_of::<T>()` and constructs a new `Ref`. If the check fails, it
- /// returns `None`.
+ /// of `size_of::<T>()` and constructs a new `LayoutVerified`. If the check
+ /// fails, it returns `None`.
///
/// If the check succeeds, then `bytes` will be initialized to zero. This
/// can be useful when re-using buffers to ensure that sensitive data
@@ -2098,20 +1319,20 @@ where
/// # Panics
///
/// `new_slice` panics if `T` is a zero-sized type.
- #[inline(always)]
- pub fn new_slice_unaligned_zeroed(bytes: B) -> Option<Ref<B, [T]>> {
+ #[inline]
+ pub fn new_slice_unaligned_zeroed(bytes: B) -> Option<LayoutVerified<B, [T]>> {
map_zeroed(Self::new_slice_unaligned(bytes))
}
- /// Constructs a new `Ref` of a slice type with no alignment requirement
- /// from the prefix of a byte slice, after zeroing the bytes.
+ /// Constructs a new `LayoutVerified` of a slice type with no alignment
+ /// requirement from the prefix of a byte slice, after zeroing the bytes.
///
/// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::<T>() *
/// count`. It consumes the first `size_of::<T>() * count` bytes from
- /// `bytes` to construct a `Ref`, and returns the remaining bytes to the
- /// caller. It also ensures that `sizeof::<T>() * count` does not overflow a
- /// `usize`. If either the length, or overflow checks fail, it returns
- /// `None`.
+ /// `bytes` to construct a `LayoutVerified`, and returns the remaining bytes
+ /// to the caller. It also ensures that `sizeof::<T>() * count` does not
+ /// overflow a `usize`. If either the length, or overflow checks fail, it
+ /// returns `None`.
///
/// If the checks succeed, then the prefix will be initialized to zero. This
/// can be useful when re-using buffers to ensure that sensitive data
@@ -2121,22 +1342,23 @@ where
///
/// `new_slice_unaligned_from_prefix_zeroed` panics if `T` is a zero-sized
/// type.
- #[inline(always)]
+ #[inline]
pub fn new_slice_unaligned_from_prefix_zeroed(
bytes: B,
count: usize,
- ) -> Option<(Ref<B, [T]>, B)> {
+ ) -> Option<(LayoutVerified<B, [T]>, B)> {
map_prefix_tuple_zeroed(Self::new_slice_unaligned_from_prefix(bytes, count))
}
- /// Constructs a new `Ref` of a slice type with no alignment requirement
- /// from the suffix of a byte slice, after zeroing the bytes.
+ /// Constructs a new `LayoutVerified` of a slice type with no alignment
+ /// requirement from the suffix of a byte slice, after zeroing the bytes.
///
/// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::<T>() *
/// count`. It consumes the last `size_of::<T>() * count` bytes from `bytes`
- /// to construct a `Ref`, and returns the remaining bytes to the caller. It
- /// also ensures that `sizeof::<T>() * count` does not overflow a `usize`.
- /// If either the length, or overflow checks fail, it returns `None`.
+ /// to construct a `LayoutVerified`, and returns the remaining bytes to the
+ /// caller. It also ensures that `sizeof::<T>() * count` does not overflow a
+ /// `usize`. If either the length, or overflow checks fail, it returns
+ /// `None`.
///
/// If the checks succeed, then the suffix will be initialized to zero. This
/// can be useful when re-using buffers to ensure that sensitive data
@@ -2146,98 +1368,96 @@ where
///
/// `new_slice_unaligned_from_suffix_zeroed` panics if `T` is a zero-sized
/// type.
- #[inline(always)]
+ #[inline]
pub fn new_slice_unaligned_from_suffix_zeroed(
bytes: B,
count: usize,
- ) -> Option<(B, Ref<B, [T]>)> {
+ ) -> Option<(B, LayoutVerified<B, [T]>)> {
map_suffix_tuple_zeroed(Self::new_slice_unaligned_from_suffix(bytes, count))
}
}
-impl<'a, B, T> Ref<B, T>
+impl<'a, B, T> LayoutVerified<B, T>
where
B: 'a + ByteSlice,
T: FromBytes,
{
- /// Converts this `Ref` into a reference.
+ /// Converts this `LayoutVerified` into a reference.
///
- /// `into_ref` consumes the `Ref`, and returns a reference to `T`.
- #[inline(always)]
+ /// `into_ref` consumes the `LayoutVerified`, and returns a reference to
+ /// `T`.
pub fn into_ref(self) -> &'a T {
- // SAFETY: This is sound because `B` is guaranteed to live for the
- // lifetime `'a`, meaning that a) the returned reference cannot outlive
- // the `B` from which `self` was constructed and, b) no mutable methods
- // on that `B` can be called during the lifetime of the returned
- // reference. See the documentation on `deref_helper` for what
- // invariants we are required to uphold.
+ // NOTE: This is safe because `B` is guaranteed to live for the lifetime
+ // `'a`, meaning that a) the returned reference cannot outlive the `B`
+ // from which `self` was constructed and, b) no mutable methods on that
+ // `B` can be called during the lifetime of the returned reference. See
+ // the documentation on `deref_helper` for what invariants we are
+ // required to uphold.
unsafe { self.deref_helper() }
}
}
-impl<'a, B, T> Ref<B, T>
+impl<'a, B, T> LayoutVerified<B, T>
where
B: 'a + ByteSliceMut,
T: FromBytes + AsBytes,
{
- /// Converts this `Ref` into a mutable reference.
+ /// Converts this `LayoutVerified` into a mutable reference.
///
- /// `into_mut` consumes the `Ref`, and returns a mutable reference to `T`.
- #[inline(always)]
+ /// `into_mut` consumes the `LayoutVerified`, and returns a mutable
+ /// reference to `T`.
pub fn into_mut(mut self) -> &'a mut T {
- // SAFETY: This is sound because `B` is guaranteed to live for the
- // lifetime `'a`, meaning that a) the returned reference cannot outlive
- // the `B` from which `self` was constructed and, b) no other methods -
- // mutable or immutable - on that `B` can be called during the lifetime
- // of the returned reference. See the documentation on
- // `deref_mut_helper` for what invariants we are required to uphold.
+ // NOTE: This is safe because `B` is guaranteed to live for the lifetime
+ // `'a`, meaning that a) the returned reference cannot outlive the `B`
+ // from which `self` was constructed and, b) no other methods - mutable
+ // or immutable - on that `B` can be called during the lifetime of the
+ // returned reference. See the documentation on `deref_mut_helper` for
+ // what invariants we are required to uphold.
unsafe { self.deref_mut_helper() }
}
}
-impl<'a, B, T> Ref<B, [T]>
+impl<'a, B, T> LayoutVerified<B, [T]>
where
B: 'a + ByteSlice,
T: FromBytes,
{
- /// Converts this `Ref` into a slice reference.
+ /// Converts this `LayoutVerified` into a slice reference.
///
- /// `into_slice` consumes the `Ref`, and returns a reference to `[T]`.
- #[inline(always)]
+ /// `into_slice` consumes the `LayoutVerified`, and returns a reference to
+ /// `[T]`.
pub fn into_slice(self) -> &'a [T] {
- // SAFETY: This is sound because `B` is guaranteed to live for the
- // lifetime `'a`, meaning that a) the returned reference cannot outlive
- // the `B` from which `self` was constructed and, b) no mutable methods
- // on that `B` can be called during the lifetime of the returned
- // reference. See the documentation on `deref_slice_helper` for what
- // invariants we are required to uphold.
+ // NOTE: This is safe because `B` is guaranteed to live for the lifetime
+ // `'a`, meaning that a) the returned reference cannot outlive the `B`
+ // from which `self` was constructed and, b) no mutable methods on that
+ // `B` can be called during the lifetime of the returned reference. See
+ // the documentation on `deref_slice_helper` for what invariants we are
+ // required to uphold.
unsafe { self.deref_slice_helper() }
}
}
-impl<'a, B, T> Ref<B, [T]>
+impl<'a, B, T> LayoutVerified<B, [T]>
where
B: 'a + ByteSliceMut,
T: FromBytes + AsBytes,
{
- /// Converts this `Ref` into a mutable slice reference.
+ /// Converts this `LayoutVerified` into a mutable slice reference.
///
- /// `into_mut_slice` consumes the `Ref`, and returns a mutable reference to
- /// `[T]`.
- #[inline(always)]
+ /// `into_mut_slice` consumes the `LayoutVerified`, and returns a mutable
+ /// reference to `[T]`.
pub fn into_mut_slice(mut self) -> &'a mut [T] {
- // SAFETY: This is sound because `B` is guaranteed to live for the
- // lifetime `'a`, meaning that a) the returned reference cannot outlive
- // the `B` from which `self` was constructed and, b) no other methods -
- // mutable or immutable - on that `B` can be called during the lifetime
- // of the returned reference. See the documentation on
- // `deref_mut_slice_helper` for what invariants we are required to
- // uphold.
+ // NOTE: This is safe because `B` is guaranteed to live for the lifetime
+ // `'a`, meaning that a) the returned reference cannot outlive the `B`
+ // from which `self` was constructed and, b) no other methods - mutable
+ // or immutable - on that `B` can be called during the lifetime of the
+ // returned reference. See the documentation on `deref_mut_slice_helper`
+ // for what invariants we are required to uphold.
unsafe { self.deref_mut_slice_helper() }
}
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes,
@@ -2254,15 +1474,11 @@ where
/// and no mutable references to the same memory may be constructed during
/// `'a`.
unsafe fn deref_helper<'a>(&self) -> &'a T {
- // TODO(#61): Add a "SAFETY" comment and remove this `allow`.
- #[allow(clippy::undocumented_unsafe_blocks)]
- unsafe {
- &*self.0.as_ptr().cast::<T>()
- }
+ &*(self.0.as_ptr() as *const T)
}
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSliceMut,
T: FromBytes + AsBytes,
@@ -2279,15 +1495,11 @@ where
/// and no other references - mutable or immutable - to the same memory may
/// be constructed during `'a`.
unsafe fn deref_mut_helper<'a>(&mut self) -> &'a mut T {
- // TODO(#61): Add a "SAFETY" comment and remove this `allow`.
- #[allow(clippy::undocumented_unsafe_blocks)]
- unsafe {
- &mut *self.0.as_mut_ptr().cast::<T>()
- }
+ &mut *(self.0.as_mut_ptr() as *mut T)
}
}
-impl<B, T> Ref<B, [T]>
+impl<B, T> LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes,
@@ -2301,22 +1513,13 @@ where
let len = self.0.len();
let elem_size = mem::size_of::<T>();
debug_assert_ne!(elem_size, 0);
- // `Ref<_, [T]>` maintains the invariant that `size_of::<T>() > 0`.
- // Thus, neither the mod nor division operations here can panic.
- #[allow(clippy::arithmetic_side_effects)]
- let elems = {
- debug_assert_eq!(len % elem_size, 0);
- len / elem_size
- };
- // TODO(#61): Add a "SAFETY" comment and remove this `allow`.
- #[allow(clippy::undocumented_unsafe_blocks)]
- unsafe {
- slice::from_raw_parts(self.0.as_ptr().cast::<T>(), elems)
- }
+ debug_assert_eq!(len % elem_size, 0);
+ let elems = len / elem_size;
+ slice::from_raw_parts(self.0.as_ptr() as *const T, elems)
}
}
-impl<B, T> Ref<B, [T]>
+impl<B, T> LayoutVerified<B, [T]>
where
B: ByteSliceMut,
T: FromBytes + AsBytes,
@@ -2331,22 +1534,17 @@ where
let len = self.0.len();
let elem_size = mem::size_of::<T>();
debug_assert_ne!(elem_size, 0);
- // `Ref<_, [T]>` maintains the invariant that `size_of::<T>() > 0`.
- // Thus, neither the mod nor division operations here can panic.
- #[allow(clippy::arithmetic_side_effects)]
- let elems = {
- debug_assert_eq!(len % elem_size, 0);
- len / elem_size
- };
- // TODO(#61): Add a "SAFETY" comment and remove this `allow`.
- #[allow(clippy::undocumented_unsafe_blocks)]
- unsafe {
- slice::from_raw_parts_mut(self.0.as_mut_ptr().cast::<T>(), elems)
- }
+ debug_assert_eq!(len % elem_size, 0);
+ let elems = len / elem_size;
+ slice::from_raw_parts_mut(self.0.as_mut_ptr() as *mut T, elems)
}
}
-impl<B, T> Ref<B, T>
+fn aligned_to(bytes: &[u8], align: usize) -> bool {
+ (bytes as *const _ as *const () as usize) % align == 0
+}
+
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSlice,
T: ?Sized,
@@ -2358,7 +1556,7 @@ where
}
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSliceMut,
T: ?Sized,
@@ -2370,7 +1568,7 @@ where
}
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes,
@@ -2378,15 +1576,15 @@ where
/// Reads a copy of `T`.
#[inline]
pub fn read(&self) -> T {
- // SAFETY: Because of the invariants on `Ref`, we know that `self.0` is
- // at least `size_of::<T>()` bytes long, and that it is at least as
- // aligned as `align_of::<T>()`. Because `T: FromBytes`, it is sound to
- // interpret these bytes as a `T`.
- unsafe { ptr::read(self.0.as_ptr().cast::<T>()) }
+ // SAFETY: Because of the invariants on `LayoutVerified`, we know that
+ // `self.0` is at least `size_of::<T>()` bytes long, and that it is at
+ // least as aligned as `align_of::<T>()`. Because `T: FromBytes`, it is
+ // sound to interpret these bytes as a `T`.
+ unsafe { ptr::read(self.0.as_ptr() as *const T) }
}
}
-impl<B, T> Ref<B, T>
+impl<B, T> LayoutVerified<B, T>
where
B: ByteSliceMut,
T: AsBytes,
@@ -2394,16 +1592,16 @@ where
/// Writes the bytes of `t` and then forgets `t`.
#[inline]
pub fn write(&mut self, t: T) {
- // SAFETY: Because of the invariants on `Ref`, we know that `self.0` is
- // at least `size_of::<T>()` bytes long, and that it is at least as
- // aligned as `align_of::<T>()`. Writing `t` to the buffer will allow
- // all of the bytes of `t` to be accessed as a `[u8]`, but because `T:
- // AsBytes`, we know this is sound.
- unsafe { ptr::write(self.0.as_mut_ptr().cast::<T>(), t) }
+ // SAFETY: Because of the invariants on `LayoutVerified`, we know that
+ // `self.0` is at least `size_of::<T>()` bytes long, and that it is at
+ // least as aligned as `align_of::<T>()`. Writing `t` to the buffer will
+ // allow all of the bytes of `t` to be accessed as a `[u8]`, but because
+ // `T: AsBytes`, we know this is sound.
+ unsafe { ptr::write(self.0.as_mut_ptr() as *mut T, t) }
}
}
-impl<B, T> Deref for Ref<B, T>
+impl<B, T> Deref for LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes,
@@ -2411,7 +1609,7 @@ where
type Target = T;
#[inline]
fn deref(&self) -> &T {
- // SAFETY: This is sound because the lifetime of `self` is the same as
+ // SAFETY: This is safe because the lifetime of `self` is the same as
// the lifetime of the return value, meaning that a) the returned
// reference cannot outlive `self` and, b) no mutable methods on `self`
// can be called during the lifetime of the returned reference. See the
@@ -2421,14 +1619,14 @@ where
}
}
-impl<B, T> DerefMut for Ref<B, T>
+impl<B, T> DerefMut for LayoutVerified<B, T>
where
B: ByteSliceMut,
T: FromBytes + AsBytes,
{
#[inline]
fn deref_mut(&mut self) -> &mut T {
- // SAFETY: This is sound because the lifetime of `self` is the same as
+ // SAFETY: This is safe because the lifetime of `self` is the same as
// the lifetime of the return value, meaning that a) the returned
// reference cannot outlive `self` and, b) no other methods on `self`
// can be called during the lifetime of the returned reference. See the
@@ -2438,7 +1636,7 @@ where
}
}
-impl<B, T> Deref for Ref<B, [T]>
+impl<B, T> Deref for LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes,
@@ -2446,7 +1644,7 @@ where
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
- // SAFETY: This is sound because the lifetime of `self` is the same as
+ // SAFETY: This is safe because the lifetime of `self` is the same as
// the lifetime of the return value, meaning that a) the returned
// reference cannot outlive `self` and, b) no mutable methods on `self`
// can be called during the lifetime of the returned reference. See the
@@ -2456,14 +1654,14 @@ where
}
}
-impl<B, T> DerefMut for Ref<B, [T]>
+impl<B, T> DerefMut for LayoutVerified<B, [T]>
where
B: ByteSliceMut,
T: FromBytes + AsBytes,
{
#[inline]
fn deref_mut(&mut self) -> &mut [T] {
- // SAFETY: This is sound because the lifetime of `self` is the same as
+ // SAFETY: This is safe because the lifetime of `self` is the same as
// the lifetime of the return value, meaning that a) the returned
// reference cannot outlive `self` and, b) no other methods on `self`
// can be called during the lifetime of the returned reference. See the
@@ -2473,7 +1671,7 @@ where
}
}
-impl<T, B> Display for Ref<B, T>
+impl<T, B> Display for LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes + Display,
@@ -2485,7 +1683,7 @@ where
}
}
-impl<T, B> Display for Ref<B, [T]>
+impl<T, B> Display for LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes,
@@ -2498,7 +1696,7 @@ where
}
}
-impl<T, B> Debug for Ref<B, T>
+impl<T, B> Debug for LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes + Debug,
@@ -2506,11 +1704,11 @@ where
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
let inner: &T = self;
- fmt.debug_tuple("Ref").field(&inner).finish()
+ fmt.debug_tuple("LayoutVerified").field(&inner).finish()
}
}
-impl<T, B> Debug for Ref<B, [T]>
+impl<T, B> Debug for LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes + Debug,
@@ -2518,25 +1716,25 @@ where
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
let inner: &[T] = self;
- fmt.debug_tuple("Ref").field(&inner).finish()
+ fmt.debug_tuple("LayoutVerified").field(&inner).finish()
}
}
-impl<T, B> Eq for Ref<B, T>
+impl<T, B> Eq for LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes + Eq,
{
}
-impl<T, B> Eq for Ref<B, [T]>
+impl<T, B> Eq for LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes + Eq,
{
}
-impl<T, B> PartialEq for Ref<B, T>
+impl<T, B> PartialEq for LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes + PartialEq,
@@ -2547,7 +1745,7 @@ where
}
}
-impl<T, B> PartialEq for Ref<B, [T]>
+impl<T, B> PartialEq for LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes + PartialEq,
@@ -2558,7 +1756,7 @@ where
}
}
-impl<T, B> Ord for Ref<B, T>
+impl<T, B> Ord for LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes + Ord,
@@ -2571,7 +1769,7 @@ where
}
}
-impl<T, B> Ord for Ref<B, [T]>
+impl<T, B> Ord for LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes + Ord,
@@ -2584,7 +1782,7 @@ where
}
}
-impl<T, B> PartialOrd for Ref<B, T>
+impl<T, B> PartialOrd for LayoutVerified<B, T>
where
B: ByteSlice,
T: FromBytes + PartialOrd,
@@ -2597,7 +1795,7 @@ where
}
}
-impl<T, B> PartialOrd for Ref<B, [T]>
+impl<T, B> PartialOrd for LayoutVerified<B, [T]>
where
B: ByteSlice,
T: FromBytes + PartialOrd,
@@ -2611,8 +1809,13 @@ where
}
mod sealed {
- pub trait ByteSliceSealed {}
- pub trait KnownLayoutSealed {}
+ use core::cell::{Ref, RefMut};
+
+ pub trait Sealed {}
+ impl<'a> Sealed for &'a [u8] {}
+ impl<'a> Sealed for &'a mut [u8] {}
+ impl<'a> Sealed for Ref<'a, [u8]> {}
+ impl<'a> Sealed for RefMut<'a, [u8]> {}
}
// ByteSlice and ByteSliceMut abstract over [u8] references (&[u8], &mut [u8],
@@ -2624,7 +1827,6 @@ mod sealed {
// unsafe code. Thus, we seal them and implement it only for known-good
// reference types. For the same reason, they're unsafe traits.
-#[allow(clippy::missing_safety_doc)] // TODO(fxbug.dev/99068)
/// A mutable or immutable reference to a byte slice.
///
/// `ByteSlice` abstracts over the mutability of a byte slice reference, and is
@@ -2636,24 +1838,11 @@ mod sealed {
/// method would involve reallocation, and `split_at` must be a very cheap
/// operation in order for the utilities in this crate to perform as designed.
///
+/// [`Vec<u8>`]: std::vec::Vec
/// [`split_at`]: crate::ByteSlice::split_at
-// It may seem overkill to go to this length to ensure that this doc link never
-// breaks. We do this because it simplifies CI - it means that generating docs
-// always succeeds, so we don't need special logic to only generate docs under
-// certain features.
-#[cfg_attr(feature = "alloc", doc = "[`Vec<u8>`]: alloc::vec::Vec")]
-#[cfg_attr(
- not(feature = "alloc"),
- doc = "[`Vec<u8>`]: https://doc.rust-lang.org/std/vec/struct.Vec.html"
-)]
-pub unsafe trait ByteSlice:
- Deref<Target = [u8]> + Sized + self::sealed::ByteSliceSealed
-{
+pub unsafe trait ByteSlice: Deref<Target = [u8]> + Sized + self::sealed::Sealed {
/// Gets a raw pointer to the first byte in the slice.
- #[inline]
- fn as_ptr(&self) -> *const u8 {
- <[u8]>::as_ptr(self)
- }
+ fn as_ptr(&self) -> *const u8;
/// Splits the slice at the midpoint.
///
@@ -2665,7 +1854,6 @@ pub unsafe trait ByteSlice:
fn split_at(self, mid: usize) -> (Self, Self);
}
-#[allow(clippy::missing_safety_doc)] // TODO(fxbug.dev/99068)
/// A mutable reference to a byte slice.
///
/// `ByteSliceMut` abstracts over various ways of storing a mutable reference to
@@ -2673,65 +1861,62 @@ pub unsafe trait ByteSlice:
/// `RefMut<[u8]>`.
pub unsafe trait ByteSliceMut: ByteSlice + DerefMut {
/// Gets a mutable raw pointer to the first byte in the slice.
- #[inline]
- fn as_mut_ptr(&mut self) -> *mut u8 {
- <[u8]>::as_mut_ptr(self)
- }
+ fn as_mut_ptr(&mut self) -> *mut u8;
}
-impl<'a> sealed::ByteSliceSealed for &'a [u8] {}
-// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
-#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for &'a [u8] {
- #[inline]
+ fn as_ptr(&self) -> *const u8 {
+ <[u8]>::as_ptr(self)
+ }
fn split_at(self, mid: usize) -> (Self, Self) {
<[u8]>::split_at(self, mid)
}
}
-
-impl<'a> sealed::ByteSliceSealed for &'a mut [u8] {}
-// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
-#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for &'a mut [u8] {
- #[inline]
+ fn as_ptr(&self) -> *const u8 {
+ <[u8]>::as_ptr(self)
+ }
fn split_at(self, mid: usize) -> (Self, Self) {
<[u8]>::split_at_mut(self, mid)
}
}
-
-impl<'a> sealed::ByteSliceSealed for cell::Ref<'a, [u8]> {}
-// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
-#[allow(clippy::undocumented_unsafe_blocks)]
-unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
- #[inline]
+unsafe impl<'a> ByteSlice for Ref<'a, [u8]> {
+ fn as_ptr(&self) -> *const u8 {
+ <[u8]>::as_ptr(self)
+ }
fn split_at(self, mid: usize) -> (Self, Self) {
- cell::Ref::map_split(self, |slice| <[u8]>::split_at(slice, mid))
+ Ref::map_split(self, |slice| <[u8]>::split_at(slice, mid))
}
}
-
-impl<'a> sealed::ByteSliceSealed for RefMut<'a, [u8]> {}
-// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
-#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> {
- #[inline]
+ fn as_ptr(&self) -> *const u8 {
+ <[u8]>::as_ptr(self)
+ }
fn split_at(self, mid: usize) -> (Self, Self) {
RefMut::map_split(self, |slice| <[u8]>::split_at_mut(slice, mid))
}
}
-// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
-#[allow(clippy::undocumented_unsafe_blocks)]
-unsafe impl<'a> ByteSliceMut for &'a mut [u8] {}
-
-// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
-#[allow(clippy::undocumented_unsafe_blocks)]
-unsafe impl<'a> ByteSliceMut for RefMut<'a, [u8]> {}
+unsafe impl<'a> ByteSliceMut for &'a mut [u8] {
+ fn as_mut_ptr(&mut self) -> *mut u8 {
+ <[u8]>::as_mut_ptr(self)
+ }
+}
+unsafe impl<'a> ByteSliceMut for RefMut<'a, [u8]> {
+ fn as_mut_ptr(&mut self) -> *mut u8 {
+ <[u8]>::as_mut_ptr(self)
+ }
+}
-#[cfg(feature = "alloc")]
+#[cfg(any(test, feature = "alloc"))]
mod alloc_support {
- use alloc::vec::Vec;
-
- use super::*;
+ pub(crate) extern crate alloc;
+ pub(crate) use super::*;
+ pub(crate) use alloc::alloc::Layout;
+ pub(crate) use alloc::boxed::Box;
+ pub(crate) use alloc::vec::Vec;
+ pub(crate) use core::mem::{align_of, size_of};
+ pub(crate) use core::ptr::NonNull;
/// Extends a `Vec<T>` by pushing `additional` new items onto the end of the
/// vector. The new items are initialized with zeroes.
@@ -2739,8 +1924,7 @@ mod alloc_support {
/// # Panics
///
/// Panics if `Vec::reserve(additional)` fails to reserve enough memory.
- #[inline(always)]
- pub fn extend_vec_zeroed<T: FromZeroes>(v: &mut Vec<T>, additional: usize) {
+ pub fn extend_vec_zeroed<T: FromBytes>(v: &mut Vec<T>, additional: usize) {
insert_vec_zeroed(v, v.len(), additional);
}
@@ -2751,198 +1935,27 @@ mod alloc_support {
///
/// * Panics if `position > v.len()`.
/// * Panics if `Vec::reserve(additional)` fails to reserve enough memory.
- #[inline]
- pub fn insert_vec_zeroed<T: FromZeroes>(v: &mut Vec<T>, position: usize, additional: usize) {
+ pub fn insert_vec_zeroed<T: FromBytes>(v: &mut Vec<T>, position: usize, additional: usize) {
assert!(position <= v.len());
v.reserve(additional);
- // SAFETY: The `reserve` call guarantees that these cannot overflow:
+ // The 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()`.
+ // position <= v.len().
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);
}
}
-
- #[cfg(test)]
- mod tests {
- use super::*;
-
- #[test]
- fn test_extend_vec_zeroed() {
- // Test extending when there is an existing allocation.
- let mut v = vec![100u64, 200, 300];
- extend_vec_zeroed(&mut v, 3);
- 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();
- extend_vec_zeroed(&mut v, 3);
- assert_eq!(v.len(), 3);
- assert_eq!(&*v, &[0, 0, 0]);
- drop(v);
- }
-
- #[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);
- 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);
- assert_eq!(&*v, &[(), (), ()]);
- drop(v);
- }
-
- #[test]
- fn test_insert_vec_zeroed() {
- // Insert at start (no existing allocation).
- let mut v: Vec<u64> = Vec::new();
- insert_vec_zeroed(&mut v, 0, 2);
- assert_eq!(v.len(), 2);
- assert_eq!(&*v, &[0, 0]);
- drop(v);
-
- // Insert at start.
- let mut v = vec![100u64, 200, 300];
- insert_vec_zeroed(&mut v, 0, 2);
- 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];
- insert_vec_zeroed(&mut v, 1, 1);
- assert_eq!(v.len(), 4);
- assert_eq!(&*v, &[100, 0, 200, 300]);
- drop(v);
-
- // Insert at end.
- let mut v = vec![100u64, 200, 300];
- insert_vec_zeroed(&mut v, 3, 1);
- assert_eq!(v.len(), 4);
- assert_eq!(&*v, &[100, 200, 300, 0]);
- drop(v);
- }
-
- #[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);
- assert_eq!(v.len(), 2);
- assert_eq!(&*v, &[(), ()]);
- drop(v);
-
- // Insert at start.
- let mut v = vec![(), (), ()];
- insert_vec_zeroed(&mut v, 0, 2);
- assert_eq!(v.len(), 5);
- assert_eq!(&*v, &[(), (), (), (), ()]);
- drop(v);
-
- // Insert at middle.
- let mut v = vec![(), (), ()];
- insert_vec_zeroed(&mut v, 1, 1);
- assert_eq!(v.len(), 4);
- assert_eq!(&*v, &[(), (), (), ()]);
- drop(v);
-
- // Insert at end.
- let mut v = vec![(), (), ()];
- insert_vec_zeroed(&mut v, 3, 1);
- assert_eq!(v.len(), 4);
- assert_eq!(&*v, &[(), (), (), ()]);
- drop(v);
- }
-
- #[test]
- fn test_new_box_zeroed() {
- assert_eq!(*u64::new_box_zeroed(), 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(), ());
- }
- }
-
- #[test]
- fn test_new_box_slice_zeroed() {
- let mut s: Box<[u64]> = u64::new_box_slice_zeroed(3);
- 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_slice_zeroed_empty() {
- let s: Box<[u64]> = u64::new_box_slice_zeroed(0);
- assert_eq!(s.len(), 0);
- }
-
- #[test]
- fn test_new_box_slice_zeroed_zst() {
- let mut s: Box<[()]> = <()>::new_box_slice_zeroed(3);
- 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_slice_zeroed_zst_empty() {
- let s: Box<[()]> = <()>::new_box_slice_zeroed(0);
- assert_eq!(s.len(), 0);
- }
-
- #[test]
- #[should_panic(expected = "mem::size_of::<Self>() * len overflows `usize`")]
- fn test_new_box_slice_zeroed_panics_mul_overflow() {
- let _ = u16::new_box_slice_zeroed(usize::MAX);
- }
-
- #[test]
- #[should_panic(expected = "assertion failed: size <= max_alloc")]
- fn test_new_box_slice_zeroed_panics_isize_overflow() {
- let max = usize::try_from(isize::MAX).unwrap();
- let _ = u16::new_box_slice_zeroed((max / mem::size_of::<u16>()) + 1);
- }
- }
}
-#[cfg(feature = "alloc")]
+#[cfg(any(test, feature = "alloc"))]
#[doc(inline)]
pub use alloc_support::*;
@@ -2952,295 +1965,25 @@ mod tests {
use core::ops::Deref;
- 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, FromZeroes, FromBytes, AsBytes, Unaligned)]
- #[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) }
- }
- }
- // 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() {
- fn layout(
- base_size: usize,
- align: usize,
- _trailing_slice_elem_size: Option<usize>,
- ) -> DstLayout {
- DstLayout {
- _base_layout: Layout::from_size_align(base_size, align).unwrap(),
- _trailing_slice_elem_size,
- }
- }
-
- /// This macro accepts arguments in the form of:
- ///
- /// layout(_, _, _).validate(_, _, _), Ok(Some((_, _)))
- /// | | | | | | | |
- /// base_size ----+ | | | | | | |
- /// align -----------+ | | | | | |
- /// trailing_size ------+ | | | | |
- /// 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
- /// `assert_matches!` to validate the computed result for each
- /// combination of input values.
- /// - If it is `Err(msg)`, then `test!` validates that the call to
- /// `validate_cast_and_convert_metadata` panics with the given panic
- /// message.
- ///
- /// 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($base_size:tt, $align:tt, $trailing_size:tt)
- .validate($addr:tt, $bytes_len:tt, $cast_type:tt), $expect:pat $(,)?
- ) => {
- itertools::iproduct!(
- test!(@generate_usize $base_size),
- test!(@generate_align $align),
- test!(@generate_opt_usize $trailing_size),
- test!(@generate_usize $addr),
- test!(@generate_usize $bytes_len),
- test!(@generate_cast_type $cast_type)
- ).for_each(|(base_size, align, trailing_size, addr, bytes_len, cast_type)| {
- let actual = std::panic::catch_unwind(|| {
- layout(base_size, align, trailing_size)._validate_cast_and_convert_metadata(addr, bytes_len, cast_type)
- }).map_err(|d| {
- *d.downcast::<&'static str>().expect("expected string panic message").as_ref()
- });
- assert_matches::assert_matches!(
- actual, $expect,
- "layout({base_size}, {align}, {trailing_size:?}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?})",
- );
- });
- };
- (@generate_usize _) => { 0..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; 5] = [0, 2, 4, 6, 8];
- const NZ_EVENS: [usize; 5] = [2, 4, 6, 8, 10];
- const ODDS: [usize; 5] = [1, 3, 5, 7, 9];
-
- // base_size is too big for the memory region.
- test!(layout((1..8), _, ((1..8).map(Some))).validate(_, [0], _), Ok(None));
- test!(layout((2..8), _, ((1..8).map(Some))).validate(_, [1], _), Ok(None));
-
- // addr is unaligned for prefix cast
- test!(layout(_, [2], [None]).validate(ODDS, _, _Prefix), Ok(None));
- test!(layout(_, [2], (NZ_EVENS.map(Some))).validate(ODDS, _, _Prefix), Ok(None));
-
- // addr is aligned, but end of buffer is unaligned for suffix cast
- test!(layout(_, [2], [None]).validate(EVENS, ODDS, _Suffix), Ok(None));
- test!(layout(_, [2], (NZ_EVENS.map(Some))).validate(EVENS, ODDS, _Suffix), Ok(None));
-
- // 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(_, _, [Some(0)]).validate(_, _, _), Err(msgs::TRAILING),);
-
- // addr + bytes_len must not overflow usize
- test!(
- layout(_, [1], (NZ_EVENS.map(Some))).validate([usize::MAX], (1..100), _),
- Err(msgs::OVERFLOW)
- );
- test!(layout(_, [1], [None]).validate((1..100), [usize::MAX], _), Err(msgs::OVERFLOW));
- test!(
- layout([1], [1], [None]).validate(
- [usize::MAX / 2 + 1, usize::MAX],
- [usize::MAX / 2 + 1, usize::MAX],
- _
- ),
- Err(msgs::OVERFLOW)
- );
-
- // 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 Some((elems, split_at)) =
- layout._validate_cast_and_convert_metadata(addr, bytes_len, cast_type)
- {
- let (base_size, align, trailing_elem_size) = (
- layout._base_layout.size(),
- layout._base_layout.align(),
- layout._trailing_slice_elem_size,
- );
-
- let debug_str = format!(
- "layout({base_size}, {align}, {trailing_elem_size:?}).validate_cast_and_convert_metadata({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.
- assert!(!(trailing_elem_size.is_none() && elems != 0), "{}", debug_str);
-
- let resulting_size = base_size + (elems * trailing_elem_size.unwrap_or(0));
- // Test that, for unsized types, `validate_cast_and_convert_metadata` computed the
- // largest possible value that fits in the given byte range.
- assert!(
- trailing_elem_size
- .map(|elem_size| resulting_size + elem_size > bytes_len)
- .unwrap_or(true),
- "{}",
- debug_str
- );
-
- // Test safety postconditions guaranteed by `validate_cast_and_convert_metadata`.
- assert!(resulting_size <= bytes_len);
- 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);
- }
- }
- }
- }
-
- let layouts = itertools::iproduct!(0..8, [1, 2, 4, 8], (1..8).map(Some).chain([None]))
- .filter(|(size, align, trailing_elem_size)| {
- size % align == 0 && trailing_elem_size.unwrap_or(*align) % align == 0
- })
- .map(|(s, a, t)| layout(s, a, t));
- itertools::iproduct!(layouts, 0..8, 0..8, [_CastType::_Prefix, _CastType::_Suffix])
- .for_each(validate_behavior);
+ // B should be [u8; N]. T will require that the entire structure is aligned
+ // to the alignment of T.
+ #[derive(Default)]
+ struct AlignedBuffer<T, B> {
+ buf: B,
+ _t: T,
}
- #[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);
- };
+ impl<T, B: Default> AlignedBuffer<T, B> {
+ fn clear_buf(&mut self) {
+ self.buf = B::default();
}
-
- let layout = |base_size, align, _trailing_slice_elem_size| DstLayout {
- _base_layout: Layout::from_size_align(base_size, align).unwrap(),
- _trailing_slice_elem_size,
- };
-
- test!((), layout(0, 1, None));
- test!(u8, layout(1, 1, None));
- // Use `align_of` because `u64` alignment may be smaller than 8 on some
- // platforms.
- test!(u64, layout(8, mem::align_of::<u64>(), None));
- test!(AU64, layout(8, 8, None));
-
- test!(Option<&'static ()>, usize::LAYOUT);
-
- test!([()], layout(0, 1, Some(0)));
- test!([u8], layout(0, 1, Some(1)));
- test!(str, layout(0, 1, Some(1)));
- }
-
- #[test]
- fn test_object_safety() {
- fn _takes_from_zeroes(_: &dyn FromZeroes) {}
- fn _takes_from_bytes(_: &dyn FromBytes) {}
- fn _takes_unaligned(_: &dyn Unaligned) {}
}
- #[test]
- fn test_from_zeroes_only() {
- // Test types that implement `FromZeroes` but not `FromBytes`.
-
- assert!(!bool::new_zeroed());
- assert_eq!(char::new_zeroed(), '\0');
-
- #[cfg(feature = "alloc")]
- {
- assert_eq!(bool::new_box_zeroed(), Box::new(false));
- assert_eq!(char::new_box_zeroed(), Box::new('\0'));
-
- assert_eq!(bool::new_box_slice_zeroed(3).as_ref(), [false, false, false]);
- assert_eq!(char::new_box_slice_zeroed(3).as_ref(), ['\0', '\0', '\0']);
-
- assert_eq!(bool::new_vec_zeroed(3).as_ref(), [false, false, false]);
- assert_eq!(char::new_vec_zeroed(3).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");
+ // convert a u64 to bytes using this platform's endianness
+ fn u64_to_bytes(u: u64) -> [u8; 8] {
+ unsafe { ptr::read(&u as *const u64 as *const [u8; 8]) }
}
#[test]
@@ -3251,7 +1994,7 @@ mod tests {
#[cfg(target_endian = "little")]
const VAL_BYTES: [u8; 8] = VAL.to_le_bytes();
- // Test `FromBytes::{read_from, read_from_prefix, read_from_suffix}`.
+ // Test FromBytes::{read_from, read_from_prefix, read_from_suffix}
assert_eq!(u64::read_from(&VAL_BYTES[..]), Some(VAL));
// The first 8 bytes are from `VAL_BYTES` and the second 8 bytes are all
@@ -3265,7 +2008,7 @@ mod tests {
assert_eq!(u64::read_from_prefix(&bytes_with_suffix[..]), Some(0));
assert_eq!(u64::read_from_suffix(&bytes_with_suffix[..]), Some(VAL));
- // Test `AsBytes::{write_to, write_to_prefix, write_to_suffix}`.
+ // Test AsBytes::{write_to, write_to_prefix, write_to_suffix}
let mut bytes = [0u8; 8];
assert_eq!(VAL.write_to(&mut bytes[..]), Some(()));
@@ -3301,608 +2044,595 @@ mod tests {
}
}
let _: () = transmute!(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);
}
#[test]
fn test_address() {
- // Test that the `Deref` and `DerefMut` implementations return a
- // reference which points to the right region of memory.
+ // test that the Deref and DerefMut implementations return a reference
+ // which points to the right region of memory
let buf = [0];
- let r = Ref::<_, u8>::new(&buf[..]).unwrap();
+ let lv = LayoutVerified::<_, u8>::new(&buf[..]).unwrap();
let buf_ptr = buf.as_ptr();
- let deref_ptr: *const u8 = r.deref();
+ let deref_ptr = lv.deref() as *const u8;
assert_eq!(buf_ptr, deref_ptr);
let buf = [0];
- let r = Ref::<_, [u8]>::new_slice(&buf[..]).unwrap();
+ let lv = LayoutVerified::<_, [u8]>::new_slice(&buf[..]).unwrap();
let buf_ptr = buf.as_ptr();
- let deref_ptr = r.deref().as_ptr();
+ let deref_ptr = lv.deref().as_ptr();
assert_eq!(buf_ptr, deref_ptr);
}
- // Verify that values written to a `Ref` are properly shared between the
- // typed and untyped representations, that reads via `deref` and `read`
- // behave the same, and that writes via `deref_mut` and `write` behave the
- // same.
- fn test_new_helper(mut r: Ref<&mut [u8], AU64>) {
+ // verify that values written to a LayoutVerified are properly shared
+ // between the typed and untyped representations, that reads via `deref` and
+ // `read` behave the same, and that writes via `deref_mut` and `write`
+ // behave the same
+ fn test_new_helper<'a>(mut lv: LayoutVerified<&'a mut [u8], u64>) {
// assert that the value starts at 0
- assert_eq!(*r, AU64(0));
- assert_eq!(r.read(), AU64(0));
-
- // Assert that values written to the typed value are reflected in the
- // byte slice.
- const VAL1: AU64 = AU64(0xFF00FF00FF00FF00);
- *r = VAL1;
- assert_eq!(r.bytes(), &VAL1.to_bytes());
- *r = AU64(0);
- r.write(VAL1);
- assert_eq!(r.bytes(), &VAL1.to_bytes());
-
- // Assert that values written to the byte slice are reflected in the
- // typed value.
- const VAL2: AU64 = AU64(!VAL1.0); // different from `VAL1`
- r.bytes_mut().copy_from_slice(&VAL2.to_bytes()[..]);
- assert_eq!(*r, VAL2);
- assert_eq!(r.read(), VAL2);
- }
-
- // Verify that values written to a `Ref` are properly shared between the
- // typed and untyped representations; pass a value with `typed_len` `AU64`s
- // backed by an array of `typed_len * 8` bytes.
- fn test_new_helper_slice(mut r: Ref<&mut [u8], [AU64]>, typed_len: usize) {
- // Assert that the value starts out zeroed.
- assert_eq!(&*r, vec![AU64(0); typed_len].as_slice());
-
- // Check the backing storage is the exact same slice.
+ assert_eq!(*lv, 0);
+ assert_eq!(lv.read(), 0);
+
+ // assert that values written to the typed value are reflected in the
+ // byte slice
+ const VAL1: u64 = 0xFF00FF00FF00FF00;
+ *lv = VAL1;
+ assert_eq!(lv.bytes(), &u64_to_bytes(VAL1));
+ *lv = 0;
+ lv.write(VAL1);
+ assert_eq!(lv.bytes(), &u64_to_bytes(VAL1));
+
+ // assert that values written to the byte slice are reflected in the
+ // typed value
+ const VAL2: u64 = !VAL1; // different from VAL1
+ lv.bytes_mut().copy_from_slice(&u64_to_bytes(VAL2)[..]);
+ assert_eq!(*lv, VAL2);
+ assert_eq!(lv.read(), VAL2);
+ }
+
+ // verify that values written to a LayoutVerified are properly shared
+ // between the typed and untyped representations; pass a value with
+ // `typed_len` `u64`s backed by an array of `typed_len * 8` bytes.
+ fn test_new_helper_slice<'a>(mut lv: LayoutVerified<&'a mut [u8], [u64]>, typed_len: usize) {
+ // assert that the value starts out zeroed
+ assert_eq!(&*lv, vec![0; typed_len].as_slice());
+
+ // check the backing storage is the exact same slice
let untyped_len = typed_len * 8;
- assert_eq!(r.bytes().len(), untyped_len);
- assert_eq!(r.bytes().as_ptr(), r.as_ptr().cast::<u8>());
+ assert_eq!(lv.bytes().len(), untyped_len);
+ assert_eq!(lv.bytes().as_ptr(), lv.as_ptr() as *const u8);
- // Assert that values written to the typed value are reflected in the
- // byte slice.
- const VAL1: AU64 = AU64(0xFF00FF00FF00FF00);
- for typed in &mut *r {
+ // assert that values written to the typed value are reflected in the
+ // byte slice
+ const VAL1: u64 = 0xFF00FF00FF00FF00;
+ for typed in &mut *lv {
*typed = VAL1;
}
- assert_eq!(r.bytes(), VAL1.0.to_ne_bytes().repeat(typed_len).as_slice());
+ assert_eq!(lv.bytes(), VAL1.to_ne_bytes().repeat(typed_len).as_slice());
- // Assert that values written to the byte slice are reflected in the
- // typed value.
- const VAL2: AU64 = AU64(!VAL1.0); // different from VAL1
- r.bytes_mut().copy_from_slice(&VAL2.0.to_ne_bytes().repeat(typed_len));
- assert!(r.iter().copied().all(|x| x == VAL2));
+ // assert that values written to the byte slice are reflected in the
+ // typed value
+ const VAL2: u64 = !VAL1; // different from VAL1
+ lv.bytes_mut().copy_from_slice(&VAL2.to_ne_bytes().repeat(typed_len));
+ assert!(lv.iter().copied().all(|x| x == VAL2));
}
- // Verify that values written to a `Ref` are properly shared between the
- // typed and untyped representations, that reads via `deref` and `read`
- // behave the same, and that writes via `deref_mut` and `write` behave the
- // same.
- fn test_new_helper_unaligned(mut r: Ref<&mut [u8], [u8; 8]>) {
+ // verify that values written to a LayoutVerified are properly shared
+ // between the typed and untyped representations, that reads via `deref` and
+ // `read` behave the same, and that writes via `deref_mut` and `write`
+ // behave the same
+ fn test_new_helper_unaligned<'a>(mut lv: LayoutVerified<&'a mut [u8], [u8; 8]>) {
// assert that the value starts at 0
- assert_eq!(*r, [0; 8]);
- assert_eq!(r.read(), [0; 8]);
+ assert_eq!(*lv, [0; 8]);
+ assert_eq!(lv.read(), [0; 8]);
- // Assert that values written to the typed value are reflected in the
- // byte slice.
+ // assert that values written to the typed value are reflected in the
+ // byte slice
const VAL1: [u8; 8] = [0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00];
- *r = VAL1;
- assert_eq!(r.bytes(), &VAL1);
- *r = [0; 8];
- r.write(VAL1);
- assert_eq!(r.bytes(), &VAL1);
-
- // Assert that values written to the byte slice are reflected in the
- // typed value.
+ *lv = VAL1;
+ assert_eq!(lv.bytes(), &VAL1);
+ *lv = [0; 8];
+ lv.write(VAL1);
+ assert_eq!(lv.bytes(), &VAL1);
+
+ // assert that values written to the byte slice are reflected in the
+ // typed value
const VAL2: [u8; 8] = [0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF]; // different from VAL1
- r.bytes_mut().copy_from_slice(&VAL2[..]);
- assert_eq!(*r, VAL2);
- assert_eq!(r.read(), VAL2);
+ lv.bytes_mut().copy_from_slice(&VAL2[..]);
+ assert_eq!(*lv, VAL2);
+ assert_eq!(lv.read(), VAL2);
}
- // Verify that values written to a `Ref` are properly shared between the
- // typed and untyped representations; pass a value with `len` `u8`s backed
- // by an array of `len` bytes.
- fn test_new_helper_slice_unaligned(mut r: Ref<&mut [u8], [u8]>, len: usize) {
- // Assert that the value starts out zeroed.
- assert_eq!(&*r, vec![0u8; len].as_slice());
+ // verify that values written to a LayoutVerified are properly shared
+ // between the typed and untyped representations; pass a value with
+ // `len` `u8`s backed by an array of `len` bytes.
+ fn test_new_helper_slice_unaligned<'a>(mut lv: LayoutVerified<&'a mut [u8], [u8]>, len: usize) {
+ // assert that the value starts out zeroed
+ assert_eq!(&*lv, vec![0u8; len].as_slice());
- // Check the backing storage is the exact same slice.
- assert_eq!(r.bytes().len(), len);
- assert_eq!(r.bytes().as_ptr(), r.as_ptr());
+ // check the backing storage is the exact same slice
+ assert_eq!(lv.bytes().len(), len);
+ assert_eq!(lv.bytes().as_ptr(), lv.as_ptr());
- // Assert that values written to the typed value are reflected in the
- // byte slice.
+ // assert that values written to the typed value are reflected in the
+ // byte slice
let mut expected_bytes = [0xFF, 0x00].iter().copied().cycle().take(len).collect::<Vec<_>>();
- r.copy_from_slice(&expected_bytes);
- assert_eq!(r.bytes(), expected_bytes.as_slice());
+ lv.copy_from_slice(&expected_bytes);
+ assert_eq!(lv.bytes(), expected_bytes.as_slice());
- // Assert that values written to the byte slice are reflected in the
- // typed value.
+ // assert that values written to the byte slice are reflected in the
+ // typed value
for byte in &mut expected_bytes {
- *byte = !*byte; // different from `expected_len`
+ *byte = !*byte; // different from expected_len
}
- r.bytes_mut().copy_from_slice(&expected_bytes);
- assert_eq!(&*r, expected_bytes.as_slice());
+ lv.bytes_mut().copy_from_slice(&expected_bytes);
+ assert_eq!(&*lv, expected_bytes.as_slice());
}
#[test]
fn test_new_aligned_sized() {
// Test that a properly-aligned, properly-sized buffer works for new,
- // new_from_prefix, and new_from_suffix, and that new_from_prefix and
+ // new_from_preifx, and new_from_suffix, and that new_from_prefix and
// new_from_suffix return empty slices. Test that a properly-aligned
// buffer whose length is a multiple of the element size works for
// new_slice. Test that xxx_zeroed behaves the same, and zeroes the
// memory.
- // A buffer with an alignment of 8.
- let mut buf = Align::<[u8; 8], AU64>::default();
- // `buf.t` should be aligned to 8, so this should always succeed.
- test_new_helper(Ref::<_, AU64>::new(&mut buf.t[..]).unwrap());
- buf.t = [0xFFu8; 8];
- test_new_helper(Ref::<_, AU64>::new_zeroed(&mut buf.t[..]).unwrap());
+ // a buffer with an alignment of 8
+ let mut buf = AlignedBuffer::<u64, [u8; 8]>::default();
+ // buf.buf should be aligned to 8, so this should always succeed
+ test_new_helper(LayoutVerified::<_, u64>::new(&mut buf.buf[..]).unwrap());
+ buf.buf = [0xFFu8; 8];
+ test_new_helper(LayoutVerified::<_, u64>::new_zeroed(&mut buf.buf[..]).unwrap());
{
- // In a block so that `r` and `suffix` don't live too long.
- buf.set_default();
- let (r, suffix) = Ref::<_, AU64>::new_from_prefix(&mut buf.t[..]).unwrap();
+ // in a block so that lv and suffix don't live too long
+ buf.clear_buf();
+ let (lv, suffix) = LayoutVerified::<_, u64>::new_from_prefix(&mut buf.buf[..]).unwrap();
assert!(suffix.is_empty());
- test_new_helper(r);
+ test_new_helper(lv);
}
{
- buf.t = [0xFFu8; 8];
- let (r, suffix) = Ref::<_, AU64>::new_from_prefix_zeroed(&mut buf.t[..]).unwrap();
+ buf.buf = [0xFFu8; 8];
+ let (lv, suffix) =
+ LayoutVerified::<_, u64>::new_from_prefix_zeroed(&mut buf.buf[..]).unwrap();
assert!(suffix.is_empty());
- test_new_helper(r);
+ test_new_helper(lv);
}
{
- buf.set_default();
- let (prefix, r) = Ref::<_, AU64>::new_from_suffix(&mut buf.t[..]).unwrap();
+ buf.clear_buf();
+ let (prefix, lv) = LayoutVerified::<_, u64>::new_from_suffix(&mut buf.buf[..]).unwrap();
assert!(prefix.is_empty());
- test_new_helper(r);
+ test_new_helper(lv);
}
{
- buf.t = [0xFFu8; 8];
- let (prefix, r) = Ref::<_, AU64>::new_from_suffix_zeroed(&mut buf.t[..]).unwrap();
+ buf.buf = [0xFFu8; 8];
+ let (prefix, lv) =
+ LayoutVerified::<_, u64>::new_from_suffix_zeroed(&mut buf.buf[..]).unwrap();
assert!(prefix.is_empty());
- test_new_helper(r);
- }
-
- // A buffer with alignment 8 and length 16.
- let mut buf = Align::<[u8; 16], AU64>::default();
- // `buf.t` should be aligned to 8 and have a length which is a multiple
- // of `size_of::<AU64>()`, so this should always succeed.
- test_new_helper_slice(Ref::<_, [AU64]>::new_slice(&mut buf.t[..]).unwrap(), 2);
- buf.t = [0xFFu8; 16];
- test_new_helper_slice(Ref::<_, [AU64]>::new_slice_zeroed(&mut buf.t[..]).unwrap(), 2);
+ test_new_helper(lv);
+ }
+
+ // a buffer with alignment 8 and length 16
+ let mut buf = AlignedBuffer::<u64, [u8; 16]>::default();
+ // buf.buf should be aligned to 8 and have a length which is a multiple
+ // of size_of::<u64>(), so this should always succeed
+ test_new_helper_slice(LayoutVerified::<_, [u64]>::new_slice(&mut buf.buf[..]).unwrap(), 2);
+ buf.buf = [0xFFu8; 16];
+ test_new_helper_slice(
+ LayoutVerified::<_, [u64]>::new_slice_zeroed(&mut buf.buf[..]).unwrap(),
+ 2,
+ );
{
- buf.set_default();
- let (r, suffix) = Ref::<_, [AU64]>::new_slice_from_prefix(&mut buf.t[..], 1).unwrap();
+ buf.clear_buf();
+ let (lv, suffix) =
+ LayoutVerified::<_, [u64]>::new_slice_from_prefix(&mut buf.buf[..], 1).unwrap();
assert_eq!(suffix, [0; 8]);
- test_new_helper_slice(r, 1);
+ test_new_helper_slice(lv, 1);
}
{
- buf.t = [0xFFu8; 16];
- let (r, suffix) =
- Ref::<_, [AU64]>::new_slice_from_prefix_zeroed(&mut buf.t[..], 1).unwrap();
+ buf.buf = [0xFFu8; 16];
+ let (lv, suffix) =
+ LayoutVerified::<_, [u64]>::new_slice_from_prefix_zeroed(&mut buf.buf[..], 1)
+ .unwrap();
assert_eq!(suffix, [0xFF; 8]);
- test_new_helper_slice(r, 1);
+ test_new_helper_slice(lv, 1);
}
{
- buf.set_default();
- let (prefix, r) = Ref::<_, [AU64]>::new_slice_from_suffix(&mut buf.t[..], 1).unwrap();
+ buf.clear_buf();
+ let (prefix, lv) =
+ LayoutVerified::<_, [u64]>::new_slice_from_suffix(&mut buf.buf[..], 1).unwrap();
assert_eq!(prefix, [0; 8]);
- test_new_helper_slice(r, 1);
+ test_new_helper_slice(lv, 1);
}
{
- buf.t = [0xFFu8; 16];
- let (prefix, r) =
- Ref::<_, [AU64]>::new_slice_from_suffix_zeroed(&mut buf.t[..], 1).unwrap();
+ buf.buf = [0xFFu8; 16];
+ let (prefix, lv) =
+ LayoutVerified::<_, [u64]>::new_slice_from_suffix_zeroed(&mut buf.buf[..], 1)
+ .unwrap();
assert_eq!(prefix, [0xFF; 8]);
- test_new_helper_slice(r, 1);
+ test_new_helper_slice(lv, 1);
}
}
#[test]
fn test_new_unaligned_sized() {
// Test that an unaligned, properly-sized buffer works for
- // `new_unaligned`, `new_unaligned_from_prefix`, and
- // `new_unaligned_from_suffix`, and that `new_unaligned_from_prefix`
- // `new_unaligned_from_suffix` return empty slices. Test that an
- // unaligned buffer whose length is a multiple of the element size works
- // for `new_slice`. Test that `xxx_zeroed` behaves the same, and zeroes
- // the memory.
+ // new_unaligned, new_unaligned_from_prefix, and
+ // new_unaligned_from_suffix, and that new_unaligned_from_prefix
+ // new_unaligned_from_suffix return empty slices. Test that an unaligned
+ // buffer whose length is a multiple of the element size works for
+ // new_slice. Test that xxx_zeroed behaves the same, and zeroes the
+ // memory.
let mut buf = [0u8; 8];
- test_new_helper_unaligned(Ref::<_, [u8; 8]>::new_unaligned(&mut buf[..]).unwrap());
+ test_new_helper_unaligned(
+ LayoutVerified::<_, [u8; 8]>::new_unaligned(&mut buf[..]).unwrap(),
+ );
buf = [0xFFu8; 8];
- test_new_helper_unaligned(Ref::<_, [u8; 8]>::new_unaligned_zeroed(&mut buf[..]).unwrap());
+ test_new_helper_unaligned(
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_zeroed(&mut buf[..]).unwrap(),
+ );
{
- // In a block so that `r` and `suffix` don't live too long.
+ // in a block so that lv and suffix don't live too long
buf = [0u8; 8];
- let (r, suffix) = Ref::<_, [u8; 8]>::new_unaligned_from_prefix(&mut buf[..]).unwrap();
+ let (lv, suffix) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_prefix(&mut buf[..]).unwrap();
assert!(suffix.is_empty());
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
{
buf = [0xFFu8; 8];
- let (r, suffix) =
- Ref::<_, [u8; 8]>::new_unaligned_from_prefix_zeroed(&mut buf[..]).unwrap();
+ let (lv, suffix) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_prefix_zeroed(&mut buf[..])
+ .unwrap();
assert!(suffix.is_empty());
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
{
buf = [0u8; 8];
- let (prefix, r) = Ref::<_, [u8; 8]>::new_unaligned_from_suffix(&mut buf[..]).unwrap();
+ let (prefix, lv) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_suffix(&mut buf[..]).unwrap();
assert!(prefix.is_empty());
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
{
buf = [0xFFu8; 8];
- let (prefix, r) =
- Ref::<_, [u8; 8]>::new_unaligned_from_suffix_zeroed(&mut buf[..]).unwrap();
+ let (prefix, lv) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_suffix_zeroed(&mut buf[..])
+ .unwrap();
assert!(prefix.is_empty());
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
let mut buf = [0u8; 16];
- // `buf.t` should be aligned to 8 and have a length which is a multiple
- // of `size_of::AU64>()`, so this should always succeed.
+ // buf.buf should be aligned to 8 and have a length which is a multiple
+ // of size_of::<u64>(), so this should always succeed
test_new_helper_slice_unaligned(
- Ref::<_, [u8]>::new_slice_unaligned(&mut buf[..]).unwrap(),
+ LayoutVerified::<_, [u8]>::new_slice_unaligned(&mut buf[..]).unwrap(),
16,
);
buf = [0xFFu8; 16];
test_new_helper_slice_unaligned(
- Ref::<_, [u8]>::new_slice_unaligned_zeroed(&mut buf[..]).unwrap(),
+ LayoutVerified::<_, [u8]>::new_slice_unaligned_zeroed(&mut buf[..]).unwrap(),
16,
);
{
buf = [0u8; 16];
- let (r, suffix) =
- Ref::<_, [u8]>::new_slice_unaligned_from_prefix(&mut buf[..], 8).unwrap();
+ let (lv, suffix) =
+ LayoutVerified::<_, [u8]>::new_slice_unaligned_from_prefix(&mut buf[..], 8)
+ .unwrap();
assert_eq!(suffix, [0; 8]);
- test_new_helper_slice_unaligned(r, 8);
+ test_new_helper_slice_unaligned(lv, 8);
}
{
buf = [0xFFu8; 16];
- let (r, suffix) =
- Ref::<_, [u8]>::new_slice_unaligned_from_prefix_zeroed(&mut buf[..], 8).unwrap();
+ let (lv, suffix) =
+ LayoutVerified::<_, [u8]>::new_slice_unaligned_from_prefix_zeroed(&mut buf[..], 8)
+ .unwrap();
assert_eq!(suffix, [0xFF; 8]);
- test_new_helper_slice_unaligned(r, 8);
+ test_new_helper_slice_unaligned(lv, 8);
}
{
buf = [0u8; 16];
- let (prefix, r) =
- Ref::<_, [u8]>::new_slice_unaligned_from_suffix(&mut buf[..], 8).unwrap();
+ let (prefix, lv) =
+ LayoutVerified::<_, [u8]>::new_slice_unaligned_from_suffix(&mut buf[..], 8)
+ .unwrap();
assert_eq!(prefix, [0; 8]);
- test_new_helper_slice_unaligned(r, 8);
+ test_new_helper_slice_unaligned(lv, 8);
}
{
buf = [0xFFu8; 16];
- let (prefix, r) =
- Ref::<_, [u8]>::new_slice_unaligned_from_suffix_zeroed(&mut buf[..], 8).unwrap();
+ let (prefix, lv) =
+ LayoutVerified::<_, [u8]>::new_slice_unaligned_from_suffix_zeroed(&mut buf[..], 8)
+ .unwrap();
assert_eq!(prefix, [0xFF; 8]);
- test_new_helper_slice_unaligned(r, 8);
+ test_new_helper_slice_unaligned(lv, 8);
}
}
#[test]
fn test_new_oversized() {
// Test that a properly-aligned, overly-sized buffer works for
- // `new_from_prefix` and `new_from_suffix`, and that they return the
- // remainder and prefix of the slice respectively. Test that
- // `xxx_zeroed` behaves the same, and zeroes the memory.
+ // new_from_prefix and new_from_suffix, and that they return the
+ // remainder and prefix of the slice respectively. Test that xxx_zeroed
+ // behaves the same, and zeroes the memory.
- let mut buf = Align::<[u8; 16], AU64>::default();
+ let mut buf = AlignedBuffer::<u64, [u8; 16]>::default();
{
- // In a block so that `r` and `suffix` don't live too long. `buf.t`
- // should be aligned to 8, so this should always succeed.
- let (r, suffix) = Ref::<_, AU64>::new_from_prefix(&mut buf.t[..]).unwrap();
+ // in a block so that lv and suffix don't live too long
+ // buf.buf should be aligned to 8, so this should always succeed
+ let (lv, suffix) = LayoutVerified::<_, u64>::new_from_prefix(&mut buf.buf[..]).unwrap();
assert_eq!(suffix.len(), 8);
- test_new_helper(r);
+ test_new_helper(lv);
}
{
- buf.t = [0xFFu8; 16];
- // `buf.t` should be aligned to 8, so this should always succeed.
- let (r, suffix) = Ref::<_, AU64>::new_from_prefix_zeroed(&mut buf.t[..]).unwrap();
- // Assert that the suffix wasn't zeroed.
+ buf.buf = [0xFFu8; 16];
+ // buf.buf should be aligned to 8, so this should always succeed
+ let (lv, suffix) =
+ LayoutVerified::<_, u64>::new_from_prefix_zeroed(&mut buf.buf[..]).unwrap();
+ // assert that the suffix wasn't zeroed
assert_eq!(suffix, &[0xFFu8; 8]);
- test_new_helper(r);
+ test_new_helper(lv);
}
{
- buf.set_default();
- // `buf.t` should be aligned to 8, so this should always succeed.
- let (prefix, r) = Ref::<_, AU64>::new_from_suffix(&mut buf.t[..]).unwrap();
+ buf.clear_buf();
+ // buf.buf should be aligned to 8, so this should always succeed
+ let (prefix, lv) = LayoutVerified::<_, u64>::new_from_suffix(&mut buf.buf[..]).unwrap();
assert_eq!(prefix.len(), 8);
- test_new_helper(r);
+ test_new_helper(lv);
}
{
- buf.t = [0xFFu8; 16];
- // `buf.t` should be aligned to 8, so this should always succeed.
- let (prefix, r) = Ref::<_, AU64>::new_from_suffix_zeroed(&mut buf.t[..]).unwrap();
- // Assert that the prefix wasn't zeroed.
+ buf.buf = [0xFFu8; 16];
+ // buf.buf should be aligned to 8, so this should always succeed
+ let (prefix, lv) =
+ LayoutVerified::<_, u64>::new_from_suffix_zeroed(&mut buf.buf[..]).unwrap();
+ // assert that the prefix wasn't zeroed
assert_eq!(prefix, &[0xFFu8; 8]);
- test_new_helper(r);
+ test_new_helper(lv);
}
}
#[test]
fn test_new_unaligned_oversized() {
// Test than an unaligned, overly-sized buffer works for
- // `new_unaligned_from_prefix` and `new_unaligned_from_suffix`, and that
+ // new_unaligned_from_prefix and new_unaligned_from_suffix, and that
// they return the remainder and prefix of the slice respectively. Test
- // that `xxx_zeroed` behaves the same, and zeroes the memory.
+ // that xxx_zeroed behaves the same, and zeroes the memory.
let mut buf = [0u8; 16];
{
- // In a block so that `r` and `suffix` don't live too long.
- let (r, suffix) = Ref::<_, [u8; 8]>::new_unaligned_from_prefix(&mut buf[..]).unwrap();
+ // in a block so that lv and suffix don't live too long
+ let (lv, suffix) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_prefix(&mut buf[..]).unwrap();
assert_eq!(suffix.len(), 8);
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
{
buf = [0xFFu8; 16];
- let (r, suffix) =
- Ref::<_, [u8; 8]>::new_unaligned_from_prefix_zeroed(&mut buf[..]).unwrap();
- // Assert that the suffix wasn't zeroed.
+ let (lv, suffix) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_prefix_zeroed(&mut buf[..])
+ .unwrap();
+ // assert that the suffix wasn't zeroed
assert_eq!(suffix, &[0xFF; 8]);
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
{
buf = [0u8; 16];
- let (prefix, r) = Ref::<_, [u8; 8]>::new_unaligned_from_suffix(&mut buf[..]).unwrap();
+ let (prefix, lv) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_suffix(&mut buf[..]).unwrap();
assert_eq!(prefix.len(), 8);
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
{
buf = [0xFFu8; 16];
- let (prefix, r) =
- Ref::<_, [u8; 8]>::new_unaligned_from_suffix_zeroed(&mut buf[..]).unwrap();
- // Assert that the prefix wasn't zeroed.
+ let (prefix, lv) =
+ LayoutVerified::<_, [u8; 8]>::new_unaligned_from_suffix_zeroed(&mut buf[..])
+ .unwrap();
+ // assert that the prefix wasn't zeroed
assert_eq!(prefix, &[0xFF; 8]);
- test_new_helper_unaligned(r);
+ test_new_helper_unaligned(lv);
}
}
#[test]
#[allow(clippy::cognitive_complexity)]
fn test_new_error() {
- // Fail because the buffer is too large.
-
- // A buffer with an alignment of 8.
- let mut buf = Align::<[u8; 16], AU64>::default();
- // `buf.t` should be aligned to 8, so only the length check should fail.
- assert!(Ref::<_, AU64>::new(&buf.t[..]).is_none());
- assert!(Ref::<_, AU64>::new_zeroed(&mut buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned(&buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned_zeroed(&mut buf.t[..]).is_none());
-
- // Fail because the buffer is too small.
-
- // A buffer with an alignment of 8.
- let mut buf = Align::<[u8; 4], AU64>::default();
- // `buf.t` should be aligned to 8, so only the length check should fail.
- assert!(Ref::<_, AU64>::new(&buf.t[..]).is_none());
- assert!(Ref::<_, AU64>::new_zeroed(&mut buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned(&buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned_zeroed(&mut buf.t[..]).is_none());
- assert!(Ref::<_, AU64>::new_from_prefix(&buf.t[..]).is_none());
- assert!(Ref::<_, AU64>::new_from_prefix_zeroed(&mut buf.t[..]).is_none());
- assert!(Ref::<_, AU64>::new_from_suffix(&buf.t[..]).is_none());
- assert!(Ref::<_, AU64>::new_from_suffix_zeroed(&mut buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned_from_prefix(&buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned_from_prefix_zeroed(&mut buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned_from_suffix(&buf.t[..]).is_none());
- assert!(Ref::<_, [u8; 8]>::new_unaligned_from_suffix_zeroed(&mut buf.t[..]).is_none());
-
- // Fail because the length is not a multiple of the element size.
-
- let mut buf = Align::<[u8; 12], AU64>::default();
- // `buf.t` has length 12, but element size is 8.
- assert!(Ref::<_, [AU64]>::new_slice(&buf.t[..]).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_zeroed(&mut buf.t[..]).is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned(&buf.t[..]).is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_zeroed(&mut buf.t[..]).is_none());
-
- // Fail because the buffer is too short.
- let mut buf = Align::<[u8; 12], AU64>::default();
- // `buf.t` has length 12, but the element size is 8 (and we're expecting
- // two of them).
- assert!(Ref::<_, [AU64]>::new_slice_from_prefix(&buf.t[..], 2).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_prefix_zeroed(&mut buf.t[..], 2).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_suffix(&buf.t[..], 2).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_suffix_zeroed(&mut buf.t[..], 2).is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix(&buf.t[..], 2).is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix_zeroed(&mut buf.t[..], 2)
+ // fail because the buffer is too large
+
+ // a buffer with an alignment of 8
+ let mut buf = AlignedBuffer::<u64, [u8; 16]>::default();
+ // buf.buf should be aligned to 8, so only the length check should fail
+ assert!(LayoutVerified::<_, u64>::new(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_zeroed(&mut buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned_zeroed(&mut buf.buf[..]).is_none());
+
+ // fail because the buffer is too small
+
+ // a buffer with an alignment of 8
+ let mut buf = AlignedBuffer::<u64, [u8; 4]>::default();
+ // buf.buf should be aligned to 8, so only the length check should fail
+ assert!(LayoutVerified::<_, u64>::new(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_zeroed(&mut buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned_zeroed(&mut buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_from_prefix(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_from_prefix_zeroed(&mut buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_from_suffix(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_from_suffix_zeroed(&mut buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned_from_prefix(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned_from_prefix_zeroed(&mut buf.buf[..])
.is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix(&buf.t[..], 2).is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix_zeroed(&mut buf.t[..], 2)
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned_from_suffix(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u8; 8]>::new_unaligned_from_suffix_zeroed(&mut buf.buf[..])
.is_none());
- // Fail because the alignment is insufficient.
-
- // A buffer with an alignment of 8. An odd buffer size is chosen so that
- // the last byte of the buffer has odd alignment.
- let mut buf = Align::<[u8; 13], AU64>::default();
- // Slicing from 1, we get a buffer with size 12 (so the length check
- // should succeed) but an alignment of only 1, which is insufficient.
- assert!(Ref::<_, AU64>::new(&buf.t[1..]).is_none());
- assert!(Ref::<_, AU64>::new_zeroed(&mut buf.t[1..]).is_none());
- assert!(Ref::<_, AU64>::new_from_prefix(&buf.t[1..]).is_none());
- assert!(Ref::<_, AU64>::new_from_prefix_zeroed(&mut buf.t[1..]).is_none());
- assert!(Ref::<_, [AU64]>::new_slice(&buf.t[1..]).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_zeroed(&mut buf.t[1..]).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_prefix(&buf.t[1..], 1).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_prefix_zeroed(&mut buf.t[1..], 1).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_suffix(&buf.t[1..], 1).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_suffix_zeroed(&mut buf.t[1..], 1).is_none());
- // Slicing is unnecessary here because `new_from_suffix[_zeroed]` use
- // the suffix of the slice, which has odd alignment.
- assert!(Ref::<_, AU64>::new_from_suffix(&buf.t[..]).is_none());
- assert!(Ref::<_, AU64>::new_from_suffix_zeroed(&mut buf.t[..]).is_none());
-
- // Fail due to arithmetic overflow.
-
- let mut buf = Align::<[u8; 16], AU64>::default();
- let unreasonable_len = usize::MAX / mem::size_of::<AU64>() + 1;
- assert!(Ref::<_, [AU64]>::new_slice_from_prefix(&buf.t[..], unreasonable_len).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_prefix_zeroed(&mut buf.t[..], unreasonable_len)
+ // fail because the length is not a multiple of the element size
+
+ let mut buf = AlignedBuffer::<u64, [u8; 12]>::default();
+ // buf.buf has length 12, but element size is 8
+ assert!(LayoutVerified::<_, [u64]>::new_slice(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [u64]>::new_slice_zeroed(&mut buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned(&buf.buf[..]).is_none());
+ assert!(
+ LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_zeroed(&mut buf.buf[..]).is_none()
+ );
+
+ // fail beacuse the buffer is too short.
+ let mut buf = AlignedBuffer::<u64, [u8; 12]>::default();
+ // buf.buf has length 12, but the element size is 8 (and we're expecting two of them).
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_prefix(&buf.buf[..], 2).is_none());
+ assert!(
+ LayoutVerified::<_, [u64]>::new_slice_from_prefix_zeroed(&mut buf.buf[..], 2).is_none()
+ );
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_suffix(&buf.buf[..], 2).is_none());
+ assert!(
+ LayoutVerified::<_, [u64]>::new_slice_from_suffix_zeroed(&mut buf.buf[..], 2).is_none()
+ );
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix(&buf.buf[..], 2)
.is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_suffix(&buf.t[..], unreasonable_len).is_none());
- assert!(Ref::<_, [AU64]>::new_slice_from_suffix_zeroed(&mut buf.t[..], unreasonable_len)
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix_zeroed(
+ &mut buf.buf[..],
+ 2
+ )
+ .is_none());
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix(&buf.buf[..], 2)
.is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix(&buf.t[..], unreasonable_len)
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix_zeroed(
+ &mut buf.buf[..],
+ 2
+ )
+ .is_none());
+
+ // fail because the alignment is insufficient
+
+ // a buffer with an alignment of 8
+ let mut buf = AlignedBuffer::<u64, [u8; 12]>::default();
+ // slicing from 4, we get a buffer with size 8 (so the length check
+ // should succeed) but an alignment of only 4, which is insufficient
+ assert!(LayoutVerified::<_, u64>::new(&buf.buf[4..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_zeroed(&mut buf.buf[4..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_from_prefix(&buf.buf[4..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_from_prefix_zeroed(&mut buf.buf[4..]).is_none());
+ assert!(LayoutVerified::<_, [u64]>::new_slice(&buf.buf[4..]).is_none());
+ assert!(LayoutVerified::<_, [u64]>::new_slice_zeroed(&mut buf.buf[4..]).is_none());
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_prefix(&buf.buf[4..], 1).is_none());
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_prefix_zeroed(&mut buf.buf[4..], 1)
+ .is_none());
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_suffix(&buf.buf[4..], 1).is_none());
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_suffix_zeroed(&mut buf.buf[4..], 1)
+ .is_none());
+ // slicing from 4 should be unnecessary because new_from_suffix[_zeroed]
+ // use the suffix of the slice
+ assert!(LayoutVerified::<_, u64>::new_from_suffix(&buf.buf[..]).is_none());
+ assert!(LayoutVerified::<_, u64>::new_from_suffix_zeroed(&mut buf.buf[..]).is_none());
+
+ // fail due to arithmetic overflow
+
+ let mut buf = AlignedBuffer::<u64, [u8; 16]>::default();
+ let unreasonable_len = std::usize::MAX / mem::size_of::<u64>() + 1;
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_prefix(&buf.buf[..], unreasonable_len)
.is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix_zeroed(
- &mut buf.t[..],
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_prefix_zeroed(
+ &mut buf.buf[..],
unreasonable_len
)
.is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix(&buf.t[..], unreasonable_len)
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_suffix(&buf.buf[..], unreasonable_len)
.is_none());
- assert!(Ref::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix_zeroed(
- &mut buf.t[..],
+ assert!(LayoutVerified::<_, [u64]>::new_slice_from_suffix_zeroed(
+ &mut buf.buf[..],
+ unreasonable_len
+ )
+ .is_none());
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix(
+ &buf.buf[..],
+ unreasonable_len
+ )
+ .is_none());
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_prefix_zeroed(
+ &mut buf.buf[..],
+ unreasonable_len
+ )
+ .is_none());
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix(
+ &buf.buf[..],
+ unreasonable_len
+ )
+ .is_none());
+ assert!(LayoutVerified::<_, [[u8; 8]]>::new_slice_unaligned_from_suffix_zeroed(
+ &mut buf.buf[..],
unreasonable_len
)
.is_none());
}
- // Tests for ensuring that, if a ZST is passed into a slice-like function,
- // we always panic. Since these tests need to be separate per-function, and
- // they tend to take up a lot of space, we generate them using a macro in a
- // submodule instead. The submodule ensures that we can just re-use the name
- // of the function under test for the name of the test itself.
+ // Tests for ensuring that, if a ZST is passed into a slice-like function, we always
+ // panic. Since these tests need to be separate per-function, and they tend to take
+ // up a lot of space, we generate them using a macro in a submodule instead. The
+ // submodule ensures that we can just re-use the name of the function under test for
+ // the name of the test itself.
mod test_zst_panics {
macro_rules! zst_test {
- ($name:ident($($tt:tt)*), $constructor_in_panic_msg:tt) => {
+ ($name:ident($($tt:tt)*)) => {
#[test]
- #[should_panic = concat!("Ref::", $constructor_in_panic_msg, " called on a zero-sized type")]
+ #[should_panic = "assertion failed"]
fn $name() {
let mut buffer = [0u8];
- let r = $crate::Ref::<_, [()]>::$name(&mut buffer[..], $($tt)*);
- unreachable!("should have panicked, got {:?}", r);
+ let lv = $crate::LayoutVerified::<_, [()]>::$name(&mut buffer[..], $($tt)*);
+ unreachable!("should have panicked, got {:?}", lv);
}
}
}
- zst_test!(new_slice(), "new_slice");
- zst_test!(new_slice_zeroed(), "new_slice");
- zst_test!(new_slice_from_prefix(1), "new_slice");
- zst_test!(new_slice_from_prefix_zeroed(1), "new_slice");
- zst_test!(new_slice_from_suffix(1), "new_slice");
- zst_test!(new_slice_from_suffix_zeroed(1), "new_slice");
- zst_test!(new_slice_unaligned(), "new_slice_unaligned");
- zst_test!(new_slice_unaligned_zeroed(), "new_slice_unaligned");
- zst_test!(new_slice_unaligned_from_prefix(1), "new_slice_unaligned");
- zst_test!(new_slice_unaligned_from_prefix_zeroed(1), "new_slice_unaligned");
- zst_test!(new_slice_unaligned_from_suffix(1), "new_slice_unaligned");
- zst_test!(new_slice_unaligned_from_suffix_zeroed(1), "new_slice_unaligned");
+ zst_test!(new_slice());
+ zst_test!(new_slice_zeroed());
+ zst_test!(new_slice_from_prefix(1));
+ zst_test!(new_slice_from_prefix_zeroed(1));
+ zst_test!(new_slice_from_suffix(1));
+ zst_test!(new_slice_from_suffix_zeroed(1));
+ zst_test!(new_slice_unaligned());
+ zst_test!(new_slice_unaligned_zeroed());
+ zst_test!(new_slice_unaligned_from_prefix(1));
+ zst_test!(new_slice_unaligned_from_prefix_zeroed(1));
+ zst_test!(new_slice_unaligned_from_suffix(1));
+ zst_test!(new_slice_unaligned_from_suffix_zeroed(1));
}
#[test]
fn test_as_bytes_methods() {
- /// Run a series of tests by calling `AsBytes` 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_bytes_mut()[0]`
- /// has had its bits flipped (by applying `^= 0xFF`).
- ///
- /// `N` is the size of `t` in bytes.
- fn test<T: FromBytes + AsBytes + 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_bytes_mut()[0] ^= 0xFF;
- assert_eq!(t, post_mutation);
- t.as_bytes_mut()[0] ^= 0xFF;
-
- // `write_to` rejects slices that are too small or too large.
- assert_eq!(t.write_to(&mut vec![0; N - 1][..]), None);
- assert_eq!(t.write_to(&mut vec![0; N + 1][..]), None);
-
- // `write_to` works as expected.
- let mut bytes = [0; N];
- assert_eq!(t.write_to(&mut bytes[..]), Some(()));
- assert_eq!(bytes, t.as_bytes());
-
- // `write_to_prefix` rejects slices that are too small.
- assert_eq!(t.write_to_prefix(&mut vec![0; N - 1][..]), None);
-
- // `write_to_prefix` works with exact-sized slices.
- let mut bytes = [0; N];
- assert_eq!(t.write_to_prefix(&mut bytes[..]), Some(()));
- 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[..]), Some(()));
- 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_eq!(t.write_to_suffix(&mut vec![0; N - 1][..]), None);
-
- // `write_to_suffix` works with exact-sized slices.
- let mut bytes = [0; N];
- assert_eq!(t.write_to_suffix(&mut bytes[..]), Some(()));
- 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[..]), Some(()));
- assert_eq!(&too_many_bytes[1..], t.as_bytes());
- assert_eq!(too_many_bytes[0], 123);
- }
-
- #[derive(Debug, Eq, PartialEq, FromZeroes, FromBytes, AsBytes)]
+ #[derive(Debug, Eq, PartialEq, FromBytes, AsBytes)]
#[repr(C)]
struct Foo {
a: u32,
- b: Wrapping<u32>,
- c: Option<NonZeroU32>,
+ b: u32,
}
- 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]),
- );
+ let mut foo = Foo { a: 1, b: 2 };
+ // Test that we can access the underlying bytes, and that we get the
+ // right bytes and the right number of bytes.
+ assert_eq!(foo.as_bytes(), [1, 0, 0, 0, 2, 0, 0, 0]);
+ // Test that changes to the underlying byte slices are reflected in the
+ // original object.
+ foo.as_bytes_mut()[0] = 3;
+ assert_eq!(foo, Foo { a: 3, b: 2 });
+
+ // Do the same tests for a slice, which ensures that this logic works
+ // for unsized types as well.
+ let foo = &mut [Foo { a: 1, b: 2 }, Foo { a: 3, b: 4 }];
+ assert_eq!(foo.as_bytes(), [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0]);
+ foo.as_bytes_mut()[8] = 5;
+ assert_eq!(foo, &mut [Foo { a: 1, b: 2 }, Foo { a: 5, b: 4 }]);
}
#[test]
fn test_array() {
- #[derive(FromZeroes, FromBytes, AsBytes)]
+ // This is a hack, as per above in `test_as_bytes_methods`.
+ mod zerocopy {
+ pub use crate::*;
+ }
+ #[derive(FromBytes, AsBytes)]
#[repr(C)]
struct Foo {
a: [u16; 33],
@@ -3915,172 +2645,209 @@ mod tests {
#[test]
fn test_display_debug() {
- let buf = Align::<[u8; 8], u64>::default();
- let r = Ref::<_, u64>::new(&buf.t[..]).unwrap();
- assert_eq!(format!("{}", r), "0");
- assert_eq!(format!("{:?}", r), "Ref(0)");
+ let buf = AlignedBuffer::<u64, [u8; 8]>::default();
+ let lv = LayoutVerified::<_, u64>::new(&buf.buf[..]).unwrap();
+ assert_eq!(format!("{}", lv), "0");
+ assert_eq!(format!("{:?}", lv), "LayoutVerified(0)");
- let buf = Align::<[u8; 8], u64>::default();
- let r = Ref::<_, [u64]>::new_slice(&buf.t[..]).unwrap();
- assert_eq!(format!("{:?}", r), "Ref([0])");
+ let buf = AlignedBuffer::<u64, [u8; 8]>::default();
+ let lv = LayoutVerified::<_, [u64]>::new_slice(&buf.buf[..]).unwrap();
+ assert_eq!(format!("{:?}", lv), "LayoutVerified([0])");
}
#[test]
fn test_eq() {
- let buf1 = 0_u64;
- let r1 = Ref::<_, u64>::new(buf1.as_bytes()).unwrap();
- let buf2 = 0_u64;
- let r2 = Ref::<_, u64>::new(buf2.as_bytes()).unwrap();
- assert_eq!(r1, r2);
+ let buf = [0u8; 8];
+ let lv1 = LayoutVerified::<_, u64>::new(&buf[..]).unwrap();
+ let lv2 = LayoutVerified::<_, u64>::new(&buf[..]).unwrap();
+ assert_eq!(lv1, lv2);
}
#[test]
fn test_ne() {
- let buf1 = 0_u64;
- let r1 = Ref::<_, u64>::new(buf1.as_bytes()).unwrap();
- let buf2 = 1_u64;
- let r2 = Ref::<_, u64>::new(buf2.as_bytes()).unwrap();
- assert_ne!(r1, r2);
+ let buf1 = [0u8; 8];
+ let lv1 = LayoutVerified::<_, u64>::new(&buf1[..]).unwrap();
+ let buf2 = [1u8; 8];
+ let lv2 = LayoutVerified::<_, u64>::new(&buf2[..]).unwrap();
+ assert_ne!(lv1, lv2);
}
#[test]
fn test_ord() {
- let buf1 = 0_u64;
- let r1 = Ref::<_, u64>::new(buf1.as_bytes()).unwrap();
- let buf2 = 1_u64;
- let r2 = Ref::<_, u64>::new(buf2.as_bytes()).unwrap();
- assert!(r1 < r2);
+ let buf1 = [0u8; 8];
+ let lv1 = LayoutVerified::<_, u64>::new(&buf1[..]).unwrap();
+ let buf2 = [1u8; 8];
+ let lv2 = LayoutVerified::<_, u64>::new(&buf2[..]).unwrap();
+ assert!(lv1 < lv2);
}
#[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(), ());
- }
+ assert_eq!(<()>::new_zeroed(), ());
}
#[test]
- fn test_transparent_packed_generic_struct() {
- #[derive(AsBytes, FromZeroes, FromBytes, Unaligned)]
- #[repr(transparent)]
- struct Foo<T> {
- _t: T,
- _phantom: PhantomData<()>,
- }
+ fn test_new_box_zeroed() {
+ assert_eq!(*u64::new_box_zeroed(), 0);
+ }
- assert_impl_all!(Foo<u32>: FromZeroes, FromBytes, AsBytes);
- assert_impl_all!(Foo<u8>: Unaligned);
+ #[test]
+ fn test_new_box_zeroed_array() {
+ drop(<[u32; 0x1000]>::new_box_zeroed());
+ }
- #[derive(AsBytes, FromZeroes, FromBytes, Unaligned)]
- #[repr(packed)]
- struct Bar<T, U> {
- _t: T,
- _u: U,
- }
+ #[test]
+ fn test_new_box_zeroed_zst() {
+ assert_eq!(*<()>::new_box_zeroed(), ());
+ }
+
+ #[test]
+ fn test_new_box_slice_zeroed() {
+ let mut s: Box<[u64]> = u64::new_box_slice_zeroed(3);
+ assert_eq!(s.len(), 3);
+ assert_eq!(&*s, &[0, 0, 0]);
+ s[1] = 3;
+ assert_eq!(&*s, &[0, 3, 0]);
+ }
- assert_impl_all!(Bar<u8, AU64>: FromZeroes, FromBytes, AsBytes, Unaligned);
+ #[test]
+ fn test_new_box_slice_zeroed_empty() {
+ let s: Box<[u64]> = u64::new_box_slice_zeroed(0);
+ assert_eq!(s.len(), 0);
}
#[test]
- fn test_impls() {
- // Asserts that `$ty` implements any `$trait` and doesn't implement any
- // `!$trait`. Note that all `$trait`s must come before any `!$trait`s.
- macro_rules! assert_impls {
- ($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);
- )*
- };
- }
+ fn test_new_box_slice_zeroed_zst() {
+ let mut s: Box<[()]> = <()>::new_box_slice_zeroed(3);
+ assert_eq!(s.len(), 3);
+ assert!(s.get(10).is_none());
+ assert_eq!(s[1], ());
+ s[2] = ();
+ }
+
+ #[test]
+ fn test_new_box_slice_zeroed_zst_empty() {
+ let s: Box<[()]> = <()>::new_box_slice_zeroed(0);
+ assert_eq!(s.len(), 0);
+ }
+
+ #[test]
+ fn test_extend_vec_zeroed() {
+ // test extending when there is an existing allocation
+ let mut v: Vec<u64> = Vec::with_capacity(3);
+ v.push(100);
+ v.push(200);
+ v.push(300);
+ extend_vec_zeroed(&mut v, 3);
+ 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();
+ extend_vec_zeroed(&mut v, 3);
+ assert_eq!(v.len(), 3);
+ assert_eq!(&*v, &[0, 0, 0]);
+ drop(v);
+ }
+
+ #[test]
+ fn test_extend_vec_zeroed_zst() {
+ // test extending when there is an existing (fake) allocation
+ let mut v: Vec<()> = Vec::with_capacity(3);
+ v.push(());
+ v.push(());
+ v.push(());
+ extend_vec_zeroed(&mut v, 3);
+ 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);
+ assert_eq!(&*v, &[(), (), ()]);
+ drop(v);
+ }
- assert_impls!((): FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(u8: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(i8: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(u16: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(i16: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(u32: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(i32: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(u64: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(i64: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(u128: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(i128: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(usize: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(isize: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(f32: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(f64: FromZeroes, FromBytes, AsBytes, !Unaligned);
-
- assert_impls!(bool: FromZeroes, AsBytes, Unaligned, !FromBytes);
- assert_impls!(char: FromZeroes, AsBytes, !FromBytes, !Unaligned);
- assert_impls!(str: FromZeroes, AsBytes, Unaligned, !FromBytes);
-
- assert_impls!(NonZeroU8: AsBytes, Unaligned, !FromZeroes, !FromBytes);
- assert_impls!(NonZeroI8: AsBytes, Unaligned, !FromZeroes, !FromBytes);
- assert_impls!(NonZeroU16: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroI16: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroU32: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroI32: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroU64: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroI64: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroU128: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroI128: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroUsize: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
- assert_impls!(NonZeroIsize: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
-
- assert_impls!(Option<NonZeroU8>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(Option<NonZeroI8>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(Option<NonZeroU16>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroI16>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroU32>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroI32>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroU64>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroI64>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroU128>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroI128>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroUsize>: FromZeroes, FromBytes, AsBytes, !Unaligned);
- assert_impls!(Option<NonZeroIsize>: FromZeroes, FromBytes, AsBytes, !Unaligned);
-
- // Implements none of the ZC traits.
- struct NotZerocopy;
-
- assert_impls!(PhantomData<NotZerocopy>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(PhantomData<[u8]>: FromZeroes, FromBytes, AsBytes, Unaligned);
-
- assert_impls!(ManuallyDrop<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(ManuallyDrop<[u8]>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(ManuallyDrop<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
- assert_impls!(ManuallyDrop<[NotZerocopy]>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
-
- assert_impls!(MaybeUninit<u8>: FromZeroes, FromBytes, Unaligned, !AsBytes);
- assert_impls!(MaybeUninit<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
-
- assert_impls!(Wrapping<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(Wrapping<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
-
- assert_impls!(Unalign<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!(Unalign<NotZerocopy>: Unaligned, !FromZeroes, !FromBytes, !AsBytes);
-
- assert_impls!([u8]: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!([NotZerocopy]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
- assert_impls!([u8; 0]: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!([NotZerocopy; 0]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
- assert_impls!([u8; 1]: FromZeroes, FromBytes, AsBytes, Unaligned);
- assert_impls!([NotZerocopy; 1]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
+ #[test]
+ fn test_insert_vec_zeroed() {
+ // insert at start (no existing allocation)
+ let mut v: Vec<u64> = Vec::new();
+ insert_vec_zeroed(&mut v, 0, 2);
+ assert_eq!(v.len(), 2);
+ assert_eq!(&*v, &[0, 0]);
+ drop(v);
+
+ // insert at start
+ let mut v: Vec<u64> = Vec::with_capacity(3);
+ v.push(100);
+ v.push(200);
+ v.push(300);
+ insert_vec_zeroed(&mut v, 0, 2);
+ assert_eq!(v.len(), 5);
+ assert_eq!(&*v, &[0, 0, 100, 200, 300]);
+ drop(v);
+
+ // insert at middle
+ let mut v: Vec<u64> = Vec::with_capacity(3);
+ v.push(100);
+ v.push(200);
+ v.push(300);
+ insert_vec_zeroed(&mut v, 1, 1);
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[100, 0, 200, 300]);
+ drop(v);
+
+ // insert at end
+ let mut v: Vec<u64> = Vec::with_capacity(3);
+ v.push(100);
+ v.push(200);
+ v.push(300);
+ insert_vec_zeroed(&mut v, 3, 1);
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[100, 200, 300, 0]);
+ drop(v);
+ }
+
+ #[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);
+ assert_eq!(v.len(), 2);
+ assert_eq!(&*v, &[(), ()]);
+ drop(v);
+
+ // insert at start
+ let mut v: Vec<()> = Vec::with_capacity(3);
+ v.push(());
+ v.push(());
+ v.push(());
+ insert_vec_zeroed(&mut v, 0, 2);
+ assert_eq!(v.len(), 5);
+ assert_eq!(&*v, &[(), (), (), (), ()]);
+ drop(v);
+
+ // insert at middle
+ let mut v: Vec<()> = Vec::with_capacity(3);
+ v.push(());
+ v.push(());
+ v.push(());
+ insert_vec_zeroed(&mut v, 1, 1);
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[(), (), (), ()]);
+ drop(v);
+
+ // insert at end
+ let mut v: Vec<()> = Vec::with_capacity(3);
+ v.push(());
+ v.push(());
+ v.push(());
+ insert_vec_zeroed(&mut v, 3, 1);
+ assert_eq!(v.len(), 4);
+ assert_eq!(&*v, &[(), (), (), ()]);
+ drop(v);
}
}
diff --git a/src/macros.rs b/src/macros.rs
deleted file mode 100644
index aebc8d6..0000000
--- a/src/macros.rs
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// Documents multiple unsafe blocks with a single safety comment.
-///
-/// Invoked as:
-///
-/// ```rust,ignore
-/// safety_comment! {
-/// // Non-doc comments come first.
-/// /// SAFETY:
-/// /// Safety comment starts on its own line.
-/// macro_1!(args);
-/// macro_2! { args };
-/// /// SAFETY:
-/// /// Subsequent safety comments are allowed but not required.
-/// macro_3! { args };
-/// }
-/// ```
-///
-/// The macro invocations are emitted, each decorated with the following
-/// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`.
-macro_rules! safety_comment {
- (#[doc = r" SAFETY:"] $($(#[doc = $_doc:literal])* $macro:ident!$args:tt;)*) => {
- #[allow(clippy::undocumented_unsafe_blocks)]
- const _: () = { $($macro!$args;)* };
- }
-}
-
-/// Unsafely implements trait(s) for a type.
-macro_rules! unsafe_impl {
- // Implement `$trait` for `$ty` with no bounds.
- ($ty:ty: $trait:ty) => {
- unsafe impl $trait for $ty { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} }
- };
- // Implement all `$traits` for `$ty` with no bounds.
- ($ty:ty: $($traits:ty),*) => {
- $( unsafe_impl!($ty: $traits); )*
- };
- // This arm is identical to the following one, except it contains a
- // preceding `const`. If we attempt to handle these with a single arm, there
- // is an inherent ambiguity between `const` (the keyword) and `const` (the
- // ident match for `$tyvar:ident`).
- //
- // To explain how this works, consider the following invocation:
- //
- // unsafe_impl!(const N: usize, T: ?Sized + Copy => Clone for Foo<T>);
- //
- // In this invocation, here are the assignments to meta-variables:
- //
- // |---------------|------------|
- // | Meta-variable | Assignment |
- // |---------------|------------|
- // | $constname | N |
- // | $constty | usize |
- // | $tyvar | T |
- // | $optbound | Sized |
- // | $bound | Copy |
- // | $trait | Clone |
- // | $ty | Foo<T> |
- // |---------------|------------|
- //
- // The following arm has the same behavior with the exception of the lack of
- // support for a leading `const` parameter.
- (
- const $constname:ident : $constty:ident $(,)?
- $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
- => $trait:ident for $ty:ty
- ) => {
- unsafe_impl!(
- @inner
- @const $constname: $constty,
- $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
- => $trait for $ty
- );
- };
- (
- $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
- => $trait:ident for $ty:ty
- ) => {
- unsafe_impl!(
- @inner
- $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
- => $trait for $ty
- );
- };
- (
- @inner
- $(@const $constname:ident : $constty:ident,)*
- $($tyvar:ident $(: $(? $optbound:ident +)* + $($bound:ident +)* )?,)*
- => $trait:ident for $ty:ty
- ) => {
- unsafe impl<$(const $constname: $constty,)* $($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> $trait for $ty {
- #[allow(clippy::missing_inline_in_public_items)]
- fn only_derive_is_allowed_to_implement_this_trait() {}
- }
- };
-}
-
-/// Implements trait(s) for a type or verifies the given implementation by
-/// referencing an existing (derived) implementation.
-///
-/// This macro exists so that we can provide zerocopy-derive as an optional
-/// dependency and still get the benefit of using its derives to validate that
-/// our trait impls are sound.
-///
-/// When compiling without `--cfg 'feature = "derive"` and without `--cfg test`,
-/// `impl_or_verify!` emits the provided trait impl. When compiling with either
-/// of those cfgs, it is expected that the type in question is deriving the
-/// traits instead. In this case, `impl_or_verify!` emits code which validates
-/// that the given trait impl is at least as restrictive as the the impl emitted
-/// by the custom derive. This has the effect of confirming that the impl which
-/// is emitted when the `derive` feature is disabled is actually sound (on the
-/// assumption that the impl emitted by the custom derive is sound).
-///
-/// The caller is still required to provide a safety comment (e.g. using the
-/// `safety_comment!` macro) . The reason for this restriction is that, while
-/// `impl_or_verify!` can guarantee that the provided impl is sound when it is
-/// compiled with the appropriate cfgs, there is no way to guarantee that it is
-/// ever compiled with those cfgs. In particular, it would be possible to
-/// accidentally place an `impl_or_verify!` call in a context that is only ever
-/// compiled when the `derive` feature is disabled. If that were to happen,
-/// there would be nothing to prevent an unsound trait impl from being emitted.
-/// Requiring a safety comment reduces the likelihood of emitting an unsound
-/// impl in this case, and also provides useful documentation for readers of the
-/// code.
-///
-/// ## Example
-///
-/// ```rust,ignore
-/// // Note that these derives are gated by `feature = "derive"`
-/// #[cfg_attr(any(feature = "derive", test), derive(FromZeroes, FromBytes, AsBytes, Unaligned))]
-/// #[repr(transparent)]
-/// struct Wrapper<T>(T);
-///
-/// safety_comment! {
-/// /// SAFETY:
-/// /// `Wrapper<T>` is `repr(transparent)`, so it is sound to implement any
-/// /// zerocopy trait if `T` implements that trait.
-/// impl_or_verify!(T: FromZeroes => FromZeroes for Wrapper<T>);
-/// impl_or_verify!(T: FromBytes => FromBytes for Wrapper<T>);
-/// impl_or_verify!(T: AsBytes => AsBytes for Wrapper<T>);
-/// impl_or_verify!(T: Unaligned => Unaligned for Wrapper<T>);
-/// }
-/// ```
-macro_rules! impl_or_verify {
- // The following two match arms follow the same pattern as their
- // counterparts in `unsafe_impl!`; see the documentation on those arms for
- // more details.
- (
- const $constname:ident : $constty:ident $(,)?
- $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
- => $trait:ident for $ty:ty
- ) => {
- impl_or_verify!(@impl { unsafe_impl!(
- const $constname: $constty, $($tyvar $(: $(? $optbound +)* $($bound +)*)?),* => $trait for $ty
- ); });
- impl_or_verify!(@verify $trait, {
- impl<const $constname: $constty, $($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
- });
- };
- (
- $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
- => $trait:ident for $ty:ty
- ) => {
- impl_or_verify!(@impl { unsafe_impl!(
- $($tyvar $(: $(? $optbound +)* $($bound +)*)?),* => $trait for $ty
- ); });
- impl_or_verify!(@verify $trait, {
- impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
- });
- };
- (
- $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
- => $trait:ident for $ty:ty
- ) => {
- unsafe_impl!(
- @inner
- $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
- => $trait for $ty
- );
- };
- (@impl $impl_block:tt) => {
- #[cfg(not(any(feature = "derive", test)))]
- const _: () = { $impl_block };
- };
- (@verify $trait:ident, $impl_block:tt) => {
- #[cfg(any(feature = "derive", test))]
- const _: () = {
- trait Subtrait: $trait {}
- $impl_block
- };
- };
-}
-
-/// Implements `KnownLayout` for a sized type.
-macro_rules! impl_known_layout {
- ($(const $constvar:ident : $constty:ty, $tyvar:ident $(: ?$optbound:ident)? => $ty:ty),* $(,)?) => {
- $(impl_known_layout!(@inner const $constvar: $constty, $tyvar $(: ?$optbound)? => $ty);)*
- };
- ($($tyvar:ident $(: ?$optbound:ident)? => $ty:ty),* $(,)?) => {
- $(impl_known_layout!(@inner , $tyvar $(: ?$optbound)? => $ty);)*
- };
- ($($ty:ty),*) => { $(impl_known_layout!(@inner , => $ty);)* };
- (@inner $(const $constvar:ident : $constty:ty)? , $($tyvar:ident $(: ?$optbound:ident)?)? => $ty:ty) => {
- impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> sealed::KnownLayoutSealed for $ty {}
- // SAFETY: Delegates safety to `DstLayout::for_type`.
- unsafe impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> KnownLayout for $ty {
- const LAYOUT: DstLayout = DstLayout::for_type::<$ty>();
- }
- };
-}
-
-/// Implements `KnownLayout` for a type in terms of the implementation of
-/// another type with the same representation.
-///
-/// # Safety
-///
-/// - `$ty` and `$repr` must have the same:
-/// - Fixed prefix size
-/// - Alignment
-/// - (For DSTs) trailing slice element size
-/// - It must be valid to perform an `as` cast from `*mut $repr` to `*mut $ty`,
-/// and this operation must preserve referent size (ie, `size_of_val_raw`).
-macro_rules! unsafe_impl_known_layout {
- ($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($repr:ty)] $ty:ty) => {
- impl<$($tyvar: ?Sized + KnownLayout)?> sealed::KnownLayoutSealed for $ty {}
- unsafe impl<$($tyvar: ?Sized + KnownLayout)?> KnownLayout for $ty {
- const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT;
- }
- };
-}
-
-/// Uses `align_of` to confirm that a type or set of types have alignment 1.
-///
-/// Note that `align_of<T>` requires `T: Sized`, so this macro doesn't work for
-/// unsized types.
-macro_rules! assert_unaligned {
- ($ty:ty) => {
- // We only compile this assertion under `cfg(test)` to avoid taking an
- // extra non-dev dependency (and making this crate more expensive to
- // compile for our dependents).
- #[cfg(test)]
- static_assertions::const_assert_eq!(core::mem::align_of::<$ty>(), 1);
- };
- ($($ty:ty),*) => {
- $(assert_unaligned!($ty);)*
- };
-}
diff --git a/src/util.rs b/src/util.rs
deleted file mode 100644
index ed810dc..0000000
--- a/src/util.rs
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use core::mem;
-
-pub(crate) trait AsAddress {
- fn addr(self) -> usize;
-}
-
-impl<'a, T: ?Sized> AsAddress for &'a T {
- #[inline(always)]
- fn addr(self) -> usize {
- let ptr: *const T = self;
- AsAddress::addr(ptr)
- }
-}
-
-impl<'a, T: ?Sized> AsAddress for &'a mut T {
- #[inline(always)]
- fn addr(self) -> usize {
- let ptr: *const T = self;
- AsAddress::addr(ptr)
- }
-}
-
-impl<T: ?Sized> AsAddress for *const T {
- #[inline(always)]
- fn addr(self) -> usize {
- // TODO(https://github.com/rust-lang/rust/issues/95228): Use `.addr()`
- // instead of `as usize` once it's stable, and get rid of this `allow`.
- // Currently, `as usize` is the only way to accomplish this.
- #[allow(clippy::as_conversions)]
- return self.cast::<()>() as usize;
- }
-}
-
-impl<T: ?Sized> AsAddress for *mut T {
- #[inline(always)]
- fn addr(self) -> usize {
- let ptr: *const T = self;
- AsAddress::addr(ptr)
- }
-}
-
-/// Is `t` aligned to `mem::align_of::<U>()`?
-#[inline(always)]
-pub(crate) fn aligned_to<T: AsAddress, U>(t: T) -> bool {
- // `mem::align_of::<U>()` is guaranteed to return a non-zero value, which in
- // turn guarantees that this mod operation will not panic.
- #[allow(clippy::arithmetic_side_effects)]
- let remainder = t.addr() % mem::align_of::<U>();
- remainder == 0
-}
-
-#[cfg(test)]
-pub(crate) mod testutil {
- use core::fmt::{self, Display, Formatter};
-
- use crate::*;
-
- /// A `T` which is aligned to at least `align_of::<A>()`.
- #[derive(Default)]
- pub(crate) struct Align<T, A> {
- pub(crate) t: T,
- _a: [A; 0],
- }
-
- impl<T: Default, A> Align<T, A> {
- pub(crate) fn set_default(&mut self) {
- self.t = T::default();
- }
- }
-
- impl<T, A> Align<T, A> {
- pub(crate) const fn new(t: T) -> Align<T, A> {
- Align { t, _a: [] }
- }
- }
-
- // A `u64` with alignment 8.
- //
- // Though `u64` has alignment 8 on some platforms, it's not guaranteed.
- // By contrast, `AU64` is guaranteed to have alignment 8.
- #[derive(
- FromZeroes, FromBytes, AsBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone,
- )]
- #[repr(C, align(8))]
- pub(crate) struct AU64(pub(crate) u64);
-
- impl AU64 {
- // Converts this `AU64` to bytes using this platform's endianness.
- pub(crate) fn to_bytes(self) -> [u8; 8] {
- crate::transmute!(self)
- }
- }
-
- impl Display for AU64 {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- Display::fmt(&self.0, f)
- }
- }
-
- impl_known_layout!(AU64);
-}
diff --git a/src/wrappers.rs b/src/wrappers.rs
deleted file mode 100644
index a0e6ac7..0000000
--- a/src/wrappers.rs
+++ /dev/null
@@ -1,497 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use core::{
- cmp::Ordering,
- fmt::{self, Debug, Display, Formatter},
- hash::Hash,
- mem::{self, ManuallyDrop},
- ops::{Deref, DerefMut},
- ptr,
-};
-
-use super::*;
-
-/// A type with no alignment requirement.
-///
-/// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign<T>`
-/// has the same size and bit validity as `T`, but not necessarily the same
-/// alignment [or ABI]. This is useful if a type with an alignment requirement
-/// needs to be read from a chunk of memory which provides no alignment
-/// guarantees.
-///
-/// Since `Unalign` has no alignment requirement, the inner `T` may not be
-/// properly aligned in memory. There are five ways to access the inner `T`:
-/// - by value, using [`get`] or [`into_inner`]
-/// - by reference inside of a callback, using [`update`]
-/// - fallibly by reference, using [`try_deref`] or [`try_deref_mut`]; these can
-/// fail if the `Unalign` does not satisfy `T`'s alignment requirement at
-/// runtime
-/// - unsafely by reference, using [`deref_unchecked`] or
-/// [`deref_mut_unchecked`]; it is the caller's responsibility to ensure that
-/// the `Unalign` satisfies `T`'s alignment requirement
-/// - (where `T: Unaligned`) infallibly by reference, using [`Deref::deref`] or
-/// [`DerefMut::deref_mut`]
-///
-/// [or ABI]: https://github.com/google/zerocopy/issues/164
-/// [`get`]: Unalign::get
-/// [`into_inner`]: Unalign::into_inner
-/// [`update`]: Unalign::update
-/// [`try_deref`]: Unalign::try_deref
-/// [`try_deref_mut`]: Unalign::try_deref_mut
-/// [`deref_unchecked`]: Unalign::deref_unchecked
-/// [`deref_mut_unchecked`]: Unalign::deref_mut_unchecked
-// NOTE: This type is sound to use with types that need to be dropped. The
-// reason is that the compiler-generated drop code automatically moves all
-// values to aligned memory slots before dropping them in-place. This is not
-// well-documented, but it's hinted at in places like [1] and [2]. However, this
-// also means that `T` must be `Sized`; unless something changes, we can never
-// support unsized `T`. [3]
-//
-// [1] https://github.com/rust-lang/rust/issues/54148#issuecomment-420529646
-// [2] https://github.com/google/zerocopy/pull/126#discussion_r1018512323
-// [3] https://github.com/google/zerocopy/issues/209
-#[allow(missing_debug_implementations)]
-#[derive(Default, Copy)]
-#[cfg_attr(any(feature = "derive", test), derive(FromZeroes, FromBytes, AsBytes, Unaligned))]
-#[repr(C, packed)]
-pub struct Unalign<T>(T);
-
-safety_comment! {
- /// SAFETY:
- /// - `Unalign<T>` is `repr(packed)`, so it is unaligned regardless of the
- /// alignment of `T`, and so we don't require that `T: Unaligned`
- /// - `Unalign<T>` has the same bit validity as `T`, and so it is
- /// `FromZeroes`, `FromBytes`, or `AsBytes` exactly when `T` is as well.
- impl_or_verify!(T => Unaligned for Unalign<T>);
- impl_or_verify!(T: FromZeroes => FromZeroes for Unalign<T>);
- impl_or_verify!(T: FromBytes => FromBytes for Unalign<T>);
- impl_or_verify!(T: AsBytes => AsBytes for Unalign<T>);
-}
-
-// Note that `Unalign: Clone` only if `T: Copy`. Since the inner `T` may not be
-// aligned, there's no way to safely call `T::clone`, and so a `T: Clone` bound
-// is not sufficient to implement `Clone` for `Unalign`.
-impl<T: Copy> Clone for Unalign<T> {
- #[inline(always)]
- fn clone(&self) -> Unalign<T> {
- *self
- }
-}
-
-impl<T> Unalign<T> {
- /// Constructs a new `Unalign`.
- #[inline(always)]
- pub const fn new(val: T) -> Unalign<T> {
- Unalign(val)
- }
-
- /// Consumes `self`, returning the inner `T`.
- #[inline(always)]
- pub const fn into_inner(self) -> T {
- // Use this instead of `mem::transmute` since the latter can't tell
- // that `Unalign<T>` and `T` have the same size.
- #[repr(C)]
- union Transmute<T> {
- u: ManuallyDrop<Unalign<T>>,
- t: ManuallyDrop<T>,
- }
-
- // SAFETY: Since `Unalign` is `#[repr(C, packed)]`, it has the same
- // layout as `T`. `ManuallyDrop<U>` is guaranteed to have the same
- // layout as `U`, and so `ManuallyDrop<Unalign<T>>` has the same layout
- // as `ManuallyDrop<T>`. Since `Transmute<T>` is `#[repr(C)]`, its `t`
- // and `u` fields both start at the same offset (namely, 0) within the
- // union.
- //
- // We do this instead of just destructuring in order to prevent
- // `Unalign`'s `Drop::drop` from being run, since dropping is not
- // supported in `const fn`s.
- //
- // TODO(https://github.com/rust-lang/rust/issues/73255): Destructure
- // instead of using unsafe.
- unsafe { ManuallyDrop::into_inner(Transmute { u: ManuallyDrop::new(self) }.t) }
- }
-
- /// Attempts to return a reference to the wrapped `T`, failing if `self` is
- /// not properly aligned.
- ///
- /// If `self` does not satisfy `mem::align_of::<T>()`, then it is unsound to
- /// return a reference to the wrapped `T`, and `try_deref` returns `None`.
- ///
- /// If `T: Unaligned`, then `Unalign<T>` implements [`Deref`], and callers
- /// may prefer [`Deref::deref`], which is infallible.
- #[inline(always)]
- pub fn try_deref(&self) -> Option<&T> {
- if !crate::util::aligned_to::<_, T>(self) {
- return None;
- }
-
- // SAFETY: `deref_unchecked`'s safety requirement is that `self` is
- // aligned to `align_of::<T>()`, which we just checked.
- unsafe { Some(self.deref_unchecked()) }
- }
-
- /// Attempts to return a mutable reference to the wrapped `T`, failing if
- /// `self` is not properly aligned.
- ///
- /// If `self` does not satisfy `mem::align_of::<T>()`, then it is unsound to
- /// return a reference to the wrapped `T`, and `try_deref_mut` returns
- /// `None`.
- ///
- /// If `T: Unaligned`, then `Unalign<T>` implements [`DerefMut`], and
- /// callers may prefer [`DerefMut::deref_mut`], which is infallible.
- #[inline(always)]
- pub fn try_deref_mut(&mut self) -> Option<&mut T> {
- if !crate::util::aligned_to::<_, T>(&*self) {
- return None;
- }
-
- // SAFETY: `deref_mut_unchecked`'s safety requirement is that `self` is
- // aligned to `align_of::<T>()`, which we just checked.
- unsafe { Some(self.deref_mut_unchecked()) }
- }
-
- /// Returns a reference to the wrapped `T` without checking alignment.
- ///
- /// If `T: Unaligned`, then `Unalign<T>` implements[ `Deref`], and callers
- /// may prefer [`Deref::deref`], which is safe.
- ///
- /// # Safety
- ///
- /// If `self` does not satisfy `mem::align_of::<T>()`, then
- /// `self.deref_unchecked()` may cause undefined behavior.
- #[inline(always)]
- pub const unsafe fn deref_unchecked(&self) -> &T {
- // SAFETY: `Unalign<T>` is `repr(transparent)`, so there is a valid `T`
- // at the same memory location as `self`. It has no alignment guarantee,
- // but the caller has promised that `self` is properly aligned, so we
- // know that it is sound to create a reference to `T` at this memory
- // location.
- //
- // We use `mem::transmute` instead of `&*self.get_ptr()` because
- // dereferencing pointers is not stable in `const` on our current MSRV
- // (1.56 as of this writing).
- unsafe { mem::transmute(self) }
- }
-
- /// Returns a mutable reference to the wrapped `T` without checking
- /// alignment.
- ///
- /// If `T: Unaligned`, then `Unalign<T>` implements[ `DerefMut`], and
- /// callers may prefer [`DerefMut::deref_mut`], which is safe.
- ///
- /// # Safety
- ///
- /// If `self` does not satisfy `mem::align_of::<T>()`, then
- /// `self.deref_mut_unchecked()` may cause undefined behavior.
- #[inline(always)]
- pub unsafe fn deref_mut_unchecked(&mut self) -> &mut T {
- // SAFETY: `self.get_mut_ptr()` returns a raw pointer to a valid `T` at
- // the same memory location as `self`. It has no alignment guarantee,
- // but the caller has promised that `self` is properly aligned, so we
- // know that the pointer itself is aligned, and thus that it is sound to
- // create a reference to a `T` at this memory location.
- unsafe { &mut *self.get_mut_ptr() }
- }
-
- /// Gets an unaligned raw pointer to the inner `T`.
- ///
- /// # Safety
- ///
- /// The returned raw pointer is not necessarily aligned to
- /// `align_of::<T>()`. Most functions which operate on raw pointers require
- /// those pointers to be aligned, so calling those functions with the result
- /// of `get_ptr` will be undefined behavior if alignment is not guaranteed
- /// using some out-of-band mechanism. In general, the only functions which
- /// are safe to call with this pointer are those which are explicitly
- /// documented as being sound to use with an unaligned pointer, such as
- /// [`read_unaligned`].
- ///
- /// [`read_unaligned`]: core::ptr::read_unaligned
- #[inline(always)]
- pub const fn get_ptr(&self) -> *const T {
- ptr::addr_of!(self.0)
- }
-
- /// Gets an unaligned mutable raw pointer to the inner `T`.
- ///
- /// # Safety
- ///
- /// The returned raw pointer is not necessarily aligned to
- /// `align_of::<T>()`. Most functions which operate on raw pointers require
- /// those pointers to be aligned, so calling those functions with the result
- /// of `get_ptr` will be undefined behavior if alignment is not guaranteed
- /// using some out-of-band mechanism. In general, the only functions which
- /// are safe to call with this pointer are those which are explicitly
- /// documented as being sound to use with an unaligned pointer, such as
- /// [`read_unaligned`].
- ///
- /// [`read_unaligned`]: core::ptr::read_unaligned
- // TODO(https://github.com/rust-lang/rust/issues/57349): Make this `const`.
- #[inline(always)]
- pub fn get_mut_ptr(&mut self) -> *mut T {
- ptr::addr_of_mut!(self.0)
- }
-
- /// Sets the inner `T`, dropping the previous value.
- // TODO(https://github.com/rust-lang/rust/issues/57349): Make this `const`.
- #[inline(always)]
- pub fn set(&mut self, t: T) {
- *self = Unalign::new(t);
- }
-
- /// Updates the inner `T` by calling a function on it.
- ///
- /// If [`T: Unaligned`], then `Unalign<T>` implements [`DerefMut`], and that
- /// impl should be preferred over this method when performing updates, as it
- /// will usually be faster and more ergonomic.
- ///
- /// For large types, this method may be expensive, as it requires copying
- /// `2 * size_of::<T>()` bytes. \[1\]
- ///
- /// \[1\] Since the inner `T` may not be aligned, it would not be sound to
- /// invoke `f` on it directly. Instead, `update` moves it into a
- /// properly-aligned location in the local stack frame, calls `f` on it, and
- /// then moves it back to its original location in `self`.
- ///
- /// [`T: Unaligned`]: Unaligned
- #[inline]
- pub fn update<O, F: FnOnce(&mut T) -> O>(&mut self, f: F) -> O {
- // On drop, this moves `copy` out of itself and uses `ptr::write` to
- // overwrite `slf`.
- struct WriteBackOnDrop<T> {
- copy: ManuallyDrop<T>,
- slf: *mut Unalign<T>,
- }
-
- impl<T> Drop for WriteBackOnDrop<T> {
- fn drop(&mut self) {
- // SAFETY: See inline comments.
- unsafe {
- // SAFETY: We never use `copy` again as required by
- // `ManuallyDrop::take`.
- let copy = ManuallyDrop::take(&mut self.copy);
- // SAFETY: `slf` is the raw pointer value of `self`. We know
- // it is valid for writes and properly aligned because
- // `self` is a mutable reference, which guarantees both of
- // these properties.
- ptr::write(self.slf, Unalign::new(copy));
- }
- }
- }
-
- // SAFETY: We know that `self` is valid for reads, properly aligned, and
- // points to an initialized `Unalign<T>` because it is a mutable
- // reference, which guarantees all of these properties.
- //
- // Since `T: !Copy`, it would be unsound in the general case to allow
- // both the original `Unalign<T>` and the copy to be used by safe code.
- // We guarantee that the copy is used to overwrite the original in the
- // `Drop::drop` impl of `WriteBackOnDrop`. So long as this `drop` is
- // called before any other safe code executes, soundness is upheld.
- // While this method can terminate in two ways (by returning normally or
- // by unwinding due to a panic in `f`), in both cases, `write_back` is
- // dropped - and its `drop` called - before any other safe code can
- // execute.
- let copy = unsafe { ptr::read(self) }.into_inner();
- let mut write_back = WriteBackOnDrop { copy: ManuallyDrop::new(copy), slf: self };
-
- let ret = f(&mut write_back.copy);
-
- drop(write_back);
- ret
- }
-}
-
-impl<T: Copy> Unalign<T> {
- /// Gets a copy of the inner `T`.
- // TODO(https://github.com/rust-lang/rust/issues/57349): Make this `const`.
- #[inline(always)]
- pub fn get(&self) -> T {
- let Unalign(val) = *self;
- val
- }
-}
-
-impl<T: Unaligned> Deref for Unalign<T> {
- type Target = T;
-
- #[inline(always)]
- fn deref(&self) -> &T {
- // SAFETY: `deref_unchecked`'s safety requirement is that `self` is
- // aligned to `align_of::<T>()`. `T: Unaligned` guarantees that
- // `align_of::<T>() == 1`, and all pointers are one-aligned because all
- // addresses are divisible by 1.
- unsafe { self.deref_unchecked() }
- }
-}
-
-impl<T: Unaligned> DerefMut for Unalign<T> {
- #[inline(always)]
- fn deref_mut(&mut self) -> &mut T {
- // SAFETY: `deref_mut_unchecked`'s safety requirement is that `self` is
- // aligned to `align_of::<T>()`. `T: Unaligned` guarantees that
- // `align_of::<T>() == 1`, and all pointers are one-aligned because all
- // addresses are divisible by 1.
- unsafe { self.deref_mut_unchecked() }
- }
-}
-
-impl<T: Unaligned + PartialOrd> PartialOrd<Unalign<T>> for Unalign<T> {
- #[inline(always)]
- fn partial_cmp(&self, other: &Unalign<T>) -> Option<Ordering> {
- PartialOrd::partial_cmp(self.deref(), other.deref())
- }
-}
-
-impl<T: Unaligned + Ord> Ord for Unalign<T> {
- #[inline(always)]
- fn cmp(&self, other: &Unalign<T>) -> Ordering {
- Ord::cmp(self.deref(), other.deref())
- }
-}
-
-impl<T: Unaligned + PartialEq> PartialEq<Unalign<T>> for Unalign<T> {
- #[inline(always)]
- fn eq(&self, other: &Unalign<T>) -> bool {
- PartialEq::eq(self.deref(), other.deref())
- }
-}
-
-impl<T: Unaligned + Eq> Eq for Unalign<T> {}
-
-impl<T: Unaligned + Hash> Hash for Unalign<T> {
- #[inline(always)]
- fn hash<H>(&self, state: &mut H)
- where
- H: Hasher,
- {
- self.deref().hash(state);
- }
-}
-
-impl<T: Unaligned + Debug> Debug for Unalign<T> {
- #[inline(always)]
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- Debug::fmt(self.deref(), f)
- }
-}
-
-impl<T: Unaligned + Display> Display for Unalign<T> {
- #[inline(always)]
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- Display::fmt(self.deref(), f)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use core::panic::AssertUnwindSafe;
-
- use super::*;
- use crate::util::testutil::*;
-
- /// A `T` which is guaranteed not to satisfy `align_of::<A>()`.
- ///
- /// It must be the case that `align_of::<T>() < align_of::<A>()` in order
- /// fot this type to work properly.
- #[repr(C)]
- struct ForceUnalign<T, A> {
- // The outer struct is aligned to `A`, and, thanks to `repr(C)`, `t` is
- // placed at the minimum offset that guarantees its alignment. If
- // `align_of::<T>() < align_of::<A>()`, then that offset will be
- // guaranteed *not* to satisfy `align_of::<A>()`.
- _u: u8,
- t: T,
- _a: [A; 0],
- }
-
- impl<T, A> ForceUnalign<T, A> {
- const fn new(t: T) -> ForceUnalign<T, A> {
- ForceUnalign { _u: 0, t, _a: [] }
- }
- }
-
- #[test]
- fn test_unalign() {
- // Test methods that don't depend on alignment.
- let mut u = Unalign::new(AU64(123));
- assert_eq!(u.get(), AU64(123));
- assert_eq!(u.into_inner(), AU64(123));
- assert_eq!(u.get_ptr(), <*const _>::cast::<AU64>(&u));
- assert_eq!(u.get_mut_ptr(), <*mut _>::cast::<AU64>(&mut u));
- u.set(AU64(321));
- assert_eq!(u.get(), AU64(321));
-
- // Test methods that depend on alignment (when alignment is satisfied).
- let mut u: Align<_, AU64> = Align::new(Unalign::new(AU64(123)));
- assert_eq!(u.t.try_deref(), Some(&AU64(123)));
- assert_eq!(u.t.try_deref_mut(), Some(&mut AU64(123)));
- // SAFETY: The `Align<_, AU64>` guarantees proper alignment.
- assert_eq!(unsafe { u.t.deref_unchecked() }, &AU64(123));
- // SAFETY: The `Align<_, AU64>` guarantees proper alignment.
- assert_eq!(unsafe { u.t.deref_mut_unchecked() }, &mut AU64(123));
- *u.t.try_deref_mut().unwrap() = AU64(321);
- assert_eq!(u.t.get(), AU64(321));
-
- // Test methods that depend on alignment (when alignment is not
- // satisfied).
- let mut u: ForceUnalign<_, AU64> = ForceUnalign::new(Unalign::new(AU64(123)));
- assert_eq!(u.t.try_deref(), None);
- assert_eq!(u.t.try_deref_mut(), None);
-
- // Test methods that depend on `T: Unaligned`.
- let mut u = Unalign::new(123u8);
- assert_eq!(u.try_deref(), Some(&123));
- assert_eq!(u.try_deref_mut(), Some(&mut 123));
- assert_eq!(u.deref(), &123);
- assert_eq!(u.deref_mut(), &mut 123);
- *u = 21;
- assert_eq!(u.get(), 21);
-
- // Test that some `Unalign` functions and methods are `const`.
- const _UNALIGN: Unalign<u64> = Unalign::new(0);
- const _UNALIGN_PTR: *const u64 = _UNALIGN.get_ptr();
- const _U64: u64 = _UNALIGN.into_inner();
- // Make sure all code is considered "used".
- //
- // TODO(https://github.com/rust-lang/rust/issues/104084): Remove this
- // attribute.
- #[allow(dead_code)]
- const _: () = {
- let x: Align<_, AU64> = Align::new(Unalign::new(AU64(123)));
- // Make sure that `deref_unchecked` is `const`.
- //
- // SAFETY: The `Align<_, AU64>` guarantees proper alignment.
- let au64 = unsafe { x.t.deref_unchecked() };
- match au64 {
- AU64(123) => {}
- _ => unreachable!(),
- }
- };
- }
-
- #[test]
- fn test_unalign_update() {
- let mut u = Unalign::new(AU64(123));
- u.update(|a| a.0 += 1);
- assert_eq!(u.get(), AU64(124));
-
- // Test that, even if the callback panics, the original is still
- // correctly overwritten. Use a `Box` so that Miri is more likely to
- // catch any unsoundness (which would likely result in two `Box`es for
- // the same heap object, which is the sort of thing that Miri would
- // probably catch).
- let mut u = Unalign::new(Box::new(AU64(123)));
- let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
- u.update(|a| {
- a.0 += 1;
- panic!();
- })
- }));
- assert!(res.is_err());
- assert_eq!(u.into_inner(), Box::new(AU64(124)));
- }
-}
diff --git a/tests/trybuild.rs b/tests/trybuild.rs
deleted file mode 100644
index 4ed01f7..0000000
--- a/tests/trybuild.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// UI tests depend on the exact error messages emitted by rustc, but those error
-// messages are not stable, and sometimes change between Rust versions. Thus, we
-// maintain one set of UI tests for each Rust version that we test in CI, and we
-// pin to specific versions in CI (a specific stable version, a specific date of
-// the nightly compiler, and a specific MSRV). Updating those pinned versions
-// may also require updating these tests.
-// - `tests/ui-nightly` - Contains the source of truth for our UI test source
-// files (`.rs`), and contains `.err` and `.out` files for nightly
-// - `tests/ui-stable` - Contains symlinks to the `.rs` files in
-// `tests/ui-nightly`, and contains `.err` and `.out` files for stable
-// - `tests/ui-msrv` - Contains symlinks to the `.rs` files in
-// `tests/ui-nightly`, and contains `.err` and `.out` files for MSRV
-
-#[rustversion::nightly]
-const SOURCE_FILES_DIR: &str = "tests/ui-nightly";
-#[rustversion::stable(1.69.0)]
-const SOURCE_FILES_DIR: &str = "tests/ui-stable";
-#[rustversion::stable(1.61.0)]
-const SOURCE_FILES_DIR: &str = "tests/ui-msrv";
-
-const SOURCE_FILES_DIR: &str = "tests/ui-stable";
-
-#[test]
-fn ui() {
- let t = trybuild::TestCases::new();
- t.compile_fail(format!("{SOURCE_FILES_DIR}/*.rs"));
-}
-
-// The file `invalid-impls.rs` directly includes `src/macros.rs` in order to
-// test the `impl_or_verify!` macro which is defined in that file. Specifically,
-// it tests the verification portion of that macro, which is enabled when
-// `cfg(any(feature = "derive", test))`. While `--cfg test` is of course passed
-// to the code in the file you're reading right now, `trybuild` does not pass
-// `--cfg test` when it invokes Cargo. As a result, this `trybuild` test only
-// tests the correct behavior when the "derive" feature is enabled.
-#[cfg(feature = "derive")]
-#[test]
-fn ui_invalid_impls() {
- let t = trybuild::TestCases::new();
- t.compile_fail(format!("{SOURCE_FILES_DIR}/invalid-impls/*.rs"));
-}
diff --git a/tests/ui-msrv/invalid-impls/invalid-impls.rs b/tests/ui-msrv/invalid-impls/invalid-impls.rs
deleted file mode 100644
index b9a60bd..0000000
--- a/tests/ui-msrv/invalid-impls/invalid-impls.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2022 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Since some macros from `macros.rs` are unused.
-#![allow(unused)]
-
-extern crate zerocopy;
-extern crate zerocopy_derive;
-
-include!("../../../src/macros.rs");
-
-use zerocopy::*;
-use zerocopy_derive::*;
-
-fn main() {}
-
-#[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
-#[repr(transparent)]
-struct Foo<T>(T);
-
-impl_or_verify!(T => FromZeroes for Foo<T>);
-impl_or_verify!(T => FromBytes for Foo<T>);
-impl_or_verify!(T => AsBytes for Foo<T>);
-impl_or_verify!(T => Unaligned for Foo<T>);
diff --git a/tests/ui-msrv/invalid-impls/invalid-impls.stderr b/tests/ui-msrv/invalid-impls/invalid-impls.stderr
deleted file mode 100644
index fee0cd9..0000000
--- a/tests/ui-msrv/invalid-impls/invalid-impls.stderr
+++ /dev/null
@@ -1,127 +0,0 @@
-error[E0277]: the trait bound `T: zerocopy::FromZeroes` is not satisfied
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
- | ^^^^^^^^ the trait `zerocopy::FromZeroes` is not implemented for `T`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:22:1
- |
-22 | impl_or_verify!(T => FromZeroes for Foo<T>);
- | ------------------------------------------- in this macro invocation
- |
-note: required because of the requirements on the impl of `zerocopy::FromZeroes` for `Foo<T>`
- --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:10
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^^
-note: required by a bound in `_::Subtrait`
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `_::Subtrait`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:22:1
- |
-22 | impl_or_verify!(T => FromZeroes for Foo<T>);
- | ------------------------------------------- in this macro invocation
- = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-22 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>);
- | ++++++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
- | ^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `T`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:23:1
- |
-23 | impl_or_verify!(T => FromBytes for Foo<T>);
- | ------------------------------------------ in this macro invocation
- |
-note: required because of the requirements on the impl of `zerocopy::FromBytes` for `Foo<T>`
- --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:22
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^
-note: required by a bound in `_::Subtrait`
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `_::Subtrait`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:23:1
- |
-23 | impl_or_verify!(T => FromBytes for Foo<T>);
- | ------------------------------------------ in this macro invocation
- = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-23 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>);
- | +++++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
- | ^^^^^^^^ the trait `zerocopy::AsBytes` is not implemented for `T`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:24:1
- |
-24 | impl_or_verify!(T => AsBytes for Foo<T>);
- | ---------------------------------------- in this macro invocation
- |
-note: required because of the requirements on the impl of `zerocopy::AsBytes` for `Foo<T>`
- --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:33
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^
-note: required by a bound in `_::Subtrait`
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `_::Subtrait`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:24:1
- |
-24 | impl_or_verify!(T => AsBytes for Foo<T>);
- | ---------------------------------------- in this macro invocation
- = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-24 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>);
- | +++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
- | ^^^^^^^^ the trait `zerocopy::Unaligned` is not implemented for `T`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:25:1
- |
-25 | impl_or_verify!(T => Unaligned for Foo<T>);
- | ------------------------------------------ in this macro invocation
- |
-note: required because of the requirements on the impl of `zerocopy::Unaligned` for `Foo<T>`
- --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:42
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^
-note: required by a bound in `_::Subtrait`
- --> tests/ui-msrv/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `_::Subtrait`
- |
- ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:25:1
- |
-25 | impl_or_verify!(T => Unaligned for Foo<T>);
- | ------------------------------------------ in this macro invocation
- = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-25 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>);
- | +++++++++++++++++++++
diff --git a/tests/ui-msrv/transmute-illegal.rs b/tests/ui-msrv/transmute-illegal.rs
deleted file mode 100644
index 74b8439..0000000
--- a/tests/ui-msrv/transmute-illegal.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2022 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-extern crate zerocopy;
-
-fn main() {}
-
-// It is unsound to inspect the usize value of a pointer during const eval.
-const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
diff --git a/tests/ui-msrv/transmute-illegal.stderr b/tests/ui-msrv/transmute-illegal.stderr
deleted file mode 100644
index 37c124a..0000000
--- a/tests/ui-msrv/transmute-illegal.stderr
+++ /dev/null
@@ -1,18 +0,0 @@
-error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied
- --> tests/ui-msrv/transmute-illegal.rs:10:30
- |
-10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `*const usize`
- |
- = help: the following implementations were found:
- <usize as AsBytes>
- <f32 as AsBytes>
- <f64 as AsBytes>
- <i128 as AsBytes>
- and $N others
-note: required by a bound in `POINTER_VALUE::transmute`
- --> tests/ui-msrv/transmute-illegal.rs:10:30
- |
-10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `POINTER_VALUE::transmute`
- = note: this error originates in the macro `zerocopy::transmute` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui-nightly/invalid-impls/invalid-impls.rs b/tests/ui-nightly/invalid-impls/invalid-impls.rs
deleted file mode 100644
index b9a60bd..0000000
--- a/tests/ui-nightly/invalid-impls/invalid-impls.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2022 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Since some macros from `macros.rs` are unused.
-#![allow(unused)]
-
-extern crate zerocopy;
-extern crate zerocopy_derive;
-
-include!("../../../src/macros.rs");
-
-use zerocopy::*;
-use zerocopy_derive::*;
-
-fn main() {}
-
-#[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
-#[repr(transparent)]
-struct Foo<T>(T);
-
-impl_or_verify!(T => FromZeroes for Foo<T>);
-impl_or_verify!(T => FromBytes for Foo<T>);
-impl_or_verify!(T => AsBytes for Foo<T>);
-impl_or_verify!(T => Unaligned for Foo<T>);
diff --git a/tests/ui-nightly/invalid-impls/invalid-impls.stderr b/tests/ui-nightly/invalid-impls/invalid-impls.stderr
deleted file mode 100644
index 7d839ac..0000000
--- a/tests/ui-nightly/invalid-impls/invalid-impls.stderr
+++ /dev/null
@@ -1,107 +0,0 @@
-error[E0277]: the trait bound `T: zerocopy::FromZeroes` is not satisfied
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:22:37
- |
-22 | impl_or_verify!(T => FromZeroes for Foo<T>);
- | ^^^^^^ the trait `zerocopy::FromZeroes` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::FromZeroes`
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:10
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-nightly/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:22:1
- |
-22 | impl_or_verify!(T => FromZeroes for Foo<T>);
- | ------------------------------------------- in this macro invocation
- = note: this error originates in the derive macro `FromZeroes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-22 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>);
- | ++++++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:23:36
- |
-23 | impl_or_verify!(T => FromBytes for Foo<T>);
- | ^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::FromBytes`
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:22
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-nightly/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:23:1
- |
-23 | impl_or_verify!(T => FromBytes for Foo<T>);
- | ------------------------------------------ in this macro invocation
- = note: this error originates in the derive macro `FromBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-23 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>);
- | +++++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:24:34
- |
-24 | impl_or_verify!(T => AsBytes for Foo<T>);
- | ^^^^^^ the trait `zerocopy::AsBytes` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::AsBytes`
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:33
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-nightly/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:24:1
- |
-24 | impl_or_verify!(T => AsBytes for Foo<T>);
- | ---------------------------------------- in this macro invocation
- = note: this error originates in the derive macro `AsBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-24 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>);
- | +++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:25:36
- |
-25 | impl_or_verify!(T => Unaligned for Foo<T>);
- | ^^^^^^ the trait `zerocopy::Unaligned` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::Unaligned`
- --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:42
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-nightly/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:25:1
- |
-25 | impl_or_verify!(T => Unaligned for Foo<T>);
- | ------------------------------------------ in this macro invocation
- = note: this error originates in the derive macro `Unaligned` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-25 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>);
- | +++++++++++++++++++++
diff --git a/tests/ui-nightly/transmute-illegal.rs b/tests/ui-nightly/transmute-illegal.rs
deleted file mode 100644
index 74b8439..0000000
--- a/tests/ui-nightly/transmute-illegal.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2022 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-extern crate zerocopy;
-
-fn main() {}
-
-// It is unsound to inspect the usize value of a pointer during const eval.
-const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
diff --git a/tests/ui-nightly/transmute-illegal.stderr b/tests/ui-nightly/transmute-illegal.stderr
deleted file mode 100644
index a57544b..0000000
--- a/tests/ui-nightly/transmute-illegal.stderr
+++ /dev/null
@@ -1,16 +0,0 @@
-error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied
- --> tests/ui-nightly/transmute-illegal.rs:10:30
- |
-10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the trait `AsBytes` is not implemented for `*const usize`
- | required by a bound introduced by this call
- |
- = help: the trait `AsBytes` is implemented for `usize`
-note: required by a bound in `POINTER_VALUE::transmute`
- --> tests/ui-nightly/transmute-illegal.rs:10:30
- |
-10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute`
- = note: this error originates in the macro `zerocopy::transmute` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui-stable/invalid-impls/invalid-impls.rs b/tests/ui-stable/invalid-impls/invalid-impls.rs
deleted file mode 100644
index b9a60bd..0000000
--- a/tests/ui-stable/invalid-impls/invalid-impls.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2022 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Since some macros from `macros.rs` are unused.
-#![allow(unused)]
-
-extern crate zerocopy;
-extern crate zerocopy_derive;
-
-include!("../../../src/macros.rs");
-
-use zerocopy::*;
-use zerocopy_derive::*;
-
-fn main() {}
-
-#[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
-#[repr(transparent)]
-struct Foo<T>(T);
-
-impl_or_verify!(T => FromZeroes for Foo<T>);
-impl_or_verify!(T => FromBytes for Foo<T>);
-impl_or_verify!(T => AsBytes for Foo<T>);
-impl_or_verify!(T => Unaligned for Foo<T>);
diff --git a/tests/ui-stable/invalid-impls/invalid-impls.stderr b/tests/ui-stable/invalid-impls/invalid-impls.stderr
deleted file mode 100644
index f613377..0000000
--- a/tests/ui-stable/invalid-impls/invalid-impls.stderr
+++ /dev/null
@@ -1,107 +0,0 @@
-error[E0277]: the trait bound `T: zerocopy::FromZeroes` is not satisfied
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:22:37
- |
-22 | impl_or_verify!(T => FromZeroes for Foo<T>);
- | ^^^^^^ the trait `zerocopy::FromZeroes` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::FromZeroes`
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:10
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-stable/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-stable/invalid-impls/invalid-impls.rs:22:1
- |
-22 | impl_or_verify!(T => FromZeroes for Foo<T>);
- | ------------------------------------------- in this macro invocation
- = note: this error originates in the derive macro `FromZeroes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-22 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>);
- | ++++++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:23:36
- |
-23 | impl_or_verify!(T => FromBytes for Foo<T>);
- | ^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::FromBytes`
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:22
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-stable/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-stable/invalid-impls/invalid-impls.rs:23:1
- |
-23 | impl_or_verify!(T => FromBytes for Foo<T>);
- | ------------------------------------------ in this macro invocation
- = note: this error originates in the derive macro `FromBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-23 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>);
- | +++++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:24:34
- |
-24 | impl_or_verify!(T => AsBytes for Foo<T>);
- | ^^^^^^ the trait `zerocopy::AsBytes` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::AsBytes`
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:33
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-stable/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-stable/invalid-impls/invalid-impls.rs:24:1
- |
-24 | impl_or_verify!(T => AsBytes for Foo<T>);
- | ---------------------------------------- in this macro invocation
- = note: this error originates in the derive macro `AsBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-24 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>);
- | +++++++++++++++++++
-
-error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:25:36
- |
-25 | impl_or_verify!(T => Unaligned for Foo<T>);
- | ^^^^^^ the trait `zerocopy::Unaligned` is not implemented for `T`
- |
-note: required for `Foo<T>` to implement `zerocopy::Unaligned`
- --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:42
- |
-18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
- | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
-note: required by a bound in `_::Subtrait`
- --> tests/ui-stable/invalid-impls/../../../src/macros.rs
- |
- | trait Subtrait: $trait {}
- | ^^^^^^ required by this bound in `Subtrait`
- |
- ::: tests/ui-stable/invalid-impls/invalid-impls.rs:25:1
- |
-25 | impl_or_verify!(T => Unaligned for Foo<T>);
- | ------------------------------------------ in this macro invocation
- = note: this error originates in the derive macro `Unaligned` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider restricting type parameter `T`
- |
-25 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>);
- | +++++++++++++++++++++
diff --git a/tests/ui-stable/transmute-illegal.rs b/tests/ui-stable/transmute-illegal.rs
deleted file mode 100644
index 74b8439..0000000
--- a/tests/ui-stable/transmute-illegal.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2022 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-extern crate zerocopy;
-
-fn main() {}
-
-// It is unsound to inspect the usize value of a pointer during const eval.
-const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
diff --git a/tests/ui-stable/transmute-illegal.stderr b/tests/ui-stable/transmute-illegal.stderr
deleted file mode 100644
index e9ac240..0000000
--- a/tests/ui-stable/transmute-illegal.stderr
+++ /dev/null
@@ -1,16 +0,0 @@
-error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied
- --> tests/ui-stable/transmute-illegal.rs:10:30
- |
-10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the trait `AsBytes` is not implemented for `*const usize`
- | required by a bound introduced by this call
- |
- = help: the trait `AsBytes` is implemented for `usize`
-note: required by a bound in `POINTER_VALUE::transmute`
- --> tests/ui-stable/transmute-illegal.rs:10:30
- |
-10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute`
- = note: this error originates in the macro `zerocopy::transmute` (in Nightly builds, run with -Z macro-backtrace for more info)