Repository: Robbepop/modular-bitfield Branch: master Commit: 3eb7cfce96ab Files: 158 Total size: 255.8 KB Directory structure: gitextract_25fixmv3/ ├── .github/ │ └── workflows/ │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches/ │ ├── benchmarks.rs │ ├── cmp_bitfield_crate.rs │ ├── cmp_handwritten.rs │ └── utils/ │ ├── handwritten.rs │ └── mod.rs ├── docs/ │ ├── bitfield.md │ ├── bitfield_specifier.md │ └── index.md ├── impl/ │ ├── Cargo.toml │ └── src/ │ ├── bitfield/ │ │ ├── analyse.rs │ │ ├── config.rs │ │ ├── expand.rs │ │ ├── field_config.rs │ │ ├── field_info.rs │ │ ├── mod.rs │ │ └── params.rs │ ├── bitfield_specifier.rs │ ├── define_specifiers.rs │ ├── errors.rs │ └── lib.rs ├── release.sh ├── src/ │ ├── error.rs │ ├── lib.rs │ └── private/ │ ├── array_bytes_conv.rs │ ├── checks.rs │ ├── impls.rs │ ├── mod.rs │ ├── proc.rs │ ├── push_pop.rs │ └── traits.rs └── tests/ ├── bitfield/ │ ├── bits_param.rs │ ├── bytes_param.rs │ ├── derive_bitfield_specifier.rs │ ├── derive_debug.rs │ ├── derive_specifier.rs │ ├── filled_param.rs │ ├── mod.rs │ ├── no_implicit_prelude.rs │ ├── regressions.rs │ ├── repr.rs │ └── skip.rs ├── lib.rs ├── panic_tests.rs ├── ui/ │ ├── access_test.rs │ ├── access_test.stderr │ ├── bitfield_attribute_wrong.rs │ ├── bitfield_attribute_wrong.stderr │ ├── bits_attribute_wrong.rs │ ├── bits_attribute_wrong.stderr │ ├── bits_param/ │ │ ├── conflicting_params.rs │ │ ├── conflicting_params.stderr │ │ ├── conflicting_repr.rs │ │ ├── conflicting_repr.stderr │ │ ├── duplicate_param_1.rs │ │ ├── duplicate_param_1.stderr │ │ ├── duplicate_param_2.rs │ │ ├── duplicate_param_2.stderr │ │ ├── invalid_param_type.rs │ │ ├── invalid_param_type.stderr │ │ ├── invalid_param_value_1.rs │ │ ├── invalid_param_value_1.stderr │ │ ├── invalid_param_value_2.rs │ │ ├── invalid_param_value_2.stderr │ │ ├── missing_param_value.rs │ │ ├── missing_param_value.stderr │ │ ├── too_few_bits.rs │ │ ├── too_few_bits.stderr │ │ ├── too_many_bits.rs │ │ └── too_many_bits.stderr │ ├── bytes_param/ │ │ ├── duplicate_parameters.rs │ │ ├── duplicate_parameters.stderr │ │ ├── fewer_bytes_than_expected.rs │ │ ├── fewer_bytes_than_expected.stderr │ │ ├── invalid_int_value.rs │ │ ├── invalid_int_value.stderr │ │ ├── invalid_type.rs │ │ ├── invalid_type.stderr │ │ ├── more_bytes_than_expected.rs │ │ └── more_bytes_than_expected.stderr │ ├── derive_bitfield_specifier/ │ │ ├── invalid_bits_attribute.rs │ │ ├── invalid_bits_attribute.stderr │ │ ├── non_power_of_two.rs │ │ ├── non_power_of_two.stderr │ │ ├── variant_out_of_range.rs │ │ └── variant_out_of_range.stderr │ ├── derive_debug/ │ │ ├── duplicate_derive_debug.rs │ │ ├── duplicate_derive_debug.stderr │ │ ├── duplicate_derive_debug_2.rs │ │ └── duplicate_derive_debug_2.stderr │ ├── derive_specifier/ │ │ ├── duplicate_derive_1.rs │ │ ├── duplicate_derive_1.stderr │ │ ├── duplicate_derive_2.rs │ │ ├── duplicate_derive_2.stderr │ │ ├── out_of_bounds.rs │ │ └── out_of_bounds.stderr │ ├── empty.rs │ ├── empty.stderr │ ├── filled_param/ │ │ ├── duplicate_parameters.rs │ │ ├── duplicate_parameters.stderr │ │ ├── invalid_bool_value.rs │ │ ├── invalid_bool_value.stderr │ │ ├── invalid_specified_as_filled.rs │ │ ├── invalid_specified_as_filled.stderr │ │ ├── invalid_specified_as_unfilled.rs │ │ └── invalid_specified_as_unfilled.stderr │ ├── generic.rs │ ├── generic.stderr │ ├── invalid_struct_specifier.rs │ ├── invalid_struct_specifier.stderr │ ├── invalid_union_specifier.rs │ ├── invalid_union_specifier.stderr │ ├── multiple_of_8bits.rs │ ├── multiple_of_8bits.stderr │ ├── regressions/ │ │ ├── invalid_bits_field_attr.rs │ │ └── invalid_bits_field_attr.stderr │ ├── repr/ │ │ ├── conflicting_ignored_reprs.rs │ │ ├── conflicting_ignored_reprs.stderr │ │ ├── duplicate_repr_1.rs │ │ ├── duplicate_repr_1.stderr │ │ ├── duplicate_repr_2.rs │ │ ├── duplicate_repr_2.stderr │ │ ├── duplicate_repr_3.rs │ │ ├── duplicate_repr_3.stderr │ │ ├── invalid_repr_1.rs │ │ ├── invalid_repr_1.stderr │ │ ├── invalid_repr_2.rs │ │ ├── invalid_repr_2.stderr │ │ ├── invalid_repr_unfilled.rs │ │ ├── invalid_repr_unfilled.stderr │ │ ├── invalid_repr_width_1.rs │ │ ├── invalid_repr_width_1.stderr │ │ ├── invalid_repr_width_2.rs │ │ └── invalid_repr_width_2.stderr │ ├── skip/ │ │ ├── duplicate_attr.rs │ │ ├── duplicate_attr.stderr │ │ ├── invalid_specifier.rs │ │ ├── invalid_specifier.stderr │ │ ├── use_skipped_getter_1.rs │ │ ├── use_skipped_getter_1.stderr │ │ ├── use_skipped_getter_2.rs │ │ ├── use_skipped_getter_2.stderr │ │ ├── use_skipped_getter_3.rs │ │ ├── use_skipped_getter_3.stderr │ │ ├── use_skipped_setter_1.rs │ │ ├── use_skipped_setter_1.stderr │ │ ├── use_skipped_setter_2.rs │ │ ├── use_skipped_setter_2.stderr │ │ ├── use_skipped_setter_3.rs │ │ └── use_skipped_setter_3.stderr │ ├── unused_must_use.rs │ └── unused_must_use.stderr └── ui.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/rust.yml ================================================ name: Rust CI on: pull_request: push: branches: - master - '[0-9]+.[0-9]+' env: CARGO_TERM_COLOR: always jobs: lint: name: Lint runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v4 - name: Use cache uses: Swatinem/rust-cache@v2 with: shared-key: stable save-if: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master with: toolchain: stable components: clippy, rustfmt, llvm-tools-preview - name: Check formatting run: cargo fmt -- --check - name: Run clippy run: cargo clippy --all-targets -- -D warnings - name: Build documentation run: cargo rustdoc -- -D warnings test: name: Test on Rust ${{ matrix.rust.name }} runs-on: ubuntu-latest strategy: matrix: rust: - name: stable components: clippy, rustfmt, llvm-tools-preview - name: nightly - name: "1.76" # rust-version, msrv steps: - name: Check out code uses: actions/checkout@v4 # Using env.HOME or $HOME does not work in the rust-cache workflow step, # so just build the path this way. - name: Find toolchain path id: toolchain_path run: echo "path=$HOME/.rustup/toolchains/${{ matrix.rust.name }}-x86_64-unknown-linux-gnu" >> $GITHUB_OUTPUT # Stable toolchain for Rust is already built into the runner image so # only other versions need caching to avoid wasting time and bandwidth # re-downloading them. if: matrix.rust.name != 'stable' - name: Use cache uses: Swatinem/rust-cache@v2 with: shared-key: ${{ matrix.rust.name }} cache-on-failure: true cache-directories: ${{ steps.toolchain_path.outputs.path }} - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust.name }} components: ${{ matrix.rust.components }} # Building is separated from testing just to more clearly differentiate in # the CI whether the build failed or a test failed - name: Build workspace run: cargo build --lib --tests - name: Run tests # UI tests are compiler-version-sensitive so can only run on one # toolchain run: cargo test ${{ matrix.rust.name != 'stable' && '-- --skip ui_trybuild' || '' }} coverage: name: Code coverage runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v4 - name: Use cache uses: Swatinem/rust-cache@v2 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master with: toolchain: stable components: clippy, rustfmt, llvm-tools-preview - name: Install llvm-cov uses: taiki-e/install-action@v2 with: tool: cargo-llvm-cov # UI tests do not contribute to coverage since trybuild builds them # independently so they do not get instrumented (even though llvm-cov # tries?), so skip them. They will get passed through runtime-macros # instead - name: Generate coverage data run: cargo llvm-cov test --no-report -- --skip ui_trybuild - name: Show coverage results run: > cargo llvm-cov report --ignore-filename-regex 'benches|tests' # https://github.com/actions/runner/issues/520 - name: Determine whether codecov.io secret is available id: has_codecov run: echo 'result=${{ secrets.CODECOV_TOKEN }}' >> $GITHUB_OUTPUT - name: Generate coverage file run: > cargo llvm-cov report --ignore-filename-regex 'benches|tests' --lcov --output-path lcov.info if: steps.has_codecov.outputs.result != 0 - name: Upload to codecov.io uses: codecov/codecov-action@v5 with: files: lcov.info token: ${{ secrets.CODECOV_TOKEN }} if: steps.has_codecov.outputs.result != 0 ================================================ FILE: .gitignore ================================================ # Ignore build artifacts from the local tests sub-crate. /target/ /impl/target/ # Ignore backup files creates by cargo fmt. **/*.rs.bk # Remove Cargo.lock when creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock # Ignore VS Code artifacts. **/.vscode/** # Ignore idea artifacts. **/.idea/** ================================================ FILE: Cargo.toml ================================================ [workspace] members = [ "impl" ] default-members = ["", "impl"] resolver = "2" [workspace.package] authors = ["Robin Freyler "] edition = "2021" license = "MIT OR Apache-2.0" publish = false # Use `release.sh` documentation = "https://docs.rs/modular-bitfield" repository = "https://github.com/modular-bitfield/modular-bitfield" rust-version = "1.76.0" version = "0.14.0-pre" [package] name = "modular-bitfield" description = "Easily define bitfield types with modular building blocks." categories = ["data-structures", "no-std"] keywords = ["bitfield", "bit", "bitfields"] readme = "README.md" authors.workspace = true documentation.workspace = true edition.workspace = true license.workspace = true publish.workspace = true repository.workspace = true rust-version.workspace = true version.workspace = true [dependencies] modular-bitfield-impl = { path = "impl", version = "0.14.0-pre" } static_assertions = "1.1" [dev-dependencies] bitfield = "0.19" tiny-bench = "0.4" trybuild = "1.0" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] } [[bench]] name = "benchmarks" harness = false [[bench]] name = "cmp_handwritten" harness = false [[bench]] name = "cmp_bitfield_crate" harness = false [[test]] name = "benches_handwritten" path = "benches/utils/handwritten.rs" [profile.bench] codegen-units = 1 ================================================ FILE: LICENSE-APACHE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: LICENSE-MIT ================================================ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Modular Bitfields for Rust [![crates](https://img.shields.io/crates/v/modular-bitfield.svg)](https://crates.io/crates/modular-bitfield) [![tests](https://github.com/modular-bitfield/modular-bitfield/actions/workflows/rust.yml/badge.svg)](https://github.com/modular-bitfield/modular-bitfield/actions/workflows/rust.yml) [![docs.rs](https://docs.rs/modular-bitfield/badge.svg)](https://docs.rs/modular-bitfield) [![codecov](https://codecov.io/gh/modular-bitfield/modular-bitfield/graph/badge.svg?token=CVN2VO1fY6)](https://codecov.io/gh/modular-bitfield/modular-bitfield) - `no_std`: Supports embedded development without `std` library. - This crate uses and generates 100% safe Rust code. ## Description Allows to have bitfield structs and enums as bitfield specifiers that work very similar to C and C++ bitfields. ## Advantages - **Safety:** Macro embraced enums and structs are checked for valid structure during compilation time. - **Speed:** Generated code is as fast as handwritten code. (See benchmarks below.) - **Modularity:** Enums can be used modular within bitfield structs. ## Attribution Implements the `#[bitfield]` macros introduced and specified in David Tolnay's [procedural macro workshop][procedural-macro-workshop]. Thanks go to David Tolnay for designing the specification for the macros implemented in this crate. ## Usage Annotate a Rust struct with the `#[bitfield]` attribute in order to convert it into a bitfield. The `B1`, `B2`, ... `B128` prelude types can be used as primitives to declare the number of bits per field. ```rust #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: B1, status: B2, } ``` This produces a `new` constructor as well as a variety of getters and setters that allows to interact with the bitfield in a safe fashion: ### Example: Constructors ```rust let data = PackedData::new() .with_header(1) .with_body(2) .with_is_alive(0) .with_status(3); assert_eq!(data.header(), 1); assert_eq!(data.body(), 2); assert_eq!(data.is_alive(), 0); assert_eq!(data.status(), 3); ``` ### Example: Primitive Types Any type that implements the `Specifier` trait can be used as a bitfield field. Besides the already mentioned `B1`, .. `B128` also the `bool`, `u8`, `u16`, `u32`, `u64` or `u128` primitive types can be used from prelude. We can use this knowledge to encode our `is_alive` as `bool` type instead of `B1`: ```rust #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: bool, status: B2, } let mut data = PackedData::new() .with_is_alive(true); assert!(data.is_alive()); data.set_is_alive(false); assert!(!data.is_alive()); ``` ### Example: Enum Specifiers It is possible to derive the `Specifier` trait for `enum` types very easily to make them also usable as a field within a bitfield type: ```rust #[derive(Specifier)] pub enum Status { Red, Green, Yellow, None, } #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: bool, status: Status, } ``` ### Example: Extra Safety Guard In order to make sure that our `Status` enum still requires exatly 2 bit we can add `#[bits = 2]` to its field: ```rust #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: bool, #[bits = 2] status: Status, } ``` Setting and getting our new `status` field is naturally as follows: ```rust let mut data = PackedData::new() .with_status(Status::Green); assert_eq!(data.status(), Status::Green); data.set_status(Status::Red); assert_eq!(data.status(), Status::Red); ``` ### Example: Recursive Bitfields It is possible to use `#[bitfield]` structs as fields of `#[bitfield]` structs. This is generally useful if there are some common fields for multiple bitfields and is achieved by adding `#[derive(Specifier)]` to the attributes of the `#[bitfield]` annotated struct: ```rust #[bitfield] #[derive(Specifier)] pub struct Header { is_compact: bool, is_secure: bool, pre_status: Status, } #[bitfield] pub struct PackedData { header: Header, body: B9, is_alive: bool, status: Status, } ``` With the `bits: int` parameter of the `#[bitfield]` macro on the `Header` struct and the `#[bits: int]` attribute of the `#[derive(Specifier)]` on the `Status` enum we can have additional compile-time guarantees about the bit widths of the resulting entities: ```rust #[derive(Specifier)] #[bits = 2] pub enum Status { Red, Green, Yellow } #[bitfield(bits = 4)] #[derive(Specifier)] pub struct Header { is_compact: bool, is_secure: bool, #[bits = 2] pre_status: Status, } #[bitfield(bits = 16)] pub struct PackedData { #[bits = 4] header: Header, body: B9, is_alive: bool, #[bits = 2] status: Status, } ``` ### Example: Advanced Enum Specifiers For our `Status` enum we actually just need 3 status variants: `Green`, `Yellow` and `Red`. We introduced the `None` status variants because `Specifier` enums by default are required to have a number of variants that is a power of two. We can ship around this by specifying `#[bits = 2]` on the top and get rid of our placeholder `None` variant while maintaining the invariant of it requiring 2 bits: ```rust # use modular_bitfield::prelude::*; #[derive(Specifier)] #[bits = 2] pub enum Status { Red, Green, Yellow, } ``` However, having such enums now yields the possibility that a bitfield might contain invalid bit patterns for such fields. We can safely access those fields with protected getters. For the sake of demonstration we will use the generated `from_bytes` constructor with which we can easily construct bitfields that may contain invalid bit patterns: ```rust let mut data = PackedData::from_bytes([0b0000_0000, 0b1100_0000]); // The 2 status field bits are invalid -----^^ // as Red = 0x00, Green = 0x01 and Yellow = 0x10 assert_eq!(data.status_or_err(), Err(InvalidBitPattern::new(0b11))); data.set_status(Status::Green); assert_eq!(data.status_or_err(), Ok(Status::Green)); ``` ## Benchmarks Below are some benchmarks between the [hand-written code][benchmark-code] and the macro-generated code for some example getters and setters that cover a decent variety of use cases. We can conclude that the macro-generated code is as fast as hand-written code would be. Please file a PR if you see a way to improve either side. - `cargo bench` to run the benchmarks - `cargo test --benches` to run the benchmark tests [Click here to view all benchmark results.][benchmark-results] [benchmark-code]: https://github.com/modular-bitfield/modular-bitfield/blob/master/benches/handwritten.rs [benchmark-results]: https://gist.github.com/Robbepop/bcff4fe149e0e622b752f0eb07b31880 ### Summary The `modular_bitfield` crate generates bitfields that are ... - just as efficient as the handwritten alternatives. - equally efficient or more efficient than the alternative [bitfield] crate. [bitfield]: https://crates.io/crates/bitfield ### Showcase: Generated vs Handwritten We tested the following `#[bitfield]` `struct`: ```rust #[bitfield] pub struct Generated { pub a: B9, // Spans 2 bytes. pub b: B6, // Within 2nd byte. pub c: B13, // Spans 3 bytes. pub d: B1, // Within 4rd byte. pub e: B3, // Within 4rd byte. pub f: B32, // Spans rest 4 bytes. } ``` **Note:** All benchmarks timing results sum 10 runs each. ### Getter Performance ``` get_a/generated time: [3.0990 ns 3.1119 ns 3.1263 ns] get_a/handwritten time: [3.1072 ns 3.1189 ns 3.1318 ns] get_b/generated time: [3.0859 ns 3.0993 ns 3.1140 ns] get_b/handwritten time: [3.1062 ns 3.1154 ns 3.1244 ns] get_c/generated time: [3.0892 ns 3.1140 ns 3.1491 ns] get_c/handwritten time: [3.1031 ns 3.1144 ns 3.1266 ns] get_d/generated time: [3.0937 ns 3.1055 ns 3.1182 ns] get_d/handwritten time: [3.1109 ns 3.1258 ns 3.1422 ns] get_e/generated time: [3.1009 ns 3.1139 ns 3.1293 ns] get_e/handwritten time: [3.1217 ns 3.1366 ns 3.1534 ns] get_f/generated time: [3.1064 ns 3.1164 ns 3.1269 ns] get_f/handwritten time: [3.1067 ns 3.1221 ns 3.1404 ns] ``` ### Setter Performance ``` set_a/generated time: [15.784 ns 15.855 ns 15.932 ns] set_a/handwritten time: [15.841 ns 15.907 ns 15.980 ns] set_b/generated time: [20.496 ns 20.567 ns 20.643 ns] set_b/handwritten time: [20.319 ns 20.384 ns 20.454 ns] set_c/generated time: [19.155 ns 19.362 ns 19.592 ns] set_c/handwritten time: [19.265 ns 19.383 ns 19.523 ns] set_d/generated time: [12.325 ns 12.376 ns 12.429 ns] set_d/handwritten time: [12.416 ns 12.472 ns 12.541 ns] set_e/generated time: [20.460 ns 20.528 ns 20.601 ns] set_e/handwritten time: [20.473 ns 20.534 ns 20.601 ns] set_f/generated time: [6.1466 ns 6.1769 ns 6.2127 ns] set_f/handwritten time: [6.1467 ns 6.1962 ns 6.2670 ns] ``` ## License Licensed under either of Apache License, Version 2.0 or MIT license at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this codebase by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. [procedural-macro-workshop]: https://github.com/dtolnay/proc-macro-workshop/blob/master/README.md ================================================ FILE: benches/benchmarks.rs ================================================ #![allow(dead_code)] mod utils; use modular_bitfield::{ bitfield, specifiers::{B12, B13, B16, B3, B32, B36, B4, B6, B7, B8, B9}, }; use utils::*; #[bitfield] pub struct Color { r: B8, g: B8, b: B8, a: B8, } #[bitfield] pub struct SingleBitsInSingleByte { b0: bool, b1: bool, b2: bool, b3: bool, b4: bool, b5: bool, b6: bool, b7: bool, } #[bitfield] pub struct TwoHalfBytes { h0: B4, h1: B4, } #[bitfield] pub struct SingleBitAndRest { head: bool, rest: B7, } #[bitfield] pub struct B7B1 { b7: B7, b1: bool, } #[bitfield] pub struct B3B1B4 { b3: B3, b1: bool, b4: B4, } #[bitfield] pub struct TwoHalfWords { fst: B16, snd: B16, } #[bitfield] pub struct B6B12B6 { front: B6, middle: B12, back: B6, } #[bitfield] pub struct B6B36B6 { front: B6, middle: B36, back: B6, } #[bitfield] pub struct Complex { a: B9, // 0th and 1st b: B6, // 1st c: B13, // 1st, 2nd, 3rd d: B4, // 3rd e: B32, // 4th, .., 7th } fn bench_set_variants() { one_shot("set - Color", &Color::new, |mut input| { repeat(|| { black_box(&mut input).set_r(1); black_box(&mut input).set_g(1); black_box(&mut input).set_b(1); black_box(&mut input).set_a(1); }); }); one_shot( "set - SingleBitsInSingleByte", &SingleBitsInSingleByte::new, |mut input| { repeat(|| { black_box(&mut input).set_b0(true); black_box(&mut input).set_b1(true); black_box(&mut input).set_b2(true); black_box(&mut input).set_b3(true); black_box(&mut input).set_b4(true); black_box(&mut input).set_b5(true); black_box(&mut input).set_b6(true); black_box(&mut input).set_b7(true); }); }, ); one_shot("set - TwoHalfBytes", &TwoHalfBytes::new, |mut input| { repeat(|| { black_box(&mut input).set_h0(1); black_box(&mut input).set_h1(1); }); }); one_shot( "set - SingleBitAndRest", &SingleBitAndRest::new, |mut input| { repeat(|| { black_box(&mut input).set_head(true); black_box(&mut input).set_rest(1); }); }, ); one_shot("set - B7B1", &B7B1::new, |mut input| { repeat(|| { black_box(&mut input).set_b7(1); black_box(&mut input).set_b1(true); }); }); one_shot("set - B3B1B4", &B3B1B4::new, |mut input| { repeat(|| { black_box(&mut input).set_b3(1); black_box(&mut input).set_b1(true); black_box(&mut input).set_b4(1); }); }); one_shot("set - TwoHalfWords", &TwoHalfWords::new, |mut input| { repeat(|| { black_box(&mut input).set_fst(1); black_box(&mut input).set_snd(1); }); }); one_shot("set - B6B12B6", &B6B12B6::new, |mut input| { repeat(|| { black_box(&mut input).set_front(1); black_box(&mut input).set_middle(1); black_box(&mut input).set_back(1); }); }); one_shot("set - B6B36B6", &B6B36B6::new, |mut input| { repeat(|| { black_box(&mut input).set_front(1); black_box(&mut input).set_middle(1); black_box(&mut input).set_back(1); }); }); one_shot("set - Complex", &Complex::new, |mut input| { repeat(|| { black_box(&mut input).set_a(1); black_box(&mut input).set_b(1); black_box(&mut input).set_c(1); black_box(&mut input).set_d(1); black_box(&mut input).set_e(1); }); }); } fn bench_get_variants() { one_shot("get - Color", &Color::new, |input| { repeat(|| { black_box(input.r()); black_box(input.g()); black_box(input.b()); black_box(input.a()); }); }); one_shot( "get - SingleBitsInSingleByte", &SingleBitsInSingleByte::new, |input| { repeat(|| { black_box(input.b0()); black_box(input.b1()); black_box(input.b2()); black_box(input.b3()); black_box(input.b4()); black_box(input.b5()); black_box(input.b6()); black_box(input.b7()); }); }, ); one_shot("get - TwoHalfBytes", &TwoHalfBytes::new, |input| { repeat(|| { black_box(input.h0()); black_box(input.h1()); }); }); one_shot("get - SingleBitAndRest", &SingleBitAndRest::new, |input| { repeat(|| { black_box(input.head()); black_box(input.rest()); }); }); one_shot("get - B7B1", &B7B1::new, |input| { repeat(|| { black_box(input.b7()); black_box(input.b1()); }); }); one_shot("get - B3B1B4", &B3B1B4::new, |input| { repeat(|| { black_box(input.b3()); black_box(input.b1()); black_box(input.b4()); }); }); one_shot("get - TwoHalfWords", &TwoHalfWords::new, |input| { repeat(|| { black_box(input.fst()); black_box(input.snd()); }); }); one_shot("get - B6B12B6", &B6B12B6::new, |input| { repeat(|| { black_box(input.front()); black_box(input.middle()); black_box(input.back()); }); }); one_shot("get - B6B36B6", &B6B36B6::new, |input| { repeat(|| { black_box(input.front()); black_box(input.middle()); black_box(input.back()); }); }); one_shot("get - Complex", &Complex::new, |input| { repeat(|| { black_box(input.a()); black_box(input.b()); black_box(input.c()); black_box(input.d()); black_box(input.e()); }); }); } fn main() { bench_set_variants(); bench_get_variants(); } ================================================ FILE: benches/cmp_bitfield_crate.rs ================================================ //! In this benchmark we compare our `modular_bitfield` crate generated bitfields //! with the ones generated by the popular `bitfield` crate. //! //! We want to find out which crate produces the more efficient code for different //! use cases and scenarios. mod utils; use bitfield::bitfield as bitfield_crate; use modular_bitfield::prelude::*; use utils::*; bitfield_crate! { pub struct OtherBitfield(u32); #[inline] pub a, set_a: 8, 0; #[inline] pub b, set_b: 14, 9; #[inline] pub c, set_c: 27, 15; #[inline] pub d, set_d: 28, 28; #[inline] pub e, set_e: 31, 29; } #[bitfield] pub struct ModularBitfield { pub a: B9, pub b: B6, pub c: B13, pub d: B1, pub e: B3, } macro_rules! generate_cmp_benchmark_for { ( test($test_name_get:ident, $test_name_set:ident) { fn $fn_get:ident($name_get:literal); fn $fn_set:ident($name_set:literal); } ) => { fn $test_name_get() { println!(); compare( $name_get, || OtherBitfield(0), |input| { repeat(|| { black_box(input.$fn_get()); }); }, ); compare($name_get, &ModularBitfield::new, |input| { repeat(|| { black_box(input.$fn_get()); }); }); } fn $test_name_set() { println!(); compare( $name_set, || OtherBitfield(0), |mut input| { repeat(|| { black_box(black_box(&mut input).$fn_set(1)); }); }, ); compare($name_set, &ModularBitfield::new, |mut input| { repeat(|| { black_box(black_box(&mut input).$fn_set(1)); }); }); } }; } generate_cmp_benchmark_for!( test(bench_get_a, bench_set_a) { fn a("bitfield vs modular-bitfield - get_a"); fn set_a("bitfield vs modular-bitfield - set_a"); } ); generate_cmp_benchmark_for!( test(bench_get_b, bench_set_b) { fn b("bitfield vs modular-bitfield - get_b"); fn set_b("bitfield vs modular-bitfield - set_b"); } ); generate_cmp_benchmark_for!( test(bench_get_c, bench_set_c) { fn c("bitfield vs modular-bitfield - get_c"); fn set_c("bitfield vs modular-bitfield - set_c"); } ); generate_cmp_benchmark_for!( test(bench_get_d, bench_set_d) { fn d("bitfield vs modular-bitfield - get_d"); fn set_d("bitfield vs modular-bitfield - set_d"); } ); generate_cmp_benchmark_for!( test(bench_get_e, bench_set_e) { fn e("bitfield vs modular-bitfield - get_e"); fn set_e("bitfield vs modular-bitfield - set_e"); } ); fn main() { bench_get_a(); bench_get_b(); bench_get_c(); bench_get_d(); bench_get_e(); bench_set_a(); bench_set_b(); bench_set_c(); bench_set_d(); bench_set_e(); } ================================================ FILE: benches/cmp_handwritten.rs ================================================ //! In this benchmark we compare the macro generated code for //! setters and getters to some hand-written code for the same //! data structure. //! //! We do a performance analysis for the getter and setter of //! all fields of both structs. //! //! Also we test here that our hand-written code and the macro //! generated code actually are semantically equivalent. //! This allows us to further enhance the hand-written code //! and to eventually come up with new optimization tricks //! for the macro generated code while staying correct. mod utils; use utils::{ handwritten::{Generated, Handwritten}, *, }; macro_rules! generate_cmp_benchmark_for { ( test($test_name_get:ident, $test_name_set:ident) { fn $fn_get:ident($name_get:literal); fn $fn_set:ident($name_set:literal); } ) => { fn $test_name_get() { println!(); compare($name_get, &Generated::new, |input| { repeat(|| { black_box(input.$fn_get()); }); }); compare($name_get, &Handwritten::new, |input| { repeat(|| { black_box(input.$fn_get()); }); }); } fn $test_name_set() { println!(); compare($name_set, &Generated::new, |mut input| { repeat(|| { black_box(black_box(&mut input).$fn_set(1)); }); }); compare($name_set, &Handwritten::new, |mut input| { repeat(|| { black_box(black_box(&mut input).$fn_set(1)); }); }); } }; } generate_cmp_benchmark_for!( test(bench_get_a, bench_set_a) { fn a("handwritten vs modular-bitfield - get_a"); fn set_a("handwritten vs modular-bitfield - set_a"); } ); generate_cmp_benchmark_for!( test(bench_get_b, bench_set_b) { fn b("handwritten vs modular-bitfield - get_b"); fn set_b("handwritten vs modular-bitfield - set_b"); } ); generate_cmp_benchmark_for!( test(bench_get_c, bench_set_c) { fn c("handwritten vs modular-bitfield - get_c"); fn set_c("handwritten vs modular-bitfield - set_c"); } ); generate_cmp_benchmark_for!( test(bench_get_d, bench_set_d) { fn d("handwritten vs modular-bitfield - get_d"); fn set_d("handwritten vs modular-bitfield - set_d"); } ); generate_cmp_benchmark_for!( test(bench_get_e, bench_set_e) { fn e("handwritten vs modular-bitfield - get_e"); fn set_e("handwritten vs modular-bitfield - set_e"); } ); generate_cmp_benchmark_for!( test(bench_get_f, bench_set_f) { fn f("handwritten vs modular-bitfield - get_f"); fn set_f("handwritten vs modular-bitfield - set_f"); } ); fn main() { bench_get_a(); bench_get_b(); bench_get_c(); bench_get_d(); bench_get_e(); bench_get_f(); bench_set_a(); bench_set_b(); bench_set_c(); bench_set_d(); bench_set_e(); bench_set_f(); } ================================================ FILE: benches/utils/handwritten.rs ================================================ //! In this benchmark we compare the macro generated code for //! setters and getters to some hand-written code for the same //! data structure. //! //! We do a performance analysis for the getter and setter of //! all fields of both structs. //! //! Also we test here that our hand-written code and the macro //! generated code actually are semantically equivalent. //! This allows us to further enhance the hand-written code //! and to eventually come up with new optimization tricks //! for the macro generated code while staying correct. use modular_bitfield::prelude::*; /// This generates code by the macros that we are going to test. /// /// For every field a getter `get_*` and a setter `set_*` is generated /// where `*` is the name of the field. /// /// Note that this tests the following cases: /// /// - `a`: Spans 2 bytes where the first byte is used fully and the /// second byte stores only one of its bits. /// - `b`: Fits into one byte but doesn't reach the bounds on either side. /// - `c`: Spans across 3 bytes in total and uses only 1 bit and 4 bits in /// the respective first and last byte. /// - `d`: Spans 3 whole bytes in total. /// /// More cases could be missing and might be added in the future. #[bitfield] pub struct Generated { pub a: B9, pub b: B6, pub c: B13, pub d: B1, pub e: B3, pub f: B32, } /// This is the hand-written part that the macro generated getters /// and setters are compared against. /// /// We try to encode the handwritten setters and getters as good as /// we can while trying to stay within reasonable bounds of readability. /// /// This code should perform as good as the macro generated code and vice versa. pub struct Handwritten { data: [u8; 8], } impl Handwritten { /// Creates a new hand-written struct initialized with all zeros. #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { data: [0x00; 8] } } /// Returns the value of `a`. pub fn a(&self) -> u16 { u16::from_le_bytes([self.data[0], self.data[1] & 0x01]) } /// Sets the value of `a`. pub fn set_a(&mut self, new_val: u16) { assert!(new_val < (0x01 << 9)); let [ls, ms] = new_val.to_le_bytes(); self.data[0] = ls; self.data[1] = (self.data[1] & (!0x01)) | (ms & 0x01); } /// Returns the value of `b`. pub fn b(&self) -> u8 { (self.data[1] >> 1) & 0b0011_1111 } /// Sets the value of `b`. pub fn set_b(&mut self, new_val: u8) { assert!(new_val < (0x01 << 6)); self.data[1] = (self.data[1] & 0x81) | (new_val << 1); } /// Returns the value of `c`. pub fn c(&self) -> u16 { let mut res = 0; res |= (self.data[1] >> 7) as u16; res |= (self.data[2] as u16) << 1; res |= ((self.data[3] & 0b0000_1111) as u16) << 9; res } /// Sets the value of `c`. pub fn set_c(&mut self, new_val: u16) { assert!(new_val < (0x01 << 13)); self.data[1] = (self.data[1] & !0x80) | (((new_val & 0x01) << 7) as u8); self.data[2] = ((new_val >> 1) & 0xFF) as u8; self.data[3] = (self.data[3] & !0x0F) | (((new_val >> 9) & 0x0F) as u8); } /// Returns the value of `d`. pub fn d(&self) -> u8 { (self.data[3] >> 4) & 0b0000_0001 } /// Sets the value of `d`. pub fn set_d(&mut self, new_val: u8) { self.data[3] = (self.data[3] & (!0b0001_0000)) | ((new_val & 0b0000_0001) << 4) } /// Returns the value of `e`. pub fn e(&self) -> u8 { (self.data[3] >> 5) & 0b0000_0111 } /// Sets the value of `e`. pub fn set_e(&mut self, new_val: u8) { assert!(new_val < (0x01 << 3)); self.data[3] = (self.data[3] & !0b1110_0000) | ((new_val & 0b0000_0111) << 5) } /// Returns the value of `f`. pub fn f(&self) -> u32 { u32::from_le_bytes([self.data[4], self.data[5], self.data[6], self.data[7]]) } /// Sets the value of `e`. pub fn set_f(&mut self, new_val: u32) { assert!((new_val as u64) < (0x01_u64 << 32)); let le_bytes = new_val.to_le_bytes(); self.data[4..].copy_from_slice(&le_bytes[..]); } } macro_rules! impl_getter_setter_tests { ( $( ($name:ident, $getter:ident, $setter:ident, $n:expr), )* ) => { #[cfg(test)] mod generated_is_equal_to_handwritten { $( #[test] fn $name() { let mut macro_struct = super::Generated::new(); let mut hand_struct = super::Handwritten::new(); assert_eq!(hand_struct.$getter(), macro_struct.$getter()); macro_struct.$setter($n); hand_struct.$setter($n); assert_eq!(hand_struct.$getter(), $n); assert_eq!(macro_struct.$getter(), $n); macro_struct.$setter(0); hand_struct.$setter(0); assert_eq!(hand_struct.$getter(), 0); assert_eq!(macro_struct.$getter(), 0); } )* } } } impl_getter_setter_tests!( (get_set_a, a, set_a, 0b0001_1111_1111), (get_set_b, b, set_b, 0b0011_1111), (get_set_c, c, set_c, 0b0001_1111_1111_1111), (get_set_d, d, set_d, 0b0001), (get_set_e, e, set_e, 0b0111), (get_set_f, f, set_f, u32::MAX), ); ================================================ FILE: benches/utils/mod.rs ================================================ #![allow(dead_code)] pub mod handwritten; pub use tiny_bench::black_box; /// Runs a benchmark without extremely slow and unnecessary warm-up. pub fn bench(label: &'static str, setup: S, closure: F, compare: bool) where F: FnMut(R) -> T, S: FnMut() -> R, { let cfg = tiny_bench::BenchmarkConfig { dump_results_to_disk: compare, warm_up_time: core::time::Duration::ZERO, ..Default::default() }; tiny_bench::bench_with_setup_configuration_labeled(label, &cfg, setup, closure); } /// Runs a benchmark that compares two or more runs with the same label. /// To do this, tiny-bench just records the last run to disk and then reads it /// back, so multiple runs without clearing target data will generate a delta on /// each subsequent run. pub fn compare(label: &'static str, setup: S, closure: F) where F: FnMut(R) -> T, S: FnMut() -> R, { bench(label, setup, closure, true); } /// Runs a one-shot benchmark. pub fn one_shot(label: &'static str, setup: S, closure: F) where F: FnMut(R) -> T, S: FnMut() -> R, { bench(label, setup, closure, false); } /// Repeats the given closure several times. /// /// We do this in order to measure benchmarks that require at least some /// amount of nanoseconds to run through. #[inline(always)] pub fn repeat(mut f: F) where F: FnMut(), { for _ in 0..10 { f(); } } ================================================ FILE: docs/bitfield.md ================================================ Applicable to structs to turn their fields into compact bitfields. # Generated API By default this generates the following API: - **Constructors:** 1. `new()`: Initializes all bits to 0 even if 0 bits may be invalid. Note that invalid bit patterns are supported in that getters and setters will be protecting accesses. - **Getters:** For every field `f` we generate the following getters: 1. `f()`: Returns the value of `f` and might panic if the value contains an invalid bit pattern. 2. `f_or_err()`: Returns the value of `f` or an error if the value contains an invalid bit pattern. - **Setters:** For every field `f` we generate the following setters: 1. `set_f(new_value)`: Sets the value of `f` to `new_value` and might panic if `new_value` is out of bounds for the bit width of `f`. 2. `set_f_checked(new_value)`: Sets the value of `f` to `new` or returns an error if `new_value` if out of bounds for the bit width of `f`. 3. `with_f(new_value)`: Similar to `set_f` but consumes and returns `Self`. Primarily useful for method chaining. 4. `with_f_checked(new_value)`: Similar to `set_f_checked` but consumes and returns `Self`. Primarily useful for method chaining. - **Conversions:** - `from_bytes(bytes)`: Allows to constructor the bitfield type from a fixed array of bytes. - `into_bytes()`: Allows to convert the bitfield into its underlying byte representation. # Parameters The following parameters for the `#[bitfield]` macro are supported: ## Parameter: `bytes = N` This ensures at compilation time that the resulting `#[bitfield]` struct consists of exactly `N` bytes. Yield a compilation error if this does not hold true. ### Example ``` # use modular_bitfield::prelude::*; #[bitfield(bytes = 2)] pub struct SingedInt { sign: bool, // 1 bit value: B15, // 15 bits } ``` ## Parameter: `filled: bool` If `filled` is `true` ensures that the `#[bitfield]` struct defines all bits and therefore has a bitwidth that is divisible by 8. If `filled` is `false` ensures the exact opposite. The default value is: `true` ### Example ``` # use modular_bitfield::prelude::*; #[bitfield(filled = false)] pub struct Package { is_received: bool, // 1 bit is_alive: bool, // 1 bit status: B2, // 2 bits } ``` ## Parameter: `bits = N` With the `bits: int` parameter it is possible to control the targeted bit width of a `#[bitfield]` annoated struct. Using `bits = N` guarantees that the resulting bitfield struct will have a bit width of exactly `N`. ### Example 1 ``` # use modular_bitfield::prelude::*; #[bitfield(bits = 16)] pub struct Package { is_received: bool, // 1 bit is_alive: bool, // 1 bit status: B14, // 14 bits } ``` ### Example 2 The `bits: int` parameter is especially useful when using this in conjunction with `#[derive(Specifier)]` and `filled = false` as shown in the below example. ``` # use modular_bitfield::prelude::*; #[bitfield(bits = 5)] #[derive(Specifier)] pub struct Package { is_received: bool, // 1 bit is_alive: bool, // 1 bit status: B3, // 3 bits } ``` ## Field Parameter: `#[bits = N]` To ensure at compile time that a field of a `#[bitfield]` struct has a bit width of exactly `N` a user may add `#[bits = N]` to the field in question. ### Example ``` # use modular_bitfield::prelude::*; # #[bitfield(filled = false)] # #[derive(Specifier)] # pub struct Header { # is_received: bool, // 1 bit # is_alive: bool, // 1 bit # status: B2, // 2 bits # } #[bitfield] pub struct Base { #[bits = 4] header: Header, // 4 bits content: B28, // 28 bits } ``` ## Field Parameter: `#[skip(..)]` It is possible to skip the entire code generation for getters or setters with the `#[skip]` field attribute. This is useful if a field just needs to be read or written exclusively. Skipping both setters and getters is useful if you want to have undefined blocks within your bitfields. ### Example ``` # use modular_bitfield::prelude::*; #[bitfield] pub struct Sparse { #[skip(getters)] no_getters: B4, #[skip(setters)] no_setters: B4, #[skip] skipped_entirely: B4, #[skip(getters, setters)] skipped_entirely_2: B2, #[skip(getters)] #[skip(setters)] skipped_entirely_2: B2, } ``` ### Trick: Wildcards If you are completely uninterested in a field of a bitfield, for example when specifying some undefined bits in your bitfield you can use double wildcards as their names: ``` # use modular_bitfield::prelude::*; #[bitfield] pub struct Sparse { #[skip] __: B10, a: bool, #[skip] __: B10, b: bool, #[skip] __: B10, } ``` # Features ## Support: `#[derive(Specifier)]` If a `#[bitfield]` struct is annotated with a `#[derive(Specifier)]` attribute an implementation of the `Specifier` trait will be generated for it. This has the effect that the bitfield struct itself can be used as the type of a field of another bitfield type. This feature is limited to bitfield types that have a total bit width of 128 bit or fewer. This restriction is ensured at compile time. ### Example ``` # use modular_bitfield::prelude::*; #[bitfield(filled = false)] #[derive(Specifier)] pub struct Header { is_received: bool, // 1 bit is_alive: bool, // 1 bit status: B2, // 2 bits } ``` Now the above `Header` bitfield type can be used in yet another `#[bitfield]` annotated type: ``` # use modular_bitfield::prelude::*; # #[bitfield(filled = false)] # #[derive(Specifier)] # pub struct Header { # is_received: bool, // 1 bit # is_alive: bool, // 1 bit # status: B2, // 2 bits # } #[bitfield] pub struct Base { header: Header, // 4 bits content: B28, // 28 bits } ``` ## Support: `#[derive(Debug)]` If a `#[derive(Debug)]` is found by the `#[bitfield]` a naturally formatting implementation is going to be generated that clearly displays all the fields and their values as the user would expect. Also invalid bit patterns for fields are clearly displayed under this implementation. ### Example ``` # use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] pub struct Package { is_received: bool, // 1 bit is_alive: bool, // 1 bit status: B6, // 6 bits } let package = Package::new() .with_is_received(false) .with_is_alive(true) .with_status(3); println!("{:?}", package); assert_eq!( format!("{:?}", package), "Package { is_received: false, is_alive: true, status: 3 }", ); ``` ## Support: `#[repr(uN)]` It is possible to additionally annotate a `#[bitfield]` annotated struct with `#[repr(uN)]` where `uN` is one of `u8`, `u16`, `u32`, `u64` or `u128` in order to make it conveniently interchangeable with such an unsigned integer value. As an effect to the user this implements `From` implementations between the chosen primitive and the bitfield as well as ensuring at compile time that the bit width of the bitfield struct matches the bit width of the primitive. ### Example ``` # use modular_bitfield::prelude::*; #[bitfield] #[repr(u16)] pub struct SignedU16 { sign: bool, // 1 bit abs_value: B15, // 15 bits } let sint = SignedU16::from(0b0111_0001); assert_eq!(sint.sign(), true); assert_eq!(sint.abs_value(), 0b0011_1000); assert_eq!(u16::from(sint), 0b0111_0001_u16); ``` ================================================ FILE: docs/bitfield_specifier.md ================================================ Derive macro generating an impl of the trait [`Specifier`]. This macro can be used on all unit enums and structs annotated with `#[bitfield]`. The enum or struct can be up to 128 bits in size; anything larger will cause a compilation error. # Options * `#[bits = N]`: Explicitly specifies the number of bits used by a unit enum. This attribute is required when an enum does not have a power-of-two number of variants, but can be used for extra validation no matter what. # Examples ## Basic usage In this example, an extra variant (`Invalid`) is required because otherwise the enum would not contain a power-of-two number of variants. The power-of-two requirement ensures conversion from raw bits is infallible. ``` use modular_bitfield::prelude::*; #[derive(Specifier)] pub enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, Invalid } ``` ## Using `#[bits = N]` To eliminate the power-of-two requirement, add the `#[bits]` attribute: ``` # use modular_bitfield::prelude::*; # #[derive(Specifier)] #[bits = 3] pub enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } ``` ## Discriminants Discriminants can be used normally to explicitly override certain values: ``` # use modular_bitfield::prelude::*; # #[derive(Specifier)] #[bits = 3] pub enum Weekday { Monday = 1, Tuesday /* 2 … */, Wednesday, Thursday, Friday, Saturday, Sunday = 0, } ``` ## With `#[bitfield]` An enum that implements `Specifier` can be used normally as a field type in a `#[bitfield]` struct: ``` # use modular_bitfield::prelude::*; # # #[derive(Debug, Eq, PartialEq, Specifier)] # #[bits = 3] # pub enum Weekday { # Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday # } #[bitfield] pub struct MeetingTimeSlot { day: Weekday, from: B6, to: B6, expired: bool, } let mut slot = MeetingTimeSlot::new() .with_day(Weekday::Friday) .with_from(14) // 14:00 .with_to(15); // 15:00 assert_eq!(slot.day(), Weekday::Friday); assert_eq!(slot.from(), 14); assert_eq!(slot.to(), 15); assert!(!slot.expired()); ``` ================================================ FILE: docs/index.md ================================================ Provides macros to support bitfield structs allowing for modular use of bit-enums. The mainly provided macros are [`#[bitfield]`](bitfield) for structs and [`#[derive(Specifier)]`](Specifier) for enums that shall be usable within bitfield structs. There are preset bitfield specifiers such as `B1`, `B2`,..,`B64` that allow for easy bitfield usage in structs very similar to how they work in C or C++. - Performance of the macro generated code is as fast as its hand-written alternative. - Compile-time checks allow for safe usage of bitfield structs and enums. ### Usage Annotate a Rust struct with the [`#[bitfield]`](bitfield) attribute in order to convert it into a bitfield, with [optional parameters](bitfield#parameters) that control how the bitfield is generated. The `B1`, `B2`, ... `B128` prelude types can be used as primitives to declare the number of bits per field. ``` # use modular_bitfield::prelude::*; # #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: B1, status: B2, } ``` This produces a `new` constructor as well as a variety of getters and setters that allows to interact with the bitfield in a safe fashion: #### Example: Constructors ``` # use modular_bitfield::prelude::*; # # #[bitfield] # pub struct PackedData { # header: B4, # body: B9, # is_alive: B1, # status: B2, # } let data = PackedData::new() .with_header(1) .with_body(2) .with_is_alive(0) .with_status(3); assert_eq!(data.header(), 1); assert_eq!(data.body(), 2); assert_eq!(data.is_alive(), 0); assert_eq!(data.status(), 3); ``` #### Example: Primitive Types Any type that implements the `Specifier` trait can be used as a bitfield field. Besides the already mentioned `B1`, .. `B128` also the `bool`, `u8`, `u16`, `u32`, `u64` or `u128` primitive types can be used from prelude. We can use this knowledge to encode our `is_alive` as `bool` type instead of `B1`: ``` # use modular_bitfield::prelude::*; # #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: bool, status: B2, } let mut data = PackedData::new() .with_is_alive(true); assert!(data.is_alive()); data.set_is_alive(false); assert!(!data.is_alive()); ``` #### Example: Enum Specifiers It is possible to derive the `Specifier` trait for `enum` types very easily to make them also usable as a field within a bitfield type: ``` # use modular_bitfield::prelude::*; # #[derive(Specifier)] pub enum Status { Red, Green, Yellow, None, } #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: bool, status: Status, } ``` #### Example: Extra Safety Guard In order to make sure that our `Status` enum still requires exatly 2 bit we can add `#[bits = 2]` to its field: ``` # use modular_bitfield::prelude::*; # # #[derive(Specifier)] # pub enum Status { # Red, Green, Yellow, None, # } # #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: bool, #[bits = 2] status: Status, } ``` Setting and getting our new `status` field is naturally as follows: ``` # use modular_bitfield::prelude::*; # # #[derive(Specifier)] # #[derive(Debug, PartialEq, Eq)] # pub enum Status { # Red, Green, Yellow, None, # } # # #[bitfield] # pub struct PackedData { # header: B4, # body: B9, # is_alive: bool, # #[bits = 2] # status: Status, # } # let mut data = PackedData::new() .with_status(Status::Green); assert_eq!(data.status(), Status::Green); data.set_status(Status::Red); assert_eq!(data.status(), Status::Red); ``` #### Example: Skipping Fields It might make sense to only allow users to set or get information from a field or even to entirely disallow interaction with a bitfield. For this the `#[skip]` attribute can be used on a bitfield of a `#[bitfield]` annotated struct. ``` # use modular_bitfield::prelude::*; # #[bitfield] pub struct SomeBitsUndefined { #[skip(setters)] read_only: bool, #[skip(getters)] write_only: bool, #[skip] unused: B6, } ``` It is possible to use `#[skip(getters, setters)]` or `#[skip(getters)]` followed by a `#[skip(setters)]` attribute applied on the same bitfield. The effects are the same. When skipping both, getters and setters, it is possible to completely avoid having to specify a name: ``` # use modular_bitfield::prelude::*; # #[bitfield] pub struct SomeBitsUndefined { #[skip] __: B2, is_activ: bool, #[skip] __: B2, is_received: bool, #[skip] __: B2, } ``` #### Example: Unfilled Bitfields Sometimes it might be useful to not be required to construct a bitfield that defines all bits and therefore is required to have a bit width divisible by 8. In this case you can use the `filled: bool` parameter of the `#[bitfield]` macro in order to toggle this for your respective bitfield: ``` # use modular_bitfield::prelude::*; # #[bitfield(filled = false)] pub struct SomeBitsUndefined { is_compact: bool, is_secure: bool, pre_status: B3, } ``` In the above example `SomeBitsUndefined` only defines the first 5 bits and leaves the rest 3 bits of its entire 8 bits undefined. The consequences are that its generated `from_bytes` method is fallible since it must guard against those undefined bits. #### Example: Recursive Bitfields It is possible to use `#[bitfield]` structs as fields of `#[bitfield]` structs. This is generally useful if there are some common fields for multiple bitfields and is achieved by adding the `#[derive(Specifier)]` attribute to the struct annotated with `#[bitfield]`: ``` # use modular_bitfield::prelude::*; # # #[derive(Specifier)] # pub enum Status { # Red, Green, Yellow, None, # } # #[bitfield(filled = false)] #[derive(Specifier)] pub struct Header { is_compact: bool, is_secure: bool, pre_status: Status, } #[bitfield] pub struct PackedData { header: Header, body: B9, is_alive: bool, status: Status, } ``` With the `bits: int` parameter of the `#[bitfield]` macro on the `Header` struct and the `#[bits: int]` attribute of the `#[derive(Specifier)]` on the `Status` enum we can have additional compile-time guarantees about the bit widths of the resulting entities: ``` # use modular_bitfield::prelude::*; # #[derive(Specifier)] #[bits = 2] pub enum Status { Red, Green, Yellow, None, } #[bitfield(bits = 4)] #[derive(Specifier)] pub struct Header { is_compact: bool, is_secure: bool, #[bits = 2] pre_status: Status, } #[bitfield(bits = 16)] pub struct PackedData { #[bits = 4] header: Header, body: B9, is_alive: bool, #[bits = 2] status: Status, } ``` #### Example: Advanced Enum Specifiers For our `Status` enum we actually just need 3 status variants: `Green`, `Yellow` and `Red`. We introduced the `None` status variants because `Specifier` enums by default are required to have a number of variants that is a power of two. We can ship around this by specifying `#[bits = 2]` on the top and get rid of our placeholder `None` variant while maintaining the invariant of it requiring 2 bits: ``` # use modular_bitfield::prelude::*; #[derive(Specifier)] #[bits = 2] pub enum Status { Red, Green, Yellow, } ``` However, having such enums now yields the possibility that a bitfield might contain invalid bit patterns for such fields. We can safely access those fields with protected getters. For the sake of demonstration we will use the generated `from_bytes` constructor with which we can easily construct bitfields that may contain invalid bit patterns: ``` # use modular_bitfield::prelude::*; # use modular_bitfield::error::InvalidBitPattern; # # #[derive(Specifier)] # #[derive(Debug, PartialEq, Eq)] # #[bits = 2] # pub enum Status { # Red, Green, Yellow, # } # # #[bitfield(filled = false)] # #[derive(Specifier)] # pub struct Header { # is_compact: bool, # is_secure: bool, # pre_status: Status, # } # # #[bitfield] # pub struct PackedData { # header: Header, # body: B9, # is_alive: bool, # status: Status, # } # let mut data = PackedData::from_bytes([0b0000_0000, 0b1100_0000]); // The 2 status field bits are invalid -----^^ // as Red = 0x00, Green = 0x01 and Yellow = 0x10 assert_eq!(data.status_or_err(), Err(InvalidBitPattern::new(0b11))); data.set_status(Status::Green); assert_eq!(data.status_or_err(), Ok(Status::Green)); ``` ## Generated Implementations For the example `#[bitfield]` struct the following implementations are going to be generated: ``` # use modular_bitfield::prelude::*; # #[bitfield] pub struct Example { a: bool, b: B7, } ``` | Signature | Description | |:--|:--| | `fn new() -> Self` | Creates a new instance of the bitfield with all bits initialized to 0. | | `fn from_bytes([u8; 1]) -> Self` | Creates a new instance of the bitfield from the given raw bytes. | | `fn into_bytes(self) -> [u8; 1]` | Returns the underlying bytes of the bitfield. | And below the generated signatures for field `a`: | Signature | Description | |:--|:--| | `fn a() -> bool` | Returns the value of `a` or panics if invalid. | | `fn a_or_err() -> Result>` | Returns the value of `a` of an error providing information about the invalid bits. | | `fn set_a(&mut self, new_value: bool)` | Sets `a` to the new value or panics if `new_value` contains invalid bits. | | `fn set_a_checked(&mut self, new_value: bool) -> Result<(), OutOfBounds>` | Sets `a` to the new value of returns an out of bounds error. | | `fn with_a(self, new_value: bool) -> Self` | Similar to `set_a` but useful for method chaining. | | `fn with_a_checked(self, new_value: bool) -> Result` | Similar to `set_a_checked` but useful for method chaining. | Getters for unnamed fields in tuple-like structs are prefixed with `get_` (e.g. `get_0()`, `get_1_or_err()`, etc.). ## Generated Structure From David Tolnay's procedural macro workshop: The macro conceptualizes given structs as a sequence of bits 0..N. The bits are grouped into fields in the order specified by the struct written by the user. The `#[bitfield]` attribute rewrites the caller's struct into a private byte array representation with public getter and setter methods for each field. The total number of bits N is required to be a multiple of 8: This is checked at compile time. ### Example The following invocation builds a struct with a total size of 32 bits or 4 bytes. It places field `a` in the least significant bit of the first byte, field `b` in the next three least significant bits, field `c` in the remaining four most significant bits of the first byte, and field `d` spanning the next three bytes. ```rust use modular_bitfield::prelude::*; #[bitfield] pub struct MyFourBytes { a: B1, b: B3, c: B4, d: B24, } ``` ```text least significant bit of third byte ┊ most significant ┊ ┊ ┊ ┊ ║ first byte ║ second byte ║ third byte ║ fourth byte ║ ╟───────────────╫───────────────╫───────────────╫───────────────╢ ║▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒║ ╟─╫─────╫───────╫───────────────────────────────────────────────╢ ║a║ b ║ c ║ d ║ ┊ ┊ ┊ ┊ least significant bit of d most significant ``` ================================================ FILE: impl/Cargo.toml ================================================ [package] name = "modular-bitfield-impl" description = "Derive macro for modular-bitfield" authors.workspace = true documentation.workspace = true edition.workspace = true license.workspace = true publish.workspace = true repository.workspace = true rust-version.workspace = true version.workspace = true [lib] proc-macro = true [dependencies] quote = "1" syn = { version = "2", features = ["full"] } proc-macro2 = "1" [dev-dependencies] glob = "0.3" runtime-macros = "1.1.1" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] } ================================================ FILE: impl/src/bitfield/analyse.rs ================================================ use super::{ config::{Config, ReprKind}, field_config::{FieldConfig, SkipWhich}, raise_skip_error, BitfieldStruct, }; use core::convert::TryFrom; use quote::quote; use syn::{self, parse::Result, punctuated::Punctuated, spanned::Spanned as _}; impl TryFrom<(&mut Config, syn::ItemStruct)> for BitfieldStruct { type Error = syn::Error; fn try_from((config, item_struct): (&mut Config, syn::ItemStruct)) -> Result { Self::ensure_has_fields(&item_struct)?; Self::ensure_valid_generics(&item_struct)?; Self::extract_attributes(&item_struct.attrs, config)?; Self::analyse_config_for_fields(&item_struct, config)?; config.ensure_no_conflicts()?; Ok(Self { item_struct }) } } impl BitfieldStruct { /// Returns an error if the input struct does not have any fields. fn ensure_has_fields(item_struct: &syn::ItemStruct) -> Result<()> { if matches!(&item_struct.fields, syn::Fields::Unit) || matches!(&item_struct.fields, syn::Fields::Unnamed(f) if f.unnamed.is_empty()) || matches!(&item_struct.fields, syn::Fields::Named(f) if f.named.is_empty()) { return Err(format_err_spanned!( item_struct, "encountered invalid bitfield struct without fields" )); } Ok(()) } /// Returns an error if the input struct contains generics that cannot be /// used in a const expression. fn ensure_valid_generics(item_struct: &syn::ItemStruct) -> Result<()> { if item_struct.generics.type_params().next().is_some() || item_struct.generics.lifetimes().next().is_some() { return Err(format_err_spanned!( item_struct.generics, "bitfield structs can only use const generics" )); } Ok(()) } /// Extracts the `#[repr(uN)]` annotations from the given `#[bitfield]` struct. fn extract_repr_attribute(attr: &syn::Attribute, config: &mut Config) -> Result<()> { let list = attr.meta.require_list()?; let repr_arguments: Punctuated = attr.parse_args_with(Punctuated::parse_terminated)?; let mut retained_reprs = Vec::new(); for meta in repr_arguments { match meta { syn::Meta::Path(path) => { let repr_kind = if path.is_ident("u8") { Some(ReprKind::U8) } else if path.is_ident("u16") { Some(ReprKind::U16) } else if path.is_ident("u32") { Some(ReprKind::U32) } else if path.is_ident("u64") { Some(ReprKind::U64) } else if path.is_ident("u128") { Some(ReprKind::U128) } else { // If other repr such as `transparent` or `C` have been found we // are going to re-expand them into a new `#[repr(..)]` that is // ignored by the rest of this macro. retained_reprs.push(path.clone().into()); None }; if let Some(repr_kind) = repr_kind { config.repr(repr_kind, path.span())?; } } other => retained_reprs.push(other), } } if !retained_reprs.is_empty() { // We only push back another re-generated `#[repr(..)]` if its contents // contained some non-bitfield representations and thus is not empty. let retained_reprs_tokens = quote! { #( #retained_reprs ),* }; config.push_retained_attribute(syn::Attribute { pound_token: attr.pound_token, style: attr.style, bracket_token: attr.bracket_token, meta: syn::Meta::List(syn::MetaList { path: list.path.clone(), delimiter: list.delimiter.clone(), tokens: retained_reprs_tokens, }), }); } Ok(()) } /// Extracts the `#[derive(Debug)]` annotations from the given `#[bitfield]` struct. fn extract_derive_debug_attribute(attr: &syn::Attribute, config: &mut Config) -> Result<()> { let list = attr.meta.require_list()?; let mut retained_derives = vec![]; attr.parse_nested_meta(|meta| { let path = &meta.path; if path.is_ident("Debug") { config.derive_debug(path.span())?; } else if path.is_ident("Specifier") { config.derive_specifier(path.span())?; } else { // Other derives are going to be re-expanded them into a new // `#[derive(..)]` that is ignored by the rest of this macro. retained_derives.push(path.clone()); } Ok(()) })?; if !retained_derives.is_empty() { // We only push back another re-generated `#[derive(..)]` if its contents // contain some remaining derives and thus is not empty. let retained_derives_tokens = quote! { #( #retained_derives ),* }; config.push_retained_attribute(syn::Attribute { pound_token: attr.pound_token, style: attr.style, bracket_token: attr.bracket_token, meta: syn::Meta::List(syn::MetaList { path: list.path.clone(), delimiter: list.delimiter.clone(), tokens: retained_derives_tokens, }), }); } Ok(()) } /// Analyses and extracts the `#[repr(uN)]` or other annotations from the given struct. fn extract_attributes(attributes: &[syn::Attribute], config: &mut Config) -> Result<()> { for attr in attributes { if attr.path().is_ident("repr") { Self::extract_repr_attribute(attr, config)?; } else if attr.path().is_ident("derive") { Self::extract_derive_debug_attribute(attr, config)?; } else { config.push_retained_attribute(attr.clone()); } } Ok(()) } /// Analyses and extracts the configuration for all bitfield fields. fn analyse_config_for_fields(item_struct: &syn::ItemStruct, config: &mut Config) -> Result<()> { for (index, field) in Self::fields(item_struct) { let span = field.span(); let field_config = Self::extract_field_config(field)?; config.field_config(index, span, field_config)?; } Ok(()) } /// Extracts the `#[bits = N]` and `#[skip(..)]` attributes for a given field. fn extract_field_config(field: &syn::Field) -> Result { let mut config = FieldConfig::default(); for attr in &field.attrs { if attr.path().is_ident("bits") { let name_value = attr.meta.require_name_value()?; let span = name_value.span(); match &name_value.value { syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(lit_int), .. }) => { config.bits(lit_int.base10_parse::()?, span)?; } value => { return Err(format_err!( value.span(), "encountered invalid value type for #[bits = N]" )) } } } else if attr.path().is_ident("skip") { match &attr.meta { syn::Meta::Path(path) => { assert!(path.is_ident("skip")); config.skip(SkipWhich::All, path.span())?; } syn::Meta::List(meta_list) => { let (mut getters, mut setters) = (None, None); meta_list.parse_nested_meta(|meta| { let path = &meta.path; if path.is_ident("getters") { if let Some(previous) = getters { return raise_skip_error("(getters)", path.span(), previous); } getters = Some(path.span()); } else if path.is_ident("setters") { if let Some(previous) = setters { return raise_skip_error("(setters)", path.span(), previous); } setters = Some(path.span()); } else { return Err(meta.error( "encountered unknown or unsupported #[skip(..)] specifier", )); } Ok(()) })?; if getters.is_some() == setters.is_some() { config.skip(SkipWhich::All, meta_list.path.span())?; } else if getters.is_some() { config.skip(SkipWhich::Getters, meta_list.path.span())?; } else { config.skip(SkipWhich::Setters, meta_list.path.span())?; } } meta @ syn::Meta::NameValue(..) => { return Err(format_err!( meta.span(), "encountered invalid format for #[skip] field attribute" )) } } } else { config.retain_attr(attr.clone()); } } Ok(config) } } #[cfg(test)] mod tests { use super::*; use quote::ToTokens as _; #[test] fn retain_repr_arguments() { let attr: syn::Attribute = syn::parse_quote!(#[repr(C, align(8))]); let mut config = Config::default(); BitfieldStruct::extract_repr_attribute(&attr, &mut config).unwrap(); assert_eq!(config.retained_attributes.len(), 1); let retained = &config.retained_attributes[0]; assert_eq!( retained.to_token_stream().to_string(), attr.to_token_stream().to_string(), "repr arguments should be preserved when re-emitting retained repr attributes" ); } } ================================================ FILE: impl/src/bitfield/config.rs ================================================ use super::field_config::FieldConfig; use crate::errors::CombineError; use core::any::TypeId; use proc_macro2::Span; use std::collections::{hash_map::Entry, HashMap}; use syn::parse::Result; /// The configuration for the `#[bitfield]` macro. #[derive(Default)] pub struct Config { pub bytes: Option>, pub bits: Option>, pub filled: Option>, pub repr: Option>, pub derive_debug: Option>, pub derive_specifier: Option>, pub retained_attributes: Vec, pub field_configs: HashMap>, } /// Kinds of `#[repr(uN)]` annotations for a `#[bitfield]` struct. #[derive(Copy, Clone)] pub enum ReprKind { /// Found a `#[repr(u8)]` annotation. U8, /// Found a `#[repr(u16)]` annotation. U16, /// Found a `#[repr(u32)]` annotation. U32, /// Found a `#[repr(u64)]` annotation. U64, /// Found a `#[repr(u128)]` annotation. U128, } impl ReprKind { /// Returns the amount of bits required to have for the bitfield to satisfy the `#[repr(uN)]`. pub fn bits(self) -> usize { match self { Self::U8 => 8, Self::U16 => 16, Self::U32 => 32, Self::U64 => 64, Self::U128 => 128, } } } impl core::fmt::Debug for ReprKind { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "#[repr(u{})]", self.bits()) } } /// A configuration value and its originating span. #[derive(Clone)] pub struct ConfigValue { /// The actual value of the config. pub value: T, /// The originating span of the config. pub span: Span, } impl ConfigValue { /// Creates a new config value. pub fn new(value: T, span: Span) -> Self { Self { value, span } } } impl Config { /// Returns the value of the `filled` parameter if provided and otherwise `true`. pub fn filled_enabled(&self) -> bool { self.filled.as_ref().map_or(true, |config| config.value) } fn ensure_no_bits_and_repr_conflict(&self) -> Result<()> { if let (Some(bits), Some(repr)) = (self.bits.as_ref(), self.repr.as_ref()) { if bits.value != repr.value.bits() { return Err(format_err!( Span::call_site(), "encountered conflicting `bits = {}` and {:?} parameters", bits.value, repr.value, ) .into_combine( format_err!(bits.span, "conflicting `bits = {}` here", bits.value,) .into_combine(format_err!(repr.span, "conflicting {:?} here", repr.value)), )); } } Ok(()) } fn ensure_no_bits_and_bytes_conflict(&self) -> Result<()> { if let (Some(bits), Some(bytes)) = (self.bits.as_ref(), self.bytes.as_ref()) { fn next_div_by_8(value: usize) -> usize { ((value.saturating_sub(1) / 8) + 1) * 8 } if next_div_by_8(bits.value) / 8 != bytes.value { return Err(format_err!( Span::call_site(), "encountered conflicting `bits = {}` and `bytes = {}` parameters", bits.value, bytes.value, ) .into_combine(format_err!( bits.span, "conflicting `bits = {}` here", bits.value )) .into_combine(format_err!( bytes.span, "conflicting `bytes = {}` here", bytes.value, ))); } } Ok(()) } pub fn ensure_no_repr_and_filled_conflict(&self) -> Result<()> { if let (Some(repr), Some(filled @ ConfigValue { value: false, .. })) = (self.repr.as_ref(), self.filled.as_ref()) { return Err(format_err!( Span::call_site(), "encountered conflicting `{:?}` and `filled = {}` parameters", repr.value, filled.value, ) .into_combine(format_err!( repr.span, "conflicting `{:?}` here", repr.value )) .into_combine(format_err!( filled.span, "conflicting `filled = {}` here", filled.value, ))); } Ok(()) } /// Ensures that there are no conflicting configuration parameters. pub fn ensure_no_conflicts(&self) -> Result<()> { self.ensure_no_bits_and_repr_conflict()?; self.ensure_no_bits_and_bytes_conflict()?; self.ensure_no_repr_and_filled_conflict()?; Ok(()) } /// Returns an error showing both the duplicate as well as the previous parameters. fn raise_duplicate_error(name: &str, span: Span, previous: &ConfigValue) -> syn::Error where T: core::fmt::Debug + 'static, { if TypeId::of::() == TypeId::of::<()>() { format_err!(span, "encountered duplicate `{}` parameter", name,) } else { format_err!( span, "encountered duplicate `{}` parameter: duplicate set to {:?}", name, previous.value ) } .into_combine(format_err!( previous.span, "previous `{}` parameter here", name )) } /// Sets the `bytes: int` #[bitfield] parameter to the given value. /// /// # Errors /// /// If the specifier has already been set. pub fn bytes(&mut self, value: usize, span: Span) -> Result<()> { match &self.bytes { Some(previous) => return Err(Self::raise_duplicate_error("bytes", span, previous)), None => self.bytes = Some(ConfigValue::new(value, span)), } Ok(()) } /// Sets the `bits: int` #[bitfield] parameter to the given value. /// /// # Errors /// /// If the specifier has already been set. pub fn bits(&mut self, value: usize, span: Span) -> Result<()> { match &self.bits { Some(previous) => return Err(Self::raise_duplicate_error("bits", span, previous)), None => self.bits = Some(ConfigValue::new(value, span)), } Ok(()) } /// Sets the `filled: bool` #[bitfield] parameter to the given value. /// /// # Errors /// /// If the specifier has already been set. pub fn filled(&mut self, value: bool, span: Span) -> Result<()> { match &self.filled { Some(previous) => return Err(Self::raise_duplicate_error("filled", span, previous)), None => self.filled = Some(ConfigValue::new(value, span)), } Ok(()) } /// Registers the `#[repr(uN)]` attribute for the #[bitfield] macro. /// /// # Errors /// /// If a `#[repr(uN)]` attribute has already been found. pub fn repr(&mut self, value: ReprKind, span: Span) -> Result<()> { match &self.repr { Some(previous) => { return Err(Self::raise_duplicate_error("#[repr(uN)]", span, previous)) } None => self.repr = Some(ConfigValue::new(value, span)), } Ok(()) } /// Registers the `#[derive(Debug)]` attribute for the #[bitfield] macro. /// /// # Errors /// /// If a `#[derive(Debug)]` attribute has already been found. pub fn derive_debug(&mut self, span: Span) -> Result<()> { match &self.derive_debug { Some(previous) => { return Err(Self::raise_duplicate_error( "#[derive(Debug)]", span, previous, )) } None => self.derive_debug = Some(ConfigValue::new((), span)), } Ok(()) } /// Registers the `#[derive(Specifier)]` attribute for the #[bitfield] macro. /// /// # Errors /// /// If a `#[derive(Specifier)]` attribute has already been found. pub fn derive_specifier(&mut self, span: Span) -> Result<()> { match &self.derive_specifier { Some(previous) => { return Err(Self::raise_duplicate_error( "#[derive(Specifier)]", span, previous, )) } None => self.derive_specifier = Some(ConfigValue::new((), span)), } Ok(()) } /// Pushes another retained attribute that the #[bitfield] macro is going to re-expand and ignore. pub fn push_retained_attribute(&mut self, retained_attr: syn::Attribute) { self.retained_attributes.push(retained_attr); } /// Sets the field configuration and retained attributes for the given field. /// /// By convention we use the fields name to identify the field if existing. /// Otherwise we turn the fields discriminant into an appropriate string. /// /// # Errors /// /// If duplicate field configurations have been found for a field. pub fn field_config(&mut self, index: usize, span: Span, config: FieldConfig) -> Result<()> { match self.field_configs.entry(index) { Entry::Occupied(occupied) => { return Err(format_err!(span, "encountered duplicate config for field") .into_combine(format_err!(occupied.get().span, "previous config here"))) } Entry::Vacant(vacant) => { vacant.insert(ConfigValue::new(config, span)); } } Ok(()) } } ================================================ FILE: impl/src/bitfield/expand.rs ================================================ use super::{ config::{Config, ReprKind}, field_info::FieldInfo, BitfieldStruct, }; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote_spanned, ToTokens}; use syn::{self, punctuated::Punctuated, spanned::Spanned as _, Token}; impl BitfieldStruct { /// Expands the given `#[bitfield]` struct into an actual bitfield definition. pub fn expand(&self, config: &Config) -> TokenStream2 { let span = self.item_struct.span(); let check_filled = self.generate_check_for_filled(config); let struct_definition = self.generate_struct(config); let constructor_definition = self.generate_constructor(config); let specifier_impl = self.generate_specifier_impl(config); let byte_conversion_impls = self.expand_byte_conversion_impls(config); let getters_and_setters = self.expand_getters_and_setters(config); let bytes_check = self.expand_optional_bytes_check(config); let repr_impls_and_checks = self.expand_repr_from_impls_and_checks(config); let debug_impl = self.generate_debug_impl(config); quote_spanned!(span=> #struct_definition #check_filled #constructor_definition #byte_conversion_impls #getters_and_setters #specifier_impl #bytes_check #repr_impls_and_checks #debug_impl ) } /// Expands to the `Specifier` impl for the `#[bitfield]` struct if the /// `#[derive(Specifier)]` attribute is applied to it as well. /// /// Otherwise returns `None`. pub fn generate_specifier_impl(&self, config: &Config) -> Option { config.derive_specifier.as_ref()?; let span = self.item_struct.span(); let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let bits = self.generate_target_or_actual_bitfield_size(config); let next_divisible_by_8 = Self::next_divisible_by_8(&bits); Some(quote_spanned!(span=> const _: () = { impl #impl_generics ::modular_bitfield::private::checks::CheckSpecifierHasAtMost128Bits for #ident #ty_generics #where_clause { type CheckType = ::modular_bitfield::private::checks::BitCount<{(#bits <= 128) as ::core::primitive::usize}>; } }; impl #impl_generics ::modular_bitfield::Specifier for #ident #ty_generics #where_clause { const BITS: ::core::primitive::usize = #bits; type Bytes = <::modular_bitfield::private::checks::BitCount<{if #bits > 128 { 128 } else { #bits }}> as ::modular_bitfield::private::SpecifierBytes>::Bytes; type InOut = Self; #[inline] fn into_bytes( value: Self::InOut, ) -> ::core::result::Result { ::core::result::Result::Ok( <::modular_bitfield::private::checks::BitCount<{#next_divisible_by_8}> as ::modular_bitfield::private::ArrayBytesConversion>::array_into_bytes( value.bytes ) ) } #[inline] fn from_bytes( bytes: Self::Bytes, ) -> ::core::result::Result> { // Truncation of BITS is always valid due to maximum of 128 #[allow(clippy::cast_possible_truncation)] let __bf_max_value: Self::Bytes = (1 as Self::Bytes) .checked_shl(Self::BITS as ::core::primitive::u32) .unwrap_or(::MAX); if bytes <= __bf_max_value { let __bf_bytes = bytes.to_le_bytes(); ::core::result::Result::Ok(Self { bytes: <::modular_bitfield::private::checks::BitCount<{#next_divisible_by_8}> as ::modular_bitfield::private::ArrayBytesConversion>::bytes_into_array(bytes) }) } else { ::core::result::Result::Err(::modular_bitfield::error::InvalidBitPattern::new(bytes)) } } } )) } /// Generates the `core::fmt::Debug` impl if `#[derive(Debug)]` is included. pub fn generate_debug_impl(&self, config: &Config) -> Option { config.derive_debug.as_ref()?; let span = self.item_struct.span(); let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let is_tuple = matches!(self.item_struct.fields, syn::Fields::Unnamed(_)); let builder_name = if is_tuple { quote_spanned!(span=> debug_tuple) } else { quote_spanned!(span=> debug_struct) }; let fields = self.field_infos(config).map(|info| { let FieldInfo { index: _, field, config, } = &info; if config.skip_getters() { return None; } let field_span = field.span(); let field_name = if field.ident.is_some() { let field_name = info.name(); quote_spanned!(field_span=> #field_name,) } else { <_>::default() }; let field_ident = info.ident_frag(); let field_getter = field.ident.as_ref().map_or_else( || format_ident!("get_{}_or_err", field_ident), |_| format_ident!("{}_or_err", field_ident), ); Some(quote_spanned!(field_span=> .field( #field_name self.#field_getter() .as_ref() .map_or_else( |__bf_err| __bf_err as &dyn ::core::fmt::Debug, |__bf_field| __bf_field as &dyn ::core::fmt::Debug ) ) )) }); Some(quote_spanned!(span=> impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause { fn fmt(&self, __bf_f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { __bf_f.#builder_name(::core::stringify!(#ident)) #( #fields )* .finish() } } )) } /// Generates the expression denoting the sum of all field bit specifier sizes. /// /// # Example /// /// For the following struct: /// /// ```no_compile /// #[bitfield] /// pub struct Color { /// r: B8, /// g: B8, /// b: B8, /// a: bool, /// rest: B7, /// } /// ``` /// /// We generate the following tokens: /// /// ```no_compile /// ::BITS + /// ::BITS + /// ::BITS + /// ::BITS + /// ::BITS /// ``` /// /// Which is a compile time evaluatable expression. fn generate_bitfield_size(&self) -> TokenStream2 { self.item_struct .fields .iter() .map(|field| -> syn::Expr { let span = field.span(); let ty = &field.ty; syn::parse_quote_spanned!(span=> <#ty as ::modular_bitfield::Specifier>::BITS ) }) .collect::>() .into_token_stream() } /// Generates the expression denoting the actual configured or implied bit width. fn generate_target_or_actual_bitfield_size(&self, config: &Config) -> TokenStream2 { config.bits.as_ref().map_or_else( || self.generate_bitfield_size(), |bits_config| { let span = bits_config.span; let value = bits_config.value; quote_spanned!(span=> #value ) }, ) } /// Generates a check in case `bits = N` is unset to verify that the actual amount of bits is either /// /// - ... equal to `N`, if `filled = true` or /// - ... smaller than `N`, if `filled = false` fn generate_filled_check_for_unaligned_bits( &self, config: &Config, required_bits: usize, ) -> TokenStream2 { let span = self.item_struct.span(); let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let actual_bits = self.generate_bitfield_size(); let check_ident = if config.filled_enabled() { quote_spanned!(span=> CheckFillsUnalignedBits) } else { quote_spanned!(span=> CheckDoesNotFillUnalignedBits) }; let comparator = if config.filled_enabled() { quote_spanned!(span=> ==) } else { quote_spanned!(span=> >) }; quote_spanned!(span=> const _: () = { impl #impl_generics ::modular_bitfield::private::checks::#check_ident for #ident #ty_generics #where_clause { type CheckType = ::modular_bitfield::private::checks::BitCount<{(#required_bits #comparator #actual_bits) as ::core::primitive::usize}>; } }; ) } /// Generates a check in case `bits = N` is unset to verify that the actual amount of bits is either /// /// - ... divisible by 8, if `filled = true` or /// - ... not divisible by 8, if `filled = false` fn generate_filled_check_for_aligned_bits(&self, config: &Config) -> TokenStream2 { let span = self.item_struct.span(); let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let actual_bits = self.generate_bitfield_size(); let check_ident = if config.filled_enabled() { quote_spanned!(span=> CheckTotalSizeMultipleOf8) } else { quote_spanned!(span=> CheckTotalSizeIsNotMultipleOf8) }; quote_spanned!(span=> const _: () = { impl #impl_generics ::modular_bitfield::private::checks::#check_ident for #ident #ty_generics #where_clause { type Size = ::modular_bitfield::private::checks::TotalSize<::modular_bitfield::private::checks::BitCount<{(#actual_bits) % 8}>>; } }; ) } /// Generate check for either of the following two cases: /// /// - `filled = true`: Check if the total number of required bits is /// - ... the same as `N` if `bits = N` was provided or /// - ... a multiple of 8, otherwise /// - `filled = false`: Check if the total number of required bits is /// - ... smaller than `N` if `bits = N` was provided or /// - ... NOT a multiple of 8, otherwise fn generate_check_for_filled(&self, config: &Config) -> TokenStream2 { match config.bits.as_ref() { Some(bits_config) => { self.generate_filled_check_for_unaligned_bits(config, bits_config.value) } None => self.generate_filled_check_for_aligned_bits(config), } } /// Returns a token stream representing the next greater value divisible by 8. fn next_divisible_by_8(value: &TokenStream2) -> TokenStream2 { let span = value.span(); quote_spanned!(span=> (((#value - 1) / 8) + 1) * 8 ) } /// Generates the actual item struct definition for the `#[bitfield]`. /// /// Internally it only contains a byte array equal to the minimum required /// amount of bytes to compactly store the information of all its bit fields. fn generate_struct(&self, config: &Config) -> TokenStream2 { let span = self.item_struct.span(); let attrs = &config.retained_attributes; let vis = &self.item_struct.vis; let ident = &self.item_struct.ident; let generics = &self.item_struct.generics; let size = self.generate_target_or_actual_bitfield_size(config); let next_divisible_by_8 = Self::next_divisible_by_8(&size); quote_spanned!(span=> #( #attrs )* #vis struct #ident #generics { bytes: [::core::primitive::u8; #next_divisible_by_8 / 8], } ) } /// Generates the constructor for the bitfield that initializes all bytes to zero. fn generate_constructor(&self, config: &Config) -> TokenStream2 { let span = self.item_struct.span(); let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let size = self.generate_target_or_actual_bitfield_size(config); let next_divisible_by_8 = Self::next_divisible_by_8(&size); quote_spanned!(span=> impl #impl_generics #ident #ty_generics #where_clause { /// Returns an instance with zero initialized data. #[allow(clippy::new_without_default)] #[must_use] pub const fn new() -> Self { Self { bytes: [0_u8; #next_divisible_by_8 / 8], } } } ) } /// Generates the compile-time assertion if the optional `byte` parameter has been set. fn expand_optional_bytes_check(&self, config: &Config) -> Option { let ident = &self.item_struct.ident; config.bytes.as_ref().map(|config| { let bytes = config.value; quote_spanned!(config.span=> const _: () = { struct ExpectedBytes { __bf_unused: [::core::primitive::u8; #bytes] } ::modular_bitfield::private::static_assertions::assert_eq_size!( ExpectedBytes, #ident ); }; ) }) } /// Generates `From` impls for a `#[repr(uN)]` annotated #[bitfield] struct. fn expand_repr_from_impls_and_checks(&self, config: &Config) -> Option { let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let where_predicates = where_clause.map(|w| &w.predicates); config.repr.as_ref().map(|repr| { let kind = &repr.value; let span = repr.span; let prim = match kind { ReprKind::U8 => quote_spanned!(span=> ::core::primitive::u8), ReprKind::U16 => quote_spanned!(span=> ::core::primitive::u16), ReprKind::U32 => quote_spanned!(span=> ::core::primitive::u32), ReprKind::U64 => quote_spanned!(span=> ::core::primitive::u64), ReprKind::U128 => quote_spanned!(span=> ::core::primitive::u128), }; let actual_bits = self.generate_target_or_actual_bitfield_size(config); let trait_check_ident = match kind { ReprKind::U8 => quote_spanned!(span=> IsU8Compatible), ReprKind::U16 => quote_spanned!(span=> IsU16Compatible), ReprKind::U32 => quote_spanned!(span=> IsU32Compatible), ReprKind::U64 => quote_spanned!(span=> IsU64Compatible), ReprKind::U128 => quote_spanned!(span=> IsU128Compatible), }; quote_spanned!(span=> impl #impl_generics ::core::convert::From<#prim> for #ident #ty_generics where ::modular_bitfield::private::checks::BitCount<{#actual_bits}>: ::modular_bitfield::private::#trait_check_ident, #where_predicates { #[inline] fn from(__bf_prim: #prim) -> Self { Self { bytes: <#prim>::to_le_bytes(__bf_prim) } } } impl #impl_generics ::core::convert::From<#ident #ty_generics> for #prim where ::modular_bitfield::private::checks::BitCount<{#actual_bits}>: ::modular_bitfield::private::#trait_check_ident, #where_predicates { #[inline] fn from(__bf_bitfield: #ident #ty_generics) -> Self { ::from_le_bytes(__bf_bitfield.bytes) } } ) }) } /// Generates routines to allow conversion from and to bytes for the `#[bitfield]` struct. fn expand_byte_conversion_impls(&self, config: &Config) -> TokenStream2 { let span = self.item_struct.span(); let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let size = self.generate_target_or_actual_bitfield_size(config); let next_divisible_by_8 = Self::next_divisible_by_8(&size); let bytes_ty = quote_spanned!(span=> [::core::primitive::u8; #next_divisible_by_8 / 8]); let (from_bytes, from_impl) = if config.filled_enabled() { ( quote_spanned!(span=> /// Converts the given bytes directly into the bitfield struct. #[inline] #[must_use] pub const fn from_bytes(bytes: #bytes_ty) -> Self { Self { bytes } } ), quote_spanned!(span=> impl #impl_generics ::core::convert::From<#bytes_ty> for #ident #ty_generics #where_clause { fn from(bytes: #bytes_ty) -> Self { Self::from_bytes(bytes) } } ), ) } else { ( quote_spanned!(span=> /// Converts the given bytes directly into the bitfield struct. /// /// # Errors /// /// If the given bytes contain bits at positions that are undefined for `Self`. #[inline] pub fn from_bytes( bytes: #bytes_ty ) -> ::core::result::Result { #[allow(clippy::identity_op)] if ::core::primitive::u16::from(bytes[(#next_divisible_by_8 / 8) - 1]) < (1 << (8 - (#next_divisible_by_8 - (#size)))) { ::core::result::Result::Ok(Self { bytes }) } else { ::core::result::Result::Err(::modular_bitfield::error::OutOfBounds) } } ), quote_spanned!(span=> impl #impl_generics ::core::convert::TryFrom<#bytes_ty> for #ident #ty_generics #where_clause { type Error = ::modular_bitfield::error::OutOfBounds; #[inline] fn try_from(bytes: #bytes_ty) -> ::core::result::Result { Self::from_bytes(bytes) } } ), ) }; quote_spanned!(span=> #from_impl impl #impl_generics ::core::convert::From<#ident #ty_generics> for #bytes_ty #where_clause { #[inline] fn from(bytes: #ident #ty_generics) -> Self { bytes.into_bytes() } } impl #impl_generics #ident #ty_generics #where_clause { /// Returns the underlying bits. /// /// # Layout /// /// The returned byte array is layed out in the same way as described /// [here](https://docs.rs/modular-bitfield/#generated-structure). #[inline] pub const fn into_bytes(self) -> #bytes_ty { self.bytes } #from_bytes } ) } /// Generates code to check for the bit size arguments of bitfields. fn expand_bits_checks_for_field(field_info: FieldInfo<'_>) -> TokenStream2 { let FieldInfo { index: _, field, config, } = field_info; config.bits.as_ref().map(|bits| { let ty = &field.ty; let expected_bits = bits.value; let expected_span = bits.span; let actual_span = field.ty.span(); let actual_ty = quote_spanned!(actual_span=> ::modular_bitfield::private::checks::BitCount<{<#ty as ::modular_bitfield::Specifier>::BITS}> ); quote_spanned!(expected_span=> let _: #actual_ty = ::modular_bitfield::private::checks::BitCount::<{#expected_bits}>; ) }).unwrap_or_default() } fn expand_getters_for_field( &self, offset: &TokenStream2, info: &FieldInfo<'_>, ) -> Option { let FieldInfo { index: _, field, config, } = info; if config.skip_getters() { return None; } let struct_ident = &self.item_struct.ident; let span = field.span(); let ident = info.ident_frag(); let name = info.name(); let retained_attrs = &config.retained_attrs; let get_ident = field .ident .clone() .unwrap_or_else(|| format_ident!("get_{}", ident)); let get_checked_ident = field.ident.as_ref().map_or_else( || format_ident!("get_{}_or_err", ident), |_| format_ident!("{}_or_err", ident), ); let ty = &field.ty; let vis = &field.vis; let get_assert_msg = format!("value contains invalid bit pattern for field {struct_ident}.{name}"); let getter_docs = format!("Returns the value of `{name}`."); let checked_getter_docs = format!( "Returns the value of `{name}`.\n\n\ # Errors\n\n\ If the returned value contains an invalid bit pattern for `{name}`.", ); let getters = quote_spanned!(span=> #[doc = #getter_docs] #[inline] #[must_use] #( #retained_attrs )* #vis fn #get_ident(&self) -> <#ty as ::modular_bitfield::Specifier>::InOut { self.#get_checked_ident().expect(#get_assert_msg) } #[doc = #checked_getter_docs] #[inline] #[allow(dead_code)] #( #retained_attrs )* #vis fn #get_checked_ident( &self, ) -> ::core::result::Result< <#ty as ::modular_bitfield::Specifier>::InOut, ::modular_bitfield::error::InvalidBitPattern<<#ty as ::modular_bitfield::Specifier>::Bytes> > { let __bf_read: <#ty as ::modular_bitfield::Specifier>::Bytes = { ::modular_bitfield::private::read_specifier::<#ty>(&self.bytes[..], #offset) }; <#ty as ::modular_bitfield::Specifier>::from_bytes(__bf_read) } ); Some(getters) } fn expand_setters_for_field( &self, offset: &TokenStream2, info: &FieldInfo<'_>, ) -> Option { let FieldInfo { index: _, field, config, } = info; if config.skip_setters() { return None; } let struct_ident = &self.item_struct.ident; let span = field.span(); let retained_attrs = &config.retained_attrs; let ident = info.ident_frag(); let name = info.name(); let ty = &field.ty; let vis = &field.vis; let set_ident = format_ident!("set_{}", ident); let set_checked_ident = format_ident!("set_{}_checked", ident); let with_ident = format_ident!("with_{}", ident); let with_checked_ident = format_ident!("with_{}_checked", ident); let set_assert_msg = format!("value out of bounds for field {struct_ident}.{name}"); let setter_docs = format!( "Sets the value of `{name}` to the given value.\n\n\ # Panics\n\n\ If the given value is out of bounds for `{name}`.", ); let checked_setter_docs = format!( "Sets the value of `{name}` to the given value.\n\n\ # Errors\n\n\ If the given value is out of bounds for `{name}`.", ); let with_docs = format!( "Returns a copy of the bitfield with the value of `{name}` \ set to the given value.\n\n\ # Panics\n\n\ If the given value is out of bounds for `{name}`.", ); let checked_with_docs = format!( "Returns a copy of the bitfield with the value of `{name}` \ set to the given value.\n\n\ # Errors\n\n\ If the given value is out of bounds for `{name}`.", ); let setters = quote_spanned!(span=> #[doc = #with_docs] #[inline] #[allow(dead_code)] #[must_use] #( #retained_attrs )* #vis fn #with_ident( mut self, new_val: <#ty as ::modular_bitfield::Specifier>::InOut ) -> Self { self.#set_ident(new_val); self } #[doc = #checked_with_docs] #[inline] #[allow(dead_code)] #( #retained_attrs )* #vis fn #with_checked_ident( mut self, new_val: <#ty as ::modular_bitfield::Specifier>::InOut, ) -> ::core::result::Result { self.#set_checked_ident(new_val)?; ::core::result::Result::Ok(self) } #[doc = #setter_docs] #[inline] #[allow(dead_code)] #( #retained_attrs )* #vis fn #set_ident(&mut self, new_val: <#ty as ::modular_bitfield::Specifier>::InOut) { self.#set_checked_ident(new_val).expect(#set_assert_msg); } #[doc = #checked_setter_docs] #[inline] #( #retained_attrs )* #vis fn #set_checked_ident( &mut self, new_val: <#ty as ::modular_bitfield::Specifier>::InOut ) -> ::core::result::Result<(), ::modular_bitfield::error::OutOfBounds> { const __BF_BASE_BITS: ::core::primitive::usize = ::core::mem::size_of::<<#ty as ::modular_bitfield::Specifier>::Bytes>() * 8; const __BF_MAX_VALUE: <#ty as ::modular_bitfield::Specifier>::Bytes = !0 >> (__BF_BASE_BITS - <#ty as ::modular_bitfield::Specifier>::BITS); let __bf_raw_val = <#ty as ::modular_bitfield::Specifier>::into_bytes(new_val)?; // Value comparison to const guarantees the optimiser eliminates // branching entirely when the maximum value is the same as the // maximum value of the underlying type. #[allow(clippy::absurd_extreme_comparisons)] if __bf_raw_val <= __BF_MAX_VALUE { ::modular_bitfield::private::write_specifier::<#ty>(&mut self.bytes[..], #offset, __bf_raw_val); ::core::result::Result::Ok(()) } else { ::core::result::Result::Err(::modular_bitfield::error::OutOfBounds) } } ); Some(setters) } fn expand_getters_and_setters_for_field( &self, offset: &mut Punctuated, info: &FieldInfo<'_>, ) -> TokenStream2 { let field = info.field; let span = field.span(); let ty = &field.ty; let offset_ts = if offset.is_empty() { quote_spanned!(span=> 0) } else { offset.to_token_stream() }; let getters = self.expand_getters_for_field(&offset_ts, info); let setters = self.expand_setters_for_field(&offset_ts, info); let getters_and_setters = quote_spanned!(span=> #getters #setters ); offset.push(syn::parse_quote_spanned!(span=> <#ty as ::modular_bitfield::Specifier>::BITS)); getters_and_setters } fn expand_getters_and_setters(&self, config: &Config) -> TokenStream2 { let span = self.item_struct.span(); let ident = &self.item_struct.ident; let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl(); let mut offset = Punctuated::::new(); let bits_checks = self .field_infos(config) .map(|field_info| Self::expand_bits_checks_for_field(field_info)); let setters_and_getters = self .field_infos(config) .map(|field_info| self.expand_getters_and_setters_for_field(&mut offset, &field_info)); quote_spanned!(span=> const _: () = { #( #bits_checks )* }; impl #impl_generics #ident #ty_generics #where_clause { #( #setters_and_getters )* } ) } } ================================================ FILE: impl/src/bitfield/field_config.rs ================================================ use super::{config::ConfigValue, raise_skip_error}; use crate::errors::CombineError; use proc_macro2::Span; #[derive(Default, Clone)] pub struct FieldConfig { /// Attributes that are re-expanded and going to be ignored by the rest of the `#[bitfield]` invocation. pub retained_attrs: Vec, /// An encountered `#[bits = N]` attribute on a field. pub bits: Option>, /// An encountered `#[skip]` attribute on a field. pub skip: Option>, } /// Controls which parts of the code generation to skip. #[derive(PartialEq, Eq, Hash, Copy, Clone)] pub enum SkipWhich { /// Skip code generation of getters and setters. All, /// Skip code generation of only getters. /// /// For field `f` these include: /// /// - `f` /// - `f_or_err` Getters, /// Skip code generation of only setters. /// /// For field `f` these include: /// /// - `set_f` /// - `set_f_checked` /// - `with_f` /// - `with_f_checked` Setters, } impl SkipWhich { /// Returns `true` if code generation of getters should be skipped. pub fn skip_getters(self) -> bool { matches!(self, Self::All | Self::Getters) } /// Returns `true` if code generation of setters should be skipped. pub fn skip_setters(self) -> bool { matches!(self, Self::All | Self::Setters) } } impl FieldConfig { /// Registers the given attribute to be re-expanded and further ignored. pub fn retain_attr(&mut self, attr: syn::Attribute) { self.retained_attrs.push(attr); } /// Sets the `#[bits = N]` if found for a `#[bitfield]` annotated field. /// /// # Errors /// /// If previously already registered a `#[bits = N]`. pub fn bits(&mut self, amount: usize, span: Span) -> Result<(), syn::Error> { match self.bits { Some(ref previous) => { return Err(format_err!( span, "encountered duplicate `#[bits = N]` attribute for field" ) .into_combine(format_err!(previous.span, "duplicate `#[bits = N]` here"))) } None => { self.bits = Some(ConfigValue { value: amount, span, }); } } Ok(()) } /// Sets the `#[skip(which)]` if found for a `#[bitfield]` annotated field. /// /// # Syntax /// /// - `#[skip]` defaults to `SkipWhich::All`. /// - `#[skip(getters)]` is `SkipWhich::Getters`. /// - `#[skip(setters)]` is `SkipWhich::Setters`. /// - `#[skip(getters, setters)]` is the same as `#[skip]`. /// - `#[skip(getters)] #[skip(setters)]` is the same as `#[skip]`. /// /// # Errors /// /// If previously already registered a `#[skip]` that overlaps with the previous. /// E.g. when skipping getters or setters twice. Note that skipping getters followed /// by skipping setters is fine. pub fn skip(&mut self, which: SkipWhich, span: Span) -> Result<(), syn::Error> { match self.skip { Some(ref previous) => { match which { SkipWhich::All => return raise_skip_error("", span, previous.span), SkipWhich::Getters => { if previous.value == SkipWhich::Getters || previous.value == SkipWhich::All { return raise_skip_error("(getters)", span, previous.span); } } SkipWhich::Setters => { if previous.value == SkipWhich::Setters || previous.value == SkipWhich::All { return raise_skip_error("(setters)", span, previous.span); } } } self.skip = Some(ConfigValue { value: SkipWhich::All, span: span.join(previous.span).unwrap_or(span), }); } None => self.skip = Some(ConfigValue { value: which, span }), } Ok(()) } /// Returns `true` if the config demands that code generation for setters should be skipped. pub fn skip_setters(&self) -> bool { self.skip .as_ref() .is_some_and(|config| SkipWhich::skip_setters(config.value)) } /// Returns `true` if the config demands that code generation for getters should be skipped. pub fn skip_getters(&self) -> bool { self.skip .as_ref() .is_some_and(|config| SkipWhich::skip_getters(config.value)) } } ================================================ FILE: impl/src/bitfield/field_info.rs ================================================ use super::{field_config::FieldConfig, BitfieldStruct, Config}; /// Compactly stores all shared and useful information about a single `#[bitfield]` field. pub struct FieldInfo<'a> { /// The index of the field. pub index: usize, /// The actual field. pub field: &'a syn::Field, /// The configuration of the field. pub config: FieldConfig, } impl<'a> FieldInfo<'a> { /// Creates a new field info. pub fn new(id: usize, field: &'a syn::Field, config: FieldConfig) -> Self { Self { index: id, field, config, } } /// Returns the ident fragment for this field. pub fn ident_frag(&self) -> &dyn quote::IdentFragment { match &self.field.ident { Some(ident) => ident, None => &self.index, } } /// Returns the field's identifier as `String`. pub fn name(&self) -> String { Self::ident_as_string(self.field, self.index) } /// Returns the field's identifier at the given index as `String`. pub fn ident_as_string(field: &'a syn::Field, index: usize) -> String { field .ident .as_ref() .map_or_else(|| format!("{index}"), ToString::to_string) } } impl BitfieldStruct { /// Returns an iterator over the names of the fields. /// /// If a field has no name it is replaced by its field number. pub fn fields(item_struct: &syn::ItemStruct) -> impl Iterator { item_struct.fields.iter().enumerate() } /// Returns an iterator over the names of the fields. /// /// If a field has no name it is replaced by its field number. pub fn field_infos<'a, 'b: 'a>( &'a self, config: &'b Config, ) -> impl Iterator> { Self::fields(&self.item_struct).map(move |(n, field)| { let field_config = config .field_configs .get(&n) .map(|config| &config.value) .cloned() .unwrap_or_default(); FieldInfo::new(n, field, field_config) }) } } ================================================ FILE: impl/src/bitfield/mod.rs ================================================ mod analyse; mod config; mod expand; mod field_config; mod field_info; mod params; use self::{config::Config, params::ParamArgs}; use core::convert::TryFrom; use proc_macro2::{Span, TokenStream as TokenStream2}; use syn::{self, parse::Result}; /// Analyzes the given token stream for `#[bitfield]` properties and expands code if valid. pub fn analyse_and_expand(args: TokenStream2, input: TokenStream2) -> TokenStream2 { match analyse_and_expand_or_error(args, input) { Ok(output) => output, Err(err) => err.to_compile_error(), } } /// Analyzes the given token stream for `#[bitfield]` properties and expands code if valid. /// /// # Errors /// /// If the given token stream does not yield a valid `#[bitfield]` specifier. fn analyse_and_expand_or_error(args: TokenStream2, input: TokenStream2) -> Result { let input = syn::parse2::(input)?; let params = syn::parse2::(args)?; let mut config = Config::default(); config.feed_params(params)?; let bitfield = BitfieldStruct::try_from((&mut config, input))?; Ok(bitfield.expand(&config)) } /// Type used to guide analysis and expansion of `#[bitfield]` structs. struct BitfieldStruct { /// The input `struct` item. item_struct: syn::ItemStruct, } fn raise_skip_error(skip_params: &str, span: Span, previous: Span) -> Result<()> { Err(crate::errors::CombineError::into_combine( format_err!( span, "encountered duplicate `#[skip{}]` attribute for field", skip_params ), format_err!(previous, "duplicate `#[skip{}]` here", skip_params), )) } ================================================ FILE: impl/src/bitfield/params.rs ================================================ use super::config::Config; use proc_macro2::Span; use syn::{parse::Result, spanned::Spanned}; /// The parameters given to the `#[bitfield]` proc. macro. pub struct ParamArgs { args: Vec, } impl syn::parse::Parse for ParamArgs { fn parse(input: syn::parse::ParseStream<'_>) -> Result { let punctuated = >::parse_terminated(input)?; Ok(Self { args: punctuated.into_iter().collect(), }) } } impl IntoIterator for ParamArgs { type Item = syn::MetaNameValue; type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.args.into_iter() } } impl Config { /// Feeds a parameter that takes an integer value to the `#[bitfield]` configuration. fn feed_int_param(name_value: &syn::MetaNameValue, name: &str, on_success: F) -> Result<()> where F: FnOnce(usize, Span) -> Result<()>, { assert!(name_value.path.is_ident(name)); match &name_value.value { syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(lit_int), .. }) => { let span = lit_int.span(); let value = lit_int.base10_parse::().map_err(|err| { format_err!( span, "encountered malformatted integer value for `{}` parameter: {}", name, err ) })?; on_success(value, name_value.span())?; } invalid => { return Err(format_err!( invalid, "encountered invalid value argument for #[bitfield] `{}` parameter", name )) } } Ok(()) } /// Feeds a `bytes: int` parameter to the `#[bitfield]` configuration. fn feed_bytes_param(&mut self, name_value: &syn::MetaNameValue) -> Result<()> { Self::feed_int_param(name_value, "bytes", |value, span| self.bytes(value, span)) } /// Feeds a `bytes: int` parameter to the `#[bitfield]` configuration. fn feed_bits_param(&mut self, name_value: &syn::MetaNameValue) -> Result<()> { Self::feed_int_param(name_value, "bits", |value, span| self.bits(value, span)) } /// Feeds a `filled: bool` parameter to the `#[bitfield]` configuration. fn feed_filled_param(&mut self, name_value: &syn::MetaNameValue) -> Result<()> { assert!(name_value.path.is_ident("filled")); match &name_value.value { syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Bool(lit_bool), .. }) => { self.filled(lit_bool.value, name_value.span())?; } invalid => { return Err(format_err!( invalid, "encountered invalid value argument for #[bitfield] `filled` parameter", )) } } Ok(()) } /// Feeds the given parameters to the `#[bitfield]` configuration. /// /// # Errors /// /// If a parameter is malformatted, unexpected, duplicate or in conflict. pub fn feed_params<'a, P>(&mut self, params: P) -> Result<()> where P: IntoIterator + 'a, { for name_value in params { if name_value.path.is_ident("bytes") { self.feed_bytes_param(&name_value)?; } else if name_value.path.is_ident("bits") { self.feed_bits_param(&name_value)?; } else if name_value.path.is_ident("filled") { self.feed_filled_param(&name_value)?; } else { return Err(format_err!( name_value, "encountered unsupported #[bitfield] attribute" )); } } Ok(()) } } ================================================ FILE: impl/src/bitfield_specifier.rs ================================================ use proc_macro2::TokenStream as TokenStream2; use quote::quote_spanned; use syn::spanned::Spanned as _; pub fn generate(input: TokenStream2) -> TokenStream2 { match generate_or_error(input) { Ok(output) => output, Err(err) => err.to_compile_error(), } } fn generate_or_error(input: TokenStream2) -> syn::Result { let input = syn::parse2::(input)?; match input.data { syn::Data::Enum(data_enum) => generate_enum(&syn::ItemEnum { attrs: input.attrs, vis: input.vis, enum_token: data_enum.enum_token, ident: input.ident, generics: input.generics, brace_token: data_enum.brace_token, variants: data_enum.variants, }), syn::Data::Struct(_) => Err(format_err!( input, "structs are not supported as bitfield specifiers", )), syn::Data::Union(_) => Err(format_err!( input, "unions are not supported as bitfield specifiers", )), } } struct Attributes { bits: Option, } fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result { let attributes = attrs .iter() .filter(|attr| attr.path().is_ident("bits")) .try_fold(Attributes { bits: None }, |mut acc, attr| { if acc.bits.is_some() { return Err(format_err_spanned!( attr, "More than one 'bits' attribute is not permitted", )); } let meta = attr.meta.require_name_value()?; acc.bits = if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(lit), .. }) = &meta.value { Some(lit.base10_parse::()?) } else { return Err(format_err_spanned!( attr, "could not parse 'bits' attribute", )); }; Ok(acc) })?; Ok(attributes) } fn generate_enum(input: &syn::ItemEnum) -> syn::Result { let span = input.span(); let attributes = parse_attrs(&input.attrs)?; let enum_ident = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let bits = if let Some(bits) = attributes.bits { bits } else { let count_variants = input.variants.iter().count(); if !count_variants.is_power_of_two() { return Err(format_err!( span, "#[derive(Specifier)] expected a number of variants which is a power of 2, specify #[bits = {}] if that was your intent", count_variants.next_power_of_two().trailing_zeros(), )); } // We can take `trailing_zeros` returns type as the required amount of bits. if let Some(power_of_two) = count_variants.checked_next_power_of_two() { power_of_two.trailing_zeros() as usize } else { return Err(format_err!( span, "#[derive(Specifier)] has too many variants to pack into a bitfield", )); } }; let variants = input .variants .iter() .filter_map(|variant| match &variant.fields { syn::Fields::Unit => Some(&variant.ident), _ => None, }) .collect::>(); let check_discriminants = variants.iter().map(|ident| { let span = ident.span(); quote_spanned!(span=> impl #impl_generics ::modular_bitfield::private::checks::CheckDiscriminantInRange< ::modular_bitfield::private::checks::BitCount<{Self::#ident as ::core::primitive::usize}> > for #enum_ident #ty_generics #where_clause { type CheckType = ::modular_bitfield::private::checks::BitCount<{ ((Self::#ident as ::core::primitive::usize) < (1 << #bits)) as ::core::primitive::usize }>; } ) }); let from_bytes_arms = variants.iter().map(|ident| { let span = ident.span(); quote_spanned!(span=> __bitfield_binding if __bitfield_binding == Self::#ident as ::Bytes => { ::core::result::Result::Ok(Self::#ident) } ) }); Ok(quote_spanned!(span=> #( #check_discriminants )* impl #impl_generics ::modular_bitfield::Specifier for #enum_ident #ty_generics #where_clause { const BITS: ::core::primitive::usize = #bits; type Bytes = <::modular_bitfield::private::checks::BitCount<#bits> as ::modular_bitfield::private::SpecifierBytes>::Bytes; type InOut = Self; #[inline] fn into_bytes(input: ::InOut) -> ::core::result::Result<::Bytes, ::modular_bitfield::error::OutOfBounds> { ::core::result::Result::Ok(input as ::Bytes) } #[inline] fn from_bytes(bytes: ::Bytes) -> ::core::result::Result<::InOut, ::modular_bitfield::error::InvalidBitPattern<::Bytes>> { match bytes { #( #from_bytes_arms ),* invalid_bytes => { ::core::result::Result::Err( <::modular_bitfield::error::InvalidBitPattern<::Bytes>>::new(invalid_bytes) ) } } } } )) } ================================================ FILE: impl/src/define_specifiers.rs ================================================ use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; pub fn generate(_input: TokenStream2) -> TokenStream2 { let specifiers = (1..=128).map(generate_specifier_for); quote! { #( #specifiers )* } } fn generate_specifier_for(bits: usize) -> TokenStream2 { let in_out = match bits { 1..=8 => quote! { ::core::primitive::u8 }, 9..=16 => quote! { ::core::primitive::u16 }, 17..=32 => quote! { ::core::primitive::u32 }, 33..=64 => quote! { ::core::primitive::u64 }, 65..=128 => quote! { ::core::primitive::u128 }, _ => unreachable!(), }; let ident = format_ident!("B{bits}"); let doc_comment = if bits == 1 { "Specifier for a single bit.".to_string() } else { format!("Specifier for {bits} bits.") }; let max_value = if bits.is_power_of_two() && bits >= 8 { // The compiler can eliminate a check against `x > MAX` entirely // so this will yield a no-op in release mode builds. quote! {{ <#in_out>::MAX }} } else { quote! {{ ((1 as #in_out) << #bits) - 1 }} }; quote! { #[doc = #doc_comment] #[derive(Copy, Clone)] pub enum #ident {} impl crate::Specifier for #ident { const BITS: usize = #bits; type Bytes = #in_out; type InOut = #in_out; #[inline] fn into_bytes(input: Self::InOut) -> Result { if input <= #max_value { Ok(input) } else { Err(crate::OutOfBounds) } } #[inline] fn from_bytes(bytes: Self::Bytes) -> Result> { if bytes <= #max_value { Ok(bytes) } else { Err(crate::InvalidBitPattern::new(bytes)) } } } impl crate::private::SpecifierBytes for crate::private::checks::BitCount<#bits> { type Bytes = #in_out; } impl crate::private::checks::private::Sealed for crate::private::checks::BitCount<#bits> {} } } ================================================ FILE: impl/src/errors.rs ================================================ /// Creates a [`syn::Error`] with the format message and infers the /// [`Span`](`proc_macro2::Span`) using [`ToTokens`](`quote::ToTokens`). /// /// # Parameters /// /// - The first argument must implement [`quote::ToTokens`] in order to /// infer a [`Span`](`proc_macro2::Span`). /// - The second argument is a format string. /// - The rest are format string arguments. /// /// # Note /// /// On stable Rust this might yield higher quality error span information to the user /// than [`format_err`]. /// - Source: /// [`syn::Error::new_spanned`](https://docs.rs/syn/1.0.33/syn/struct.Error.html#method.new_spanned) /// - Tracking issue: [`#54725`](https://github.com/rust-lang/rust/issues/54725) macro_rules! format_err_spanned { ( $tokens:expr, $($msg:tt)* ) => {{ ::syn::Error::new_spanned( &$tokens, format_args!($($msg)*) ) }} } /// Creates a [`syn::Error`] with the format message and infers the /// [`Span`](`proc_macro2::Span`) using [`Spanned`](`syn::spanned::Spanned`). /// /// # Parameters /// /// - The first argument must be a type that implements [`syn::spanned::Spanned`]. /// - The second argument is a format string. /// - The rest are format string arguments. /// /// # Note /// /// On stable Rust this might yield worse error span information to the user /// than [`format_err_spanned`]. /// - Source: /// [`syn::Error::new_spanned`](https://docs.rs/syn/1.0.33/syn/struct.Error.html#method.new_spanned) /// - Tracking issue: [`#54725`](https://github.com/rust-lang/rust/issues/54725) macro_rules! format_err { ( $spanned:expr, $($msg:tt)* ) => {{ ::syn::Error::new( <_ as ::syn::spanned::Spanned>::span(&$spanned), format_args!($($msg)*) ) }} } pub trait CombineError { /// Combines `self` with the given `another` error and returns back combined `self`. fn into_combine(self, another: syn::Error) -> Self; } impl CombineError for syn::Error { fn into_combine(mut self, another: syn::Error) -> Self { self.combine(another); self } } ================================================ FILE: impl/src/lib.rs ================================================ #![recursion_limit = "256"] #![forbid(unsafe_code)] #![warn(clippy::pedantic, rust_2018_idioms)] #[macro_use] mod errors; mod bitfield; mod bitfield_specifier; mod define_specifiers; use proc_macro::TokenStream; /// Generates the `B1`, `B2`, ..., `B128` bitfield specifiers. /// /// Only of use witihn the `modular_bitfield` crate itself. #[proc_macro] pub fn define_specifiers(input: TokenStream) -> TokenStream { define_specifiers::generate(input.into()).into() } #[proc_macro_attribute] pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream { bitfield::analyse_and_expand(args.into(), input.into()).into() } #[proc_macro_derive(Specifier, attributes(bits))] pub fn specifier(input: TokenStream) -> TokenStream { bitfield_specifier::generate(input.into()).into() } #[cfg(coverage)] #[test] fn ui_code_coverage() { use runtime_macros::{emulate_attributelike_macro_expansion, emulate_derive_macro_expansion}; use std::fs::File; let mut run_success = true; for entry in glob::glob("../tests/ui/**/*.rs").unwrap() { let entry = entry.unwrap(); run_success &= emulate_attributelike_macro_expansion( File::open(entry.as_path()).unwrap(), &[("bitfield", bitfield::analyse_and_expand)], ) .is_ok(); run_success &= emulate_derive_macro_expansion( File::open(entry.as_path()).unwrap(), &[("Specifier", bitfield_specifier::generate)], ) .is_ok(); } assert!(run_success); } ================================================ FILE: release.sh ================================================ #!/usr/bin/env bash set -ueo pipefail DEFAULT_REPO=modular-bitfield/modular-bitfield DEFAULT_BRANCH=master usage() { echo "Usage: $0 [branch] [version]" echo echo "Specifying a branch will create a new patch release from that branch." echo "Otherwise, a new release will be created from '$DEFAULT_BRANCH'." echo echo "Specifying a version will create a release with that specific version." echo "Otherwise, the version number for the release will be adapted from" echo "Cargo.toml." echo echo "After tagging, the version number in Cargo.toml will automatically be" echo "bumped unless a pre-release version number was passed explicitly to" echo "this script, in which case the version number in Cargo.toml will be" echo "restored." echo echo "If the version number for the new release ends in .0, a new minor" echo "release branch will also be created." echo echo "Supported environment variables:" echo " REPO: The GitHub repository (user/repo) to use for the release." echo " Defaults to $DEFAULT_REPO." exit 0 } set_package_publish() { sed -i'' -e "s/^\\(publish[[:space:]]*=[[:space:]]*\\).*$/\\1$1/" Cargo.toml } set_package_version() { sed -i'' -e "s/^\\(version[[:space:]]*=[[:space:]]*\\)\"[^\"]*\"/\\1\"$1\"/" Cargo.toml sed -i'' -e "s/^\\(modular-bitfield-impl[[:space:]]*=.*version[[:space:]]*=[[:space:]]*\\)\"[^\"]*\"/\\1\"$1\"/" Cargo.toml } if [ "${1-}" == "--help" ]; then usage elif [ -n "${1-}" ]; then BRANCH=$1 else BRANCH=$DEFAULT_BRANCH fi if [ -n "${2-}" ]; then VERSION=$2 if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-.*)?$ ]]; then echo "Invalid version '$VERSION'; versions must be in the form" echo "\`major.minor.patch[-extra]\`." exit 1 fi else VERSION= fi REPO=${REPO:-$DEFAULT_REPO} ROOT_DIR=$(cd "$(dirname "$0")" && pwd) BUILD_DIR="$ROOT_DIR/build-release" if [ -d "$BUILD_DIR" ]; then echo "Existing build directory detected at $BUILD_DIR" echo "Aborted." exit 1 fi echo "This is an internal modular-bitfield release script!" echo -n "Press 'y' to create a new modular-bitfield release from $REPO branch $BRANCH" if [ -z "$VERSION" ]; then echo "." else echo -e "\nwith version override $VERSION." fi echo "(You can abort pushing upstream later on if something goes wrong.)" read -r -s -n 1 if [ "$REPLY" != "y" ]; then echo "Aborted." exit 0 fi # Using a pristine copy of the repo for the release to avoid local changes # making their way into the release process, and to avoid polluting the local # repo with wrong changes if the release process is aborted cd "$ROOT_DIR" mkdir "$BUILD_DIR" git clone --recursive "git@github.com:$REPO" "$BUILD_DIR" cd "$BUILD_DIR" # Newly created tags and updated branches are stored so they can be pushed all # at once at the end after the other work is guaranteed to be successful PUSH_BRANCHES="$BRANCH" echo -e "\nBuilding $BRANCH branch...\n" git checkout "$BRANCH" # head is needed in case the version number was manually updated to something # which has a tag with a number in it, otherwise there will be multiple matches # on the line instead of just the first one VERSION_CARGO_NO_TAGS=$(grep -o '^version[[:space:]]*=[[:space:]]*"[^"]*"' Cargo.toml | grep -o "[0-9][0-9.]*" | head -1) if [ -z "$VERSION" ]; then VERSION=$VERSION_CARGO_NO_TAGS VERSION_NO_TAGS=$VERSION_CARGO_NO_TAGS else VERSION_NO_TAGS=$(echo "$VERSION" | grep -o "[0-9][0-9.]*") fi # Converting the version number to an array to auto-generate the next # release version number IFS="." read -r -a PRE_VERSION_SPLIT <<< "$VERSION_NO_TAGS" if [[ "$VERSION" =~ ^[0-9][0-9.]*\.0$ ]]; then # Minor release gets a branch MAKE_BRANCH="${PRE_VERSION_SPLIT[0]}.${PRE_VERSION_SPLIT[1]}" BRANCH_VERSION="${PRE_VERSION_SPLIT[0]}.${PRE_VERSION_SPLIT[1]}.$((PRE_VERSION_SPLIT[2] + 1))-pre" # The next release is usually going to be a minor release; if the next # version is to be a major release, the package version in Git will need # to be manually updated or someone will have to pass a major version number # to the release script at the last second PRE_VERSION="${PRE_VERSION_SPLIT[0]}.$((PRE_VERSION_SPLIT[1] + 1)).0-pre" else # Patch releases do not get branches MAKE_BRANCH= BRANCH_VERSION= if [[ "$VERSION" == "$VERSION_NO_TAGS" ]]; then # The next release version will always be another patch version PRE_VERSION="${PRE_VERSION_SPLIT[0]}.${PRE_VERSION_SPLIT[1]}.$((PRE_VERSION_SPLIT[2] + 1))-pre" else # The version being released is a pre-release, so do not bump anything PRE_VERSION="$VERSION_CARGO_NO_TAGS-pre" fi fi TAG_VERSION="v$VERSION" # At this point: # $VERSION is the version of modular-bitfield that is being released # $TAG_VERSION is the name that will be used for the Git tag for the release # $PRE_VERSION is the next pre-release version of modular-bitfield that will be # set on the original branch after tagging # $MAKE_BRANCH is the name of the new minor release branch that should be # created (if this is not a patch release) # $BRANCH_VERSION is the pre-release version of modular-bitfield that will be # set on the minor release branch # Something is messed up and this release has already happened if [ "$(git tag | grep -c "^$TAG_VERSION$")" -gt 0 ]; then echo -e "\nTag $TAG_VERSION already exists! Please check the branch.\n" exit 1 fi set_package_version "$VERSION" set_package_publish "true" git commit -m "Updating metadata for $VERSION" -m "[ci skip]" Cargo.toml git tag -s -m "Release $VERSION" "$TAG_VERSION" set_package_version "$PRE_VERSION" set_package_publish "false # Use \`release.sh\`" git commit -m "Updating source version to $PRE_VERSION" -m "[ci skip]" Cargo.toml if [ "$MAKE_BRANCH" != "" ]; then git checkout -b "$MAKE_BRANCH" "$TAG_VERSION" set_package_version "$BRANCH_VERSION" set_package_publish "false # Use \`release.sh\`" git commit -m "Updating source version to $BRANCH_VERSION" -m "[ci skip]" Cargo.toml PUSH_BRANCHES="$PUSH_BRANCHES $MAKE_BRANCH" PUSH_BRANCHES_MSG=" and branches ${PUSH_BRANCHES}" fi echo -e "\nDone!\n" echo "Please confirm packaging success, then press 'y', ENTER to push" echo "tags $TAG_VERSION${PUSH_BRANCHES_MSG:-}, or any other key to bail." read -r -p "> " if [ "$REPLY" != "y" ]; then echo "Aborted." exit 0 fi for BRANCH in $PUSH_BRANCHES; do git push origin "$BRANCH" done git push origin --tags git checkout "$TAG_VERSION" cargo publish -p modular-bitfield-impl cargo publish -p modular-bitfield cd "$ROOT_DIR" rm -rf "$BUILD_DIR" echo -e "\nAll done! Yay!" ================================================ FILE: src/error.rs ================================================ //! Errors that can occur while operating on modular bitfields. use core::fmt::Debug; /// The given value was out of range for the bitfield. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct OutOfBounds; impl core::fmt::Display for OutOfBounds { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "encountered an out of bounds value") } } /// The bitfield contained an invalid bit pattern. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct InvalidBitPattern { /// The invalid bits. invalid_bytes: Bytes, } impl core::fmt::Display for InvalidBitPattern where Bytes: Debug, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "encountered an invalid bit pattern: 0x{:X?}", self.invalid_bytes ) } } impl InvalidBitPattern { /// Creates a new invalid bit pattern error. #[inline] pub fn new(invalid_bytes: Bytes) -> Self { Self { invalid_bytes } } /// Returns the invalid bit pattern. #[inline] pub fn invalid_bytes(self) -> Bytes { self.invalid_bytes } } ================================================ FILE: src/lib.rs ================================================ #![doc = include_str!("../docs/index.md")] #![no_std] #![forbid(unsafe_code)] #![warn(clippy::pedantic, missing_docs, rust_2018_idioms)] pub mod error; #[doc(hidden)] pub mod private; use self::error::{InvalidBitPattern, OutOfBounds}; #[doc = include_str!("../docs/bitfield.md")] pub use modular_bitfield_impl::bitfield; #[doc = include_str!("../docs/bitfield_specifier.md")] pub use modular_bitfield_impl::Specifier; /// The prelude: `use modular_bitfield::prelude::*;` pub mod prelude { pub use super::{bitfield, specifiers::*, Specifier}; } /// The `Specifier` trait describes a sequence of bits stored in an integer /// primitive (the [`Bytes`](Self::Bytes) type) and how to convert them to/from /// a more convenient higher-level interface type (the [`InOut`](Self::InOut) /// type). /// /// For example: /// /// * The specifier for `bool` converts between a `u8` with a 1 or 0 bit in /// the lowest bit position and a native `bool`. /// * The specifier for a unit enum with variants `{0, 1, 14}` converts /// between a `u16` matching those variants and the enum type. /// * The specifier for a 20-bit struct converts between a `u32` and the /// struct type. /// /// All types used in a `#[bitfield]` struct must implement this trait, and it /// should usually only be implemented with /// [`#[derive(Specifier)]`](macro@crate::Specifier). pub trait Specifier { /// The number of bits used by the `Specifier`. const BITS: usize; /// The storage type. This is typically the smallest integer primitive that /// can store all possible values of the [`InOut`](Self::InOut) type. type Bytes; /// The interface type. This type is used by getters and setters. For /// integers, this is the same as the [`Bytes`](Self::Bytes) type; for other /// types with more logical representations, like an enum or struct, this is /// the enum or struct. type InOut; /// Converts an interface type into its storage type. /// /// # Errors /// /// If the input value is out of bounds, an error will be returned. For /// example, the value `b100_u8` cannot be converted with `B2` because it is /// three bits wide. fn into_bytes(input: Self::InOut) -> Result; /// Converts a storage type into its interface type. /// /// # Errors /// /// If the given bit pattern is invalid for the interface type, an error /// will be returned. For example, `3_u8` cannot be converted to an enum /// which only has variants `{0, 1, 2}`. fn from_bytes(bytes: Self::Bytes) -> Result>; } /// The default set of predefined specifiers. pub mod specifiers { ::modular_bitfield_impl::define_specifiers!(); } ================================================ FILE: src/private/array_bytes_conv.rs ================================================ use crate::private::{checks::BitCount, SpecifierBytes}; pub trait ArrayBytesConversion { type Array; type Bytes; fn bytes_into_array(bytes: Self::Bytes) -> Self::Array; fn array_into_bytes(bytes: Self::Array) -> Self::Bytes; } macro_rules! impl_array_bytes_conversion_for_prim { ( $( $prim:ty ),* ) => { $( impl ArrayBytesConversion for BitCount<{::core::mem::size_of::<$prim>() * 8}> { type Array = [u8; ::core::mem::size_of::<$prim>()]; type Bytes = ::Bytes; fn bytes_into_array(bytes: Self::Bytes) -> Self::Array { bytes.to_le_bytes() } fn array_into_bytes(bytes: Self::Array) -> Self::Bytes { () * 8}> as SpecifierBytes>::Bytes::from_le_bytes(bytes) } } )* }; } impl_array_bytes_conversion_for_prim!(u8, u16, u32, u64, u128); macro_rules! impl_array_bytes_conversion_for_size { ( $( $size:literal ),* ) => { $( impl ArrayBytesConversion for BitCount<$size> { type Array = [u8; $size / 8]; type Bytes = ::Bytes; #[inline] fn bytes_into_array(bytes: Self::Bytes) -> Self::Array { let array = bytes.to_le_bytes(); debug_assert!(array[($size / 8)..].iter().all(|&byte| byte == 0)); let mut result = ::default(); result.copy_from_slice(&array[0..($size / 8)]); result } #[inline] fn array_into_bytes(bytes: Self::Array) -> Self::Bytes { let array: Self::Array = bytes; let mut result = [0; ::core::mem::size_of::()]; result[0..($size / 8)].copy_from_slice(&array[..]); ::from_le_bytes(result) } } )* }; } impl_array_bytes_conversion_for_size!(24, 40, 48, 56, 72, 80, 88, 96, 104, 112, 120); ================================================ FILE: src/private/checks.rs ================================================ pub(crate) mod private { /// Prevents internal traits from being implemented from dependencies. pub trait Sealed {} } macro_rules! impl_sealed_for { ( $($primitive:ty),* ) => { $( impl private::Sealed for $primitive {} )* } } impl_sealed_for!(bool, u8, u16, u32, u64, u128); /// Helper trait to check whether the size of bitfield structs /// is a multiple of 8 to form complete bytes. pub trait TotalSizeIsMultipleOfEightBits: private::Sealed {} /// Helper trait used to check whether a bitfield struct does not /// fill its entire value space, e.g. has undefined bits. pub trait TotalSizeIsNotMultipleOfEightBits: private::Sealed {} /// Helper trait to improve compile error messages. pub trait RenameSizeType: private::Sealed { type CheckType; } /// Helper type to sum up bit size of a bitfield at compile time. pub struct TotalSize(::core::marker::PhantomData); macro_rules! impl_total_size_for { ( $(($n:expr, $name:ident)),* ) => { $( pub enum $name {} impl private::Sealed for TotalSize> {} impl private::Sealed for $name {} impl RenameSizeType for TotalSize> { type CheckType = $name; } )* } } impl_total_size_for!( (0, ZeroMod8), (1, OneMod8), (2, TwoMod8), (3, ThreeMod8), (4, FourMod8), (5, FiveMod8), (6, SixMod8), (7, SevenMod8) ); impl TotalSizeIsMultipleOfEightBits for ZeroMod8 {} impl TotalSizeIsNotMultipleOfEightBits for OneMod8 {} impl TotalSizeIsNotMultipleOfEightBits for TwoMod8 {} impl TotalSizeIsNotMultipleOfEightBits for ThreeMod8 {} impl TotalSizeIsNotMultipleOfEightBits for FourMod8 {} impl TotalSizeIsNotMultipleOfEightBits for FiveMod8 {} impl TotalSizeIsNotMultipleOfEightBits for SixMod8 {} impl TotalSizeIsNotMultipleOfEightBits for SevenMod8 {} /// Public facing trait implemented by bitfield structs in order to let the compiler /// check if their sizes match a multiple of 8. pub trait CheckTotalSizeMultipleOf8 where ::CheckType: TotalSizeIsMultipleOfEightBits, { type Size: RenameSizeType; } /// Public facing trait implemented by bitfield structs in order to let the compiler /// check if their sizes does not match a multiple of 8. pub trait CheckTotalSizeIsNotMultipleOf8 where ::CheckType: TotalSizeIsNotMultipleOfEightBits, { type Size: RenameSizeType; } /// Helper trait to check if an enum discriminant of a bitfield specifier /// is within valid bounds. pub trait DiscriminantInRange: private::Sealed {} /// Helper trait to check if a `#[derive(Specifier)]` flagged bitfield /// requires /// at most 128 bits. pub trait SpecifierHasAtMost128Bits: private::Sealed {} /// Helper type to state that something is `true`. /// /// # Note /// /// Used for some compile time evaluation contexts. pub enum True {} /// Helper type to state that something is `false`. /// /// # Note /// /// Used for some compile time evaluation contexts. pub enum False {} impl private::Sealed for True {} impl DiscriminantInRange for True {} impl SpecifierHasAtMost128Bits for True {} impl FillsUnalignedBits for True {} impl DoesNotFillUnalignedBits for True {} /// Helper trait to improve compile time error messages. pub trait DispatchTrueFalse: private::Sealed { type Out; } /// Helper type for compile time evaluation of the number of bits. pub struct BitCount; impl private::Sealed for BitCount<0> {} impl DispatchTrueFalse for BitCount<0> { type Out = False; } // impl private::Sealed for BitCount<1> {} // <-- Already implemented by `define_specifiers` macro! impl DispatchTrueFalse for BitCount<1> { type Out = True; } /// Public facing trait that is implemented by bitfield specifiers to /// let the compiler check if all its variant discriminants are within /// valid bounds. pub trait CheckDiscriminantInRange where ::Out: DiscriminantInRange, { type CheckType: DispatchTrueFalse; } /// Traits to check at compile-time if a `#[derive(Specifier)]` type requires /// no more than 128 bits. pub trait CheckSpecifierHasAtMost128Bits where ::Out: SpecifierHasAtMost128Bits, { type CheckType: DispatchTrueFalse; } pub trait CheckFillsUnalignedBits where ::Out: FillsUnalignedBits, { type CheckType: DispatchTrueFalse; } pub trait FillsUnalignedBits {} pub trait CheckDoesNotFillUnalignedBits where ::Out: DoesNotFillUnalignedBits, { type CheckType: DispatchTrueFalse; } pub trait DoesNotFillUnalignedBits {} ================================================ FILE: src/private/impls.rs ================================================ use crate::{ error::{InvalidBitPattern, OutOfBounds}, Specifier, }; impl Specifier for bool { const BITS: usize = 1; type Bytes = u8; type InOut = bool; #[inline] fn into_bytes(input: Self::InOut) -> Result { Ok(input.into()) } #[inline] fn from_bytes(bytes: Self::Bytes) -> Result> { match bytes { 0 => Ok(false), 1 => Ok(true), invalid_bytes => Err(InvalidBitPattern::new(invalid_bytes)), } } } macro_rules! impl_specifier_for_primitive { ( $( ($prim:ty: $bits:literal) ),* $(,)? ) => { $( impl Specifier for $prim { const BITS: usize = $bits; type Bytes = $prim; type InOut = $prim; #[inline] fn into_bytes(input: Self::InOut) -> Result { Ok(input) } #[inline] fn from_bytes(bytes: Self::Bytes) -> Result> { Ok(bytes) } } )* }; } impl_specifier_for_primitive!( (u8: 8), (u16: 16), (u32: 32), (u64: 64), (u128: 128), ); ================================================ FILE: src/private/mod.rs ================================================ mod array_bytes_conv; pub mod checks; mod impls; mod proc; mod push_pop; mod traits; pub mod static_assertions { pub use static_assertions::*; } pub use self::{ array_bytes_conv::ArrayBytesConversion, proc::{read_specifier, write_specifier}, push_pop::{PopBuffer, PushBuffer}, traits::{ IsU128Compatible, IsU16Compatible, IsU32Compatible, IsU64Compatible, IsU8Compatible, PopBits, PushBits, SpecifierBytes, }, }; ================================================ FILE: src/private/proc.rs ================================================ use crate::{ private::{PopBits, PopBuffer, PushBits, PushBuffer}, Specifier, }; /// Creates a new push buffer with all bits initialized to 0. #[inline] fn push_buffer() -> PushBuffer<::Bytes> where T: Specifier, PushBuffer: Default, { ::Bytes> as Default>::default() } #[doc(hidden)] #[inline] #[must_use] pub fn read_specifier(bytes: &[u8], offset: usize) -> ::Bytes where T: Specifier, PushBuffer: Default + PushBits, { let end = offset + ::BITS; let ls_byte = offset / 8; // compile-time let ms_byte = (end - 1) / 8; // compile-time // Truncation is always valid due to mod 8 value range #[allow(clippy::cast_possible_truncation)] let lsb_offset = (offset % 8) as u32; // compile-time #[allow(clippy::cast_possible_truncation)] let msb_offset = (end % 8) as u32; // compile-time let msb_offset = if msb_offset == 0 { 8 } else { msb_offset }; let mut buffer = push_buffer::(); if lsb_offset == 0 && msb_offset == 8 { // Edge-case for whole bytes manipulation. for byte in bytes[ls_byte..=ms_byte].iter().rev() { buffer.push_bits(8, *byte); } } else { if ls_byte != ms_byte { // Most-significant byte buffer.push_bits(msb_offset, bytes[ms_byte]); } if ms_byte - ls_byte >= 2 { // Middle bytes for byte in bytes[(ls_byte + 1)..ms_byte].iter().rev() { buffer.push_bits(8, *byte); } } if ls_byte == ms_byte { buffer.push_bits( u32::try_from(::BITS).unwrap(), bytes[ls_byte] >> lsb_offset, ); } else { buffer.push_bits(8 - lsb_offset, bytes[ls_byte] >> lsb_offset); } } buffer.into_bytes() } #[doc(hidden)] #[inline] pub fn write_specifier(bytes: &mut [u8], offset: usize, new_val: ::Bytes) where T: Specifier, PopBuffer: PopBits, { let end = offset + ::BITS; let ls_byte = offset / 8; // compile-time let ms_byte = (end - 1) / 8; // compile-time // Truncation is always valid due to mod 8 value range #[allow(clippy::cast_possible_truncation)] let lsb_offset = (offset % 8) as u32; // compile-time #[allow(clippy::cast_possible_truncation)] let msb_offset = (end % 8) as u32; // compile-time let msb_offset = if msb_offset == 0 { 8 } else { msb_offset }; let mut buffer = >::from_bytes(new_val); if lsb_offset == 0 && msb_offset == 8 { // Edge-case for whole bytes manipulation. for byte in &mut bytes[ls_byte..=ms_byte] { *byte = buffer.pop_bits(8); } } else { // Least-significant byte let stays_same = bytes[ls_byte] & (if ls_byte == ms_byte && msb_offset != 8 { !((1 << msb_offset) - 1) } else { 0u8 } | ((1 << lsb_offset) - 1)); let overwrite = buffer.pop_bits(8 - lsb_offset); bytes[ls_byte] = stays_same | (overwrite << lsb_offset); if ms_byte - ls_byte >= 2 { // Middle bytes for byte in &mut bytes[(ls_byte + 1)..ms_byte] { *byte = buffer.pop_bits(8); } } if ls_byte != ms_byte { // Most-significant byte if msb_offset == 8 { // We don't need to respect what was formerly stored in the byte. bytes[ms_byte] = buffer.pop_bits(msb_offset); } else { // All bits that do not belong to this field should be preserved. let stays_same = bytes[ms_byte] & !((1 << msb_offset) - 1); let overwrite = buffer.pop_bits(msb_offset); bytes[ms_byte] = stays_same | overwrite; } } } } ================================================ FILE: src/private/push_pop.rs ================================================ use crate::private::{checks::private::Sealed, PopBits, PushBits}; /// A bit buffer that allows to pop bits from it. pub struct PopBuffer { bytes: T, } impl PopBuffer { /// Creates a new pop buffer from the given bytes. #[inline] pub(super) fn from_bytes(bytes: T) -> Self { Self { bytes } } } impl Sealed for PopBuffer {} impl PopBits for PopBuffer { #[inline] fn pop_bits(&mut self, amount: u32) -> u8 { let Self { bytes } = self; let orig_ones = bytes.count_ones(); debug_assert!((1..=8).contains(&amount)); // Truncation is always valid due to shift range #[allow(clippy::cast_possible_truncation)] let res = *bytes & (1_u16.wrapping_shl(amount).wrapping_sub(1) as u8); *bytes = bytes.checked_shr(amount).unwrap_or(0); debug_assert_eq!(res.count_ones() + bytes.count_ones(), orig_ones); res } } macro_rules! impl_pop_bits { ( $($type:ty),+ ) => { $( impl Sealed for PopBuffer<$type> {} impl PopBits for PopBuffer<$type> { #[inline] fn pop_bits(&mut self, amount: u32) -> u8 { let Self { bytes } = self; let orig_ones = bytes.count_ones(); debug_assert!((1..=8).contains(&amount)); let bitmask = 0xFF >> (8 - amount); // Truncation is always valid due to mask size #[allow(clippy::cast_possible_truncation)] let res = (*bytes as u8) & bitmask; *bytes = bytes.checked_shr(amount).unwrap_or(0); debug_assert_eq!(res.count_ones() + bytes.count_ones(), orig_ones); res } } )+ }; } impl_pop_bits!(u16, u32, u64, u128); /// A bit buffer that allows to push bits onto it. pub struct PushBuffer { bytes: T, } impl PushBuffer { /// Returns the underlying bytes of the push buffer. #[inline] pub(super) fn into_bytes(self) -> T { self.bytes } } macro_rules! impl_push_bits { ( $($type:ty),+ ) => { $( impl Sealed for PushBuffer<$type> {} impl Default for PushBuffer<$type> { #[inline] fn default() -> Self { Self { bytes: <$type as Default>::default() } } } impl PushBits for PushBuffer<$type> { #[inline] fn push_bits(&mut self, amount: u32, bits: u8) { let Self { bytes } = self; let orig_ones = bytes.count_ones(); debug_assert!((1..=8).contains(&amount)); let bitmask = 0xFF >> (8 - amount); *bytes = bytes.wrapping_shl(amount) | <$type>::from(bits & bitmask); debug_assert_eq!((bits & bitmask).count_ones() + orig_ones, bytes.count_ones()); } } )+ } } impl_push_bits!(u8, u16, u32, u64, u128); ================================================ FILE: src/private/traits.rs ================================================ use super::checks; /// Helper trait for underlying primitives handling of bitfields. /// /// # Note /// /// Must not and cannot be implemented by dependencies. #[doc(hidden)] pub trait PushBits: checks::private::Sealed { fn push_bits(&mut self, amount: u32, bits: u8); } /// Helper trait for underlying primitives handling of bitfields. /// /// # Note /// /// Must not and cannot be implemented by dependencies. #[doc(hidden)] pub trait PopBits: checks::private::Sealed { fn pop_bits(&mut self, amount: u32) -> u8; } /// Trait implemented by primitives that drive bitfield manipulations generically. #[doc(hidden)] pub trait SpecifierBytes: checks::private::Sealed { /// The base type that the specifier is operating on. type Bytes; } pub trait IsU8Compatible: checks::private::Sealed {} pub trait IsU16Compatible: checks::private::Sealed {} pub trait IsU32Compatible: checks::private::Sealed {} pub trait IsU64Compatible: checks::private::Sealed {} pub trait IsU128Compatible: checks::private::Sealed {} impl IsU8Compatible for checks::BitCount<8> {} impl IsU16Compatible for checks::BitCount<16> {} impl IsU32Compatible for checks::BitCount<32> {} impl IsU64Compatible for checks::BitCount<64> {} impl IsU128Compatible for checks::BitCount<128> {} ================================================ FILE: tests/bitfield/bits_param.rs ================================================ //! Tests for `#[bitfield(bits = N)]` use modular_bitfield::prelude::*; #[test] fn bits_non_filled_1() { #[bitfield(bits = 32, filled = false)] #[derive(Specifier)] pub struct SignIntegerShort { sign: bool, value: B7, } assert_eq!(::BITS, 32); } #[test] fn bits_non_filled_2() { #[bitfield(bits = 32, filled = false)] #[derive(Specifier)] pub struct SignIntegerLong { sign: bool, value: B30, } assert_eq!(::BITS, 32); } #[test] fn complex_use_case() { #[derive(Specifier)] #[bits = 2] pub enum Status { Red, Green, Yellow, } #[bitfield(bits = 4)] #[derive(Specifier)] pub struct Header { is_compact: bool, is_secure: bool, #[bits = 2] pre_status: Status, } #[bitfield(bits = 16, bytes = 2, filled = false)] #[derive(Specifier)] pub struct PackedData { #[bits = 4] header: Header, body: B9, #[bits = 2] status: Status, } assert_eq!(::BITS, 2); assert_eq!(
::BITS, 4); assert_eq!(::BITS, 16); } #[test] fn low_bits_filled() { #[bitfield(bits = 4)] #[derive(Specifier)] pub struct Header { is_compact: bool, is_secure: bool, #[bits = 2] pre_status: B2, } assert_eq!(
::BITS, 4); } #[test] fn valid_use_1() { #[bitfield(bits = 32)] pub struct SignInteger { sign: bool, value: B31, } } #[test] fn valid_use_2() { #[bitfield(bits = 32)] #[repr(u32)] pub struct SignInteger { sign: bool, value: B31, } } #[test] fn valid_use_3() { #[bitfield(bits = 32, bytes = 4)] #[repr(u32)] pub struct SignInteger { sign: bool, value: B31, } } #[test] fn valid_use_4() { #[bitfield(bits = 32, bytes = 4)] #[repr(u32)] #[derive(Debug, Specifier)] pub struct SignInteger { sign: bool, value: B31, } } ================================================ FILE: tests/bitfield/bytes_param.rs ================================================ //! Tests for `bytes = N` #[bitfield] parameter use modular_bitfield::prelude::*; #[test] fn valid_bitfield() { // Just requires exactly 32 bits (4 bytes) as expected. #[bitfield(bytes = 4)] pub struct Base { a: B2, b: B6, c: u8, d: u16, } assert_eq!(core::mem::size_of::(), 4); } #[test] fn valid_specifier_bitfield() { // Is only 9 bits, so will be 2 bytes in size. #[bitfield(bytes = 2, filled = false)] #[derive(Specifier)] pub struct Header { a: B6, b: bool, c: bool, d: bool, } assert_eq!(core::mem::size_of::
(), 2); } ================================================ FILE: tests/bitfield/derive_bitfield_specifier.rs ================================================ //! Tests specific to the `#[derive(Specifier)]` proc. macro use modular_bitfield::prelude::*; // For some bitfield members, working with them as enums will make more sense to // the user than working with them as integers. We will require enums that have // a power-of-two number of variants so that they exhaustively cover a fixed // range of bits. // // // Works like B3, but getter and setter signatures will use // // the enum instead of u8. // #[derive(Specifier)] // enum DeliveryMode { // Fixed = 0b000, // Lowest = 0b001, // SMI = 0b010, // RemoteRead = 0b011, // NMI = 0b100, // Init = 0b101, // Startup = 0b110, // External = 0b111, // } // // For this test case it is okay to require that every enum variant has an // explicit discriminant that is an integer literal. We will relax this // requirement in a later test case. // // Additionally, enums may support a "bits" attribute which allows to enum to // have a number of variants that is not a power of two. If the #[bits = N] // attribute is specified, like so: // // #[derive(Specifier)] // #[bits = 4] // enum SmallPrime { // Two = 0b0010, // Three = 0b0011, // Five = 0b0101, // Seven = 0b0111, // Eleven = 0b1011, // Thirteen = 0b1101, // } // // then the number of bits required to represent the struct is coerced to N. // // let mut bitfield = MyBitfield::new(); // assert_eq!(0, bitfield.small_prime_or_err().unwrap_err().invalid_bytes()); // // bitfield.set_small_prime(SmallPrime::Seven); // let p = bitfield.small_prime_or_err().unwrap_or(SmallPrime::Two); #[test] fn enums() { use modular_bitfield::error::InvalidBitPattern; #[bitfield] pub struct RedirectionTableEntry { acknowledged: bool, trigger_mode: TriggerMode, delivery_mode: DeliveryMode, small_prime: SmallPrime, reserved: B3, another_small_prime: SmallPrime, } #[derive(Specifier, Debug, PartialEq)] pub enum TriggerMode { Edge = 0, Level = 1, } #[derive(Specifier, Debug, PartialEq)] pub enum DeliveryMode { Fixed = 0b000, Lowest = 0b001, Smi = 0b010, RemoteRead = 0b011, Nmi = 0b100, Init = 0b101, Startup = 0b110, External = 0b111, } #[derive(Specifier, Debug, PartialEq)] #[bits = 4] pub enum SmallPrime { Two = 0b0010, Three = 0b0011, Five = 0b0101, Seven = 0b0111, Eleven = 0b1011, Thirteen = 0b1101, } assert_eq!(core::mem::size_of::(), 2); // Initialized to all 0 bits. let mut entry = RedirectionTableEntry::new(); assert!(!entry.acknowledged()); assert_eq!(entry.trigger_mode(), TriggerMode::Edge); assert_eq!(entry.delivery_mode(), DeliveryMode::Fixed); assert_eq!(entry.small_prime_or_err(), Err(InvalidBitPattern::new(0))); assert_eq!(entry.small_prime_or_err().unwrap_err().invalid_bytes(), 0); entry.set_acknowledged(true); entry.set_delivery_mode(DeliveryMode::Smi); entry.set_small_prime(SmallPrime::Five); assert!(entry.acknowledged()); assert_eq!(entry.trigger_mode(), TriggerMode::Edge); assert_eq!(entry.delivery_mode(), DeliveryMode::Smi); assert_eq!(entry.small_prime(), SmallPrime::Five); assert_eq!(entry.small_prime_or_err(), Ok(SmallPrime::Five)); } #[test] fn name_conflict() { #[derive(Specifier)] pub enum SuspiciouslyAmbiguous { Bytes, InOut, } } // For bitfield use limited to a single binary, such as a space optimization for // some in-memory data structure, we may not care what exact bit representation // is used for enums. // // Make your Specifier derive macro for enums use the underlying // discriminant determined by the Rust compiler as the bit representation. Do // not assume that the compiler uses any particular scheme like PREV+1 for // implicit discriminants; make sure your implementation respects Rust's choice // of discriminant regardless of what scheme Rust uses. This is important for // performance so that the getter and setter both compile down to very simple // machine code after optimizations. // // Do not worry about what happens if discriminants are outside of the range // 0..2^BITS. We will do a compile-time check in a later test case to ensure // they are in range. #[test] fn optional_discriminant() { #[bitfield] pub struct RedirectionTableEntry { delivery_mode: DeliveryMode, reserved: B5, } const F: isize = 3; const G: isize = 0; #[derive(Specifier, Debug, PartialEq)] pub enum DeliveryMode { Fixed = F, Lowest, Smi, RemoteRead, Nmi, Init = G, Startup, External, } assert_eq!(core::mem::size_of::(), 1); // Initialized to all 0 bits. let mut entry = RedirectionTableEntry::new(); assert_eq!(entry.delivery_mode(), DeliveryMode::Init); entry.set_delivery_mode(DeliveryMode::Lowest); assert_eq!(entry.delivery_mode(), DeliveryMode::Lowest); } ================================================ FILE: tests/bitfield/derive_debug.rs ================================================ //! Tests for `#[derive(Debug)]` extern crate alloc; use alloc::format; use modular_bitfield::prelude::*; #[test] fn print_invalid_bits() { #[derive(Specifier, Debug)] #[bits = 2] pub enum Status { Green = 0, Yellow = 1, Red = 2, // 0x11 (= 3) is undefined here for Status! } #[bitfield] #[derive(Debug)] pub struct DataPackage { status: Status, contents: B4, is_alive: bool, is_received: bool, } let package = DataPackage::from_bytes([0b0101_1011]); assert_eq!( format!("{package:?}"), "DataPackage { status: InvalidBitPattern { invalid_bytes: 3 }, contents: 6, is_alive: true, is_received: false }", ); assert_eq!( format!("{package:#X?}"), "DataPackage {\n \ status: InvalidBitPattern {\n \ invalid_bytes: 0x3,\n \ },\n \ contents: 0x6,\n \ is_alive: true,\n \ is_received: false,\n\ }", ); } #[test] fn respects_other_derives() { #[bitfield] #[derive(Debug, Clone, PartialEq, Eq)] pub struct Color { r: B6, g: B6, b: B6, a: B6, } let color1 = Color::new().with_r(63).with_g(32).with_b(16).with_a(8); let color2 = color1.clone(); assert_eq!(color1, color2); assert_eq!(format!("{color1:?}"), "Color { r: 63, g: 32, b: 16, a: 8 }",); assert_eq!( format!("{color2:#x?}"), "Color {\n r: 0x3f,\n g: 0x20,\n b: 0x10,\n a: 0x8,\n}", ); } #[test] fn valid_use_2() { #[derive(Specifier, Debug)] pub enum Status { Green, Yellow, Red, None, } #[bitfield] #[derive(Debug)] pub struct DataPackage { status: Status, contents: B60, is_alive: bool, is_received: bool, } let package = DataPackage::new() .with_status(Status::Green) .with_contents(0xC0DE_CAFE) .with_is_alive(true) .with_is_received(false); assert_eq!( format!("{package:?}"), "DataPackage { status: Green, contents: 3235826430, is_alive: true, is_received: false }", ); assert_eq!( format!("{package:#X?}"), "DataPackage {\n status: Green,\n contents: 0xC0DECAFE,\n is_alive: true,\n is_received: false,\n}", ); } #[test] fn valid_use_specifier() { #[bitfield(filled = false)] // Requires just 4 bits! #[derive(Specifier, Debug)] pub struct Header { status: B2, is_alive: bool, is_received: bool, } let header = Header::new() .with_status(1) .with_is_alive(true) .with_is_received(false); assert_eq!( format!("{header:?}"), "Header { status: 1, is_alive: true, is_received: false }", ); assert_eq!( format!("{header:#X?}"), "Header {\n status: 0x1,\n is_alive: true,\n is_received: false,\n}", ); } #[test] fn valid_use() { #[bitfield] #[derive(Debug)] pub struct Color { r: B6, g: B6, b: B6, a: B6, } let color = Color::new().with_r(63).with_g(32).with_b(16).with_a(8); assert_eq!(format!("{color:?}"), "Color { r: 63, g: 32, b: 16, a: 8 }",); assert_eq!( format!("{color:#x?}"), "Color {\n r: 0x3f,\n g: 0x20,\n b: 0x10,\n a: 0x8,\n}", ); } #[test] fn valid_use_tuple() { #[bitfield] #[derive(Debug)] pub struct Color(B6, B6, B6, B6); let color = Color::new().with_0(63).with_1(32).with_2(16).with_3(8); assert_eq!(format!("{color:?}"), "Color(63, 32, 16, 8)",); assert_eq!( format!("{color:#x?}"), "Color(\n 0x3f,\n 0x20,\n 0x10,\n 0x8,\n)", ); } ================================================ FILE: tests/bitfield/derive_specifier.rs ================================================ //! Tests for `#[derive(Specifier)]` using `#[bitfield]` use modular_bitfield::prelude::*; #[test] fn struct_in_struct() { #[bitfield(filled = false)] #[derive(Specifier, Debug, PartialEq, Eq, Copy, Clone)] pub struct Header { a: B2, b: B3, } #[bitfield] #[derive(Debug, PartialEq, Eq)] pub struct Base { pub header: Header, pub rest: B3, } let mut base = Base::new(); assert_eq!(base.header(), Header::new()); let h = Header::new().with_a(1).with_b(2); base.set_header(h); let h2 = base.header(); assert_eq!(h2, h); assert_eq!(h2.a(), 1); assert_eq!(h2.b(), 2); } #[test] fn unfilled_from_bytes() { use modular_bitfield::error::OutOfBounds; #[bitfield(filled = false)] #[derive(Specifier, Debug, PartialEq, Eq, Copy, Clone)] pub struct Unfilled { a: B2, } assert_eq!(Unfilled::from_bytes([0x00]), Ok(Unfilled::new())); assert_eq!( Unfilled::from_bytes([0b0000_0001]), Ok(Unfilled::new().with_a(1)) ); assert_eq!( Unfilled::from_bytes([0b0000_0010]), Ok(Unfilled::new().with_a(2)) ); assert_eq!( Unfilled::from_bytes([0b0000_0011]), Ok(Unfilled::new().with_a(3)) ); assert_eq!(Unfilled::from_bytes([0b0000_0100]), Err(OutOfBounds)); } #[test] fn valid_use() { #[bitfield] #[derive(Specifier)] pub struct Header { live: bool, received: bool, status: B2, rest: B4, } assert_eq!(
::BITS, 8); } ================================================ FILE: tests/bitfield/filled_param.rs ================================================ //! Tests for `filled: bool` #[bitfield] parameter use modular_bitfield::prelude::*; #[test] fn valid_bitfield_1() { // The bitfield has only 7 bits and therefore is unfilled. #[bitfield(filled = false)] pub struct UnfilledBitfield { a: B7, } } #[test] fn valid_bitfield_2() { // The bitfield has exactly 8 bits and therefore is filled. #[bitfield(filled = true)] pub struct UnfilledBitfield { a: B8, } } #[test] fn valid_bitfield_specifier_1() { // The bitfield only has 23 bits and therefore is unfilled. #[bitfield(filled = false)] #[derive(Specifier)] pub struct UnfilledSpecifier { a: B7, b: u16, } } #[test] fn valid_bitfield_specifier_2() { // The bitfield has 24 bits and therefore is filled. #[bitfield(filled = true)] #[derive(Specifier, Debug, PartialEq, Eq, Clone, Copy)] pub struct FilledSpecifier { a: B8, b: u16, } // Testing impl_array_bytes_conversion_for_size let value = FilledSpecifier::new().with_a(1).with_b(0x302); assert_eq!( ::from_bytes(0x0003_0201), Ok(value) ); assert_eq!( ::into_bytes(value), Ok(0x0003_0201) ); } ================================================ FILE: tests/bitfield/mod.rs ================================================ mod bits_param; mod bytes_param; mod derive_bitfield_specifier; mod derive_debug; mod derive_specifier; mod filled_param; mod no_implicit_prelude; mod regressions; mod repr; mod skip; use modular_bitfield::prelude::*; #[test] fn accessors() { #[bitfield] pub struct MyFourBytes { a: B1, b: B3, c: B4, d: B24, } let mut bitfield = MyFourBytes::new(); assert_eq!(0, bitfield.a()); assert_eq!(0, bitfield.b()); assert_eq!(0, bitfield.c()); assert_eq!(0, bitfield.d()); bitfield.set_c(14); assert_eq!(0, bitfield.a()); assert_eq!(0, bitfield.b()); assert_eq!(14, bitfield.c()); assert_eq!(0, bitfield.d()); } #[test] // Bad names are fine, it is just a test #[allow(clippy::many_single_char_names)] fn accessor_signatures() { use core::mem::size_of_val; // For getters and setters, we would like for the signature to be in terms of // the narrowest unsigned integer type that can hold the right number of bits. // That means the accessors for B1 through B8 would use u8, B9 through B16 would // use u16 etc. type A = B1; type B = B3; type C = B4; type D = B24; #[bitfield] pub struct MyFourBytes { a: A, b: B, c: C, d: D, } let mut x = MyFourBytes::new(); // I am testing the signatures in this roundabout way to avoid making it // possible to pass this test with a generic signature that is inconvenient // for callers, such as `fn a>(&self) -> T`. let a = 1; x.set_a(a); // expect fn(&mut MyFourBytes, u8) let b = 1; x.set_b(b); let c = 1; x.set_c(c); let d = 1; x.set_d(d); // expect fn(&mut MyFourBytes, u32) assert_eq!(size_of_val(&a), 1); assert_eq!(size_of_val(&b), 1); assert_eq!(size_of_val(&c), 1); assert_eq!(size_of_val(&d), 4); assert_eq!(size_of_val(&x.a()), 1); // expect fn(&MyFourBytes) -> u8 assert_eq!(size_of_val(&x.b()), 1); assert_eq!(size_of_val(&x.c()), 1); assert_eq!(size_of_val(&x.d()), 4); // expect fn(&MyFourBytes) -> u32 } // This test is equivalent to accessors but with some fields spanning across // byte boundaries. This may or may not already work depending on how your // implementation has been done so far. // // // ║ first byte ║ second byte ║ third byte ║ fourth byte ║ // ╟───────────────╫───────────────╫───────────────╫───────────────╢ // ║▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒║ // ╟─────────────────╫───────────╫─────────────────────────╫───────╢ // ║ a ║ b ║ c ║ d ║ #[test] fn accessors_edge() { #[bitfield] pub struct EdgeCaseBytes { a: B9, b: B6, c: B13, d: B4, } let mut bitfield = EdgeCaseBytes::new(); assert_eq!(0, bitfield.a()); assert_eq!(0, bitfield.b()); assert_eq!(0, bitfield.c()); assert_eq!(0, bitfield.d()); let a = 0b1_1000_0111; let b = 0b101_010; let c = 0x1675; let d = 0b1110; bitfield.set_a(a); bitfield.set_b(b); bitfield.set_c(c); bitfield.set_d(d); assert_eq!(a, bitfield.a()); assert_eq!(b, bitfield.b()); assert_eq!(c, bitfield.c()); assert_eq!(d, bitfield.d()); } // We also want to allow for tuple structs to be accepted by the `#[bitfield]` macro. // // For this we generate getters and setters in a way that refer to the corresponding // number of the field within the annotated tuple struct. #[test] fn tuple_structs() { #[bitfield] struct MyTwoBytes(bool, B7, B8); let mut test = MyTwoBytes::new(); assert!(!test.get_0()); assert_eq!(test.get_1(), 0); assert_eq!(test.get_2(), 0); test.set_0(true); test.set_1(42); test.set_2(0xFF); assert!(test.get_0()); assert_eq!(test.get_1(), 42); assert_eq!(test.get_2(), 0xFF); } #[test] fn bool_specifier() { assert_eq!(bool::from_bytes(0), Ok(false)); assert_eq!(bool::from_bytes(1), Ok(true)); assert_eq!( bool::from_bytes(2), Err(modular_bitfield::error::InvalidBitPattern::new(2)) ); } // Tests to check for correct execution of checked setters. #[test] fn checked_setters() { use modular_bitfield::error::OutOfBounds; #[bitfield] #[derive(Debug, PartialEq)] pub struct MyTwoBytes { a: B1, b: B2, c: B13, } let mut bitfield = MyTwoBytes::new(); // Everything is initialized to zero. assert_eq!(bitfield.a(), 0); assert_eq!(bitfield.b(), 0); assert_eq!(bitfield.c(), 0); // Do some invalid manipulations. assert_eq!(bitfield.set_a_checked(2), Err(OutOfBounds)); assert_eq!(bitfield.set_b_checked(4), Err(OutOfBounds)); assert_eq!(bitfield.set_c_checked(12345), Err(OutOfBounds)); // Asserts that nothing has changed. assert_eq!(bitfield.a(), 0); assert_eq!(bitfield.b(), 0); assert_eq!(bitfield.c(), 0); // Do some valid manipulations. assert_eq!(bitfield.set_a_checked(1), Ok(())); assert_eq!(bitfield.set_b_checked(3), Ok(())); assert_eq!(bitfield.set_c_checked(42), Ok(())); // Asserts that the valid manipulation has had effect. assert_eq!(bitfield.a(), 1); assert_eq!(bitfield.b(), 3); assert_eq!(bitfield.c(), 42); // Check the checked with statement throws error assert_eq!(MyTwoBytes::new().with_a_checked(2), Err(OutOfBounds)); assert_eq!( MyTwoBytes::new() .with_a_checked(1) .unwrap() .with_b_checked(4), Err(OutOfBounds) ); // Check that with_checked populates values without touching other fields let bitfield = bitfield .with_a_checked(0) .unwrap() .with_b_checked(2) .unwrap(); assert_eq!(bitfield.a(), 0); assert_eq!(bitfield.b(), 2); assert_eq!(bitfield.c(), 42); } #[test] fn errors() { extern crate alloc; use alloc::format; let msg = format!("{}", modular_bitfield::error::OutOfBounds); assert!( msg.contains("out of bounds"), "unexpected error text: {msg}" ); let msg = format!( "{}", modular_bitfield::error::InvalidBitPattern::new(0x1234_5678) ); assert!( msg.contains("invalid bit pattern"), "unexpected error text: {msg}" ); assert!(msg.contains("0x12345678"), "unexpected error text: {msg}"); } // Tests if it is possible to manually reset the bitfields again. #[test] fn manual_reset() { #[bitfield] pub struct MyTwoBytes { a: B1, b: B2, c: B13, } let mut bitfield = MyTwoBytes::new(); // Everything is initialized to zero. assert_eq!(bitfield.a(), 0); assert_eq!(bitfield.b(), 0); assert_eq!(bitfield.c(), 0); // Manipulate bitfield. bitfield.set_a(1); bitfield.set_b(3); bitfield.set_c(42); // Check that manipulation was successful. assert_eq!(bitfield.a(), 1); assert_eq!(bitfield.b(), 3); assert_eq!(bitfield.c(), 42); // Manually reset the bitfield. bitfield.set_a(0); bitfield.set_b(0); bitfield.set_c(0); // Check if reset was successful. assert_eq!(bitfield.a(), 0); assert_eq!(bitfield.b(), 0); assert_eq!(bitfield.c(), 0); } // Tests bitfield specifiers of more than 64 bit. #[test] fn u128_specifier() { #[bitfield] pub struct SomeMoreBytes { a: B47, b: B65, c: B128, } let mut bitfield = SomeMoreBytes::new(); // Everything is initialized to zero. assert_eq!(bitfield.a(), 0); assert_eq!(bitfield.b(), 0); assert_eq!(bitfield.c(), 0); // Manipulate bitfield. assert_eq!(bitfield.set_a_checked(1), Ok(())); assert_eq!(bitfield.set_b_checked(3), Ok(())); assert_eq!(bitfield.set_c_checked(42), Ok(())); // Check that manipulation was successful. assert_eq!(bitfield.a(), 1); assert_eq!(bitfield.b(), 3); assert_eq!(bitfield.c(), 42); // // Manually reset the bitfield. bitfield.set_a(0); bitfield.set_b(0); bitfield.set_c(0); // // Check if reset was successful. assert_eq!(bitfield.a(), 0); assert_eq!(bitfield.b(), 0); assert_eq!(bitfield.c(), 0); } // These tests check the conversions from and to bytes. #[test] fn byte_conversions() { #[bitfield] #[derive(PartialEq, Eq, Debug)] pub struct MyFourBytes { a: bool, b: B2, c: B13, d: B16, } let mut bitfield_1 = MyFourBytes::new(); bitfield_1.set_a(true); bitfield_1.set_b(0b11); bitfield_1.set_c(444); bitfield_1.set_d(1337); assert!(bitfield_1.a()); assert_eq!(bitfield_1.b(), 3); assert_eq!(bitfield_1.c(), 444); assert_eq!(bitfield_1.d(), 1337); let bytes = bitfield_1.into_bytes(); assert_eq!(bytes, [231, 13, 57, 5]); let bitfield2 = MyFourBytes::from_bytes(bytes); assert!(bitfield2.a()); assert_eq!(bitfield2.b(), 3); assert_eq!(bitfield2.c(), 444); assert_eq!(bitfield2.d(), 1337); } #[test] fn within_single_byte() { #[derive(Specifier, Debug, PartialEq, Copy, Clone)] pub enum Mode { A = 0b00, B = 0b01, C = 0b10, D = 0b11, } #[bitfield] #[derive(Debug)] pub struct StatFlag { x: bool, y: bool, z: B2, #[bits = 2] mode: Mode, w: B2, } let mut flag = StatFlag::new(); assert!(!flag.x()); assert!(!flag.y()); assert_eq!(flag.z(), 0); assert_eq!(flag.w(), 0); assert_eq!(flag.mode(), Mode::A); let new_mode = Mode::B; flag.set_mode(new_mode); assert!(!flag.x()); assert!(!flag.y()); assert_eq!(flag.z(), 0); assert_eq!(flag.w(), 0); assert_eq!(flag.mode(), new_mode); flag.set_x(true); assert!(flag.x()); assert!(!flag.y()); assert_eq!(flag.z(), 0); assert_eq!(flag.w(), 0); assert_eq!(flag.mode(), new_mode); flag.set_y(true); assert!(flag.x()); assert!(flag.y()); assert_eq!(flag.z(), 0); assert_eq!(flag.w(), 0); assert_eq!(flag.mode(), new_mode); flag.set_z(0b11); assert!(flag.x()); assert!(flag.y()); assert_eq!(flag.z(), 0b11); assert_eq!(flag.w(), 0); assert_eq!(flag.mode(), new_mode); flag.set_w(0b01); assert!(flag.x()); assert!(flag.y()); assert_eq!(flag.z(), 0b11); assert_eq!(flag.w(), 0b01); assert_eq!(flag.mode(), new_mode); } #[test] fn get_spanning_data() { #[bitfield] #[derive(Copy, Clone, Eq, PartialEq)] pub struct ColorEntry { r: B5, g: B5, b: B5, unused: B1, } for i in 0..=u16::MAX { let entry = ColorEntry::from_bytes(i.to_le_bytes()); let mut new = ColorEntry::new(); new.set_r(entry.r()); assert_eq!(new.r(), entry.r()); new.set_g(entry.g()); assert_eq!(new.g(), entry.g()); new.set_b(entry.b()); assert_eq!(new.b(), entry.b()); new.set_unused(entry.unused()); assert_eq!(new.r(), entry.r()); assert_eq!(new.g(), entry.g()); assert_eq!(new.b(), entry.b()); assert_eq!(new.unused(), entry.unused()); } } #[test] fn raw_identifiers() { #[bitfield] struct RawIdentifiers { r#struct: B5, r#bool: B3, } let r = RawIdentifiers::new(); let _ = r.r#struct(); let _ = r.r#bool(); } // Generate getters and.with() setters that manipulate the right range of bits // corresponding to each field. // // // ║ first byte ║ second byte ║ third byte ║ fourth byte ║ // ╟───────────────╫───────────────╫───────────────╫───────────────╢ // ║▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒║ // ╟─╫─────╫───────╫───────────────────────────────────────────────╢ // ║a║ b ║ c ║ d ║ #[test] fn with_setter() { #[bitfield] pub struct MyFourBytes { a: bool, b: B3, c: B4, d: B24, } let bitfield = MyFourBytes::new() .with_a(true) .with_b(2) .with_c(14) .with_d(1_000_000); assert!(bitfield.a()); assert_eq!(bitfield.b(), 2); assert_eq!(bitfield.c(), 14); assert_eq!(bitfield.d(), 1_000_000); } // Checks that no implicit paths are generated by the `#[bitfield]` proc. macro // and `#[derive(Specifier)]` derive macro. #[test] fn primitives_as_specifiers() { #[bitfield] pub struct PrimitivesBitfield { a: bool, b: u8, c: u16, d: u32, e: u64, f: u128, rest: B7, } } // Validates that in a degenerate case with a single bit, non-power-of-two enums // behave as expected. #[test] fn single_bit_enum() { use modular_bitfield::error::InvalidBitPattern; #[bitfield] pub struct UselessStruct { reserved: B3, field: ForciblyTrue, reserved2: B4, } #[derive(Specifier, Debug, PartialEq)] #[bits = 1] pub enum ForciblyTrue { True = 1, } assert_eq!(core::mem::size_of::(), 1); // Initialized to all 0 bits. let entry = UselessStruct::new(); assert_eq!(entry.field_or_err(), Err(InvalidBitPattern::new(0))); let entry = UselessStruct::new().with_field(ForciblyTrue::True); assert_eq!(entry.field_or_err(), Ok(ForciblyTrue::True)); } #[test] fn generic() { #[bitfield] struct Generic { v: u8, } } #[test] fn impl_from_trait() { #[bitfield] #[derive(Clone, Copy)] struct Test { value: u8, } let v = Test::from([127]); assert_eq!(v.value(), 127); let v: [u8; 1] = v.into(); assert_eq!(v, [127]); } #[test] fn impl_try_from_trait() { #[bitfield(filled = false)] #[derive(Clone, Copy, Debug)] struct Test { value: B7, } Test::try_from([128]).unwrap_err(); let v = Test::try_from([127]).unwrap(); assert_eq!(v.value(), 127); let v: [u8; 1] = v.into(); assert_eq!(v, [127]); } ================================================ FILE: tests/bitfield/no_implicit_prelude.rs ================================================ //! Checks that no implicit paths are generated by the `#[bitfield]` proc. macro //! and `#[derive(Specifier)]` derive macro. #![no_implicit_prelude] struct OverrideLanguagePrelude; macro_rules! override_language_primitives { ($($ty:ident),* $(,)?) => { $( #[allow(non_camel_case_types)] type $ty = OverrideLanguagePrelude; )* } } override_language_primitives!( bool, char, str, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize, f32, f64 ); #[test] fn no_implicit_prelude() { #[::modular_bitfield::bitfield] pub struct TestBitfield { a: ::core::primitive::bool, b: ::modular_bitfield::specifiers::B3, c: ::modular_bitfield::specifiers::B4, d: ::modular_bitfield::specifiers::B24, } #[derive(::modular_bitfield::Specifier, Debug)] pub enum TestSpecifier { A = 0, B = 1, } } #[test] fn no_implicit_prelude_2() { #[::modular_bitfield::bitfield] #[derive(Specifier, Debug)] pub struct TestBitfield { a: ::core::primitive::bool, b: ::modular_bitfield::specifiers::B3, c: ::modular_bitfield::specifiers::B4, d: ::modular_bitfield::specifiers::B24, } } ================================================ FILE: tests/bitfield/regressions.rs ================================================ //! Tests for regressions found in published versions use modular_bitfield::prelude::*; #[test] fn deny_elided_lifetime() { #[bitfield] #[derive(Debug)] #[repr(u8)] struct KeyFlags { certify: bool, fill: B7, } } #[test] fn regression_issue_8() { #[derive(Specifier, Debug, PartialEq, Copy, Clone)] pub enum Mode { A = 0b00, B = 0b01, C = 0b10, D = 0b11, } #[bitfield] #[derive(Debug)] pub struct StatFlag { x: bool, y: bool, z: B4, #[bits = 2] mode: Mode, } let mut flag = StatFlag::new(); assert!(!flag.x()); assert!(!flag.y()); assert_eq!(flag.z(), 0); assert_eq!(flag.mode(), Mode::A); let new_mode = Mode::B; flag.set_mode(new_mode); assert_eq!(flag.mode(), new_mode); flag.set_x(true); assert!(flag.x()); assert_eq!(flag.mode(), new_mode); flag.set_y(true); assert!(flag.y()); assert_eq!(flag.mode(), new_mode); flag.set_z(0b01); assert_eq!(flag.z(), 0b01); assert_eq!(flag.mode(), new_mode); flag.set_z(0b11); assert_eq!(flag.z(), 0b11); assert_eq!(flag.mode(), new_mode); } #[test] fn regression_v0_11() { #[bitfield(bits = 8)] #[derive(Copy, Clone)] pub struct Flags { pub a: bool, #[skip] __: bool, pub b: bool, #[skip] __: B5, } let mut flags = Flags::new(); assert!(!flags.a()); assert!(!flags.b()); assert_eq!(flags.into_bytes(), [0b0000_0000]); flags.set_a(true); flags.set_b(true); assert!(flags.a()); assert!(flags.b()); assert_eq!(flags.into_bytes(), [0b0000_0101]); } #[test] fn regression_issue_132() { #[repr(C, align(1024))] #[bitfield] pub struct PackedData { header: B4, body: B9, is_alive: B1, status: B2, } // check alignment of PackedData assert_eq!(core::mem::align_of::(), 1024); } ================================================ FILE: tests/bitfield/repr.rs ================================================ //! Tests for `#[repr(uN)]` and `#[cfg_attr(cond, repr(uN))]` use modular_bitfield::prelude::*; #[test] fn complex_use() { #[bitfield] #[repr(u32)] #[derive(Debug, PartialEq, Eq)] struct TtResp { mregion: u8, sregion: u8, mrvalid: bool, srvalid: bool, r: bool, rw: bool, nsr: bool, nsrw: bool, s: bool, irvalid: bool, iregion: u8, } let mut rsp = TtResp::new(); rsp.set_mregion(u8::MAX); rsp.set_sregion(0xEE); rsp.set_mrvalid(true); rsp.set_srvalid(true); rsp.set_r(true); rsp.set_rw(true); rsp.set_nsr(true); rsp.set_nsrw(true); rsp.set_s(true); rsp.set_irvalid(true); rsp.set_iregion(0xDD); assert_eq!(rsp, TtResp::from(0xDDFF_EEFF_u32)); assert_eq!(rsp.mregion(), u8::MAX); assert_eq!(rsp.sregion(), 0xEE); assert!(rsp.mrvalid()); assert!(rsp.srvalid()); assert!(rsp.r()); assert!(rsp.rw()); assert!(rsp.nsr()); assert!(rsp.nsrw()); assert!(rsp.s()); assert!(rsp.irvalid()); assert_eq!(rsp.iregion(), 0xDD); rsp.set_mregion(0); rsp.set_sregion(0); rsp.set_mrvalid(false); rsp.set_srvalid(false); rsp.set_r(false); rsp.set_rw(false); rsp.set_nsr(false); rsp.set_nsrw(false); rsp.set_s(false); rsp.set_irvalid(false); rsp.set_iregion(0x00); assert_eq!(rsp, TtResp::new()); } #[test] fn generic_repr() { #[bitfield] #[repr(u8)] struct Generic { v: u8, } } #[test] fn multiple_valid_reprs_1() { #[bitfield] #[repr(C, u32)] // The macro simply ignores `repr(C)` pub struct SignedInt { sign: bool, value: B31, } } #[test] fn multiple_valid_reprs_2() { #[bitfield] #[repr(u32)] #[repr(C)] // The macro simply ignores `repr(C)` pub struct SignedInt { sign: bool, value: B31, } } #[test] fn valid_cond_use() { #[bitfield] #[cfg_attr(all(), repr(u32))] #[derive(Debug, PartialEq, Eq)] pub struct SignedInt { sign: bool, value: B31, } let i1 = SignedInt::new().with_sign(true).with_value(0b1001_0011); let i2 = SignedInt::from(0b0000_0000_0000_0000_0000_0001_0010_0111_u32); assert_eq!(i1, i2); assert_eq!(i1.sign(), i2.sign()); assert_eq!(i1.value(), i2.value()); } macro_rules! valid_use { ($(($name:ident, $repr_ty:ty, $value_ty:ty)),*$(,)?) => { $(#[test] fn $name() { #[bitfield] #[repr($repr_ty)] #[derive(Debug, PartialEq, Eq)] pub struct SignedInt { value: $value_ty, sign: bool, } let value = 0b101_0011; let bits = (1 << (<$repr_ty>::BITS - 1)) | value; let i1 = SignedInt::new().with_sign(true).with_value(value); let i2 = SignedInt::from(bits); assert_eq!(i1, i2); assert_eq!(i1.sign(), i2.sign()); assert_eq!(i1.value(), i2.value()); })* } } valid_use!( (valid_use_u8, u8, B7), (valid_use_u16, u16, B15), (valid_use_u32, u32, B31), (valid_use_u64, u64, B63), (valid_use_u128, u128, B127) ); ================================================ FILE: tests/bitfield/skip.rs ================================================ //! Tests for `#[skip(..)]` extern crate alloc; use alloc::format; use modular_bitfield::prelude::*; #[test] fn double_wildcards_1() { #[bitfield] pub struct Sparse { #[skip] __: B10, a: bool, #[skip] __: B10, b: bool, #[skip] __: B10, } } #[test] fn double_wildcards_2() { #[bitfield] pub struct Sparse { #[skip(getters, setters)] __: B10, a: bool, #[skip(getters, setters)] __: B10, b: bool, #[skip(getters, setters)] __: B10, } } #[test] fn skip_default() { #[bitfield] pub struct Sparse { #[skip] unused_1: B10, a: bool, #[skip] unused_2: B10, b: bool, #[skip] unused_3: B10, } let mut sparse = Sparse::new(); assert!(!sparse.a()); assert!(!sparse.b()); sparse.set_a(true); sparse.set_b(true); assert!(sparse.a()); assert!(sparse.b()); } #[test] fn skip_getters_and_setters_1() { #[bitfield] pub struct Sparse { #[skip(getters, setters)] unused_1: B10, a: bool, #[skip(getters, setters)] unused_2: B10, b: bool, #[skip(getters, setters)] unused_3: B10, } let mut sparse = Sparse::new(); assert!(!sparse.a()); assert!(!sparse.b()); sparse.set_a(true); sparse.set_b(true); assert!(sparse.a()); assert!(sparse.b()); } #[test] fn skip_getters_and_setters_2() { #[bitfield] pub struct Sparse { #[skip(getters)] #[skip(setters)] unused_1: B10, a: bool, #[skip(setters)] #[skip(getters)] unused_2: B10, b: bool, #[skip(getters)] #[skip(setters)] unused_3: B10, } let mut sparse = Sparse::new(); assert!(!sparse.a()); assert!(!sparse.b()); sparse.set_a(true); sparse.set_b(true); assert!(sparse.a()); assert!(sparse.b()); } #[test] fn skip_getters() { #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(getters)] unused_1: B10, a: bool, #[skip(getters)] unused_2: B10, b: bool, #[skip(getters)] unused_3: B10, } let mut sparse = Sparse::new(); assert!(!sparse.a()); assert!(!sparse.b()); sparse.set_a(true); sparse.set_b(true); assert!(sparse.a()); assert!(sparse.b()); // Use setters of fields with skipped getters: sparse.set_unused_1(0b0011_1111_1111); sparse.set_unused_2(0b0011_1111_1111); sparse.set_unused_3(0b0011_1111_1111); assert_eq!(sparse.into_bytes(), [0xFF; 4]); } #[test] fn skip_setters() { #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(setters)] unused_1: B10, a: bool, #[skip(setters)] unused_2: B10, b: bool, #[skip(setters)] unused_3: B10, } let mut sparse = Sparse::new(); assert!(!sparse.a()); assert!(!sparse.b()); sparse.set_a(true); sparse.set_b(true); assert!(sparse.a()); assert!(sparse.b()); // Use setters of fields with skipped getters: assert_eq!(sparse.unused_1(), 0); assert_eq!(sparse.unused_2(), 0); assert_eq!(sparse.unused_3(), 0); } #[test] fn skip_with_debug() { #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(getters)] no_getters: B10, a: bool, #[skip(setters)] no_setters: B10, b: bool, #[skip] skipped: B10, } let sparse = Sparse::new().with_a(true).with_b(false); assert_eq!( format!("{sparse:?}"), "Sparse { a: true, no_setters: 0, b: false }", ); assert_eq!( format!("{sparse:#X?}"), "Sparse {\n a: true,\n no_setters: 0x0,\n b: false,\n}", ); } ================================================ FILE: tests/lib.rs ================================================ #![no_std] #![deny(elided_lifetimes_in_paths)] #![warn(clippy::pedantic, rust_2018_idioms)] #![allow(dead_code)] mod bitfield; ================================================ FILE: tests/panic_tests.rs ================================================ #![allow(dead_code)] use modular_bitfield::prelude::*; #[bitfield] pub struct EdgeCaseBytes { a: B9, b: B6, c: B13, d: B4, } #[test] #[should_panic(expected = "value out of bounds for field EdgeCaseBytes.a")] fn invalid_access_a() { let mut bytes = EdgeCaseBytes::new(); bytes.set_a(0b0010_0000_0000_u16); } #[test] #[should_panic(expected = "value out of bounds for field EdgeCaseBytes.b")] fn invalid_access_b() { let mut bytes = EdgeCaseBytes::new(); bytes.set_b(0b0000_0100_0000_u8); } #[test] #[should_panic(expected = "value out of bounds for field EdgeCaseBytes.c")] fn invalid_access_c() { let mut bytes = EdgeCaseBytes::new(); bytes.set_c(0x2000_u16); } #[test] #[should_panic(expected = "value out of bounds for field EdgeCaseBytes.d")] fn invalid_access_d() { let mut bytes = EdgeCaseBytes::new(); bytes.set_d(0b0001_0000_u8); } ================================================ FILE: tests/ui/access_test.rs ================================================ mod inner { use modular_bitfield::prelude::*; #[bitfield] #[derive(Copy, Clone, Eq, PartialEq, Default)] pub struct ColorEntry { a: B5, pub(crate) b: B3, } } use inner::*; fn main() { let c = ColorEntry::new(); let _ = c.a(); // Notice no error for calling b let _ = c.b(); // Also no error for using default let c = ColorEntry::default(); } ================================================ FILE: tests/ui/access_test.stderr ================================================ error[E0624]: method `a` is private --> tests/ui/access_test.rs:14:15 | 6 | a: B5, | - private method defined here ... 14 | let _ = c.a(); | ^ private method ================================================ FILE: tests/ui/bitfield_attribute_wrong.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(unknown_attribute = 0)] struct Foo { value: u8, } fn main() {} ================================================ FILE: tests/ui/bitfield_attribute_wrong.stderr ================================================ error: encountered unsupported #[bitfield] attribute --> tests/ui/bitfield_attribute_wrong.rs:3:12 | 3 | #[bitfield(unknown_attribute = 0)] | ^^^^^^^^^^^^^^^^^ ================================================ FILE: tests/ui/bits_attribute_wrong.rs ================================================ use modular_bitfield::prelude::*; #[derive(Specifier, Debug)] pub enum TriggerMode { Edge = 0, Level = 1, } #[bitfield] pub struct MismatchedTypes { #[bits = 9] trigger_mode: TriggerMode, reserved: B7, } const NOT_A_LITERAL: u32 = 9; #[bitfield] pub struct InvalidValueType { #[bits = NOT_A_LITERAL] trigger_mode: TriggerMode, reserved: B7, } #[bitfield] pub struct DuplicateAttribute { #[bits = 1] #[bits = 1] trigger_mode: TriggerMode, reserved: B7, } #[bitfield] pub struct NotANameValue { #[bits(1)] trigger_mode: TriggerMode, reserved: B7, } fn main() {} ================================================ FILE: tests/ui/bits_attribute_wrong.stderr ================================================ error: encountered invalid value type for #[bits = N] --> tests/ui/bits_attribute_wrong.rs:20:14 | 20 | #[bits = NOT_A_LITERAL] | ^^^^^^^^^^^^^ error: encountered duplicate `#[bits = N]` attribute for field --> tests/ui/bits_attribute_wrong.rs:28:7 | 28 | #[bits = 1] | ^^^^ error: duplicate `#[bits = N]` here --> tests/ui/bits_attribute_wrong.rs:27:7 | 27 | #[bits = 1] | ^^^^ error: expected `=` --> tests/ui/bits_attribute_wrong.rs:35:11 | 35 | #[bits(1)] | ^ error[E0308]: mismatched types --> tests/ui/bits_attribute_wrong.rs:11:7 | 11 | #[bits = 9] | ^^^^ expected `1`, found `9` 12 | trigger_mode: TriggerMode, | ----------- expected due to this | = note: expected struct `BitCount<1>` found struct `BitCount<9>` ================================================ FILE: tests/ui/bits_param/conflicting_params.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = 16, bytes = 4)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/conflicting_params.stderr ================================================ error: encountered conflicting `bits = 16` and `bytes = 4` parameters --> tests/ui/bits_param/conflicting_params.rs:3:1 | 3 | #[bitfield(bits = 16, bytes = 4)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the attribute macro `bitfield` (in Nightly builds, run with -Z macro-backtrace for more info) error: conflicting `bits = 16` here --> tests/ui/bits_param/conflicting_params.rs:3:12 | 3 | #[bitfield(bits = 16, bytes = 4)] | ^^^^ error: conflicting `bytes = 4` here --> tests/ui/bits_param/conflicting_params.rs:3:23 | 3 | #[bitfield(bits = 16, bytes = 4)] | ^^^^^ ================================================ FILE: tests/ui/bits_param/conflicting_repr.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = 16)] #[repr(u32)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/conflicting_repr.stderr ================================================ error: encountered conflicting `bits = 16` and #[repr(u32)] parameters --> tests/ui/bits_param/conflicting_repr.rs:3:1 | 3 | #[bitfield(bits = 16)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the attribute macro `bitfield` (in Nightly builds, run with -Z macro-backtrace for more info) error: conflicting `bits = 16` here --> tests/ui/bits_param/conflicting_repr.rs:3:12 | 3 | #[bitfield(bits = 16)] | ^^^^ error: conflicting #[repr(u32)] here --> tests/ui/bits_param/conflicting_repr.rs:4:8 | 4 | #[repr(u32)] | ^^^ ================================================ FILE: tests/ui/bits_param/duplicate_param_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = 32, bits = 32)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/duplicate_param_1.stderr ================================================ error: encountered duplicate `bits` parameter: duplicate set to 32 --> tests/ui/bits_param/duplicate_param_1.rs:3:23 | 3 | #[bitfield(bits = 32, bits = 32)] | ^^^^ error: previous `bits` parameter here --> tests/ui/bits_param/duplicate_param_1.rs:3:12 | 3 | #[bitfield(bits = 32, bits = 32)] | ^^^^ ================================================ FILE: tests/ui/bits_param/duplicate_param_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = 32, bits = 16)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/duplicate_param_2.stderr ================================================ error: encountered duplicate `bits` parameter: duplicate set to 32 --> tests/ui/bits_param/duplicate_param_2.rs:3:23 | 3 | #[bitfield(bits = 32, bits = 16)] | ^^^^ error: previous `bits` parameter here --> tests/ui/bits_param/duplicate_param_2.rs:3:12 | 3 | #[bitfield(bits = 32, bits = 16)] | ^^^^ ================================================ FILE: tests/ui/bits_param/invalid_param_type.rs ================================================ use modular_bitfield::prelude::*; const NOT_LITERAL: u32 = 32; #[bitfield(bits = NOT_LITERAL)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/invalid_param_type.stderr ================================================ error: encountered invalid value argument for #[bitfield] `bits` parameter --> tests/ui/bits_param/invalid_param_type.rs:5:19 | 5 | #[bitfield(bits = NOT_LITERAL)] | ^^^^^^^^^^^ ================================================ FILE: tests/ui/bits_param/invalid_param_value_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = true)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/invalid_param_value_1.stderr ================================================ error: encountered invalid value argument for #[bitfield] `bits` parameter --> tests/ui/bits_param/invalid_param_value_1.rs:3:19 | 3 | #[bitfield(bits = true)] | ^^^^ ================================================ FILE: tests/ui/bits_param/invalid_param_value_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = -1)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/invalid_param_value_2.stderr ================================================ error: encountered malformatted integer value for `bits` parameter: invalid digit found in string --> tests/ui/bits_param/invalid_param_value_2.rs:3:19 | 3 | #[bitfield(bits = -1)] | ^ ================================================ FILE: tests/ui/bits_param/missing_param_value.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits)] pub struct Missing { sign: bool, value: B31, } #[bitfield(bits(32))] pub struct WrongMetaType { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/missing_param_value.stderr ================================================ error: expected `=` --> tests/ui/bits_param/missing_param_value.rs:3:1 | 3 | #[bitfield(bits)] | ^^^^^^^^^^^^^^^^^ | = note: this error originates in the attribute macro `bitfield` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected `=` --> tests/ui/bits_param/missing_param_value.rs:9:16 | 9 | #[bitfield(bits(32))] | ^ ================================================ FILE: tests/ui/bits_param/too_few_bits.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = 16)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/too_few_bits.stderr ================================================ error[E0277]: the trait bound `False: FillsUnalignedBits` is not satisfied --> tests/ui/bits_param/too_few_bits.rs:4:1 | 4 | pub struct SignInteger { | ^^^ the trait `FillsUnalignedBits` is not implemented for `False` | = help: the trait `FillsUnalignedBits` is implemented for `True` note: required by a bound in `CheckFillsUnalignedBits::CheckType` --> src/private/checks.rs | | ::Out: FillsUnalignedBits, | ^^^^^^^^^^^^^^^^^^ required by this bound in `CheckFillsUnalignedBits::CheckType` | { | type CheckType: DispatchTrueFalse; | --------- required by a bound in this associated type error[E0277]: the trait bound `False: FillsUnalignedBits` is not satisfied --> tests/ui/bits_param/too_few_bits.rs:4:1 | 4 | pub struct SignInteger { | ^^^ the trait `FillsUnalignedBits` is not implemented for `False` | = help: the trait `FillsUnalignedBits` is implemented for `True` note: required by a bound in `CheckFillsUnalignedBits` --> src/private/checks.rs | | pub trait CheckFillsUnalignedBits | ----------------------- required by a bound in this trait | where | ::Out: FillsUnalignedBits, | ^^^^^^^^^^^^^^^^^^ required by this bound in `CheckFillsUnalignedBits` = note: `CheckFillsUnalignedBits` is a "sealed trait", because to implement it you also need to implement `modular_bitfield::private::checks::FillsUnalignedBits`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it = help: the following type implements the trait: modular_bitfield::private::checks::True ================================================ FILE: tests/ui/bits_param/too_many_bits.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(bits = 33)] pub struct SignInteger { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/bits_param/too_many_bits.stderr ================================================ error[E0277]: the trait bound `False: FillsUnalignedBits` is not satisfied --> tests/ui/bits_param/too_many_bits.rs:4:1 | 4 | pub struct SignInteger { | ^^^ the trait `FillsUnalignedBits` is not implemented for `False` | = help: the trait `FillsUnalignedBits` is implemented for `True` note: required by a bound in `CheckFillsUnalignedBits::CheckType` --> src/private/checks.rs | | ::Out: FillsUnalignedBits, | ^^^^^^^^^^^^^^^^^^ required by this bound in `CheckFillsUnalignedBits::CheckType` | { | type CheckType: DispatchTrueFalse; | --------- required by a bound in this associated type error[E0277]: the trait bound `False: FillsUnalignedBits` is not satisfied --> tests/ui/bits_param/too_many_bits.rs:4:1 | 4 | pub struct SignInteger { | ^^^ the trait `FillsUnalignedBits` is not implemented for `False` | = help: the trait `FillsUnalignedBits` is implemented for `True` note: required by a bound in `CheckFillsUnalignedBits` --> src/private/checks.rs | | pub trait CheckFillsUnalignedBits | ----------------------- required by a bound in this trait | where | ::Out: FillsUnalignedBits, | ^^^^^^^^^^^^^^^^^^ required by this bound in `CheckFillsUnalignedBits` = note: `CheckFillsUnalignedBits` is a "sealed trait", because to implement it you also need to implement `modular_bitfield::private::checks::FillsUnalignedBits`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it = help: the following type implements the trait: modular_bitfield::private::checks::True ================================================ FILE: tests/ui/bytes_param/duplicate_parameters.rs ================================================ use modular_bitfield::prelude::*; // There are 2 duplicate bytes parameters. #[bitfield(bytes = 4, bytes = 4)] pub struct Base { a: B2, b: B6, c: u8, d: u16, } fn main() {} ================================================ FILE: tests/ui/bytes_param/duplicate_parameters.stderr ================================================ error: encountered duplicate `bytes` parameter: duplicate set to 4 --> tests/ui/bytes_param/duplicate_parameters.rs:4:23 | 4 | #[bitfield(bytes = 4, bytes = 4)] | ^^^^^ error: previous `bytes` parameter here --> tests/ui/bytes_param/duplicate_parameters.rs:4:12 | 4 | #[bitfield(bytes = 4, bytes = 4)] | ^^^^^ ================================================ FILE: tests/ui/bytes_param/fewer_bytes_than_expected.rs ================================================ use modular_bitfield::prelude::*; // Requires 3 bytes in total instead of 4. #[bitfield(bytes = 4)] pub struct Base { a: B2, b: B6, c: u8, d: u8, } fn main() { assert_eq!(core::mem::size_of::(), 3) } ================================================ FILE: tests/ui/bytes_param/fewer_bytes_than_expected.stderr ================================================ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> tests/ui/bytes_param/fewer_bytes_than_expected.rs:4:12 | 4 | #[bitfield(bytes = 4)] | ^^^^^ | = note: source type: `ExpectedBytes` (32 bits) = note: target type: `Base` (24 bits) = note: this error originates in the macro `::modular_bitfield::private::static_assertions::assert_eq_size` (in Nightly builds, run with -Z macro-backtrace for more info) ================================================ FILE: tests/ui/bytes_param/invalid_int_value.rs ================================================ use modular_bitfield::prelude::*; // The integer value cannot be parsed into a `usize` since it is negative. #[bitfield(bytes = -1)] pub struct Base { a: B2, b: B6, c: u8, d: u32, } fn main() {} ================================================ FILE: tests/ui/bytes_param/invalid_int_value.stderr ================================================ error: encountered malformatted integer value for `bytes` parameter: invalid digit found in string --> tests/ui/bytes_param/invalid_int_value.rs:4:20 | 4 | #[bitfield(bytes = -1)] | ^ ================================================ FILE: tests/ui/bytes_param/invalid_type.rs ================================================ use modular_bitfield::prelude::*; // The bytes parameter is required to have an integer value. #[bitfield(bytes = true)] pub struct Base { a: B2, b: B6, c: u8, d: u32, } fn main() {} ================================================ FILE: tests/ui/bytes_param/invalid_type.stderr ================================================ error: encountered invalid value argument for #[bitfield] `bytes` parameter --> tests/ui/bytes_param/invalid_type.rs:4:20 | 4 | #[bitfield(bytes = true)] | ^^^^ ================================================ FILE: tests/ui/bytes_param/more_bytes_than_expected.rs ================================================ use modular_bitfield::prelude::*; // Requires 6 bytes in total instead of 4. #[bitfield(bytes = 4)] pub struct Base { a: B2, b: B6, c: u8, d: u32, } fn main() { assert_eq!(core::mem::size_of::(), 6) } ================================================ FILE: tests/ui/bytes_param/more_bytes_than_expected.stderr ================================================ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> tests/ui/bytes_param/more_bytes_than_expected.rs:4:12 | 4 | #[bitfield(bytes = 4)] | ^^^^^ | = note: source type: `ExpectedBytes` (32 bits) = note: target type: `Base` (48 bits) = note: this error originates in the macro `::modular_bitfield::private::static_assertions::assert_eq_size` (in Nightly builds, run with -Z macro-backtrace for more info) ================================================ FILE: tests/ui/derive_bitfield_specifier/invalid_bits_attribute.rs ================================================ use modular_bitfield::prelude::*; #[derive(Specifier)] #[bits = 1] #[bits = 1] enum TooManyAttrs { Zero = 0, One = 1, } #[derive(Specifier)] #[bits = 1.0] enum NotAnInt { Zero = 0, One = 1, } fn main() {} ================================================ FILE: tests/ui/derive_bitfield_specifier/invalid_bits_attribute.stderr ================================================ error: More than one 'bits' attribute is not permitted --> tests/ui/derive_bitfield_specifier/invalid_bits_attribute.rs:5:1 | 5 | #[bits = 1] | ^^^^^^^^^^^ error: could not parse 'bits' attribute --> tests/ui/derive_bitfield_specifier/invalid_bits_attribute.rs:12:1 | 12 | #[bits = 1.0] | ^^^^^^^^^^^^^ ================================================ FILE: tests/ui/derive_bitfield_specifier/non_power_of_two.rs ================================================ // Bitfield enums with a number of variants other than a power of two should // fail to compile. // // (Or, if you implemented the optional #[bits = N] enum approach mentioned in // the explanation of test case 06, then enums with non-power-of-two variants // without a #[bits = N] attribute should fail to compile.) use modular_bitfield::prelude::*; #[derive(Specifier)] pub enum Bad { Zero, One, Two, } fn main() {} ================================================ FILE: tests/ui/derive_bitfield_specifier/non_power_of_two.stderr ================================================ error: #[derive(Specifier)] expected a number of variants which is a power of 2, specify #[bits = 2] if that was your intent --> tests/ui/derive_bitfield_specifier/non_power_of_two.rs:11:1 | 11 | pub enum Bad { | ^^^ ================================================ FILE: tests/ui/derive_bitfield_specifier/variant_out_of_range.rs ================================================ // Bitfield enums with any discriminant (implicit or explicit) outside of the // range 0..2^BITS should fail to compile. use modular_bitfield::prelude::*; const F: isize = 1; #[derive(Specifier)] pub enum DeliveryMode { Fixed = F, Lowest, SMI, RemoteRead, NMI, Init, Startup, External, } fn main() {} ================================================ FILE: tests/ui/derive_bitfield_specifier/variant_out_of_range.stderr ================================================ error[E0277]: the trait bound `False: DiscriminantInRange` is not satisfied --> tests/ui/derive_bitfield_specifier/variant_out_of_range.rs:17:5 | 17 | External, | ^^^^^^^^ the trait `DiscriminantInRange` is not implemented for `False` | = help: the trait `DiscriminantInRange` is implemented for `True` note: required by a bound in `CheckDiscriminantInRange::CheckType` --> src/private/checks.rs | | ::Out: DiscriminantInRange, | ^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckDiscriminantInRange::CheckType` | { | type CheckType: DispatchTrueFalse; | --------- required by a bound in this associated type error[E0277]: the trait bound `False: DiscriminantInRange` is not satisfied --> tests/ui/derive_bitfield_specifier/variant_out_of_range.rs:17:5 | 17 | External, | ^^^^^^^^ the trait `DiscriminantInRange` is not implemented for `False` | = help: the trait `DiscriminantInRange` is implemented for `True` note: required by a bound in `CheckDiscriminantInRange` --> src/private/checks.rs | | pub trait CheckDiscriminantInRange | ------------------------ required by a bound in this trait | where | ::Out: DiscriminantInRange, | ^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckDiscriminantInRange` = note: `CheckDiscriminantInRange` is a "sealed trait", because to implement it you also need to implement `modular_bitfield::private::checks::DiscriminantInRange`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it = help: the following type implements the trait: modular_bitfield::private::checks::True ================================================ FILE: tests/ui/derive_debug/duplicate_derive_debug.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] #[derive(Debug)] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/derive_debug/duplicate_derive_debug.stderr ================================================ error: encountered duplicate `#[derive(Debug)]` parameter --> tests/ui/derive_debug/duplicate_derive_debug.rs:4:27 | 4 | #[derive(Debug)] #[derive(Debug)] | ^^^^^ error: previous `#[derive(Debug)]` parameter here --> tests/ui/derive_debug/duplicate_derive_debug.rs:4:10 | 4 | #[derive(Debug)] #[derive(Debug)] | ^^^^^ ================================================ FILE: tests/ui/derive_debug/duplicate_derive_debug_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug, Debug)] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/derive_debug/duplicate_derive_debug_2.stderr ================================================ error: encountered duplicate `#[derive(Debug)]` parameter --> tests/ui/derive_debug/duplicate_derive_debug_2.rs:4:17 | 4 | #[derive(Debug, Debug)] | ^^^^^ error: previous `#[derive(Debug)]` parameter here --> tests/ui/derive_debug/duplicate_derive_debug_2.rs:4:10 | 4 | #[derive(Debug, Debug)] | ^^^^^ ================================================ FILE: tests/ui/derive_specifier/duplicate_derive_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Specifier, Specifier)] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/derive_specifier/duplicate_derive_1.stderr ================================================ error: encountered duplicate `#[derive(Specifier)]` parameter --> tests/ui/derive_specifier/duplicate_derive_1.rs:4:21 | 4 | #[derive(Specifier, Specifier)] | ^^^^^^^^^ error: previous `#[derive(Specifier)]` parameter here --> tests/ui/derive_specifier/duplicate_derive_1.rs:4:10 | 4 | #[derive(Specifier, Specifier)] | ^^^^^^^^^ ================================================ FILE: tests/ui/derive_specifier/duplicate_derive_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Specifier)] #[derive(Specifier)] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/derive_specifier/duplicate_derive_2.stderr ================================================ error: encountered duplicate `#[derive(Specifier)]` parameter --> tests/ui/derive_specifier/duplicate_derive_2.rs:4:31 | 4 | #[derive(Specifier)] #[derive(Specifier)] | ^^^^^^^^^ error: previous `#[derive(Specifier)]` parameter here --> tests/ui/derive_specifier/duplicate_derive_2.rs:4:10 | 4 | #[derive(Specifier)] #[derive(Specifier)] | ^^^^^^^^^ ================================================ FILE: tests/ui/derive_specifier/out_of_bounds.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(filled = false)] #[derive(Specifier, Debug)] pub struct Header { a: B1, b: B128, } fn main() {} ================================================ FILE: tests/ui/derive_specifier/out_of_bounds.stderr ================================================ error[E0277]: the trait bound `False: SpecifierHasAtMost128Bits` is not satisfied --> tests/ui/derive_specifier/out_of_bounds.rs:4:1 | 4 | #[derive(Specifier, Debug)] | ^ the trait `SpecifierHasAtMost128Bits` is not implemented for `False` | = help: the trait `SpecifierHasAtMost128Bits` is implemented for `True` note: required by a bound in `CheckSpecifierHasAtMost128Bits::CheckType` --> src/private/checks.rs | | ::Out: SpecifierHasAtMost128Bits, | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckSpecifierHasAtMost128Bits::CheckType` | { | type CheckType: DispatchTrueFalse; | --------- required by a bound in this associated type error[E0277]: the trait bound `False: SpecifierHasAtMost128Bits` is not satisfied --> tests/ui/derive_specifier/out_of_bounds.rs:4:1 | 4 | #[derive(Specifier, Debug)] | ^ the trait `SpecifierHasAtMost128Bits` is not implemented for `False` | = help: the trait `SpecifierHasAtMost128Bits` is implemented for `True` note: required by a bound in `CheckSpecifierHasAtMost128Bits` --> src/private/checks.rs | | pub trait CheckSpecifierHasAtMost128Bits | ------------------------------ required by a bound in this trait | where | ::Out: SpecifierHasAtMost128Bits, | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckSpecifierHasAtMost128Bits` = note: `CheckSpecifierHasAtMost128Bits` is a "sealed trait", because to implement it you also need to implement `modular_bitfield::private::checks::SpecifierHasAtMost128Bits`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it = help: the following type implements the trait: modular_bitfield::private::checks::True error[E0277]: the trait bound `BitCount<136>: ArrayBytesConversion` is not satisfied --> tests/ui/derive_specifier/out_of_bounds.rs:4:1 | 4 | #[derive(Specifier, Debug)] | ^ the trait `ArrayBytesConversion` is not implemented for `BitCount<136>` | = help: the following other types implement trait `ArrayBytesConversion`: BitCount<104> BitCount<112> BitCount<120> BitCount<128> BitCount<16> BitCount<24> BitCount<32> BitCount<40> and $N others ================================================ FILE: tests/ui/empty.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] struct Unit; #[bitfield] struct Named {} #[bitfield] struct Unnamed(); fn main() {} ================================================ FILE: tests/ui/empty.stderr ================================================ error: encountered invalid bitfield struct without fields --> tests/ui/empty.rs:4:1 | 4 | struct Unit; | ^^^^^^^^^^^^ error: encountered invalid bitfield struct without fields --> tests/ui/empty.rs:7:1 | 7 | struct Named {} | ^^^^^^^^^^^^^^^ error: encountered invalid bitfield struct without fields --> tests/ui/empty.rs:10:1 | 10 | struct Unnamed(); | ^^^^^^^^^^^^^^^^^ ================================================ FILE: tests/ui/filled_param/duplicate_parameters.rs ================================================ use modular_bitfield::prelude::*; // There are 2 duplicate `filled` parameters. #[bitfield(filled = true, filled = true)] pub struct Base { a: B2, b: B6, c: u8, d: u16, } fn main() {} ================================================ FILE: tests/ui/filled_param/duplicate_parameters.stderr ================================================ error: encountered duplicate `filled` parameter: duplicate set to true --> tests/ui/filled_param/duplicate_parameters.rs:4:27 | 4 | #[bitfield(filled = true, filled = true)] | ^^^^^^ error: previous `filled` parameter here --> tests/ui/filled_param/duplicate_parameters.rs:4:12 | 4 | #[bitfield(filled = true, filled = true)] | ^^^^^^ ================================================ FILE: tests/ui/filled_param/invalid_bool_value.rs ================================================ use modular_bitfield::prelude::*; // The boolean value cannot be parsed from a string. #[bitfield(filled = "yes")] pub struct Base { a: B2, b: B6, c: u8, d: u32, } fn main() {} ================================================ FILE: tests/ui/filled_param/invalid_bool_value.stderr ================================================ error: encountered invalid value argument for #[bitfield] `filled` parameter --> tests/ui/filled_param/invalid_bool_value.rs:4:21 | 4 | #[bitfield(filled = "yes")] | ^^^^^ ================================================ FILE: tests/ui/filled_param/invalid_specified_as_filled.rs ================================================ use modular_bitfield::prelude::*; // The bitfield has exactly 15 bits and therefore is unfilled but not specified as such. #[bitfield(filled = true)] pub struct UnfilledBitfield { a: B7, b: u8, } fn main() {} ================================================ FILE: tests/ui/filled_param/invalid_specified_as_filled.stderr ================================================ error[E0277]: the trait bound `SevenMod8: TotalSizeIsMultipleOfEightBits` is not satisfied --> tests/ui/filled_param/invalid_specified_as_filled.rs:5:1 | 5 | pub struct UnfilledBitfield { | ^^^ the trait `TotalSizeIsMultipleOfEightBits` is not implemented for `SevenMod8` | = help: the trait `TotalSizeIsMultipleOfEightBits` is implemented for `ZeroMod8` note: required by a bound in `CheckTotalSizeMultipleOf8::Size` --> src/private/checks.rs | | ::CheckType: TotalSizeIsMultipleOfEightBits, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckTotalSizeMultipleOf8::Size` | { | type Size: RenameSizeType; | ---- required by a bound in this associated type error[E0277]: the trait bound `SevenMod8: TotalSizeIsMultipleOfEightBits` is not satisfied --> tests/ui/filled_param/invalid_specified_as_filled.rs:5:1 | 5 | pub struct UnfilledBitfield { | ^^^ the trait `TotalSizeIsMultipleOfEightBits` is not implemented for `SevenMod8` | = help: the trait `TotalSizeIsMultipleOfEightBits` is implemented for `ZeroMod8` note: required by a bound in `CheckTotalSizeMultipleOf8` --> src/private/checks.rs | | pub trait CheckTotalSizeMultipleOf8 | ------------------------- required by a bound in this trait | where | ::CheckType: TotalSizeIsMultipleOfEightBits, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckTotalSizeMultipleOf8` = note: `CheckTotalSizeMultipleOf8` is a "sealed trait", because to implement it you also need to implement `modular_bitfield::private::checks::TotalSizeIsMultipleOfEightBits`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it = help: the following type implements the trait: modular_bitfield::private::checks::ZeroMod8 ================================================ FILE: tests/ui/filled_param/invalid_specified_as_unfilled.rs ================================================ use modular_bitfield::prelude::*; // The bitfield has exactly 16 bits and therefore is filled but not specified as such. #[bitfield(filled = false)] pub struct UnfilledBitfield { a: B8, b: u8, } fn main() {} ================================================ FILE: tests/ui/filled_param/invalid_specified_as_unfilled.stderr ================================================ error[E0277]: the trait bound `ZeroMod8: TotalSizeIsNotMultipleOfEightBits` is not satisfied --> tests/ui/filled_param/invalid_specified_as_unfilled.rs:5:1 | 5 | pub struct UnfilledBitfield { | ^^^ the trait `TotalSizeIsNotMultipleOfEightBits` is not implemented for `ZeroMod8` | = help: the following other types implement trait `TotalSizeIsNotMultipleOfEightBits`: FiveMod8 FourMod8 OneMod8 SevenMod8 SixMod8 ThreeMod8 TwoMod8 note: required by a bound in `CheckTotalSizeIsNotMultipleOf8::Size` --> src/private/checks.rs | | ::CheckType: TotalSizeIsNotMultipleOfEightBits, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckTotalSizeIsNotMultipleOf8::Size` | { | type Size: RenameSizeType; | ---- required by a bound in this associated type error[E0277]: the trait bound `ZeroMod8: TotalSizeIsNotMultipleOfEightBits` is not satisfied --> tests/ui/filled_param/invalid_specified_as_unfilled.rs:5:1 | 5 | pub struct UnfilledBitfield { | ^^^ the trait `TotalSizeIsNotMultipleOfEightBits` is not implemented for `ZeroMod8` | = help: the following other types implement trait `TotalSizeIsNotMultipleOfEightBits`: FiveMod8 FourMod8 OneMod8 SevenMod8 SixMod8 ThreeMod8 TwoMod8 note: required by a bound in `CheckTotalSizeIsNotMultipleOf8` --> src/private/checks.rs | | pub trait CheckTotalSizeIsNotMultipleOf8 | ------------------------------ required by a bound in this trait | where | ::CheckType: TotalSizeIsNotMultipleOfEightBits, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckTotalSizeIsNotMultipleOf8` = note: `CheckTotalSizeIsNotMultipleOf8` is a "sealed trait", because to implement it you also need to implement `modular_bitfield::private::checks::TotalSizeIsNotMultipleOfEightBits`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it = help: the following types implement the trait: modular_bitfield::private::checks::OneMod8 modular_bitfield::private::checks::TwoMod8 modular_bitfield::private::checks::ThreeMod8 modular_bitfield::private::checks::FourMod8 modular_bitfield::private::checks::FiveMod8 modular_bitfield::private::checks::SixMod8 modular_bitfield::private::checks::SevenMod8 ================================================ FILE: tests/ui/generic.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] struct Generic { t: core::marker::PhantomData, } fn main() {} ================================================ FILE: tests/ui/generic.stderr ================================================ error: bitfield structs can only use const generics --> tests/ui/generic.rs:4:15 | 4 | struct Generic { | ^^^ ================================================ FILE: tests/ui/invalid_struct_specifier.rs ================================================ use modular_bitfield::prelude::*; #[derive(Specifier)] pub struct InvalidStructSpecifier { a: bool, b: B7, c: u8, } fn main() {} ================================================ FILE: tests/ui/invalid_struct_specifier.stderr ================================================ error: structs are not supported as bitfield specifiers --> tests/ui/invalid_struct_specifier.rs:4:1 | 4 | pub struct InvalidStructSpecifier { | ^^^ ================================================ FILE: tests/ui/invalid_union_specifier.rs ================================================ use modular_bitfield::prelude::*; #[derive(Specifier)] pub union InvalidUnionSpecifier { a: bool, b: B7, c: u8, } fn main() {} ================================================ FILE: tests/ui/invalid_union_specifier.stderr ================================================ error: unions are not supported as bitfield specifiers --> tests/ui/invalid_union_specifier.rs:4:1 | 4 | pub union InvalidUnionSpecifier { | ^^^ ================================================ FILE: tests/ui/multiple_of_8bits.rs ================================================ // Make it so that a bitfield with a size not a multiple of 8 bits will not // compile. // // Aim to make the error message as relevant and free of distractions as you can // get. The stderr file next to this test case should give some idea as to the // approach taken by the reference implementation for this project, but feel // free to overwrite the stderr file to match the implementation you come up // with. // // --- // Tangent // // There is only one profound insight about Rust macro development, and this // test case begins to touch on it: what makes someone an "expert at macros" // mostly has nothing to do with how good they are "at macros". // // 95% of what enables people to write powerful and user-friendly macro // libraries is in their mastery of everything else about Rust outside of // macros, and their creativity to put together ordinary language features in // interesting ways that may not occur in handwritten code. // // You may occasionally come across procedural macros that you feel are really // advanced or magical. If you ever feel this way, I encourage you to take a // closer look and you'll discover that as far as the macro implementation // itself is concerned, none of those libraries are doing anything remotely // interesting. They always just parse some input in a boring way, crawl some // syntax trees in a boring way to find out about the input, and paste together // some output code in a boring way exactly like what you've been doing so far. // In fact once you've made it this far in the workshop, it's okay to assume you // basically know everything there is to know about the mechanics of writing // procedural macros. // // To the extent that there are any tricks to macro development, all of them // revolve around *what* code the macros emit, not *how* the macros emit the // code. This realization can be surprising to people who entered into macro // development with a vague notion of procedural macros as a "compiler plugin" // which they imagine must imply all sorts of complicated APIs for *how* to // integrate with the rest of the compiler. That's not how it works. The only // thing macros do is emit code that could have been written by hand. If you // couldn't have come up with some piece of tricky code from one of those // magical macros, learning more "about macros" won't change that; but learning // more about every other part of Rust will. Inversely, once you come up with // what code you want to generate, writing the macro to generate it is generally // the easy part. use modular_bitfield::prelude::*; type A = B1; type B = B3; type C = B4; type D = B23; #[bitfield] pub struct NotQuiteFourBytes { a: A, b: B, c: C, d: D, } fn main() {} ================================================ FILE: tests/ui/multiple_of_8bits.stderr ================================================ error[E0277]: the trait bound `SevenMod8: TotalSizeIsMultipleOfEightBits` is not satisfied --> tests/ui/multiple_of_8bits.rs:54:1 | 54 | pub struct NotQuiteFourBytes { | ^^^ the trait `TotalSizeIsMultipleOfEightBits` is not implemented for `SevenMod8` | = help: the trait `TotalSizeIsMultipleOfEightBits` is implemented for `ZeroMod8` note: required by a bound in `CheckTotalSizeMultipleOf8::Size` --> src/private/checks.rs | | ::CheckType: TotalSizeIsMultipleOfEightBits, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckTotalSizeMultipleOf8::Size` | { | type Size: RenameSizeType; | ---- required by a bound in this associated type error[E0277]: the trait bound `SevenMod8: TotalSizeIsMultipleOfEightBits` is not satisfied --> tests/ui/multiple_of_8bits.rs:54:1 | 54 | pub struct NotQuiteFourBytes { | ^^^ the trait `TotalSizeIsMultipleOfEightBits` is not implemented for `SevenMod8` | = help: the trait `TotalSizeIsMultipleOfEightBits` is implemented for `ZeroMod8` note: required by a bound in `CheckTotalSizeMultipleOf8` --> src/private/checks.rs | | pub trait CheckTotalSizeMultipleOf8 | ------------------------- required by a bound in this trait | where | ::CheckType: TotalSizeIsMultipleOfEightBits, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `CheckTotalSizeMultipleOf8` = note: `CheckTotalSizeMultipleOf8` is a "sealed trait", because to implement it you also need to implement `modular_bitfield::private::checks::TotalSizeIsMultipleOfEightBits`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it = help: the following type implements the trait: modular_bitfield::private::checks::ZeroMod8 ================================================ FILE: tests/ui/regressions/invalid_bits_field_attr.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] pub struct SignInt { #[bits = 1] // This one is valid. sign: bool, #[whoat_bits = 31] // Should error! value: B31, } fn main() {} ================================================ FILE: tests/ui/regressions/invalid_bits_field_attr.stderr ================================================ error: cannot find attribute `whoat_bits` in this scope --> $DIR/invalid_bits_field_attr.rs:8:7 | 8 | #[whoat_bits = 31] // Should error! | ^^^^^^^^^^ ================================================ FILE: tests/ui/repr/conflicting_ignored_reprs.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[repr(C, transparent, u32)] // The macro simply ignores `repr(C)` and `repr(transparent)` pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/conflicting_ignored_reprs.stderr ================================================ error[E0692]: transparent struct cannot have other repr hints --> tests/ui/repr/conflicting_ignored_reprs.rs:4:8 | 4 | #[repr(C, transparent, u32)] // The macro simply ignores `repr(C)` and `repr(transparent)` | ^ ^^^^^^^^^^^ ================================================ FILE: tests/ui/repr/duplicate_repr_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[repr(u32)] #[repr(u32)] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/duplicate_repr_1.stderr ================================================ error: encountered duplicate `#[repr(uN)]` parameter: duplicate set to #[repr(u32)] --> tests/ui/repr/duplicate_repr_1.rs:5:8 | 5 | #[repr(u32)] | ^^^ error: previous `#[repr(uN)]` parameter here --> tests/ui/repr/duplicate_repr_1.rs:4:8 | 4 | #[repr(u32)] | ^^^ ================================================ FILE: tests/ui/repr/duplicate_repr_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[cfg_attr(not(test), repr(u32))] #[cfg_attr(not(test), repr(u32))] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/duplicate_repr_2.stderr ================================================ error: encountered duplicate `#[repr(uN)]` parameter: duplicate set to #[repr(u32)] --> tests/ui/repr/duplicate_repr_2.rs:5:28 | 5 | #[cfg_attr(not(test), repr(u32))] | ^^^ error: previous `#[repr(uN)]` parameter here --> tests/ui/repr/duplicate_repr_2.rs:4:28 | 4 | #[cfg_attr(not(test), repr(u32))] | ^^^ ================================================ FILE: tests/ui/repr/duplicate_repr_3.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[repr(u32)] #[cfg_attr(not(test), repr(u32))] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/duplicate_repr_3.stderr ================================================ error: encountered duplicate `#[repr(uN)]` parameter: duplicate set to #[repr(u32)] --> tests/ui/repr/duplicate_repr_3.rs:5:28 | 5 | #[cfg_attr(not(test), repr(u32))] | ^^^ error: previous `#[repr(uN)]` parameter here --> tests/ui/repr/duplicate_repr_3.rs:4:8 | 4 | #[repr(u32)] | ^^^ ================================================ FILE: tests/ui/repr/invalid_repr_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[repr(invalid)] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/invalid_repr_1.stderr ================================================ error[E0552]: unrecognized representation hint --> tests/ui/repr/invalid_repr_1.rs:4:8 | 4 | #[repr(invalid)] | ^^^^^^^ | = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` = note: for more information, visit ================================================ FILE: tests/ui/repr/invalid_repr_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[cfg_attr(all(), repr(invalid))] pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/invalid_repr_2.stderr ================================================ error[E0552]: unrecognized representation hint --> tests/ui/repr/invalid_repr_2.rs:4:24 | 4 | #[cfg_attr(all(), repr(invalid))] | ^^^^^^^ | = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` = note: for more information, visit ================================================ FILE: tests/ui/repr/invalid_repr_unfilled.rs ================================================ use modular_bitfield::prelude::*; #[bitfield(filled = false)] #[repr(u32)] pub struct SignedInt { sign: bool, value: B30, } fn main() {} ================================================ FILE: tests/ui/repr/invalid_repr_unfilled.stderr ================================================ error: encountered conflicting `#[repr(u32)]` and `filled = false` parameters --> tests/ui/repr/invalid_repr_unfilled.rs:3:1 | 3 | #[bitfield(filled = false)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the attribute macro `bitfield` (in Nightly builds, run with -Z macro-backtrace for more info) error: conflicting `#[repr(u32)]` here --> tests/ui/repr/invalid_repr_unfilled.rs:4:8 | 4 | #[repr(u32)] | ^^^ error: conflicting `filled = false` here --> tests/ui/repr/invalid_repr_unfilled.rs:3:12 | 3 | #[bitfield(filled = false)] | ^^^^^^ ================================================ FILE: tests/ui/repr/invalid_repr_width_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[repr(u16)] // Too few bits! pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/invalid_repr_width_1.stderr ================================================ error[E0277]: the trait bound `BitCount<32>: IsU16Compatible` is not satisfied --> tests/ui/repr/invalid_repr_width_1.rs:4:8 | 4 | #[repr(u16)] // Too few bits! | ^^^ the trait `IsU16Compatible` is not implemented for `BitCount<32>` | = help: the trait `IsU16Compatible` is implemented for `BitCount<16>` = help: see issue #48214 error[E0308]: mismatched types --> tests/ui/repr/invalid_repr_width_1.rs:4:8 | 4 | #[repr(u16)] // Too few bits! | ^^^ expected an array with a size of 4, found one with a size of 2 error[E0308]: mismatched types --> tests/ui/repr/invalid_repr_width_1.rs:4:8 | 4 | #[repr(u16)] // Too few bits! | ^^^ | | | expected an array with a size of 2, found one with a size of 4 | arguments to this function are incorrect | note: associated function defined here --> $RUST/core/src/num/mod.rs = note: this error originates in the macro `uint_impl` (in Nightly builds, run with -Z macro-backtrace for more info) ================================================ FILE: tests/ui/repr/invalid_repr_width_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[repr(u64)] // Too many bits! pub struct SignedInt { sign: bool, value: B31, } fn main() {} ================================================ FILE: tests/ui/repr/invalid_repr_width_2.stderr ================================================ error[E0277]: the trait bound `BitCount<32>: IsU64Compatible` is not satisfied --> tests/ui/repr/invalid_repr_width_2.rs:4:8 | 4 | #[repr(u64)] // Too many bits! | ^^^ the trait `IsU64Compatible` is not implemented for `BitCount<32>` | = help: the trait `IsU64Compatible` is implemented for `BitCount<64>` = help: see issue #48214 error[E0308]: mismatched types --> tests/ui/repr/invalid_repr_width_2.rs:4:8 | 4 | #[repr(u64)] // Too many bits! | ^^^ expected an array with a size of 4, found one with a size of 8 error[E0308]: mismatched types --> tests/ui/repr/invalid_repr_width_2.rs:4:8 | 4 | #[repr(u64)] // Too many bits! | ^^^ | | | expected an array with a size of 8, found one with a size of 4 | arguments to this function are incorrect | note: associated function defined here --> $RUST/core/src/num/mod.rs = note: this error originates in the macro `uint_impl` (in Nightly builds, run with -Z macro-backtrace for more info) ================================================ FILE: tests/ui/skip/duplicate_attr.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] pub struct TwoExplicitAll { #[skip] #[skip] unused_1: B15, a: bool, } #[bitfield] pub struct TwoExplicitListAll { #[skip()] #[skip()] unused_1: B15, a: bool, } #[bitfield] pub struct TwoExplicitGetters { #[skip(getters)] #[skip(getters)] unused_1: B15, a: bool, } #[bitfield] pub struct TwoExplicitListGetters { #[skip(getters, getters)] unused_1: B15, a: bool, } #[bitfield] pub struct ImplicitExplicitGetters { #[skip] #[skip(getters)] unused_1: B15, a: bool, } #[bitfield] pub struct ExplicitImplicitGetters { #[skip(getters)] #[skip] unused_1: B15, a: bool, } #[bitfield] pub struct TwoExplicitSetters { #[skip(setters)] #[skip(setters)] unused_1: B15, a: bool, } #[bitfield] pub struct TwoExplicitListSetters { #[skip(setters, setters)] unused_1: B15, a: bool, } #[bitfield] pub struct ImplicitExplicitSetters { #[skip] #[skip(setters)] unused_1: B15, a: bool, } #[bitfield] pub struct ExplicitImplicitSetters { #[skip(setters)] #[skip] unused_1: B15, a: bool, } fn main() {} ================================================ FILE: tests/ui/skip/duplicate_attr.stderr ================================================ error: encountered duplicate `#[skip]` attribute for field --> tests/ui/skip/duplicate_attr.rs:6:7 | 6 | #[skip] | ^^^^ error: duplicate `#[skip]` here --> tests/ui/skip/duplicate_attr.rs:5:7 | 5 | #[skip] | ^^^^ error: encountered duplicate `#[skip]` attribute for field --> tests/ui/skip/duplicate_attr.rs:14:7 | 14 | #[skip()] | ^^^^ error: duplicate `#[skip]` here --> tests/ui/skip/duplicate_attr.rs:13:7 | 13 | #[skip()] | ^^^^ error: encountered duplicate `#[skip(getters)]` attribute for field --> tests/ui/skip/duplicate_attr.rs:22:7 | 22 | #[skip(getters)] | ^^^^ error: duplicate `#[skip(getters)]` here --> tests/ui/skip/duplicate_attr.rs:21:7 | 21 | #[skip(getters)] | ^^^^ error: encountered duplicate `#[skip(getters)]` attribute for field --> tests/ui/skip/duplicate_attr.rs:29:21 | 29 | #[skip(getters, getters)] | ^^^^^^^ error: duplicate `#[skip(getters)]` here --> tests/ui/skip/duplicate_attr.rs:29:12 | 29 | #[skip(getters, getters)] | ^^^^^^^ error: encountered duplicate `#[skip(getters)]` attribute for field --> tests/ui/skip/duplicate_attr.rs:37:7 | 37 | #[skip(getters)] | ^^^^ error: duplicate `#[skip(getters)]` here --> tests/ui/skip/duplicate_attr.rs:36:7 | 36 | #[skip] | ^^^^ error: encountered duplicate `#[skip]` attribute for field --> tests/ui/skip/duplicate_attr.rs:45:7 | 45 | #[skip] | ^^^^ error: duplicate `#[skip]` here --> tests/ui/skip/duplicate_attr.rs:44:7 | 44 | #[skip(getters)] | ^^^^ error: encountered duplicate `#[skip(setters)]` attribute for field --> tests/ui/skip/duplicate_attr.rs:53:7 | 53 | #[skip(setters)] | ^^^^ error: duplicate `#[skip(setters)]` here --> tests/ui/skip/duplicate_attr.rs:52:7 | 52 | #[skip(setters)] | ^^^^ error: encountered duplicate `#[skip(setters)]` attribute for field --> tests/ui/skip/duplicate_attr.rs:60:21 | 60 | #[skip(setters, setters)] | ^^^^^^^ error: duplicate `#[skip(setters)]` here --> tests/ui/skip/duplicate_attr.rs:60:12 | 60 | #[skip(setters, setters)] | ^^^^^^^ error: encountered duplicate `#[skip(setters)]` attribute for field --> tests/ui/skip/duplicate_attr.rs:68:7 | 68 | #[skip(setters)] | ^^^^ error: duplicate `#[skip(setters)]` here --> tests/ui/skip/duplicate_attr.rs:67:7 | 67 | #[skip] | ^^^^ error: encountered duplicate `#[skip]` attribute for field --> tests/ui/skip/duplicate_attr.rs:76:7 | 76 | #[skip] | ^^^^ error: duplicate `#[skip]` here --> tests/ui/skip/duplicate_attr.rs:75:7 | 75 | #[skip(setters)] | ^^^^ ================================================ FILE: tests/ui/skip/invalid_specifier.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] pub struct InvalidSpecifier { #[skip(invalid_specifier)] unused_1: B10, a: bool, #[skip] unused_2: B10, b: bool, #[skip] unused_3: B10, } #[bitfield] pub struct InvalidFormat { #[skip = 1] unused_1: B10, a: bool, #[skip] unused_2: B10, b: bool, #[skip] unused_3: B10, } fn main() {} ================================================ FILE: tests/ui/skip/invalid_specifier.stderr ================================================ error: encountered unknown or unsupported #[skip(..)] specifier --> tests/ui/skip/invalid_specifier.rs:5:12 | 5 | #[skip(invalid_specifier)] | ^^^^^^^^^^^^^^^^^ error: encountered invalid format for #[skip] field attribute --> tests/ui/skip/invalid_specifier.rs:17:7 | 17 | #[skip = 1] | ^^^^ ================================================ FILE: tests/ui/skip/use_skipped_getter_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(getters)] unused_1: B7, a: bool, } fn main() { let sparse = Sparse::from_bytes([0xFF; 1]); sparse.set_unused_1(0); assert_eq!(sparse.unused_1(), 0); // ERROR! } ================================================ FILE: tests/ui/skip/use_skipped_getter_1.stderr ================================================ error[E0599]: no method named `unused_1` found for struct `Sparse` in the current scope --> tests/ui/skip/use_skipped_getter_1.rs:14:23 | 4 | / #[derive(Debug)] 5 | | pub struct Sparse { | |___- method `unused_1` not found for this struct ... 14 | assert_eq!(sparse.unused_1(), 0); // ERROR! | ^^^^^^^^ | help: there is a method `set_unused_1` with a similar name, but with different arguments --> tests/ui/skip/use_skipped_getter_1.rs:6:5 | 6 | #[skip(getters)] | ^ ================================================ FILE: tests/ui/skip/use_skipped_getter_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(getters, setters)] unused_1: B7, a: bool, } fn main() { let sparse = Sparse::from_bytes([0xFF; 1]); assert_eq!(sparse.unused_1(), 0xFE); // ERROR! } ================================================ FILE: tests/ui/skip/use_skipped_getter_2.stderr ================================================ error[E0599]: no method named `unused_1` found for struct `Sparse` in the current scope --> tests/ui/skip/use_skipped_getter_2.rs:13:23 | 4 | / #[derive(Debug)] 5 | | pub struct Sparse { | |___- method `unused_1` not found for this struct ... 13 | assert_eq!(sparse.unused_1(), 0xFE); // ERROR! | ^^^^^^^^ method not found in `Sparse` ================================================ FILE: tests/ui/skip/use_skipped_getter_3.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(getters)] #[skip(setters)] unused_1: B7, a: bool, } fn main() { let sparse = Sparse::from_bytes([0xFF; 1]); assert_eq!(sparse.unused_1(), 0xFE); // ERROR! } ================================================ FILE: tests/ui/skip/use_skipped_getter_3.stderr ================================================ error[E0599]: no method named `unused_1` found for struct `Sparse` in the current scope --> tests/ui/skip/use_skipped_getter_3.rs:13:23 | 4 | / #[derive(Debug)] 5 | | pub struct Sparse { | |___- method `unused_1` not found for this struct ... 13 | assert_eq!(sparse.unused_1(), 0xFE); // ERROR! | ^^^^^^^^ method not found in `Sparse` ================================================ FILE: tests/ui/skip/use_skipped_setter_1.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(setters)] unused_1: B7, a: bool, } fn main() { let sparse = Sparse::new(); assert_eq!(sparse.unused_1(), 0); sparse.set_unused_1(0b11_1111_1111); // ERROR! } ================================================ FILE: tests/ui/skip/use_skipped_setter_1.stderr ================================================ error[E0599]: no method named `set_unused_1` found for struct `Sparse` in the current scope --> tests/ui/skip/use_skipped_setter_1.rs:14:12 | 4 | / #[derive(Debug)] 5 | | pub struct Sparse { | |___- method `set_unused_1` not found for this struct ... 14 | sparse.set_unused_1(0b11_1111_1111); // ERROR! | ^^^^^^^^^^^^ | help: there is a method `unused_1` with a similar name, but with different arguments --> tests/ui/skip/use_skipped_setter_1.rs:6:5 | 6 | #[skip(setters)] | ^ ================================================ FILE: tests/ui/skip/use_skipped_setter_2.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(getters, setters)] unused_1: B7, a: bool, } fn main() { let sparse = Sparse::from_bytes([0xFF; 1]); sparse.set_unused_1(0); // ERROR! } ================================================ FILE: tests/ui/skip/use_skipped_setter_2.stderr ================================================ error[E0599]: no method named `set_unused_1` found for struct `Sparse` in the current scope --> tests/ui/skip/use_skipped_setter_2.rs:13:12 | 4 | / #[derive(Debug)] 5 | | pub struct Sparse { | |___- method `set_unused_1` not found for this struct ... 13 | sparse.set_unused_1(0); // ERROR! | ^^^^^^^^^^^^ method not found in `Sparse` ================================================ FILE: tests/ui/skip/use_skipped_setter_3.rs ================================================ use modular_bitfield::prelude::*; #[bitfield] #[derive(Debug)] pub struct Sparse { #[skip(getters)] #[skip(setters)] unused_1: B7, a: bool, } fn main() { let sparse = Sparse::from_bytes([0xFF; 1]); sparse.set_unused_1(0); // ERROR! } ================================================ FILE: tests/ui/skip/use_skipped_setter_3.stderr ================================================ error[E0599]: no method named `set_unused_1` found for struct `Sparse` in the current scope --> tests/ui/skip/use_skipped_setter_3.rs:13:12 | 4 | / #[derive(Debug)] 5 | | pub struct Sparse { | |___- method `set_unused_1` not found for this struct ... 13 | sparse.set_unused_1(0); // ERROR! | ^^^^^^^^^^^^ method not found in `Sparse` ================================================ FILE: tests/ui/unused_must_use.rs ================================================ #![deny(unused_must_use)] use modular_bitfield::prelude::*; #[bitfield] struct Foo { a: B8, } fn main() { Foo::new(); Foo::new().with_a(0); Foo::new().a(); Foo::from_bytes([0]); } ================================================ FILE: tests/ui/unused_must_use.stderr ================================================ error: unused return value of `Foo::new` that must be used --> tests/ui/unused_must_use.rs:11:5 | 11 | Foo::new(); | ^^^^^^^^^^ | note: the lint level is defined here --> tests/ui/unused_must_use.rs:1:9 | 1 | #![deny(unused_must_use)] | ^^^^^^^^^^^^^^^ help: use `let _ = ...` to ignore the resulting value | 11 | let _ = Foo::new(); | +++++++ error: unused return value of `Foo::with_a` that must be used --> tests/ui/unused_must_use.rs:12:5 | 12 | Foo::new().with_a(0); | ^^^^^^^^^^^^^^^^^^^^ | help: use `let _ = ...` to ignore the resulting value | 12 | let _ = Foo::new().with_a(0); | +++++++ error: unused return value of `Foo::a` that must be used --> tests/ui/unused_must_use.rs:13:5 | 13 | Foo::new().a(); | ^^^^^^^^^^^^^^ | help: use `let _ = ...` to ignore the resulting value | 13 | let _ = Foo::new().a(); | +++++++ error: unused return value of `Foo::from_bytes` that must be used --> tests/ui/unused_must_use.rs:14:5 | 14 | Foo::from_bytes([0]); | ^^^^^^^^^^^^^^^^^^^^ | help: use `let _ = ...` to ignore the resulting value | 14 | let _ = Foo::from_bytes([0]); | +++++++ ================================================ FILE: tests/ui.rs ================================================ //! Tests for emitted diagnostics #[cfg(all(test, not(miri)))] #[test] fn ui_trybuild() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/**/*.rs"); }